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>
|
||||
<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>
|
||||
<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