feat: add backend

This commit is contained in:
2025-08-28 23:25:59 +08:00
parent 2edf8753a7
commit 47f36cd625
29 changed files with 1489 additions and 253 deletions

5
apps/server/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
node_modules
# Keep environment variables out of version control
.env
/generated/prisma

10
apps/server/app.ts Normal file
View File

@@ -0,0 +1,10 @@
import Koa from 'koa';
import { registerMiddlewares } from './middlewares/index.ts';
const app = new Koa();
registerMiddlewares(app);
app.listen(3000, () => {
console.log('server started at http://localhost:3000');
});

View File

@@ -0,0 +1,11 @@
import type { Context } from 'koa';
import prisma from '../libs/db.ts';
export async function list(ctx: Context) {
const list = await prisma.application.findMany({
where: {
valid: 1,
},
});
ctx.body = list;
}

7
apps/server/libs/db.ts Normal file
View File

@@ -0,0 +1,7 @@
import { PrismaClient } from '../generated/prisma/index.js'
const prismaClientSingleton = () => {
return new PrismaClient();
};
export default prismaClientSingleton();

View File

@@ -0,0 +1,10 @@
import { Router } from './router.ts';
import { ResponseTime } from './responseTime.ts';
import type Koa from 'koa';
export function registerMiddlewares(app: Koa) {
const router = new Router();
const responseTime = new ResponseTime();
responseTime.apply(app);
router.apply(app);
}

View File

@@ -0,0 +1,13 @@
import type { Middleware } from './types.ts';
import type Koa from 'koa';
export class ResponseTime implements Middleware {
apply(app: Koa): void {
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.set('X-Response-Time', `${ms}ms`);
});
}
}

View File

@@ -0,0 +1,19 @@
import KoaRouter from '@koa/router';
import type Koa from 'koa';
import type { Middleware } from './types.ts';
import * as application from '../controllers/application.ts';
export class Router implements Middleware {
private router: KoaRouter;
constructor() {
this.router = new KoaRouter({
prefix: '/api',
});
this.router.get('/application/list', application.list);
}
apply(app: Koa) {
app.use(this.router.routes());
app.use(this.router.allowedMethods());
}
}

View File

@@ -0,0 +1,5 @@
import type Koa from 'koa';
export abstract class Middleware {
abstract apply(app: Koa, options?: unknown): void;
}

27
apps/server/package.json Normal file
View File

@@ -0,0 +1,27 @@
{
"name": "server",
"version": "1.0.0",
"description": "",
"scripts": {
"dev": "tsx watch ./app.ts"
},
"keywords": [],
"type": "module",
"author": "",
"license": "ISC",
"dependencies": {
"@koa/router": "^14.0.0",
"@prisma/client": "^6.15.0",
"koa": "^3.0.1"
},
"devDependencies": {
"@tsconfig/node-ts": "^23.6.1",
"@tsconfig/node22": "^22.0.2",
"@types/koa": "^3.0.0",
"@types/koa__router": "^12.0.4",
"@types/node": "^24.3.0",
"prisma": "^6.15.0",
"tsx": "^4.20.5",
"typescript": "^5.9.2"
}
}

Binary file not shown.

View File

@@ -0,0 +1,35 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
output = "../generated/prisma"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model Application {
id Int @id @default(autoincrement())
name String
description String?
repository String
valid Int @default(1)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdBy String
updatedBy String
}
model Environment {
id Int @id @default(autoincrement())
name String
description String?
valid Int @default(1)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdBy String
updatedBy String
}

View File

@@ -0,0 +1,6 @@
{
"extends": [
"@tsconfig/node22/tsconfig.json",
"@tsconfig/node-ts/tsconfig.json"
]
}

32
apps/web/package.json Normal file
View File

@@ -0,0 +1,32 @@
{
"name": "web",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "rsbuild build",
"check": "biome check --write",
"dev": "rsbuild dev --open",
"format": "biome format --write",
"preview": "rsbuild preview"
},
"dependencies": {
"@arco-design/web-react": "^2.66.4",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router": "^7.8.0"
},
"devDependencies": {
"@arco-plugins/unplugin-react": "2.0.0-beta.5",
"@rsbuild/core": "^1.4.13",
"@rsbuild/plugin-less": "^1.4.0",
"@rsbuild/plugin-react": "^1.3.4",
"@rsbuild/plugin-svgr": "^1.2.2",
"@tailwindcss/postcss": "^4.1.11",
"@types/react": "^19.1.9",
"@types/react-dom": "^19.1.7",
"tailwindcss": "^4.1.11",
"typescript": "^5.9.2"
},
"packageManager": "pnpm@9.15.2+sha512.93e57b0126f0df74ce6bff29680394c0ba54ec47246b9cf321f0121d8d9bb03f750a705f24edc3c1180853afd7c2c3b94196d0a3d53d3e069d9e2793ef11f321"
}

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,9 +1,9 @@
import { Route, Routes } from 'react-router'; import { Route, Routes } from 'react-router';
import '@styles/index.css';
import Home from '@pages/home'; import Home from '@pages/home';
import Login from '@pages/login'; import Login from '@pages/login';
import Application from '@pages/application'; import Application from '@pages/application';
import '@styles/index.css';
const App = () => { const App = () => {
return ( return (
<Routes> <Routes>

View File

@@ -1,4 +1,7 @@
import { useState } from "react";
function Application() { function Application() {
const [apps, setApps] = useState<Application[]>([]);
return ( return (
<div> <div>
application application

View File

@@ -0,0 +1,15 @@
enum AppStatus {
Idle = "Pending",
Running = "Running",
Stopped = "Stopped",
}
export interface Application {
id: string;
name: string;
description: string;
git: string;
env: Record<string, string>;
createdAt: string;
status: AppStatus;
}

View File

@@ -1,33 +1,15 @@
{ {
"name": "foka-ci", "name": "ark-ci",
"version": "1.0.0", "version": "1.0.0",
"private": true, "description": "",
"type": "module",
"scripts": { "scripts": {
"build": "rsbuild build", "dev": "pnpm run dev:server && pnpm run dev:web"
"check": "biome check --write",
"dev": "rsbuild dev --open",
"format": "biome format --write",
"preview": "rsbuild preview"
},
"dependencies": {
"@arco-design/web-react": "^2.66.4",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router": "^7.8.0"
}, },
"devDependencies": { "devDependencies": {
"@arco-plugins/unplugin-react": "2.0.0-beta.5", "@biomejs/biome": "2.0.6"
"@biomejs/biome": "2.0.6",
"@rsbuild/core": "^1.4.13",
"@rsbuild/plugin-less": "^1.4.0",
"@rsbuild/plugin-react": "^1.3.4",
"@rsbuild/plugin-svgr": "^1.2.2",
"@tailwindcss/postcss": "^4.1.11",
"@types/react": "^19.1.9",
"@types/react-dom": "^19.1.7",
"tailwindcss": "^4.1.11",
"typescript": "^5.9.2"
}, },
"keywords": ["ci", "ark", "ark-ci"],
"author": "hurole",
"license": "ISC",
"packageManager": "pnpm@9.15.2+sha512.93e57b0126f0df74ce6bff29680394c0ba54ec47246b9cf321f0121d8d9bb03f750a705f24edc3c1180853afd7c2c3b94196d0a3d53d3e069d9e2793ef11f321" "packageManager": "pnpm@9.15.2+sha512.93e57b0126f0df74ce6bff29680394c0ba54ec47246b9cf321f0121d8d9bb03f750a705f24edc3c1180853afd7c2c3b94196d0a3d53d3e069d9e2793ef11f321"
} }

1508
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

2
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,2 @@
packages:
- 'apps/*'