From d803a58ab802b5816945b3913ce1cf1d00625d22 Mon Sep 17 00:00:00 2001 From: Cory Sanin Date: Wed, 26 Nov 2025 18:20:44 -0500 Subject: [PATCH] Add flag to persist builds --- package-lock.json | 78 +++++++++++++++++++------------------------ package.json | 14 ++++---- scripts/copy.js | 7 ---- scripts/form.js | 23 +++++++++++++ src/DB.ts | 18 +++++++++- src/Web.ts | 11 ++++++ styles/01-styles.scss | 4 +++ views/build.ejs | 8 ++++- views/head.ejs | 2 +- 9 files changed, 104 insertions(+), 61 deletions(-) delete mode 100644 scripts/copy.js create mode 100644 scripts/form.js diff --git a/package-lock.json b/package-lock.json index db91563..f12d272 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,20 @@ { "name": "archery", - "version": "0.2.4", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "archery", - "version": "0.2.4", + "version": "0.3.0", "license": "MIT", "dependencies": { - "body-parser": "^2.2.0", + "body-parser": "^2.2.1", "ejs": "3.1.10", "express": "^5.1.0", "express-session": "1.18.2", "express-ws": "^5.0.2", - "ky": "1.10.0", + "ky": "1.14.0", "passport": "0.7.0", "passport-openidconnect": "0.1.2", "pg": "^8.16.3", @@ -23,9 +23,9 @@ "sqids": "0.3.0" }, "devDependencies": { - "@types/express": "^5.0.3", + "@types/express": "^5.0.5", "@types/express-session": "^1.18.2", - "@types/express-ws": "3.0.5", + "@types/express-ws": "3.0.6", "@types/node": "^24.10.1", "@types/passport": "1.0.17", "@types/passport-openidconnect": "0.1.3", @@ -376,15 +376,15 @@ } }, "node_modules/@types/express": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", - "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.5.tgz", + "integrity": "sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==", "dev": true, "license": "MIT", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "*" + "@types/serve-static": "^1" } }, "node_modules/@types/express-serve-static-core": { @@ -411,9 +411,9 @@ } }, "node_modules/@types/express-ws": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/express-ws/-/express-ws-3.0.5.tgz", - "integrity": "sha512-lbWMjoHrm/v85j81UCmb/GNZFO3genxRYBW1Ob7rjRI+zxUBR+4tcFuOpKKsYQ1LYTYiy3356epLeYi/5zxUwA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/express-ws/-/express-ws-3.0.6.tgz", + "integrity": "sha512-6ZDt+tMEQgM4RC1sMX1fIO7kHQkfUDlWfxoPddXUeeDjmc+Yt/fCzqXfp8rFahNr5eIxdomrWphLEWDkB2q3UQ==", "dev": true, "license": "MIT", "dependencies": { @@ -589,23 +589,27 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", + "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", - "debug": "^4.4.0", + "debug": "^4.4.3", "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", + "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" + "raw-body": "^3.0.1", + "type-is": "^2.0.1" }, "engines": { "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/brace-expansion": { @@ -934,7 +938,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", @@ -1239,15 +1242,19 @@ } }, "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/immutable": { @@ -1342,9 +1349,9 @@ } }, "node_modules/ky": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/ky/-/ky-1.10.0.tgz", - "integrity": "sha512-YRPCzHEWZffbfvmRrfwa+5nwBHwZuYiTrfDX0wuhGBPV0pA/zCqcOq93MDssON/baIkpYbvehIX5aLpMxrRhaA==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/ky/-/ky-1.14.0.tgz", + "integrity": "sha512-Rczb6FMM6JT0lvrOlP5WUOCB7s9XKxzwgErzhKlKde1bEV90FXplV1o87fpt4PU/asJFiqjYJxAJyzJhcrxOsQ==", "license": "MIT", "engines": { "node": ">=18" @@ -1608,7 +1615,6 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", "license": "MIT", - "peer": true, "dependencies": { "pg-connection-string": "^2.9.1", "pg-pool": "^3.10.1", @@ -1819,22 +1825,6 @@ "node": ">= 0.10" } }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", diff --git a/package.json b/package.json index 366d4cd..8d35ffc 100644 --- a/package.json +++ b/package.json @@ -1,31 +1,31 @@ { "name": "archery", - "version": "0.2.4", + "version": "0.3.0", "description": "Build Arch packages through a web interface", "keywords": [ "docker", "arch", "artix" ], - "homepage": "https://github.com/CorySanin/archery#readme", + "homepage": "https://git.sanin.dev/corysanin/archery#readme", "bugs": { "url": "https://github.com/CorySanin/archery/issues" }, "repository": { "type": "git", - "url": "git+https://github.com/CorySanin/archery.git" + "url": "git+https://git.sanin.dev/corysanin/archery.git" }, "license": "MIT", "author": "Cory Sanin", "type": "module", "main": "index.ts", "dependencies": { - "body-parser": "^2.2.0", + "body-parser": "^2.2.1", "ejs": "3.1.10", "express": "^5.1.0", "express-session": "1.18.2", "express-ws": "^5.0.2", - "ky": "1.10.0", + "ky": "1.14.0", "passport": "0.7.0", "passport-openidconnect": "0.1.2", "pg": "^8.16.3", @@ -34,9 +34,9 @@ "sqids": "0.3.0" }, "devDependencies": { - "@types/express": "^5.0.3", + "@types/express": "^5.0.5", "@types/express-session": "^1.18.2", - "@types/express-ws": "3.0.5", + "@types/express-ws": "3.0.6", "@types/node": "^24.10.1", "@types/passport": "1.0.17", "@types/passport-openidconnect": "0.1.3", diff --git a/scripts/copy.js b/scripts/copy.js deleted file mode 100644 index 5a65a16..0000000 --- a/scripts/copy.js +++ /dev/null @@ -1,7 +0,0 @@ -document.addEventListener('DOMContentLoaded', function () { - for (let btn of document.getElementsByClassName('copybtn')) { - btn.addEventListener('click', e => { - navigator.clipboard.writeText(e.target.previousElementSibling.innerText); - }); - } -}); diff --git a/scripts/form.js b/scripts/form.js new file mode 100644 index 0000000..5d7bece --- /dev/null +++ b/scripts/form.js @@ -0,0 +1,23 @@ +document.addEventListener('DOMContentLoaded', function () { + for (let btn of document.getElementsByClassName('copybtn')) { + btn.addEventListener('click', e => { + navigator.clipboard.writeText(e.target.previousElementSibling.innerText); + }); + } + + document.getElementById('persistChk')?.addEventListener('change', async e => { + const sqid = document.getElementById('sqid').textContent; + const persist = !!e.target?.checked; + const resp = await fetch(`/build/${sqid}/persist`, { + method: 'post', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ persist }) + }); + if (!resp.ok) { + this.location.reload(); + } + }); +}); diff --git a/src/DB.ts b/src/DB.ts index 4b1e937..3d79de7 100644 --- a/src/DB.ts +++ b/src/DB.ts @@ -30,6 +30,7 @@ interface Build { pid?: number; sqid?: string; uuid: string; + persist: boolean; } interface User { @@ -128,6 +129,10 @@ class DB extends Store { uuid: { type: DataTypes.STRING, unique: true + }, + persist: { + type: DataTypes.BOOLEAN, + defaultValue: false } }); @@ -262,6 +267,16 @@ class DB extends Store { }); } + public async persist(id: number, persist: boolean = true): Promise { + await this.build.update({ + persist + }, { + where: { + id + } + }); + } + public async appendLog(buildId: number, type: LogType, chunk: string): Promise { await this.logChunk.create({ buildId, @@ -366,7 +381,8 @@ class DB extends Store { public async cleanup(): Promise { await this.build.destroy({ where: { - startTime: { [Op.lt]: new Date(Date.now() - MONTH * 6) } + startTime: { [Op.lt]: new Date(Date.now() - MONTH * 6) }, + persist: { [Op.eq]: false } }, force: true }); diff --git a/src/Web.ts b/src/Web.ts index 7805358..1ba722c 100644 --- a/src/Web.ts +++ b/src/Web.ts @@ -297,6 +297,17 @@ class Web { res.redirect(`/build/${req.params.id}/`); }); + app.post('/build/:id/persist', async (req, res) => { + const build = await this.db.getBuild(sqids.decode(req.params.id)?.[0]); + const persist = !! req?.body?.persist; + if (!build) { + res.sendStatus(404); + return; + } + await this.db.persist(build.id, persist); + res.sendStatus(200); + }) + this._webserver = this.app.listen(this.port, () => console.log(`archery is running on port ${this.port}`)); } diff --git a/styles/01-styles.scss b/styles/01-styles.scss index b9d5a26..70172ba 100644 --- a/styles/01-styles.scss +++ b/styles/01-styles.scss @@ -268,6 +268,10 @@ a { } } +.display-none { + display: none; +} + #followCheckmarkContainer { display: inline-block; position: fixed; diff --git a/views/build.ejs b/views/build.ejs index 6f910dc..331797a 100644 --- a/views/build.ejs +++ b/views/build.ejs @@ -10,6 +10,9 @@

Build #<%= build.id %>

<%= build.status %>

+ <% if (!public) { %> + + <% } %>
<%= build.repo %> @@ -21,6 +24,9 @@ <% if (build.userId && build.userId !== '-1') { %> <%= build.user.displayName %> (<%= build.user.username %>) <% } %> + <% if (!public) { %> + checked<% } %>/> + <% } %> <% if (locals.shareable) { %> <%= shareable %> <% } %> @@ -47,7 +53,7 @@
<%- include("footer", locals) %> - + <% if (!ended) { %> <% } %> diff --git a/views/head.ejs b/views/head.ejs index 561d5db..c0c8488 100644 --- a/views/head.ejs +++ b/views/head.ejs @@ -14,7 +14,7 @@ <% } %> - +