create public build endpoint
This commit is contained in:
7
scripts/copy.js
Normal file
7
scripts/copy.js
Normal file
@@ -0,0 +1,7 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
for (let btn of document.getElementsByClassName('copybtn')) {
|
||||
btn.addEventListener('click', e => {
|
||||
navigator.clipboard.writeText(e.target.previousElementSibling.innerText);
|
||||
});
|
||||
}
|
||||
});
|
17
src/DB.ts
17
src/DB.ts
@@ -25,6 +25,7 @@ interface Build {
|
||||
status: Status;
|
||||
pid?: number;
|
||||
sqid?: string;
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
interface User {
|
||||
@@ -105,6 +106,10 @@ class DB {
|
||||
pid: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true
|
||||
},
|
||||
uuid: {
|
||||
type: DataTypes.STRING,
|
||||
unique: true
|
||||
}
|
||||
});
|
||||
|
||||
@@ -182,13 +187,14 @@ class DB {
|
||||
return user.id;
|
||||
}
|
||||
|
||||
public async createBuild(repo: string, commit: string, patch: string, distro: string, dependencies: string, author: string): Promise<number> {
|
||||
public async createBuild(repo: string, commit: string, patch: string, distro: string, dependencies: string, author: string, uuid: string): Promise<number> {
|
||||
const buildRec = await this.build.create({
|
||||
repo,
|
||||
commit: commit || null,
|
||||
patch: patch || null,
|
||||
distro,
|
||||
dependencies,
|
||||
uuid,
|
||||
userId: author || '-1'
|
||||
});
|
||||
return buildRec.id;
|
||||
@@ -241,6 +247,15 @@ class DB {
|
||||
});
|
||||
}
|
||||
|
||||
public async getBuildByUuid(uuid: string): Promise<Build> {
|
||||
return await this.build.findOne({
|
||||
where: {
|
||||
uuid
|
||||
},
|
||||
include: this.user
|
||||
});
|
||||
}
|
||||
|
||||
public async getBuilds(): Promise<Build[]> {
|
||||
return await this.build.findAll({
|
||||
attributes: SELECT,
|
||||
|
127
src/Web.ts
127
src/Web.ts
@@ -92,24 +92,70 @@ class Web {
|
||||
});
|
||||
});
|
||||
|
||||
const showBuild = async (req: express.Request, res: express.Response, build: Build) => {
|
||||
if (!build) {
|
||||
res.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
build.sqid = sqids.encode([build.id]);
|
||||
const log = splitLines(await this.db.getLog(build.id));
|
||||
const createBuildPages = (slug: string, getBuildFn: (str: string) => Promise<Build>) => {
|
||||
app.get(`/${slug}/:id/`, async (req, res) => {
|
||||
const build = await getBuildFn(req.params.id);
|
||||
if (!build) {
|
||||
res.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
build.sqid = sqids.encode([build.id]);
|
||||
const log = splitLines(await this.db.getLog(build.id));
|
||||
|
||||
res.render('build', {
|
||||
page: {
|
||||
title: 'Archery',
|
||||
titlesuffix: `Build #${build.id}`,
|
||||
description: `Building ${build.repo} on ${build.distro}`,
|
||||
},
|
||||
user: req?.user,
|
||||
build,
|
||||
log,
|
||||
ended: build.status !== 'queued' && build.status !== 'running'
|
||||
if (req?.user) {
|
||||
res.locals.shareable = `${req.protocol}://${req.host}/b/${build.uuid}/`;
|
||||
}
|
||||
|
||||
res.render('build', {
|
||||
page: {
|
||||
title: 'Archery',
|
||||
titlesuffix: `Build #${build.id}`,
|
||||
description: `Building ${build.repo} on ${build.distro}`,
|
||||
},
|
||||
user: req?.user,
|
||||
build,
|
||||
log,
|
||||
ended: build.status !== 'queued' && build.status !== 'running',
|
||||
public: !!oidc && !req?.user
|
||||
});
|
||||
});
|
||||
|
||||
app.get(`/${slug}/:id/logs{/}`, async (req, res) => {
|
||||
const build = await getBuildFn(req.params.id);
|
||||
if (!build) {
|
||||
res.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
const log = (await this.db.getLog(build.id)).map(logChunk => logChunk.chunk).join('\n');
|
||||
res.set('Content-Type', 'text/plain').send(log);
|
||||
});
|
||||
|
||||
app.get(`/${slug}/:id/patch{/}`, async (req, res) => {
|
||||
const build = await getBuildFn(req.params.id);
|
||||
if (!build || !build.patch) {
|
||||
res.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
res.set('Content-Type', 'text/plain').send(build.patch);
|
||||
});
|
||||
|
||||
wsApp.ws(`/${slug}/:id/ws`, async (ws, req) => {
|
||||
const build = await getBuildFn(req.params.id);
|
||||
if (! build || (build.status !== 'queued' && build.status !== 'running')) {
|
||||
return ws.close();
|
||||
}
|
||||
console.log('WS Opened');
|
||||
const eventListener = (be: BuildEvent) => {
|
||||
if (be.id === build.id) {
|
||||
ws.send(JSON.stringify(be));
|
||||
}
|
||||
};
|
||||
this.buildController.on('log', eventListener);
|
||||
|
||||
ws.on('close', () => {
|
||||
console.log('WS Closed');
|
||||
this.buildController.removeListener('log', eventListener);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -138,7 +184,7 @@ class Web {
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
app.get('/login', (req, res) => {
|
||||
if(req?.user) {
|
||||
if (req?.user) {
|
||||
return res.redirect('/');
|
||||
}
|
||||
res.append('X-Robots-Tag', 'none');
|
||||
@@ -164,6 +210,7 @@ class Web {
|
||||
res.redirect('/login');
|
||||
});
|
||||
});
|
||||
createBuildPages('b', (id) => this.db.getBuildByUuid(id));
|
||||
app.use((req, res, next) => {
|
||||
if (!req?.user) {
|
||||
res.redirect('/login');
|
||||
@@ -216,16 +263,14 @@ class Web {
|
||||
req.body.patch || null,
|
||||
req.body.distro || 'arch',
|
||||
req.body.dependencies || 'stable',
|
||||
req?.user?.['id']
|
||||
req?.user?.['id'],
|
||||
crypto.randomUUID()
|
||||
);
|
||||
res.redirect(`/build/${sqids.encode([buildId])}/`);
|
||||
this.buildController.triggerBuild();
|
||||
});
|
||||
|
||||
app.get('/build/:id/', async (req, res) => {
|
||||
const build = await this.db.getBuild(sqids.decode(req.params.id)?.[0]);
|
||||
showBuild(req, res, build);
|
||||
});
|
||||
createBuildPages('build', (id) => this.db.getBuild(sqids.decode(id)?.[0]));
|
||||
|
||||
app.get('/build/:id/cancel', async (req, res) => {
|
||||
const build = await this.db.getBuild(sqids.decode(req.params.id)?.[0]);
|
||||
@@ -239,46 +284,12 @@ class Web {
|
||||
catch (ex) {
|
||||
console.error(ex);
|
||||
}
|
||||
res.redirect(`/build/${req.params.id}`);
|
||||
});
|
||||
|
||||
app.get('/build/:id/logs{/}', async (req, res) => {
|
||||
const build = await this.db.getBuild(sqids.decode(req.params.id)?.[0]);
|
||||
if (!build) {
|
||||
res.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
const log = (await this.db.getLog(build.id)).map(logChunk => logChunk.chunk).join('\n');
|
||||
res.set('Content-Type', 'text/plain').send(log);
|
||||
});
|
||||
|
||||
app.get('/build/:id/patch{/}', async (req, res) => {
|
||||
const build = await this.db.getBuild(sqids.decode(req.params.id)?.[0]);
|
||||
if (!build || !build.patch) {
|
||||
res.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
res.set('Content-Type', 'text/plain').send(build.patch);
|
||||
res.redirect(`/build/${req.params.id}/`);
|
||||
});
|
||||
|
||||
app.get('/healthcheck', (_, res) => {
|
||||
res.send('Healthy');
|
||||
});
|
||||
|
||||
wsApp.ws('/build/:id/ws', (ws, req) => {
|
||||
console.log('WS Opened');
|
||||
const eventListener = (be: BuildEvent) => {
|
||||
if (be.id === sqids.decode(req.params.id)?.[0]) {
|
||||
ws.send(JSON.stringify(be));
|
||||
}
|
||||
};
|
||||
this.buildController.on('log', eventListener);
|
||||
|
||||
ws.on('close', () => {
|
||||
console.log('WS Closed');
|
||||
this.buildController.removeListener('log', eventListener);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
close = () => {
|
||||
|
@@ -118,6 +118,15 @@ input[type="submit"] {
|
||||
}
|
||||
}
|
||||
|
||||
span,
|
||||
a {
|
||||
|
||||
&:has(+ button.copybtn) {
|
||||
float: left;
|
||||
margin-right: .75em;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.sidebar {
|
||||
background: var(--primary);
|
||||
|
@@ -12,18 +12,21 @@
|
||||
<h2 id="buildStatus"><%= build.status %></h2>
|
||||
<div class="overflow-x">
|
||||
<div class="grid-2col">
|
||||
<label>Repo</label> <span><%= build.repo %></span>
|
||||
<label>Repo</label> <span><span><%= build.repo %></span> <button class="copybtn">Copy</button></span>
|
||||
<label>Commit</label> <span><% if (build.commit) { %><%= build.commit %><% } else { %>latest<% } %></span>
|
||||
<label>Patch</label> <span><% if (build.patch) { %><a href="/build/<%= build.sqid %>/patch">patch file</a><% } else { %>none<% } %></span>
|
||||
<label>Distro</label> <span><%= build.distro %></span>
|
||||
<label>Dependencies</label> <span><%= build.dependencies %></span>
|
||||
<label>Start time</label> <span class="to-local-time"><%= build.startTime %></span>
|
||||
<% if (build.userId !== '-1') { %>
|
||||
<% if (build.userId && build.userId !== '-1') { %>
|
||||
<label>Triggered by</label> <span><%= build.user.displayName %> (<%= build.user.username %>)</span>
|
||||
<% } %>
|
||||
<% if (locals.shareable) { %>
|
||||
<label>Shareable link</label> <span><a href="<%= shareable %>"><%= shareable %></a> <button class="copybtn">Copy</button></span>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
<% if (build.sqid) { %>
|
||||
<% if (build.sqid && !public) { %>
|
||||
<div>
|
||||
<a href="/build?id=<%= build.sqid %>" class="button">Clone</a>
|
||||
</div>
|
||||
@@ -44,6 +47,7 @@
|
||||
</div>
|
||||
<%- include("footer", locals) %>
|
||||
<script src="/assets/js/timezone.js?v1" nonce="<%= cspNonce %>"></script>
|
||||
<script src="/assets/js/copy.js?v1" nonce="<%= cspNonce %>"></script>
|
||||
<% if (!ended) { %>
|
||||
<script src="/assets/js/build.js?v3" nonce="<%= cspNonce %>"></script>
|
||||
<% } %>
|
||||
|
Reference in New Issue
Block a user