From 77ea3018c1bfb3ce348274dd4ba05f95a2fd57b8 Mon Sep 17 00:00:00 2001 From: Cory Sanin Date: Sat, 1 Nov 2025 00:31:52 -0500 Subject: [PATCH] create admin form --- docker-compose.yml | 5 +++++ package-lock.json | 1 + package.json | 1 + scripts/form.js | 28 ++++++++++++++++++++++++++++ src/Web.ts | 34 ++++++++++++++++++++++++++++------ views/index.ejs | 3 +++ views/installform.ejs | 19 +++++++++++++++++++ 7 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 scripts/form.js create mode 100644 views/installform.ejs diff --git a/docker-compose.yml b/docker-compose.yml index 1c70c34..d835132 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,5 +7,10 @@ services: context: ./ dockerfile: Dockerfile restart: "no" + environment: + ADMINPARAM: password + ADMINPASS: letmein + volumes: + - ./config/:/usr/src/app/config/ ports: - 8080:8080 diff --git a/package-lock.json b/package-lock.json index 3e9317a..02b6748 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.5", "license": "MIT", "dependencies": { + "body-parser": "^2.2.0", "ejs": "3.1.10", "express": "5.1.0", "ics": "^3.8.1", diff --git a/package.json b/package.json index 9bc3d12..6125dec 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "views" ], "dependencies": { + "body-parser": "^2.2.0", "ejs": "3.1.10", "express": "5.1.0", "ics": "^3.8.1", diff --git a/scripts/form.js b/scripts/form.js new file mode 100644 index 0000000..62fd637 --- /dev/null +++ b/scripts/form.js @@ -0,0 +1,28 @@ +document.addEventListener('DOMContentLoaded', function () { + const submit = document.getElementById('submitbtn'); + submit.addEventListener('click', async () => { + submit.disabled = true; + const action = submit.parentElement.action; + try { + const resp = await fetch(action, { + method: 'POST', + body: JSON.stringify({ + os: document.getElementById('os-select').value, + form: document.getElementById('form-select').value + }), + headers: { + "Content-Type": "application/json", + } + }); + if (!resp.ok) { + throw new Error(`Response status: ${resp.status}`); + } + location.reload(); + return false; + } + catch (error) { + console.error(error.message); + alert(error.message); + } + }); +}); diff --git a/src/Web.ts b/src/Web.ts index a5bb4c8..34ea718 100644 --- a/src/Web.ts +++ b/src/Web.ts @@ -41,21 +41,21 @@ class Web { initialize = async () => { // const options = this.options; const app: Express = this.app = express(); + const installPath = process.env['installstat'] || process.env['INSTALLSTAT'] || path.join(process.cwd(), 'config', 'installs.json'); try { - this.installs = JSON.parse(await fsp.readFile(process.env['config'] || process.env['CONFIG'] || path.join(process.cwd(), 'config', 'installs.json'), 'utf-8')); + this.installs = JSON.parse(await fsp.readFile(installPath, 'utf-8')); } - catch(ex) { + catch (ex) { console.error(ex); this.installs = []; } - + app.set('trust proxy', 1); app.set('view engine', 'ejs'); app.set('view options', { outputFunctionName: 'echo' }); app.use('/assets', express.static('assets', { maxAge: '30 days' })); app.use(bodyParser.json()); - app.use(bodyParser.urlencoded({ extended: true })); app.use((_req, res, next) => { crypto.randomBytes(32, (err, randomBytes) => { if (err) { @@ -72,11 +72,15 @@ class Web { res.send('Healthy'); }); + const adminParam = process.env['ADMINPARAM']; + const adminPass = process.env['ADMINPASS']; + app.get('/', (req, res) => { + const adminMode = adminParam && adminPass && adminParam in req.query && req.query[adminParam] === adminPass; if (req.query?.['utm_medium']) { console.log(`${req.query['utm_medium']} | ${req.headers?.['user-agent']}`); } - else { + else if(!adminMode){ console.log(req.headers?.['user-agent']); } res.render('index', { @@ -87,10 +91,28 @@ class Web { }, date: DATE, installs: this.installs, - time: TIME + time: TIME, + adminMode }); }); + app.post('/', async (req, res) => { + const adminMode = adminParam && adminPass && adminParam in req.query && req.query[adminParam] === adminPass; + if (!adminMode) { + res.redirect('/'); + return; + } + const submit = req.body; + if (!('os' in submit && 'form' in submit)) { + res.status(400).send('bad request'); + return; + } + this.installs.push(submit); + console.log(`updating ${installPath}`); + await fsp.writeFile(installPath, JSON.stringify(this.installs, null, 2)); + res.send('ok'); + }); + app.get('/os', (_, res) => { res.render('os', { page: { diff --git a/views/index.ejs b/views/index.ejs index 11150ac..a956a16 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -8,6 +8,9 @@ <%- include("navigation", locals) %>
+ <% if (adminMode) { %> + <%- include("installform", locals) %> + <% } %> <% if (installs && installs.length) { %>

We have saved <%= installs.length %> computer<% if (installs.length > 1) { %>s<% } %>!

diff --git a/views/installform.ejs b/views/installform.ejs new file mode 100644 index 0000000..12c96ca --- /dev/null +++ b/views/installform.ejs @@ -0,0 +1,19 @@ +
+ +
+
+ +
+
+ +
+ \ No newline at end of file