diff --git a/backend/package.json b/backend/package.json index 482d14c..d66ee74 100644 --- a/backend/package.json +++ b/backend/package.json @@ -14,6 +14,7 @@ "build": "tsc --build --clean && tsc --build" }, "dependencies": { + "@eighty4/install-github": "workspace:^0.0.1", "@eighty4/install-template": "workspace:^0.0.1", "cookie-parser": "^1.4.6", "express": "^5.0.0-beta.1", diff --git a/backend/src/Repositories.ts b/backend/src/Repositories.ts deleted file mode 100644 index c4b9b2d..0000000 --- a/backend/src/Repositories.ts +++ /dev/null @@ -1,75 +0,0 @@ -import type {Repository} from '@eighty4/install-template' - -export interface UserRepositoriesRequest { - accessToken: string -} - -export async function queryPinnedUserRepositories(accessToken: string): Promise> { - const query = ` -query { - viewer { - pinnedItems(first: 6, types: REPOSITORY) { - nodes { - ... on Repository { - name - owner { - login - } - } - } - } - } -}` - const response = await fetch('https://api.github.com/graphql', { - method: 'POST', - headers: { - 'Authorization': 'Bearer ' + accessToken, - }, - body: JSON.stringify({query}), - }) - if (response.status === 200) { - const json = await response.json() - if (json.errors && json.errors.length) { - throw new Error('queryPinnedUserRepositories POST https://api.github.com/graphql ' + json.errors[0].message) - } else { - const result = [] - for (const repo of json.data.viewer.pinnedItems.nodes) { - result.push({owner: repo.owner.login, name: repo.name}) - } - return result - } - } else { - throw new Error('queryPinnedUserRepositories POST https://api.github.com/graphql ' + response.status) - } -} - -export async function queryUserRepositories({accessToken}: UserRepositoriesRequest): Promise> { - const query = ` -query { - viewer { - repositories(first: 20, types: REPOSITORY, affiliations: [OWNER, COLLABORATOR, ORGANIZATION_MEMBER]) { - nodes { - ... on Repository { - name - owner { - login - } - } - } - } - } -}` - const response = await fetch('https://api.github.com/graphql', { - method: 'POST', - headers: { - 'Authorization': 'Bearer ' + accessToken, - }, - body: JSON.stringify({query}), - }) - const json = await response.json() - const result = [] - for (const repo of json.data.viewer.repositories.nodes) { - result.push({owner: repo.owner.login, name: repo.name}) - } - return result -} diff --git a/backend/src/Server.ts b/backend/src/Server.ts index da2cf9e..684c299 100644 --- a/backend/src/Server.ts +++ b/backend/src/Server.ts @@ -1,11 +1,11 @@ import net from 'node:net' import express, {RequestHandler} from 'express' import cookieParser from 'cookie-parser' +import {queryLatestRelease} from '@eighty4/install-github' import {generateScript} from '@eighty4/install-template' import {loadGeneratedScripts, saveGeneratedScript} from './Database.js' import {initiateLogin, resolveLogin} from './Login.js' -import {queryLatestRelease} from './Releases.js' -import {resolveTemplateRuntimeVersion} from './Resolution.js' +import {resolveTemplateRuntimeVersion} from './Template.js' import {verifyAccessToken} from './User.js' const parsePortEnvVariable = (key: string, def: number): number => { @@ -15,7 +15,7 @@ const parsePortEnvVariable = (key: string, def: number): number => { } else { try { return parseInt(val, 10) - } catch(e) { + } catch (e) { console.error(`env variable ${key} must be numeric`) process.exit(1) } @@ -48,7 +48,7 @@ app.use((req, res, next) => { }) const authorize: RequestHandler = (req, res, next) => { - const accessToken = req.cookies.gat + const accessToken = req.cookies.gh verifyAccessToken(accessToken) .then((user) => { if (user === false) { @@ -89,7 +89,7 @@ app.get('/login/notify', async (req, res) => { } else { try { const authData = await resolveLogin(loginId) - res.setHeader('Set-Cookie', `gat=${authData.accessToken}; Secure; HttpOnly; SameSite=Strict; Path=/api`) + res.setHeader('Set-Cookie', `gh=${authData.accessToken}; Secure; SameSite=Strict; Path=/api`) res.json({newUser: authData.newUser}) } catch (e: any) { console.log('auth resolve wtf', e.message) @@ -110,11 +110,9 @@ app.get('/api/projects', authorize, async (req, res) => { }) app.get('/api/release/:repoOwner/:repoName', authorize, async (req, res) => { - const release = await queryLatestRelease({ - accessToken: req.user!.accessToken, repository: { - owner: req.params['repoOwner'], - name: req.params['repoName'], - }, + const release = await queryLatestRelease(req.user!.accessToken, { + owner: req.params['repoOwner'], + name: req.params['repoName'], }) if (release) { res.json(release) diff --git a/backend/src/Template.ts b/backend/src/Template.ts new file mode 100644 index 0000000..5058dbb --- /dev/null +++ b/backend/src/Template.ts @@ -0,0 +1,13 @@ +import {readFileSync} from 'node:fs' + +export function resolveTemplateRuntimeVersion() { + try { + let packageJson = import.meta.resolve('../node_modules/@eighty4/install-template/package.json') + if (packageJson.startsWith('file:///')) { + packageJson = packageJson.substring(7) + } + return JSON.parse(readFileSync(packageJson).toString()).version + } catch (e) { + throw new Error('failed resolving version of @eighty4/install-template') + } +} diff --git a/backend/src/req.d.ts b/backend/src/req.d.ts index dc6180d..b65e734 100644 --- a/backend/src/req.d.ts +++ b/backend/src/req.d.ts @@ -1,5 +1,6 @@ declare namespace Express { export interface Request { user?: { accessToken: string, login: string, userId: number }, + cookies: { gh?: string }, } } diff --git a/backend/tsconfig.json b/backend/tsconfig.json index f8bef3d..2f9f8e3 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -17,6 +17,9 @@ "src/**/*.ts" ], "references": [ + { + "path": "../github" + }, { "path": "../template" } diff --git a/cloud/aws.md b/cloud/aws.md new file mode 100644 index 0000000..f19cc9f --- /dev/null +++ b/cloud/aws.md @@ -0,0 +1,24 @@ +t4g arm instance +t4g.medium +$12 per month +2 cpu 4 gb + +t3 intel instance +t3.medium +$15 per month +2 cpu 4 gb + +CICD on AWS would be with CodeBuild or CodePipeline + https://aws.amazon.com/blogs/devops/build-arm-based-applications-using-codebuild/ + +install docker +configure systemd postgres and install.sh services + https://blog.container-solutions.com/running-docker-containers-with-systemd + +cicd update install.sh with ssh restart + https://unix.stackexchange.com/a/395781 + +send to grafana cloud +- machine metrics +- container metrics +- container logs diff --git a/cloud/backend.nomad b/cloud/backend.nomad new file mode 100644 index 0000000..94574b4 --- /dev/null +++ b/cloud/backend.nomad @@ -0,0 +1,30 @@ +job "install.backend" { + type = "service" + + group "install.backend" { + count = 1 + + task "install.backend" { + driver = "docker" + + config { + image = "84tech/install.backend:latest" + + auth { + config = "/Users/adam/.docker/config.json" + } + } + + env { + } + } + } + + update { + max_parallel = 1 + min_healthy_time = "5s" + healthy_deadline = "3m" + auto_revert = false + canary = 0 + } +} diff --git a/cloud/postgres.nomad b/cloud/postgres.nomad new file mode 100644 index 0000000..1a375b5 --- /dev/null +++ b/cloud/postgres.nomad @@ -0,0 +1,27 @@ +job "postgres" { + type = "service" + + group "postgres" { + count = 1 + + task "postgres" { + driver = "docker" + + config { + image = "postgres:16" + } + + env { + POSTGRES_PASSWORD = eighty4 + } + } + } + + update { + max_parallel = 1 + min_healthy_time = "5s" + healthy_deadline = "3m" + auto_revert = false + canary = 0 + } +} diff --git a/frontend/index.html b/frontend/index.html index 07573fa..f18a39b 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -369,8 +369,9 @@