feat: initial stable job handling + progress on saving logs

Co-authored-by: Jacob Nguyen <jacoobes@users.noreply.github.com>
This commit is contained in:
2024-01-26 22:00:35 +00:00
committed by GitHub
parent b9af34fd14
commit 1878845a6b
30 changed files with 1056 additions and 129 deletions

2
.gitignore vendored
View File

@@ -1,5 +1,5 @@
node_modules
.env
*.env
/apps/api/repos
dist

4
apps/api/.prettierrc Normal file
View File

@@ -0,0 +1,4 @@
{
"tabWidth": 4,
"useTabs": true
}

View File

@@ -1,19 +1,23 @@
import express from 'express';
import 'dotenv/config';
import { execa } from 'execa';
import { validateJsonWebhook } from './util/validateJsonWebhook.js';
import babashkaScripts from './babashka/scripts.json' assert { type: 'json' };
import { FeedbackRequestBody, FeedbackRequestBodySchema } from './util/types.js';
import validateJsonWebhook from './plugins/validateJsonWebhook.js';
import { FeedbackRequestBody, FeedbackRequestBodySchema, Logs } from './util/types.js';
import cors from 'cors'
import rateLimit from 'express-rate-limit';
import { Webhook } from 'simple-discord-webhooks';
import { codeBlock } from './util/discordCodeBlock.js';
import db, { schema } from 'database/dist/index.js';
import jobs, { LogGroup } from './jobs.js';
import expressWs from 'express-ws';
import resolvePlugins from './util/resolvePlugins.js';
import { PassThrough } from 'node:stream';
const devMode = process.argv[2] === '--dev';
if (devMode) console.log('You\'re a developer 😎 (sorry for that emoji jumpscare)')
const cwd = process.cwd()
const app = express()
const { app } = expressWs(express())
app.use(express.json())
app.use(cors())
@@ -28,6 +32,100 @@ app.get('/', (req, res) => {
res.send('hi this is the api what did you even expect')
})
for (const job of jobs) {
const jobLogs: Logs[] = []
switch (job.method) {
case "POST":
app.post(job.route, async (req, res) => {
await expressCode(req, res);
});
break;
case "GET":
app.get(job.route, async (req, res) => {
await expressCode(req, res);
});
break;
}
const expressCode = async (req: express.Request, res: express.Response) => {
if (resolvePlugins(job.plugins, req, res).includes(false))
// Believe it or not, the code 418 I'm a teapot is the most appropiate one IMO.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418
return res.status(418).send({ success: false, message: "Plugins didn't pass" });
res.send({ success: true, message: "Command is running" });
const stream_stdout = new PassThrough();
const stream_stderr = new PassThrough();
const parse_payload = (level: 'info' | 'error', payload: any) => ({
timestamp: new Date(),
message: String(payload),
level
})
stream_stdout.on('data', (chunk) => {
jobLogs.push(parse_payload('info', chunk))
});
stream_stderr.on('data', chunk => {
jobLogs.push(parse_payload('error', chunk))
})
for (const steps of job.steps) {
console.log(`Running step ${steps.name}`);
const cmd = execa(
"bash",
[`${cwd}/scripts/${job.stepsMainDir}/${steps.script}`],
{
cwd: steps.cwd,
shell: true,
env: { NT_ARGS: JSON.stringify(job.cmdArgs) },
},
)
cmd?.pipeStdout?.(stream_stdout)
cmd?.pipeStderr?.(stream_stderr)
const exitCode = await new Promise((resolve) => {
cmd.once("exit", (code) => {
if (code !== 0) {
console.log(
`Step ${steps.name} failed with code ${code}`,
);
} else {
console.log(`Step ${steps.name} finished successfully`);
}
resolve(code);
});
});
if (exitCode !== 0) {
db.insert(schema.stepLogs).values({
pkey: crypto.randomUUID(),
id: steps.id.toString(),
logs: jobLogs,
// i gtg but we need to register the job run first
// tysm seren for helping me <3
// np
})
}
}
// const cmd = execa(
// "bash",
// [`${cwd}/scripts/${job.stepsMainDir}/${steps.script}`],
// {
// cwd: steps.cwd,
// shell: true,
// env: { NT_ARGS: JSON.stringify(job.cmdArgs) },
// },
// );
}
};
app.ws('/ws/jobs/logs/:id', (ws, req) => {
const id = req.params.id
if (!id) {
ws.send(JSON.stringify({ success: false, error: 'No id provided' }))
return ws.close()
}
})
app.post('/wh/updateDocsJson', async (req, res) => {
const validate = validateJsonWebhook(req)
if (!validate) {
@@ -35,7 +133,7 @@ app.post('/wh/updateDocsJson', async (req, res) => {
success: false,
error: 'Invalid token'
})
return
return
}
if (req.body.action !== 'released') {
res.send({
@@ -143,32 +241,7 @@ app.get('/ping', (req, res) => {
res.send('Pong')
})
for (const script of babashkaScripts) {
switch (script.method) {
case 'GET':
app.get(script.route, async (req, res) => {
const command = await execa('bb', [`babashka/${script.file}`])
res.send({
success: command.exitCode === 0 ? true : false,
cmdoutput: command.stdout
})
})
break;
case 'POST':
app.post(script.route, async (req, res) => {
const command = await execa('bb', [`babashka/${script.file}`])
res.send({
success: command.exitCode === 0 ? true : false,
cmdoutput: command.stdout
})
})
break;
}
console.log(`Babashka script ${script.file} was registered successfully in ${script.method} ${script.route}`)
}
const port = 4000
app.listen(port, '::', () => {
console.log(`Server listening on [::]${port}`)
})

103
apps/api/jobs.ts Normal file
View File

@@ -0,0 +1,103 @@
export default [
{
name: 'Update docs',
method: 'POST',
route: '/wh/updateDocs',
plugins: ['validateJsonWebhook'],
cmdArgs: {
githubToken: process.env.GHTOKEN!,
email: process.env.EMAIL!
},
stepsMainDir: 'updateDocs',
steps: [
{
id: 1,
name: 'Move docusaurus config files',
cwd: 'repos/website',
script: 'moveFiles.sh'
},
{
id: 2,
name: 'Build docs',
cwd: 'repos/website',
script: 'buildWebsite.sh'
},
{
id: 3,
name: 'Revert moved config files',
cwd: 'repos/website',
script: 'revertMovedFiles.sh'
},
{
id: 4,
name: 'Generate Typedoc JSON',
cwd: 'repos/website',
script: 'typedocJson.sh'
},
{
id: 5,
name: 'Push website',
cwd: 'repos/website',
script: 'pushWebsite.sh'
},
{
id: 6,
name: 'Push community bot',
cwd: 'repos/sern-community',
script: 'pushCommunityBot.sh'
}
]
},
{
name: 'Test',
method: 'GET',
route: '/test',
plugins: [],
cmdArgs: {
randomVariable: 'hey this is a variable'
},
stepsMainDir: 'test',
steps: [
{
id: 1,
name: 'Hello world',
cwd: 'scripts/test',
script: 'test.sh'
},
{
id: 2,
name: 'Hello world from variable',
cwd: 'scripts/test',
script: 'variable.sh'
}
]
}
] satisfies Jobs[]
export interface Jobs {
name: string;
method: 'GET' | 'POST';
route: string;
plugins: string[];
cmdArgs: Record<string, string>;
stepsMainDir: string;
steps: Step[];
}
export interface Step {
id: number;
name: string;
cwd: string;
script: string
}
export interface Logs {
timestamp: Date;
message: string;
level: 'info' | 'error';
}
export interface LogGroup {
stepId: string;
logs: Logs[];
}

View File

@@ -15,12 +15,14 @@
"execa": "^7.1.1",
"express": "^4.18.2",
"express-rate-limit": "^6.11.1",
"express-ws": "^5.0.2",
"simple-discord-webhooks": "^2.1.0",
"zod": "^3.22.4"
},
"devDependencies": {
"@types/cors": "^2.8.14",
"@types/express": "^4.17.17",
"@types/express-ws": "^3.0.4",
"@types/node": "^18.15.11",
"tsc-watch": "^6.0.0",
"typescript": "^5.0.4"

View File

@@ -1,7 +1,7 @@
import type { Request } from "express";
import type { Request, Response } from "express";
import * as crypto from 'crypto';
export function validateJsonWebhook(request: Request) {
export default function validateJsonWebhook(request: Request, _response?: Response) {
// calculate the signature
const expectedSignature = "sha256=" +

View File

@@ -0,0 +1 @@
echo "hi"

View File

@@ -0,0 +1,3 @@
RANDOMVARIABLE=$(echo $NT_ARGS | jq -r '.randomVariable')
echo $RANDOMVARIABLE

View File

@@ -0,0 +1 @@
yarn build

View File

@@ -0,0 +1,2 @@
mv ./docusaurus.config.js ./original.docusaurus.config.js
mv ./docgen.docusaurus.config.js ./docusaurus.config.js

View File

@@ -0,0 +1,6 @@
GITHUBTOKEN=$(echo $@ | jq ".githubToken")
EMAIL=$(echo $@ | jq ".email")
git add .
git -c user.name="sern bot" -c user.email="$EMAIL" commit -m "chore: update typedoc"
git push https://sernbot:$GITHUBTOKEN@github.com/sern-handler/sern-community.git

View File

@@ -0,0 +1,6 @@
GITHUBTOKEN=$(echo $@ | jq ".githubToken")
EMAIL=$(echo $@ | jq ".email")
git add .
git -c user.name="sern bot" -c user.email="$EMAIL" commit -m "chore: update api documentation"
git push --force https://sernbot:$GITHUBTOKEN@github.com/sern-handler/website.git

View File

@@ -0,0 +1,2 @@
mv docusaurus.config.js docgen.docusaurus.config.js
mv original.docusaurus.config.js docusaurus.config.js

View File

@@ -0,0 +1 @@
yarn typedoc-json

View File

@@ -1,22 +0,0 @@
#!/bin/bash
cd repos/sern-community
git checkout main
git pull
cd ..
cd website
mv ./docusaurus.config.js ./original.docusaurus.config.js
mv ./docgen.docusaurus.config.js ./docusaurus.config.js
npm run build
mv docusaurus.config.js docgen.docusaurus.config.js
mv original.docusaurus.config.js docusaurus.config.js
npm run typedoc-json
git add .
git -c user.name="sern bot" -c user.email="$2" commit -m "chore: update api documentation"
git push --force https://sernbot:$1@github.com/sern-handler/website.git
cd ..
cd sern-community
git add .
git -c user.name="sern bot" -c user.email="$2" commit -m "chore: update typedoc"
git push https://sernbot:$1@github.com/sern-handler/sern-community.git

View File

@@ -0,0 +1,14 @@
import type { Request, Response } from "express"
export default function resolvePlugins(plugins: string[], req: Request, res: Response) {
if (plugins.length === 0)
// not doing any crazy types today sorry
return [true]
const resolvedPlugins: boolean[] = []
plugins.forEach(async (plugin) => {
const resolvedPlugin = await import(`../plugins/${plugin}.js`)
.then((plugin) => plugin.default(req, res)) as boolean
resolvedPlugins.push(resolvedPlugin)
})
return resolvedPlugins
}

View File

@@ -1,11 +1,5 @@
import { z } from "zod";
/* export interface FeedbackRequestBody {
turnstileToken?: string;
feedback: 'up' | 'down';
inputText?: string;
route: string;
} */
export const FeedbackRequestBodySchema = z.object({
turnstileToken: z.string().min(1),
feedback: z.enum(['up', 'down']),
@@ -13,3 +7,9 @@ export const FeedbackRequestBodySchema = z.object({
route: z.string(),
})
export type FeedbackRequestBody = z.infer<typeof FeedbackRequestBodySchema>
export interface Logs {
timestamp: Date;
message: string;
level: 'info' | 'error';
}

View File

@@ -0,0 +1,11 @@
CREATE TABLE IF NOT EXISTS "jobsList" (
"id" "smallserial" PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"steps" json NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "jobsLogs" (
"id" text PRIMARY KEY NOT NULL,
"stepId" text NOT NULL,
"log" text NOT NULL
);

View File

@@ -0,0 +1,2 @@
ALTER TABLE "jobsLogs" RENAME COLUMN "log" TO "logs";--> statement-breakpoint
ALTER TABLE "jobsLogs" ALTER COLUMN "logs" SET DATA TYPE json;

View File

@@ -0,0 +1,321 @@
{
"id": "34e7f7f7-fb3d-492d-8247-a1f6b5dcfbe7",
"prevId": "3848e593-34c7-49c2-8f2c-d9558a25f99e",
"version": "5",
"dialect": "pg",
"tables": {
"account": {
"name": "account",
"schema": "",
"columns": {
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": true
},
"provider": {
"name": "provider",
"type": "text",
"primaryKey": false,
"notNull": true
},
"providerAccountId": {
"name": "providerAccountId",
"type": "text",
"primaryKey": false,
"notNull": true
},
"refresh_token": {
"name": "refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"access_token": {
"name": "access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"token_type": {
"name": "token_type",
"type": "text",
"primaryKey": false,
"notNull": false
},
"scope": {
"name": "scope",
"type": "text",
"primaryKey": false,
"notNull": false
},
"id_token": {
"name": "id_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"session_state": {
"name": "session_state",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"account_userId_user_id_fk": {
"name": "account_userId_user_id_fk",
"tableFrom": "account",
"tableTo": "user",
"columnsFrom": [
"userId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"account_provider_providerAccountId_pk": {
"name": "account_provider_providerAccountId_pk",
"columns": [
"provider",
"providerAccountId"
]
}
},
"uniqueConstraints": {}
},
"guideFeedback": {
"name": "guideFeedback",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"feedback": {
"name": "feedback",
"type": "text",
"primaryKey": false,
"notNull": true
},
"route": {
"name": "route",
"type": "text",
"primaryKey": false,
"notNull": true
},
"inputText": {
"name": "inputText",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"jobsList": {
"name": "jobsList",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "smallserial",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"steps": {
"name": "steps",
"type": "json",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"jobsLogs": {
"name": "jobsLogs",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"stepId": {
"name": "stepId",
"type": "text",
"primaryKey": false,
"notNull": true
},
"log": {
"name": "log",
"type": "text",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"session": {
"name": "session",
"schema": "",
"columns": {
"sessionToken": {
"name": "sessionToken",
"type": "text",
"primaryKey": true,
"notNull": true
},
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true
},
"expires": {
"name": "expires",
"type": "timestamp",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"session_userId_user_id_fk": {
"name": "session_userId_user_id_fk",
"tableFrom": "session",
"tableTo": "user",
"columnsFrom": [
"userId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"user": {
"name": "user",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"emailVerified": {
"name": "emailVerified",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"image": {
"name": "image",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"verificationToken": {
"name": "verificationToken",
"schema": "",
"columns": {
"identifier": {
"name": "identifier",
"type": "text",
"primaryKey": false,
"notNull": true
},
"token": {
"name": "token",
"type": "text",
"primaryKey": false,
"notNull": true
},
"expires": {
"name": "expires",
"type": "timestamp",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"verificationToken_identifier_token_pk": {
"name": "verificationToken_identifier_token_pk",
"columns": [
"identifier",
"token"
]
}
},
"uniqueConstraints": {}
}
},
"enums": {},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@@ -0,0 +1,321 @@
{
"id": "0de7bdb6-1399-4a95-80fe-567c0a49bfd7",
"prevId": "34e7f7f7-fb3d-492d-8247-a1f6b5dcfbe7",
"version": "5",
"dialect": "pg",
"tables": {
"account": {
"name": "account",
"schema": "",
"columns": {
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": true
},
"provider": {
"name": "provider",
"type": "text",
"primaryKey": false,
"notNull": true
},
"providerAccountId": {
"name": "providerAccountId",
"type": "text",
"primaryKey": false,
"notNull": true
},
"refresh_token": {
"name": "refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"access_token": {
"name": "access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"token_type": {
"name": "token_type",
"type": "text",
"primaryKey": false,
"notNull": false
},
"scope": {
"name": "scope",
"type": "text",
"primaryKey": false,
"notNull": false
},
"id_token": {
"name": "id_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"session_state": {
"name": "session_state",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"account_userId_user_id_fk": {
"name": "account_userId_user_id_fk",
"tableFrom": "account",
"tableTo": "user",
"columnsFrom": [
"userId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"account_provider_providerAccountId_pk": {
"name": "account_provider_providerAccountId_pk",
"columns": [
"provider",
"providerAccountId"
]
}
},
"uniqueConstraints": {}
},
"guideFeedback": {
"name": "guideFeedback",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"feedback": {
"name": "feedback",
"type": "text",
"primaryKey": false,
"notNull": true
},
"route": {
"name": "route",
"type": "text",
"primaryKey": false,
"notNull": true
},
"inputText": {
"name": "inputText",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"jobsList": {
"name": "jobsList",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "smallserial",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"steps": {
"name": "steps",
"type": "json",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"jobsLogs": {
"name": "jobsLogs",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"stepId": {
"name": "stepId",
"type": "text",
"primaryKey": false,
"notNull": true
},
"logs": {
"name": "logs",
"type": "json",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"session": {
"name": "session",
"schema": "",
"columns": {
"sessionToken": {
"name": "sessionToken",
"type": "text",
"primaryKey": true,
"notNull": true
},
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true
},
"expires": {
"name": "expires",
"type": "timestamp",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"session_userId_user_id_fk": {
"name": "session_userId_user_id_fk",
"tableFrom": "session",
"tableTo": "user",
"columnsFrom": [
"userId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"user": {
"name": "user",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"emailVerified": {
"name": "emailVerified",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"image": {
"name": "image",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"verificationToken": {
"name": "verificationToken",
"schema": "",
"columns": {
"identifier": {
"name": "identifier",
"type": "text",
"primaryKey": false,
"notNull": true
},
"token": {
"name": "token",
"type": "text",
"primaryKey": false,
"notNull": true
},
"expires": {
"name": "expires",
"type": "timestamp",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"verificationToken_identifier_token_pk": {
"name": "verificationToken_identifier_token_pk",
"columns": [
"identifier",
"token"
]
}
},
"uniqueConstraints": {}
}
},
"enums": {},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@@ -15,6 +15,20 @@
"when": 1703508532165,
"tag": "0001_chief_ricochet",
"breakpoints": true
},
{
"idx": 2,
"version": "5",
"when": 1704641056957,
"tag": "0002_rainy_catseye",
"breakpoints": true
},
{
"idx": 3,
"version": "5",
"when": 1704642053129,
"tag": "0003_nervous_songbird",
"breakpoints": true
}
]
}

View File

@@ -7,6 +7,7 @@
"dev": "tsc-watch --preserveWatchOutput",
"migrate": "node dist/migrations.js",
"generateMigrations": "drizzle-kit generate:pg --schema ./src/schema.ts",
"deploy": "yarn generateMigrations && yarn migrate",
"build": "tsc"
},
"dependencies": {

View File

@@ -4,6 +4,8 @@ import {
text,
primaryKey,
integer,
smallserial,
json
} from "drizzle-orm/pg-core";
import type { AdapterAccount } from "@auth/core/adapters";
@@ -15,6 +17,25 @@ export const guideFeedback = pgTable("guideFeedback", {
inputText: text("inputText"),
})
export const jobsList = pgTable("jobsList", {
// note to reviewers:
// id is a smallserial and smallserials
// is an autoincrementing 2-byte integer
// so the max value is 32767
// is this fine? or should I use a bigserial (8-byte int)?
// https://orm.drizzle.team/docs/column-types/pg#smallserial
id: smallserial('id').primaryKey().notNull(),
name: text("name").notNull(),
steps: json("steps").notNull(),
})
export const stepLogs = pgTable("jobsLogs", {
pkey: text("pkey").notNull().primaryKey(),
id: text("id").notNull(),
jobId: text("stepId").notNull(),
logs: json('logs').$type<JobLog[]>().notNull(),
})
// next-auth schema
export const users = pgTable("user", {
id: text("id").notNull().primaryKey(),
@@ -65,3 +86,10 @@ export const verificationTokens = pgTable(
compoundKey: primaryKey(vt.identifier, vt.token),
})
);
// types
interface JobLog {
timestamp: Date;
message: string;
level: 'info' | 'error';
}

View File

@@ -21,7 +21,7 @@
"@t3-oss/env-nextjs": "^0.7.1",
"database": "1.0.0",
"drizzle-orm": "^0.29.1",
"next": "^14.0.4",
"next": "14.0.0",
"next-auth": "^4.24.5",
"react": "18.2.0",
"react-dom": "18.2.0",

View File

@@ -26,9 +26,7 @@ export default function RootLayout({
<body className={`font-sans ${roboto.variable}`}>
<NextAuthProvider>
<AppRouterCacheProvider options={{ enableCssLayer: true }}>
<StyledEngineProvider injectFirst>
{children}
</StyledEngineProvider>
{children}
</AppRouterCacheProvider>
</NextAuthProvider>
</body>

View File

@@ -127,7 +127,7 @@ export default function NavBar() {
<Box sx={{ flexGrow: 0 }}>
<Tooltip title="Open settings">
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
<Avatar alt={'Account'} src={session ? session.user.image! : '...'} />
<Avatar alt={'Account'} src={session!.user.image!} />
</IconButton>
</Tooltip>
<Menu

View File

@@ -1,13 +0,0 @@
version: '3.8'
services:
db:
image: postgres
restart: always
environment:
POSTGRES_USER: development
POSTGRES_PASSWORD: development
POSTGRES_DB: automatadev
volumes:
- ./data:/var/lib/postgresql/data
ports:
- 5431:5432

View File

@@ -15,6 +15,7 @@
"build:frontend": "yarn workspace frontend build",
"start:api": "yarn workspace api start",
"start:frontend": "yarn workspace frontend start",
"db:deploy": "yarn workspace database deploy",
"dev": "concurrently \"yarn workspace frontend dev\" \"yarn workspace api dev\" \"yarn workspace database dev\""
},
"//": [

147
yarn.lock
View File

@@ -1126,10 +1126,10 @@ __metadata:
languageName: node
linkType: hard
"@next/env@npm:14.0.4":
version: 14.0.4
resolution: "@next/env@npm:14.0.4"
checksum: 59b893d30aea0556379a24f6e4eac830677feb149bd8416b72383ea2600ce194fa22a79b2dd86e0b295c4a8f0702e461f48edaff1ac9173eddef42a4cce7fd98
"@next/env@npm:14.0.0":
version: 14.0.0
resolution: "@next/env@npm:14.0.0"
checksum: c43e81dbd162a29a4b380342e416209d69d731e8ced7688d09668ec8196f543e358ed65adad81a26e943c63a293d7a018552f8389b6b1ac95cd0f63f4ef257c0
languageName: node
linkType: hard
@@ -1142,65 +1142,65 @@ __metadata:
languageName: node
linkType: hard
"@next/swc-darwin-arm64@npm:14.0.4":
version: 14.0.4
resolution: "@next/swc-darwin-arm64@npm:14.0.4"
"@next/swc-darwin-arm64@npm:14.0.0":
version: 14.0.0
resolution: "@next/swc-darwin-arm64@npm:14.0.0"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@next/swc-darwin-x64@npm:14.0.4":
version: 14.0.4
resolution: "@next/swc-darwin-x64@npm:14.0.4"
"@next/swc-darwin-x64@npm:14.0.0":
version: 14.0.0
resolution: "@next/swc-darwin-x64@npm:14.0.0"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@next/swc-linux-arm64-gnu@npm:14.0.4":
version: 14.0.4
resolution: "@next/swc-linux-arm64-gnu@npm:14.0.4"
"@next/swc-linux-arm64-gnu@npm:14.0.0":
version: 14.0.0
resolution: "@next/swc-linux-arm64-gnu@npm:14.0.0"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard
"@next/swc-linux-arm64-musl@npm:14.0.4":
version: 14.0.4
resolution: "@next/swc-linux-arm64-musl@npm:14.0.4"
"@next/swc-linux-arm64-musl@npm:14.0.0":
version: 14.0.0
resolution: "@next/swc-linux-arm64-musl@npm:14.0.0"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard
"@next/swc-linux-x64-gnu@npm:14.0.4":
version: 14.0.4
resolution: "@next/swc-linux-x64-gnu@npm:14.0.4"
"@next/swc-linux-x64-gnu@npm:14.0.0":
version: 14.0.0
resolution: "@next/swc-linux-x64-gnu@npm:14.0.0"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard
"@next/swc-linux-x64-musl@npm:14.0.4":
version: 14.0.4
resolution: "@next/swc-linux-x64-musl@npm:14.0.4"
"@next/swc-linux-x64-musl@npm:14.0.0":
version: 14.0.0
resolution: "@next/swc-linux-x64-musl@npm:14.0.0"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard
"@next/swc-win32-arm64-msvc@npm:14.0.4":
version: 14.0.4
resolution: "@next/swc-win32-arm64-msvc@npm:14.0.4"
"@next/swc-win32-arm64-msvc@npm:14.0.0":
version: 14.0.0
resolution: "@next/swc-win32-arm64-msvc@npm:14.0.0"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@next/swc-win32-ia32-msvc@npm:14.0.4":
version: 14.0.4
resolution: "@next/swc-win32-ia32-msvc@npm:14.0.4"
"@next/swc-win32-ia32-msvc@npm:14.0.0":
version: 14.0.0
resolution: "@next/swc-win32-ia32-msvc@npm:14.0.0"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@next/swc-win32-x64-msvc@npm:14.0.4":
version: 14.0.4
resolution: "@next/swc-win32-x64-msvc@npm:14.0.4"
"@next/swc-win32-x64-msvc@npm:14.0.0":
version: 14.0.0
resolution: "@next/swc-win32-x64-msvc@npm:14.0.0"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
@@ -1371,7 +1371,7 @@ __metadata:
languageName: node
linkType: hard
"@types/express-serve-static-core@npm:^4.17.33":
"@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.33":
version: 4.17.41
resolution: "@types/express-serve-static-core@npm:4.17.41"
dependencies:
@@ -1383,7 +1383,18 @@ __metadata:
languageName: node
linkType: hard
"@types/express@npm:^4.17.17":
"@types/express-ws@npm:^3.0.4":
version: 3.0.4
resolution: "@types/express-ws@npm:3.0.4"
dependencies:
"@types/express": "npm:*"
"@types/express-serve-static-core": "npm:*"
"@types/ws": "npm:*"
checksum: 15c94bd5b8fbf8ee131703d04d8753f31601863a6d19363938e51bf425a011bd2af8139bbf899e00424687146a3b6302b498348e417aca786e3ffecb1e1beccd
languageName: node
linkType: hard
"@types/express@npm:*, @types/express@npm:^4.17.17":
version: 4.17.21
resolution: "@types/express@npm:4.17.21"
dependencies:
@@ -1544,6 +1555,15 @@ __metadata:
languageName: node
linkType: hard
"@types/ws@npm:*":
version: 8.5.10
resolution: "@types/ws@npm:8.5.10"
dependencies:
"@types/node": "npm:*"
checksum: e9af279b984c4a04ab53295a40aa95c3e9685f04888df5c6920860d1dd073fcc57c7bd33578a04b285b2c655a0b52258d34bee0a20569dca8defb8393e1e5d29
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:^6.11.0":
version: 6.16.0
resolution: "@typescript-eslint/eslint-plugin@npm:6.16.0"
@@ -1802,6 +1822,7 @@ __metadata:
dependencies:
"@types/cors": "npm:^2.8.14"
"@types/express": "npm:^4.17.17"
"@types/express-ws": "npm:^3.0.4"
"@types/node": "npm:^18.15.11"
body-parser: "npm:^1.20.2"
cors: "npm:^2.8.5"
@@ -1810,6 +1831,7 @@ __metadata:
execa: "npm:^7.1.1"
express: "npm:^4.18.2"
express-rate-limit: "npm:^6.11.1"
express-ws: "npm:^5.0.2"
simple-discord-webhooks: "npm:^2.1.0"
tsc-watch: "npm:^6.0.0"
typescript: "npm:^5.0.4"
@@ -3152,6 +3174,17 @@ __metadata:
languageName: node
linkType: hard
"express-ws@npm:^5.0.2":
version: 5.0.2
resolution: "express-ws@npm:5.0.2"
dependencies:
ws: "npm:^7.4.6"
peerDependencies:
express: ^4.0.0 || ^5.0.0-alpha.1
checksum: 8bb7b6645022db7f41ba3925ed2d0874cce6af83728e3fb4b0b905e76cc747d901b7fe53bec370f9040adfac68fca404fd2c1388b0d9c64dd624649a0fe4b0a2
languageName: node
linkType: hard
"express@npm:^4.18.2":
version: 4.18.2
resolution: "express@npm:4.18.2"
@@ -3376,7 +3409,7 @@ __metadata:
drizzle-orm: "npm:^0.29.1"
eslint: "npm:^8.54.0"
mysql2: "npm:^3.6.1"
next: "npm:^14.0.4"
next: "npm:14.0.0"
next-auth: "npm:^4.24.5"
postcss: "npm:^8.4.31"
prettier: "npm:^3.1.0"
@@ -3600,7 +3633,7 @@ __metadata:
languageName: node
linkType: hard
"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.6":
"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.2.6":
version: 4.2.11
resolution: "graceful-fs@npm:4.2.11"
checksum: 386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2
@@ -4520,24 +4553,23 @@ __metadata:
languageName: node
linkType: hard
"next@npm:^14.0.4":
version: 14.0.4
resolution: "next@npm:14.0.4"
"next@npm:14.0.0":
version: 14.0.0
resolution: "next@npm:14.0.0"
dependencies:
"@next/env": "npm:14.0.4"
"@next/swc-darwin-arm64": "npm:14.0.4"
"@next/swc-darwin-x64": "npm:14.0.4"
"@next/swc-linux-arm64-gnu": "npm:14.0.4"
"@next/swc-linux-arm64-musl": "npm:14.0.4"
"@next/swc-linux-x64-gnu": "npm:14.0.4"
"@next/swc-linux-x64-musl": "npm:14.0.4"
"@next/swc-win32-arm64-msvc": "npm:14.0.4"
"@next/swc-win32-ia32-msvc": "npm:14.0.4"
"@next/swc-win32-x64-msvc": "npm:14.0.4"
"@next/env": "npm:14.0.0"
"@next/swc-darwin-arm64": "npm:14.0.0"
"@next/swc-darwin-x64": "npm:14.0.0"
"@next/swc-linux-arm64-gnu": "npm:14.0.0"
"@next/swc-linux-arm64-musl": "npm:14.0.0"
"@next/swc-linux-x64-gnu": "npm:14.0.0"
"@next/swc-linux-x64-musl": "npm:14.0.0"
"@next/swc-win32-arm64-msvc": "npm:14.0.0"
"@next/swc-win32-ia32-msvc": "npm:14.0.0"
"@next/swc-win32-x64-msvc": "npm:14.0.0"
"@swc/helpers": "npm:0.5.2"
busboy: "npm:1.6.0"
caniuse-lite: "npm:^1.0.30001406"
graceful-fs: "npm:^4.2.11"
postcss: "npm:8.4.31"
styled-jsx: "npm:5.1.1"
watchpack: "npm:2.4.0"
@@ -4572,7 +4604,7 @@ __metadata:
optional: true
bin:
next: dist/bin/next
checksum: e6c829fd473d8c3605b2b62d15e1bf41e9d90cf59a2c213b4adeadff2846999bc9a653ffef18f6aa13cc9f5d6de02469c222acf5a4184901a4690a4504bd468f
checksum: cfb18a72d6e1d875efb1bb3806f9a06551f482c5cb87231e77e179a71d26f3d43700290988ad27e739302bfa7ff8ac8081aafd5456c39a2819fdd315617e5acf
languageName: node
linkType: hard
@@ -6511,6 +6543,21 @@ __metadata:
languageName: node
linkType: hard
"ws@npm:^7.4.6":
version: 7.5.9
resolution: "ws@npm:7.5.9"
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ^5.0.2
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
checksum: aec4ef4eb65821a7dde7b44790f8699cfafb7978c9b080f6d7a98a7f8fc0ce674c027073a78574c94786ba7112cc90fa2cc94fc224ceba4d4b1030cff9662494
languageName: node
linkType: hard
"xtend@npm:^4.0.0":
version: 4.0.2
resolution: "xtend@npm:4.0.2"