diff --git a/docker/server/Dockerfile b/docker/server/Dockerfile index 1bbceae..0387b81 100644 --- a/docker/server/Dockerfile +++ b/docker/server/Dockerfile @@ -17,7 +17,8 @@ COPY --chown=node:node src src COPY --chown=node:node tsconfig.json tsconfig.json RUN npm run build -FROM node:18 as dependencies +FROM node:18 as app +LABEL org.opencontainers.image.source https://github.com/cgduncan7/autobaan ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true ENV PUPPETEER_EXECUTABLE_PATH /usr/bin/chromium @@ -28,6 +29,7 @@ RUN npm i -g node-gyp WORKDIR /app RUN chown -R node:node . +COPY --from=builder /app/dist ./dist COPY --chown=node:node package.json package.json COPY --chown=node:node package-lock.json package-lock.json @@ -35,10 +37,5 @@ USER node RUN CXX=g++-12 npm install --omit=dev -FROM dependencies as app -LABEL org.opencontainers.image.source https://github.com/cgduncan7/autobaan - -COPY --from=builder /app/dist ./dist - EXPOSE 3000 ENTRYPOINT node dist/server/index.js \ No newline at end of file diff --git a/src/common/database/index.ts b/src/common/database/index.ts index 4e9a27f..d7f5a46 100644 --- a/src/common/database/index.ts +++ b/src/common/database/index.ts @@ -8,7 +8,9 @@ const getDatabase = () => new sqlite.Database(resolve('./db/autobaan_db')) export const run = async (sql: string, params?: unknown) => { const db = getDatabase() await new Promise((res, rej) => { - asyncLocalStorage.getStore()?.debug(` run ~> ${sql.replace(/\s*\n\s*/g, ' ')} (${params})`) + asyncLocalStorage + .getStore() + ?.debug(` run ~> ${sql.replace(/\s*\n\s*/g, ' ')} (${params})`) db.run(sql, params, (err) => { if (err) rej(err) res() @@ -20,13 +22,14 @@ export const run = async (sql: string, params?: unknown) => { export const all = async (sql: string, params?: unknown) => { const db = getDatabase() const rows = await new Promise((res, rej) => { - asyncLocalStorage.getStore()?.debug(` all ~> ${sql.replace(/\s*\n\s*/g, ' ')} (${params})`) + asyncLocalStorage + .getStore() + ?.debug(` all ~> ${sql.replace(/\s*\n\s*/g, ' ')} (${params})`) db.all(sql, params, (err, rows) => { if (err) rej(err) res(rows) }) - } - ) + }) db.close() return rows } diff --git a/src/common/reserver.ts b/src/common/reserver.ts index a76d4e4..15ee11e 100644 --- a/src/common/reserver.ts +++ b/src/common/reserver.ts @@ -8,7 +8,6 @@ const getRunner = () => { if (!runner) { runner = new Runner({ headless: true, - args: ['--no-sandbox', '--disable-setuid-sandbox'], }) } return runner diff --git a/src/common/runner.ts b/src/common/runner.ts index 347d0c6..93f38fb 100644 --- a/src/common/runner.ts +++ b/src/common/runner.ts @@ -35,7 +35,32 @@ export class Runner { BrowserLaunchArgumentOptions & BrowserConnectOptions ) { - this.options = options + const defaultArgs = [ + '--disable-gpu', + '--disable-dev-shm-usage', + '--disable-setuid-sandbox', + '--no-sandbox', + ] + this.options = { args: defaultArgs, ...options } + } + + public async test() { + l.getStore()?.debug('Runner test') + try { + if (!this.browser) { + this.browser = await puppeteer.launch(this.options) + } + } catch (error: unknown) { + l.getStore()?.error('Browser error', { error: (error as Error).message }) + throw new PuppeteerBrowserLaunchError(error as Error) + } + + try { + this.page = await this.browser?.newPage() + } catch (error) { + l.getStore()?.error('Page error', { error: (error as Error).message }) + throw new PuppeteerNewPageError(error as Error) + } } public async run(reservation: Reservation) { diff --git a/src/server/http/index.ts b/src/server/http/index.ts index aa72756..b2487e0 100644 --- a/src/server/http/index.ts +++ b/src/server/http/index.ts @@ -3,9 +3,11 @@ import { v4 } from 'uuid' import { asyncLocalStorage, Logger, LogLevel } from '../../common/logger' import { CronRouter } from './routes/cron' import { ReservationsRouter } from './routes/reservations' +import { RunnerRouter } from './routes/runner' const cronRouter = new CronRouter() const reservationsRouter = new ReservationsRouter() +const runnerRouter = new RunnerRouter() // Handles POST requests to /reservations const server = http.createServer(async (req, res) => { @@ -32,6 +34,10 @@ const server = http.createServer(async (req, res) => { await reservationsRouter.handleRequest(req, res) break } + case /^\/runner/.test(url): { + await runnerRouter.handleRequest(req, res) + break + } default: { logger?.info('Not found', { url, method, location: 'root' }) res.writeHead(404, 'Not found') diff --git a/src/server/http/routes/reservations.ts b/src/server/http/routes/reservations.ts index f1fd63f..81b767d 100644 --- a/src/server/http/routes/reservations.ts +++ b/src/server/http/routes/reservations.ts @@ -63,7 +63,11 @@ export class ReservationsRouter extends Router { date = dayjs(rawDate) } - l.getStore()?.debug('Fetching reservations', { pageNumber, pageSize, date: date?.format() }) + l.getStore()?.debug('Fetching reservations', { + pageNumber, + pageSize, + date: date?.format(), + }) try { let reservations: Reservation[] diff --git a/src/server/http/routes/runner.ts b/src/server/http/routes/runner.ts new file mode 100644 index 0000000..c5c1305 --- /dev/null +++ b/src/server/http/routes/runner.ts @@ -0,0 +1,36 @@ +import { IncomingMessage, ServerResponse } from 'http' +import { asyncLocalStorage as l } from '../../../common/logger' +import { Router } from './index' +import { getStatus, startTasks, stopTasks } from '../../cron' +import { Runner } from '../../../common/runner' + +export class RunnerRouter extends Router { + public async handleRequest( + req: IncomingMessage, + res: ServerResponse + ) { + const { url = '', method } = req + const [route] = url.split('?') + switch (true) { + case /^\/runner\/test$/.test(route) && method === 'GET': { + await this.GET_runner_test(req, res) + break + } + default: { + this.handle404(req, res) + } + } + } + + private async GET_runner_test( + _req: IncomingMessage, + res: ServerResponse + ) { + try { + await new Runner({ headless: false }).test() + res.writeHead(200, 'OK') + } catch (e) { + res.writeHead(500, 'Internal server error') + } + } +}