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"
]
}