initial commit
This commit is contained in:
118
README.md
118
README.md
@@ -1 +1,117 @@
|
|||||||
# uploader
|
# File Uploader
|
||||||
|
|
||||||
|
A simple file uploader application that allows authenticated users to upload, list, and delete files.
|
||||||
|
The application uses PHP and a Python authentication service running on Apache2.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Apache2, configured, up and running
|
||||||
|
- PHP 8.1 or higher
|
||||||
|
- Python 3
|
||||||
|
- Required PHP extensions: `php-json`, `php-curl`
|
||||||
|
- PAM authentication for Python
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
For simplicity, I'll use my current Ubuntu instance user name, you should replace by yours.
|
||||||
|
|
||||||
|
### Clone or download this repository
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/yourusername/uploader.git
|
||||||
|
cd uploader
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install Python prerequisites
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install flask pam
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create Python authentication service
|
||||||
|
(note: **port 7000** is used; if you need to change port number, make needful changes in the **app.py** and php scripts - search for '7000')
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo nano /etc/systemd/system/flaskapp.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the following content to this file (but replace **User**, **WorkingDirectory** and **ExecStart**):
|
||||||
|
|
||||||
|
```
|
||||||
|
[Unit]
|
||||||
|
Description=Flask Application
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=ubuntu
|
||||||
|
WorkingDirectory=/home/ubuntu/uploader
|
||||||
|
ExecStart=/usr/bin/python3 /home/ubuntu/uploader/app.py
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
### Enable and start the service:
|
||||||
|
```
|
||||||
|
sudo systemctl enable flaskapp
|
||||||
|
sudo systemctl start flaskapp
|
||||||
|
sudo systemctl status flaskapp.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configure PHP
|
||||||
|
|
||||||
|
Ensure the following PHP settings are in your **php.ini**:
|
||||||
|
```
|
||||||
|
log_errors = On
|
||||||
|
error_log = /var/log/php_errors.log
|
||||||
|
```
|
||||||
|
|
||||||
|
Also check for max upload file/post size limits in **php.ini** (adjust to your needs, like 10G):
|
||||||
|
```
|
||||||
|
upload_max_filesize = 10M
|
||||||
|
post_max_size = 10M
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create the upload directory and set the necessary permissions:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo mkdir -p /var/www/html/upload
|
||||||
|
sudo chown -R www-data:www-data /var/www/html/upload
|
||||||
|
sudo chmod -R 755 /var/www/html/upload
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create a limited user for uploading files
|
||||||
|
(please note, I don't recommend you to use your actual ssh-enabled user account):
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo useradd -M -d /var/www/html/upload -s /usr/sbin/nologin uploader
|
||||||
|
sudo passwd uploader
|
||||||
|
sudo chown -R uploader:www-data /var/www/html/upload
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create application directory at webroot (or configure app/site):
|
||||||
|
(note: with my Apache configuration, I just need to create a subdirectory)
|
||||||
|
```
|
||||||
|
sudo mkdir -p /var/www/html/uploader
|
||||||
|
```
|
||||||
|
|
||||||
|
### Copy all files to the folder created above:
|
||||||
|
```
|
||||||
|
sudo cp -r * /var/www/html/uploader
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restart Apache to apply changes:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo systemctl restart apache2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
Open your web browser and navigate to https://yourserveraddress/uploader
|
||||||
|
|
||||||
|
Enter your username and password to authenticate.
|
||||||
|
|
||||||
|
Choose a file to upload and click the "Upload" button.
|
||||||
|
|
||||||
|
The uploaded files will be listed on the page, and you can delete them using the "Delete" button.
|
||||||
|
|||||||
25
app.py
Normal file
25
app.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
from flask import Flask, request, jsonify
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
def authenticate(username, password):
|
||||||
|
command = f"echo {password} | su -c 'whoami' {username}"
|
||||||
|
try:
|
||||||
|
result = subprocess.run(command, shell=True, capture_output=True, text=True, check=True)
|
||||||
|
return result.stdout.strip() == username
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@app.route('/auth', methods=['POST'])
|
||||||
|
def auth():
|
||||||
|
data = request.json
|
||||||
|
username = data.get('username')
|
||||||
|
password = data.get('password')
|
||||||
|
if authenticate(username, password):
|
||||||
|
return jsonify({"authenticated": True}), 200
|
||||||
|
else:
|
||||||
|
return jsonify({"authenticated": False}), 401
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0', port=7000)
|
||||||
42
auth.js
Normal file
42
auth.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
let authCredentials = { username: '', password: '' };
|
||||||
|
|
||||||
|
async function authenticateUser(username, password) {
|
||||||
|
const response = await fetch('auth.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ username, password }),
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
return result.authenticated;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAuthentication() {
|
||||||
|
if (authCredentials.username && authCredentials.password) {
|
||||||
|
document.getElementById('loginForm').style.display = 'none';
|
||||||
|
document.getElementById('uploadForm').style.display = 'block';
|
||||||
|
loadFileList();
|
||||||
|
} else {
|
||||||
|
document.getElementById('loginForm').style.display = 'block';
|
||||||
|
document.getElementById('uploadForm').style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('authForm').addEventListener('submit', async function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const username = document.getElementById('loginUsername').value;
|
||||||
|
const password = document.getElementById('loginPassword').value;
|
||||||
|
|
||||||
|
const isAuthenticated = await authenticateUser(username, password);
|
||||||
|
|
||||||
|
if (isAuthenticated) {
|
||||||
|
authCredentials = { username, password };
|
||||||
|
checkAuthentication();
|
||||||
|
} else {
|
||||||
|
document.getElementById('statusMessage').innerHTML = 'Incorrect username or password!';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', checkAuthentication);
|
||||||
35
auth.php
Normal file
35
auth.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
if (session_status() == PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$username = $data['username'];
|
||||||
|
$password = $data['password'];
|
||||||
|
|
||||||
|
function authenticate($username, $password) {
|
||||||
|
$url = 'http://localhost:7000/auth';
|
||||||
|
$data = json_encode(array("username" => $username, "password" => $password));
|
||||||
|
$options = array(
|
||||||
|
'http' => array(
|
||||||
|
'header' => "Content-Type: application/json\r\n",
|
||||||
|
'method' => 'POST',
|
||||||
|
'content' => $data,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$context = stream_context_create($options);
|
||||||
|
$result = file_get_contents($url, false, $context);
|
||||||
|
$response = json_decode($result, true);
|
||||||
|
return $response['authenticated'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$isAuthenticated = authenticate($username, $password);
|
||||||
|
|
||||||
|
if ($isAuthenticated) {
|
||||||
|
$_SESSION['authenticated'] = true;
|
||||||
|
echo json_encode(['authenticated' => true]);
|
||||||
|
} else {
|
||||||
|
$_SESSION['authenticated'] = false;
|
||||||
|
echo json_encode(['authenticated' => false]);
|
||||||
|
}
|
||||||
|
?>
|
||||||
8
config.php
Normal file
8
config.php
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
// config.php
|
||||||
|
define('UPLOAD_DIR', '/var/www/html/upload/');
|
||||||
|
define('BASE_URL', 'https://yoursite.com/upload/');
|
||||||
|
define('TIMEZONE', 'America/New_York');
|
||||||
|
define('DATE_TIME_FORMAT', 'm/d/y H:i');
|
||||||
|
date_default_timezone_set(TIMEZONE);
|
||||||
|
?>
|
||||||
70
displayFileList.js
Normal file
70
displayFileList.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
let sortDirection = {};
|
||||||
|
let sortFunctions = {
|
||||||
|
'File Name': (a, b) => a.name.localeCompare(b.name),
|
||||||
|
'File Size': (a, b) => a.sizeBytes - b.sizeBytes,
|
||||||
|
'File Time': (a, b) => new Date(a.modified) - new Date(b.modified),
|
||||||
|
'Upload Time': (a, b) => new Date(a.uploaded) - new Date(b.uploaded)
|
||||||
|
};
|
||||||
|
|
||||||
|
function displayFileList(fileList) {
|
||||||
|
const fileListContainer = document.getElementById('fileList');
|
||||||
|
fileListContainer.innerHTML = '';
|
||||||
|
|
||||||
|
const table = document.createElement('table');
|
||||||
|
const headerRow = table.insertRow();
|
||||||
|
|
||||||
|
['File Name', 'File Size', 'File Time', 'Upload Time', '', ''].forEach(headerText => {
|
||||||
|
const th = document.createElement('th');
|
||||||
|
if (headerText && headerText !== '') {
|
||||||
|
const button = document.createElement('button');
|
||||||
|
button.textContent = headerText;
|
||||||
|
button.onclick = () => sortTable(headerText, fileList);
|
||||||
|
button.style.background = 'none';
|
||||||
|
button.style.border = 'none';
|
||||||
|
button.style.cursor = 'pointer';
|
||||||
|
th.appendChild(button);
|
||||||
|
} else {
|
||||||
|
th.textContent = headerText;
|
||||||
|
}
|
||||||
|
headerRow.appendChild(th);
|
||||||
|
});
|
||||||
|
|
||||||
|
fileList.forEach(file => {
|
||||||
|
const row = table.insertRow();
|
||||||
|
row.insertCell().textContent = file.name;
|
||||||
|
row.insertCell().textContent = file.size;
|
||||||
|
row.insertCell().textContent = file.modified;
|
||||||
|
row.insertCell().textContent = file.uploaded;
|
||||||
|
|
||||||
|
const linkCell = row.insertCell();
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = file.url;
|
||||||
|
link.textContent = 'Download';
|
||||||
|
linkCell.appendChild(link);
|
||||||
|
|
||||||
|
const deleteCell = row.insertCell();
|
||||||
|
const deleteButton = document.createElement('button');
|
||||||
|
deleteButton.textContent = 'Delete';
|
||||||
|
deleteButton.className = 'btn btn-delete';
|
||||||
|
deleteButton.onclick = () => deleteFile(file.name);
|
||||||
|
deleteCell.appendChild(deleteButton);
|
||||||
|
});
|
||||||
|
|
||||||
|
fileListContainer.appendChild(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortTable(column, fileList) {
|
||||||
|
if (!sortDirection[column]) {
|
||||||
|
sortDirection[column] = 'asc';
|
||||||
|
} else {
|
||||||
|
sortDirection[column] = sortDirection[column] === 'asc' ? 'desc' : 'asc';
|
||||||
|
}
|
||||||
|
|
||||||
|
fileList.sort(sortFunctions[column]);
|
||||||
|
|
||||||
|
if (sortDirection[column] === 'desc') {
|
||||||
|
fileList.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
displayFileList(fileList);
|
||||||
|
}
|
||||||
69
file_list.php
Normal file
69
file_list.php
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'config.php';
|
||||||
|
|
||||||
|
function authenticate($username, $password) {
|
||||||
|
$url = 'http://localhost:7000/auth';
|
||||||
|
$data = json_encode(array("username" => $username, "password" => $password));
|
||||||
|
$options = array(
|
||||||
|
'http' => array(
|
||||||
|
'header' => "Content-Type: application/json\r\n",
|
||||||
|
'method' => 'POST',
|
||||||
|
'content' => $data,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$context = stream_context_create($options);
|
||||||
|
$result = file_get_contents($url, false, $context);
|
||||||
|
$response = json_decode($result, true);
|
||||||
|
return $response['authenticated'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$username = $data['username'];
|
||||||
|
$password = $data['password'];
|
||||||
|
$deleteFile = isset($data['delete']) ? basename($data['delete']) : null;
|
||||||
|
|
||||||
|
if (!authenticate($username, $password)) {
|
||||||
|
http_response_code(401);
|
||||||
|
echo json_encode(['error' => 'Unauthorized']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($deleteFile) {
|
||||||
|
$filePath = UPLOAD_DIR . $deleteFile;
|
||||||
|
if (file_exists($filePath)) {
|
||||||
|
unlink($filePath);
|
||||||
|
echo json_encode(['success' => 'File deleted']);
|
||||||
|
} else {
|
||||||
|
http_response_code(404);
|
||||||
|
echo json_encode(['error' => 'File not found']);
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$files = array_diff(scandir(UPLOAD_DIR), array('.', '..'));
|
||||||
|
|
||||||
|
$fileList = [];
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$filePath = UPLOAD_DIR . $file;
|
||||||
|
$fileSizeBytes = filesize($filePath);
|
||||||
|
$fileDate = date(DATE_TIME_FORMAT, filemtime($filePath));
|
||||||
|
$uploadDate = date(DATE_TIME_FORMAT, filectime($filePath));
|
||||||
|
$fileSizeFormatted = ($fileSizeBytes >= 1048576) ? sprintf("%.1f MB (%s bytes)", $fileSizeBytes / 1048576, number_format($fileSizeBytes)) : sprintf("%s bytes", number_format($fileSizeBytes));
|
||||||
|
$fileUrl = BASE_URL . urlencode($file);
|
||||||
|
$fileList[] = [
|
||||||
|
'name' => htmlspecialchars($file, ENT_QUOTES, 'UTF-8'),
|
||||||
|
'size' => $fileSizeFormatted,
|
||||||
|
'sizeBytes' => $fileSizeBytes,
|
||||||
|
'modified' => $fileDate,
|
||||||
|
'uploaded' => $uploadDate,
|
||||||
|
'url' => htmlspecialchars($fileUrl, ENT_QUOTES, 'UTF-8')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
usort($fileList, function($a, $b) {
|
||||||
|
return strtotime($b['uploaded']) - strtotime($a['uploaded']);
|
||||||
|
});
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode($fileList);
|
||||||
|
?>
|
||||||
158
index.html
Normal file
158
index.html
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>File Upload</title>
|
||||||
|
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.btn-upload {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.btn-upload:disabled {
|
||||||
|
background-color: gray;
|
||||||
|
}
|
||||||
|
.btn-choose-file {
|
||||||
|
background-color: #6c757d;
|
||||||
|
color: white;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.file-list {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.progress {
|
||||||
|
margin-top: 10px;
|
||||||
|
height: 20px; /* Narrow progress bar */
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.progress-bar {
|
||||||
|
height: 100%; /* Fill the entire height */
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
table, th, td {
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 10px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.5em; /* Smaller font size */
|
||||||
|
}
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 5px; /* Reduce vertical space between form groups */
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
font-size: 0.9em; /* Smaller font size */
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
font-size: 0.9em; /* Smaller font size for buttons */
|
||||||
|
}
|
||||||
|
.align-items-center {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.table th button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
#loginForm, #uploadForm {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.form-row {
|
||||||
|
align-items: flex-start; /* Align items by top */
|
||||||
|
}
|
||||||
|
.full-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.btn-delete {
|
||||||
|
background-color: #dc3545;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 5px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.btn-delete:hover {
|
||||||
|
background-color: #c82333;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row" id="loginForm">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<form id="authForm" method="post">
|
||||||
|
<div class="form-row align-items-center">
|
||||||
|
<div class="form-group col-4 col-md-4">
|
||||||
|
<label for="loginUsername">User:</label>
|
||||||
|
<input type="text" class="form-control" id="loginUsername" name="username" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group col-4 col-md-4">
|
||||||
|
<label for="loginPassword">Password:</label>
|
||||||
|
<input type="password" class="form-control" id="loginPassword" name="password" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group col-4 col-md-4 align-items-center" style="padding-top: 26px;">
|
||||||
|
<button type="submit" class="btn btn-upload btn-block" style="transform: translateY(2px);">Login</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row" id="uploadForm">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<form id="uploadFileForm" method="post" enctype="multipart/form-data">
|
||||||
|
<div class="form-row align-items-center">
|
||||||
|
<div class="form-group col-4 col-md-4" style="transform: translateY(4px);">
|
||||||
|
<label class="btn btn-choose-file btn-block">
|
||||||
|
Choose File <input type="file" id="file" name="file" required hidden>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group col-4 col-md-4">
|
||||||
|
<input type="submit" value="Upload" id="uploadBtn" class="btn btn-upload btn-block" disabled>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-12 full-width">
|
||||||
|
<span id="fileName"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-12 full-width">
|
||||||
|
<div id="statusMessage" class="ml-0"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row" id="progressRow" style="display: none;">
|
||||||
|
<div class="form-group col-12">
|
||||||
|
<div class="progress">
|
||||||
|
<div class="progress-bar" role="progressbar" style="width: 0%;" id="progressBar"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="file-list">
|
||||||
|
<div id="fileList" class="full-width"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
|
||||||
|
<script src="auth.js"></script>
|
||||||
|
<script src="upload.js"></script>
|
||||||
|
<script src="displayFileList.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
120
upload.js
Normal file
120
upload.js
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
document.getElementById('file').addEventListener('change', function() {
|
||||||
|
const fileInput = document.getElementById('file');
|
||||||
|
const uploadBtn = document.getElementById('uploadBtn');
|
||||||
|
if (fileInput.files.length > 0) {
|
||||||
|
document.getElementById('fileName').innerHTML = `<b>${fileInput.files[0].name}</b>`;
|
||||||
|
uploadBtn.disabled = false;
|
||||||
|
} else {
|
||||||
|
document.getElementById('fileName').innerHTML = '';
|
||||||
|
uploadBtn.disabled = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('uploadFileForm').addEventListener('submit', async function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const { username, password } = authCredentials;
|
||||||
|
const fileInput = document.getElementById('file');
|
||||||
|
const statusMessage = document.getElementById('statusMessage');
|
||||||
|
const progressBar = document.getElementById('progressBar');
|
||||||
|
const progressRow = document.getElementById('progressRow');
|
||||||
|
|
||||||
|
if (fileInput.files.length > 0) {
|
||||||
|
const file = fileInput.files[0];
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
formData.append('username', username);
|
||||||
|
formData.append('password', password);
|
||||||
|
const fileDateTime = file.lastModified;
|
||||||
|
formData.append('fileDateTime', fileDateTime);
|
||||||
|
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('POST', 'upload.php', true);
|
||||||
|
|
||||||
|
const uploadBtn = document.getElementById('uploadBtn');
|
||||||
|
const fileBtn = document.getElementById('file');
|
||||||
|
uploadBtn.disabled = true;
|
||||||
|
fileBtn.disabled = true;
|
||||||
|
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
xhr.upload.onprogress = function(e) {
|
||||||
|
if (e.lengthComputable) {
|
||||||
|
const percentComplete = (e.loaded / e.total) * 100;
|
||||||
|
progressBar.style.width = percentComplete + '%';
|
||||||
|
progressBar.innerText = Math.round(percentComplete) + '%';
|
||||||
|
progressRow.style.display = 'block'; // Show the progress bar
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onload = function() {
|
||||||
|
const endTime = Date.now();
|
||||||
|
const uploadTime = (endTime - startTime) / 1000;
|
||||||
|
const fileSize = fileInput.files[0].size;
|
||||||
|
const uploadRate = (fileSize / 1024 / uploadTime).toFixed(2);
|
||||||
|
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
statusMessage.innerHTML = `File <b>${fileInput.files[0].name}</b> successfully uploaded. Upload time: <b>${uploadTime.toFixed(2)}</b> seconds. Upload rate: <b>${uploadRate}</b> KBps.`;
|
||||||
|
loadFileList();
|
||||||
|
} else {
|
||||||
|
statusMessage.innerHTML = 'Upload failed!';
|
||||||
|
}
|
||||||
|
progressBar.style.width = '0%';
|
||||||
|
progressBar.innerText = '';
|
||||||
|
progressRow.style.display = 'none'; // Hide the progress bar
|
||||||
|
document.getElementById('fileName').innerHTML = '';
|
||||||
|
uploadBtn.disabled = true;
|
||||||
|
fileBtn.disabled = false;
|
||||||
|
fileBtn.value = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
statusMessage.innerHTML = `Uploading file ${fileInput.files[0].name}...`;
|
||||||
|
xhr.send(formData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function loadFileList() {
|
||||||
|
try {
|
||||||
|
const { username, password } = authCredentials;
|
||||||
|
console.log('Loading file list with credentials:', { username, password }); // Debugging
|
||||||
|
const response = await fetch('file_list.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ username, password }),
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
const fileList = await response.json();
|
||||||
|
console.log('File list loaded:', fileList); // Debugging: Log the file list to the console
|
||||||
|
displayFileList(fileList);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading file list:', error); // Debugging: Log any errors to the console
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteFile(fileName) {
|
||||||
|
const { username, password } = authCredentials;
|
||||||
|
try {
|
||||||
|
const response = await fetch('file_list.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ username, password, delete: fileName }),
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
const result = await response.json();
|
||||||
|
if (result.success) {
|
||||||
|
loadFileList();
|
||||||
|
} else {
|
||||||
|
console.error('Error deleting file:', result.error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting file:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
37
upload.php
Normal file
37
upload.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'config.php';
|
||||||
|
require_once 'auth.php';
|
||||||
|
|
||||||
|
// Check if the form was submitted
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
// Get the username and password
|
||||||
|
$username = $_POST['username'];
|
||||||
|
$password = $_POST['password'];
|
||||||
|
$fileDateTime = isset($_POST['fileDateTime']) ? (int)$_POST['fileDateTime'] / 1000 : time();
|
||||||
|
|
||||||
|
// Validate the credentials using the Flask backend
|
||||||
|
if (authenticate($username, $password)) {
|
||||||
|
// Check if a file was uploaded
|
||||||
|
if (isset($_FILES['file']) && $_FILES['file']['error'] == 0) {
|
||||||
|
$uploadFile = UPLOAD_DIR . basename($_FILES['file']['name']);
|
||||||
|
$tmpFile = $_FILES['file']['tmp_name'];
|
||||||
|
|
||||||
|
// Move the uploaded file to the specified directory
|
||||||
|
if (move_uploaded_file($tmpFile, $uploadFile)) {
|
||||||
|
// Preserve the original file modification time
|
||||||
|
touch($uploadFile, $fileDateTime);
|
||||||
|
echo "File is valid, and was successfully uploaded.\n";
|
||||||
|
} else {
|
||||||
|
echo "File upload failed! ";
|
||||||
|
print_r(error_get_last());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "No file uploaded or file upload error!\n";
|
||||||
|
echo "Error code: " . $_FILES['file']['error'];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "Invalid username or password!\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "Invalid request method!\n";
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user