mirror of
https://codeberg.org/valpackett/tiddlypwa.git
synced 2025-12-06 02:30:48 -08:00
Server: break up into more files
This commit is contained in:
parent
9bc7573415
commit
17879bca00
4 changed files with 168 additions and 168 deletions
|
|
@ -2,7 +2,7 @@
|
|||
// deno test --unstable --allow-env --allow-read=.
|
||||
import 'https://deno.land/std@0.192.0/dotenv/load.ts';
|
||||
import { assertEquals } from 'https://deno.land/std@0.192.0/testing/asserts.ts';
|
||||
import * as app from './tiddlypwa-server.ts';
|
||||
import * as app from './app.ts';
|
||||
|
||||
const api = (data: any) =>
|
||||
app.handle(
|
||||
|
|
@ -7,173 +7,8 @@ import { parse as argparse } from 'https://deno.land/std@0.192.0/flags/mod.ts';
|
|||
import { serveListener } from 'https://deno.land/std@0.192.0/http/server.ts';
|
||||
import * as argon from 'https://deno.land/x/argon2ian@2.0.0/src/argon2.ts';
|
||||
import * as brotli from 'https://deno.land/x/brotli@0.1.7/mod.ts';
|
||||
import { SQLiteDatastore } from './server/sqlite.ts';
|
||||
|
||||
const html = String.raw; // For tools/editors
|
||||
|
||||
const homePage = html`
|
||||
<!doctype html>
|
||||
<html lang=en>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>TiddlyPWA Sync Server Control Panel</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
html { background: #252525; color: #fbfbfb; -webkit-text-size-adjust: none; text-size-adjust: none; accent-color: limegreen; }
|
||||
body { margin: 2rem auto; min-width: 300px; max-width: 99ch; line-height: 1.5; word-wrap: break-word; font-family: system-ui, sans-serif; }
|
||||
a { color: limegreen; }
|
||||
a:hover { color: lime; }
|
||||
h1 { font: 1.25rem monospace; text-align: center; color: limegreen; margin-bottom: 2rem; }
|
||||
h2 { font-size: 1.15rem; margin: 1rem 0; }
|
||||
fieldset { border: none; text-align: center; }
|
||||
thead { font-weight: bolder; background: rgba(0,240,0,.1); }
|
||||
footer { text-align: center; margin-top: 2rem; }
|
||||
table { border-collapse: collapse; margin: 1rem 0; }
|
||||
td { padding: 0.25rem 0.6rem; }
|
||||
tr:nth-child(even) { background: rgba(255,255,255,.08); }
|
||||
#wikirows td:first-of-type, #wikirows td:nth-of-type(2) { font-family: monospace; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>TiddlyPWA Sync Server Control Panel</h1>
|
||||
<noscript>Enable JavaScript!</noscript>
|
||||
<form id=login>
|
||||
<fieldset>
|
||||
<input type=password id=atoken>
|
||||
<button>Log in</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
<div id=loggedin hidden>
|
||||
<h2>Wikis on the server:</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Token</td>
|
||||
<td>Salt</td>
|
||||
<td>Content Size</td>
|
||||
<td>App Files Size</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id=wikirows>
|
||||
</tbody>
|
||||
</table>
|
||||
<button id=refresh>Refresh</button>
|
||||
<button id=create>Create new wiki</button>
|
||||
</div>
|
||||
<footer>
|
||||
<a href=https://tiddly.packett.cool/>TiddlyPWA</a> sync server ✦ software by <a href=https://val.packett.cool/>Val Packett</a>
|
||||
</footer>
|
||||
<script>
|
||||
const knownErrors = {
|
||||
EAUTH: 'Wrong token',
|
||||
};
|
||||
function formatBytes(bytes) {
|
||||
const sizes = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB'];
|
||||
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
|
||||
if (i >= sizes.length) return 'too much';
|
||||
return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
|
||||
}
|
||||
async function serverReq(data) {
|
||||
const resp = await fetch('tid.dly', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
tiddlypwa: 1,
|
||||
atoken: document.getElementById('atoken').value,
|
||||
...data,
|
||||
}),
|
||||
});
|
||||
if (!resp.ok) {
|
||||
alert(await resp.json().then(({ error }) => knownErrors[error] || error).catch((_e) =>
|
||||
'Server returned error ' + resp.status
|
||||
));
|
||||
return false;
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
async function refreshTokens() {
|
||||
const resp = await serverReq({ op: 'list' });
|
||||
if (!resp) return false;
|
||||
const { wikis } = await resp.json();
|
||||
const wikirows = document.getElementById('wikirows')
|
||||
wikirows.replaceChildren();
|
||||
for (const { token, salt, tidsize, appsize } of wikis) {
|
||||
const tr = document.createElement('tr');
|
||||
const tokenTd = document.createElement('td');
|
||||
tokenTd.innerText = token;
|
||||
tr.appendChild(tokenTd);
|
||||
const saltTd = document.createElement('td');
|
||||
saltTd.innerText = salt;
|
||||
tr.appendChild(saltTd);
|
||||
const tidsizeTd = document.createElement('td');
|
||||
tidsizeTd.innerText = tidsize > 0 ? formatBytes(tidsize) : '-';
|
||||
tr.appendChild(tidsizeTd);
|
||||
const appsizeTd = document.createElement('td');
|
||||
if (appsize > 0) {
|
||||
const appsizeA = document.createElement('a');
|
||||
appsizeA.href = '/' + token.slice(0, token.length / 2) + '/app.html';
|
||||
appsizeA.innerText = formatBytes(appsize);
|
||||
appsizeTd.appendChild(appsizeA);
|
||||
} else {
|
||||
appsizeTd.innerText = '-';
|
||||
}
|
||||
tr.appendChild(appsizeTd);
|
||||
const btnsTd = document.createElement('td');
|
||||
const btnReauth = document.createElement('button');
|
||||
btnReauth.innerText = 'Clear Auth';
|
||||
btnReauth.onclick = (e) => {
|
||||
if (!confirm('Do you really want to clear authentication checks for the wiki with token ' + token + '?')) return;
|
||||
serverReq({ op: 'reauth', token }).then(() => document.getElementById('refresh').click());
|
||||
};
|
||||
btnsTd.appendChild(btnReauth);
|
||||
const btnDel = document.createElement('button');
|
||||
btnDel.innerText = 'Delete';
|
||||
btnDel.onclick = (e) => {
|
||||
if (!confirm('Do you really want to delete the wiki with token ' + token + '?')) return;
|
||||
serverReq({ op: 'delete', token }).then(() => document.getElementById('refresh').click());
|
||||
};
|
||||
btnsTd.appendChild(btnDel);
|
||||
tr.appendChild(btnsTd);
|
||||
wikirows.appendChild(tr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
window.addEventListener('DOMContentLoaded', (_) => {
|
||||
const loginForm = document.getElementById('login');
|
||||
loginForm.onsubmit = (e) => {
|
||||
e.preventDefault();
|
||||
loginForm.querySelector('fieldset').disabled = true;
|
||||
refreshTokens().then((suc) => {
|
||||
document.getElementById('loggedin').hidden = !suc;
|
||||
loginForm.hidden = suc;
|
||||
loginForm.querySelector('fieldset').disabled = suc;
|
||||
}).catch((e) => {
|
||||
console.error(e);
|
||||
alert('Unexpected error!');
|
||||
loginForm.querySelector('fieldset').disabled = false;
|
||||
});
|
||||
};
|
||||
const refreshBtn = document.getElementById('refresh');
|
||||
const createBtn = document.getElementById('create');
|
||||
refreshBtn.onclick = () => {
|
||||
refreshBtn.disabled = createBtn.disabled = true;
|
||||
refreshTokens().then(() => {
|
||||
refreshBtn.disabled = createBtn.disabled = false;
|
||||
}).catch((e) => {
|
||||
console.error(e);
|
||||
alert('Unexpected error!');
|
||||
refreshBtn.disabled = createBtn.disabled = false;
|
||||
});
|
||||
};
|
||||
createBtn.onclick = () => {
|
||||
serverReq({ op: 'create' }).then(() => refreshBtn.click());
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
import { SQLiteDatastore } from './sqlite.ts';
|
||||
import { homePage } from './pages.ts';
|
||||
|
||||
const args = argparse(Deno.args);
|
||||
const denv = args.dotenv ? await dotenv.load() : {};
|
||||
165
server/pages.ts
Normal file
165
server/pages.ts
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
const html = String.raw; // For tools/editors
|
||||
|
||||
export const homePage = html`
|
||||
<!doctype html>
|
||||
<html lang=en>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>TiddlyPWA Sync Server Control Panel</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
html { background: #252525; color: #fbfbfb; -webkit-text-size-adjust: none; text-size-adjust: none; accent-color: limegreen; }
|
||||
body { margin: 2rem auto; min-width: 300px; max-width: 99ch; line-height: 1.5; word-wrap: break-word; font-family: system-ui, sans-serif; }
|
||||
a { color: limegreen; }
|
||||
a:hover { color: lime; }
|
||||
h1 { font: 1.25rem monospace; text-align: center; color: limegreen; margin-bottom: 2rem; }
|
||||
h2 { font-size: 1.15rem; margin: 1rem 0; }
|
||||
fieldset { border: none; text-align: center; }
|
||||
thead { font-weight: bolder; background: rgba(0,240,0,.1); }
|
||||
footer { text-align: center; margin-top: 2rem; }
|
||||
table { border-collapse: collapse; margin: 1rem 0; }
|
||||
td { padding: 0.25rem 0.6rem; }
|
||||
tr:nth-child(even) { background: rgba(255,255,255,.08); }
|
||||
#wikirows td:first-of-type, #wikirows td:nth-of-type(2) { font-family: monospace; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>TiddlyPWA Sync Server Control Panel</h1>
|
||||
<noscript>Enable JavaScript!</noscript>
|
||||
<form id=login>
|
||||
<fieldset>
|
||||
<input type=password id=atoken>
|
||||
<button>Log in</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
<div id=loggedin hidden>
|
||||
<h2>Wikis on the server:</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Token</td>
|
||||
<td>Salt</td>
|
||||
<td>Content Size</td>
|
||||
<td>App Files Size</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id=wikirows>
|
||||
</tbody>
|
||||
</table>
|
||||
<button id=refresh>Refresh</button>
|
||||
<button id=create>Create new wiki</button>
|
||||
</div>
|
||||
<footer>
|
||||
<a href=https://tiddly.packett.cool/>TiddlyPWA</a> sync server ✦ software by <a href=https://val.packett.cool/>Val Packett</a>
|
||||
</footer>
|
||||
<script>
|
||||
const knownErrors = {
|
||||
EAUTH: 'Wrong token',
|
||||
};
|
||||
function formatBytes(bytes) {
|
||||
const sizes = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB'];
|
||||
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
|
||||
if (i >= sizes.length) return 'too much';
|
||||
return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
|
||||
}
|
||||
async function serverReq(data) {
|
||||
const resp = await fetch('tid.dly', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
tiddlypwa: 1,
|
||||
atoken: document.getElementById('atoken').value,
|
||||
...data,
|
||||
}),
|
||||
});
|
||||
if (!resp.ok) {
|
||||
alert(await resp.json().then(({ error }) => knownErrors[error] || error).catch((_e) =>
|
||||
'Server returned error ' + resp.status
|
||||
));
|
||||
return false;
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
async function refreshTokens() {
|
||||
const resp = await serverReq({ op: 'list' });
|
||||
if (!resp) return false;
|
||||
const { wikis } = await resp.json();
|
||||
const wikirows = document.getElementById('wikirows')
|
||||
wikirows.replaceChildren();
|
||||
for (const { token, salt, tidsize, appsize } of wikis) {
|
||||
const tr = document.createElement('tr');
|
||||
const tokenTd = document.createElement('td');
|
||||
tokenTd.innerText = token;
|
||||
tr.appendChild(tokenTd);
|
||||
const saltTd = document.createElement('td');
|
||||
saltTd.innerText = salt;
|
||||
tr.appendChild(saltTd);
|
||||
const tidsizeTd = document.createElement('td');
|
||||
tidsizeTd.innerText = tidsize > 0 ? formatBytes(tidsize) : '-';
|
||||
tr.appendChild(tidsizeTd);
|
||||
const appsizeTd = document.createElement('td');
|
||||
if (appsize > 0) {
|
||||
const appsizeA = document.createElement('a');
|
||||
appsizeA.href = '/' + token.slice(0, token.length / 2) + '/app.html';
|
||||
appsizeA.innerText = formatBytes(appsize);
|
||||
appsizeTd.appendChild(appsizeA);
|
||||
} else {
|
||||
appsizeTd.innerText = '-';
|
||||
}
|
||||
tr.appendChild(appsizeTd);
|
||||
const btnsTd = document.createElement('td');
|
||||
const btnReauth = document.createElement('button');
|
||||
btnReauth.innerText = 'Clear Auth';
|
||||
btnReauth.onclick = (e) => {
|
||||
if (!confirm('Do you really want to clear authentication checks for the wiki with token ' + token + '?')) return;
|
||||
serverReq({ op: 'reauth', token }).then(() => document.getElementById('refresh').click());
|
||||
};
|
||||
btnsTd.appendChild(btnReauth);
|
||||
const btnDel = document.createElement('button');
|
||||
btnDel.innerText = 'Delete';
|
||||
btnDel.onclick = (e) => {
|
||||
if (!confirm('Do you really want to delete the wiki with token ' + token + '?')) return;
|
||||
serverReq({ op: 'delete', token }).then(() => document.getElementById('refresh').click());
|
||||
};
|
||||
btnsTd.appendChild(btnDel);
|
||||
tr.appendChild(btnsTd);
|
||||
wikirows.appendChild(tr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
window.addEventListener('DOMContentLoaded', (_) => {
|
||||
const loginForm = document.getElementById('login');
|
||||
loginForm.onsubmit = (e) => {
|
||||
e.preventDefault();
|
||||
loginForm.querySelector('fieldset').disabled = true;
|
||||
refreshTokens().then((suc) => {
|
||||
document.getElementById('loggedin').hidden = !suc;
|
||||
loginForm.hidden = suc;
|
||||
loginForm.querySelector('fieldset').disabled = suc;
|
||||
}).catch((e) => {
|
||||
console.error(e);
|
||||
alert('Unexpected error!');
|
||||
loginForm.querySelector('fieldset').disabled = false;
|
||||
});
|
||||
};
|
||||
const refreshBtn = document.getElementById('refresh');
|
||||
const createBtn = document.getElementById('create');
|
||||
refreshBtn.onclick = () => {
|
||||
refreshBtn.disabled = createBtn.disabled = true;
|
||||
refreshTokens().then(() => {
|
||||
refreshBtn.disabled = createBtn.disabled = false;
|
||||
}).catch((e) => {
|
||||
console.error(e);
|
||||
alert('Unexpected error!');
|
||||
refreshBtn.disabled = createBtn.disabled = false;
|
||||
});
|
||||
};
|
||||
createBtn.onclick = () => {
|
||||
serverReq({ op: 'create' }).then(() => refreshBtn.click());
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
Loading…
Add table
Add a link
Reference in a new issue