Server: break up into more files

This commit is contained in:
Val Packett 2023-07-12 01:49:52 -03:00
parent 9bc7573415
commit 17879bca00
4 changed files with 168 additions and 168 deletions

View file

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

View file

@ -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
View 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>
`;