feat: web frontend with Alpine.js
This commit is contained in:
Vendored
+5
-1
File diff suppressed because one or more lines are too long
+38
-1
@@ -1 +1,38 @@
|
|||||||
// placeholder
|
function app() {
|
||||||
|
return {
|
||||||
|
inboxFiles: [],
|
||||||
|
outboxFiles: [],
|
||||||
|
async loadFiles() {
|
||||||
|
const [inbox, outbox] = await Promise.all([
|
||||||
|
fetch('/api/files?dir=inbox').then(r => r.json()),
|
||||||
|
fetch('/api/files?dir=outbox').then(r => r.json())
|
||||||
|
]);
|
||||||
|
this.inboxFiles = inbox.files || [];
|
||||||
|
this.outboxFiles = outbox.files || [];
|
||||||
|
},
|
||||||
|
async upload(event, dir) {
|
||||||
|
const file = event.target.files[0];
|
||||||
|
const form = new FormData();
|
||||||
|
form.append('file', file);
|
||||||
|
form.append('dir', dir);
|
||||||
|
await fetch('/api/upload', {method: 'POST', body: form});
|
||||||
|
this.loadFiles();
|
||||||
|
},
|
||||||
|
download(id) {
|
||||||
|
window.location.href = '/api/files/' + id + '/download';
|
||||||
|
},
|
||||||
|
async deleteFile(id) {
|
||||||
|
await fetch('/api/files/' + id, {method: 'DELETE'});
|
||||||
|
this.loadFiles();
|
||||||
|
},
|
||||||
|
async share(id) {
|
||||||
|
const res = await fetch('/api/files/' + id + '/share', {method: 'POST'});
|
||||||
|
const data = await res.json();
|
||||||
|
alert('Share URL: ' + data.url);
|
||||||
|
},
|
||||||
|
async logout() {
|
||||||
|
await fetch('/api/logout', {method: 'POST'});
|
||||||
|
window.location.href = '/login';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
+41
-1
@@ -1,2 +1,42 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html><head><title>yDropbox</title></head><body><h1>yDropbox</h1></body></html>
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>yDropbox</title>
|
||||||
|
<link rel="stylesheet" href="/style.css">
|
||||||
|
<script defer src="/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div x-data="app()" x-init="loadFiles()">
|
||||||
|
<header>
|
||||||
|
<h1>yDropbox</h1>
|
||||||
|
<button @click="logout()">Logout</button>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Inbox</h2>
|
||||||
|
<input type="file" @change="upload($event, 'inbox')">
|
||||||
|
<template x-for="file in inboxFiles" :key="file.id">
|
||||||
|
<div class="file-row">
|
||||||
|
<span x-text="file.original_name"></span>
|
||||||
|
<button @click="download(file.id)">Download</button>
|
||||||
|
<button @click="deleteFile(file.id)">Delete</button>
|
||||||
|
<button @click="share(file.id)">Share</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Outbox</h2>
|
||||||
|
<template x-for="file in outboxFiles" :key="file.id">
|
||||||
|
<div class="file-row">
|
||||||
|
<span x-text="file.original_name"></span>
|
||||||
|
<button @click="download(file.id)">Download</button>
|
||||||
|
<button @click="deleteFile(file.id)">Delete</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<script src="/app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
+29
-1
@@ -1,2 +1,30 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html><head><title>Login</title></head><body><h1>Login</h1></body></html>
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>yDropbox Login</title>
|
||||||
|
<link rel="stylesheet" href="/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>yDropbox</h1>
|
||||||
|
<form id="loginForm">
|
||||||
|
<input type="password" id="token" placeholder="Access Token" required>
|
||||||
|
<button type="submit">Login</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.getElementById('loginForm').addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const token = document.getElementById('token').value;
|
||||||
|
const res = await fetch('/api/login', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({token})
|
||||||
|
});
|
||||||
|
if (res.ok) window.location.href = '/';
|
||||||
|
else alert('Invalid token');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
+8
-1
@@ -1 +1,8 @@
|
|||||||
/* placeholder */
|
body { font-family: sans-serif; max-width: 800px; margin: 40px auto; padding: 0 20px; }
|
||||||
|
header { display: flex; justify-content: space-between; align-items: center; }
|
||||||
|
section { margin: 20px 0; }
|
||||||
|
.file-row { display: flex; gap: 10px; align-items: center; margin: 10px 0; }
|
||||||
|
button { cursor: pointer; }
|
||||||
|
.container { max-width: 400px; margin: 100px auto; }
|
||||||
|
form { display: flex; flex-direction: column; gap: 10px; }
|
||||||
|
input[type="password"] { padding: 8px; font-size: 16px; }
|
||||||
|
|||||||
Reference in New Issue
Block a user