Compare commits

...

3 Commits

37 changed files with 7303 additions and 86 deletions

View File

@@ -0,0 +1 @@
{"files":{"packages/ui/.turbo/turbo-typecheck.log":{"size":110,"mtime_nanos":1782463882776752800,"mode":420,"is_dir":false}},"order":["packages/ui/.turbo/turbo-typecheck.log"]}

View File

@@ -0,0 +1 @@
{"hash":"1bb9376dadaf6981","duration":3041,"sha":"8d659317b9d990b14324262928aa78e4e15fa4b6","dirty_hash":"fa9510cea9388d4bc05912f5175c0d4e7a04f11461edc6bd345dc3b51e6b0716"}

BIN
.turbo/cache/1bb9376dadaf6981.tar.zst vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
{"files":{"packages/config/dist/index.d.ts":{"size":1874,"mtime_nanos":1782463696518849500,"mode":420,"is_dir":false},"packages/config/dist/index.js":{"size":3043,"mtime_nanos":1782463901598335900,"mode":420,"is_dir":false},"packages/config/dist/index.js.map":{"size":3214,"mtime_nanos":1782463901597337500,"mode":420,"is_dir":false},"packages/config/dist/index.d.ts.map":{"size":521,"mtime_nanos":1782463901632066900,"mode":420,"is_dir":false},"packages/config/.turbo/turbo-build.log":{"size":105,"mtime_nanos":1782463901731813200,"mode":420,"is_dir":false},"packages/config/dist":{"size":0,"mtime_nanos":0,"mode":0,"is_dir":true}},"order":["packages/config/.turbo/turbo-build.log","packages/config/dist","packages/config/dist/index.d.ts","packages/config/dist/index.d.ts.map","packages/config/dist/index.js","packages/config/dist/index.js.map"]}

View File

@@ -0,0 +1 @@
{"hash":"355c1dee9a97e514","duration":3276,"sha":"fdf8392d42c0988594a73a8c0214d44856ec48c3","dirty_hash":"5bc41ac9379475cbd00453e982eb11026dc0bb28a119fca3c562993b73edb696"}

BIN
.turbo/cache/355c1dee9a97e514.tar.zst vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
{"files":{"apps/web/.turbo/turbo-test.log":{"size":116,"mtime_nanos":1782463899572072300,"mode":420,"is_dir":false}},"order":["apps/web/.turbo/turbo-test.log"]}

View File

@@ -0,0 +1 @@
{"hash":"61b6599d632adec7","duration":1122,"sha":"fdf8392d42c0988594a73a8c0214d44856ec48c3","dirty_hash":"5bc41ac9379475cbd00453e982eb11026dc0bb28a119fca3c562993b73edb696"}

BIN
.turbo/cache/61b6599d632adec7.tar.zst vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
{"files":{"packages/config/.turbo/turbo-typecheck.log":{"size":118,"mtime_nanos":1782463883269330100,"mode":420,"is_dir":false}},"order":["packages/config/.turbo/turbo-typecheck.log"]}

View File

@@ -0,0 +1 @@
{"hash":"6abc0126d916703e","duration":3531,"sha":"8d659317b9d990b14324262928aa78e4e15fa4b6","dirty_hash":"fa9510cea9388d4bc05912f5175c0d4e7a04f11461edc6bd345dc3b51e6b0716"}

BIN
.turbo/cache/6abc0126d916703e.tar.zst vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
{"files":{"packages/database/.turbo/turbo-typecheck.log":{"size":1438,"mtime_nanos":1782463887467109000,"mode":420,"is_dir":false}},"order":["packages/database/.turbo/turbo-typecheck.log"]}

View File

@@ -0,0 +1 @@
{"hash":"7c07bbd04939c5ca","duration":7728,"sha":"8d659317b9d990b14324262928aa78e4e15fa4b6","dirty_hash":"fa9510cea9388d4bc05912f5175c0d4e7a04f11461edc6bd345dc3b51e6b0716"}

BIN
.turbo/cache/7c07bbd04939c5ca.tar.zst vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
{"files":{"packages/contracts/.turbo/turbo-typecheck.log":{"size":124,"mtime_nanos":1782463881488108400,"mode":420,"is_dir":false}},"order":["packages/contracts/.turbo/turbo-typecheck.log"]}

View File

@@ -0,0 +1 @@
{"hash":"80b11947ece0af90","duration":1747,"sha":"8d659317b9d990b14324262928aa78e4e15fa4b6","dirty_hash":"fa9510cea9388d4bc05912f5175c0d4e7a04f11461edc6bd345dc3b51e6b0716"}

BIN
.turbo/cache/80b11947ece0af90.tar.zst vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
{"files":{"packages/database/dist":{"size":0,"mtime_nanos":0,"mode":0,"is_dir":true},"packages/database/dist/index.d.ts":{"size":585,"mtime_nanos":1782463905198409500,"mode":420,"is_dir":false},"packages/database/dist/index.d.ts.map":{"size":447,"mtime_nanos":1782463905197280200,"mode":420,"is_dir":false},"packages/database/dist/index.js":{"size":962,"mtime_nanos":1782463905155282000,"mode":420,"is_dir":false},"packages/database/.turbo/turbo-build.log":{"size":552,"mtime_nanos":1782463905303835800,"mode":420,"is_dir":false},"packages/database/dist/index.js.map":{"size":617,"mtime_nanos":1782463905154791000,"mode":420,"is_dir":false}},"order":["packages/database/.turbo/turbo-build.log","packages/database/dist","packages/database/dist/index.d.ts","packages/database/dist/index.d.ts.map","packages/database/dist/index.js","packages/database/dist/index.js.map"]}

View File

@@ -0,0 +1 @@
{"hash":"a03f7958fef5192d","duration":6846,"sha":"fdf8392d42c0988594a73a8c0214d44856ec48c3","dirty_hash":"5bc41ac9379475cbd00453e982eb11026dc0bb28a119fca3c562993b73edb696"}

BIN
.turbo/cache/a03f7958fef5192d.tar.zst vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
{"files":{"packages/auth/.turbo/turbo-typecheck.log":{"size":114,"mtime_nanos":1782463881442659700,"mode":420,"is_dir":false}},"order":["packages/auth/.turbo/turbo-typecheck.log"]}

View File

@@ -0,0 +1 @@
{"hash":"eccb56f09230a2d5","duration":1701,"sha":"8d659317b9d990b14324262928aa78e4e15fa4b6","dirty_hash":"fa9510cea9388d4bc05912f5175c0d4e7a04f11461edc6bd345dc3b51e6b0716"}

BIN
.turbo/cache/eccb56f09230a2d5.tar.zst vendored Normal file

Binary file not shown.

View File

@@ -1,17 +1,39 @@
{ {
"name": "@hexahost/api", "name": "@hexahost/api",
"version": "0.1.0", "version": "0.0.0",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "tsc", "build": "nest build",
"dev": "nest start --watch",
"start": "node dist/main.js",
"start:prod": "node dist/main.js",
"lint": "node -e \"process.exit(0)\"", "lint": "node -e \"process.exit(0)\"",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"test": "node --test test/**/*.test.js", "test": "node --test test/**/*.test.js"
"start": "node dist/main.js", },
"dev": "tsc && node dist/main.js" "dependencies": {
"@fastify/static": "^8.0.4",
"@hexahost/config": "workspace:*",
"@hexahost/contracts": "workspace:*",
"@hexahost/database": "workspace:*",
"@nestjs/common": "^11.0.7",
"@nestjs/core": "^11.0.7",
"@nestjs/platform-fastify": "^11.0.7",
"@nestjs/swagger": "^11.0.3",
"@nestjs/throttler": "^6.4.0",
"fastify": "^5.2.1",
"nestjs-pino": "^4.3.0",
"pino": "^9.6.0",
"pino-http": "^10.4.0",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.10.2", "@hexahost/typescript-config": "workspace:*",
"typescript": "^5.7.2" "@nestjs/cli": "^11.0.2",
"@nestjs/schematics": "^11.0.0",
"@types/node": "^22.10.7",
"pino-pretty": "^13.0.0",
"typescript": "^5.7.3"
} }
} }

View File

@@ -39,7 +39,8 @@ export class Rfc7807ExceptionFilter implements ExceptionFilter {
instance: string, instance: string,
): ProblemDetails { ): ProblemDetails {
if (exception instanceof HttpException) { if (exception instanceof HttpException) {
const response = exception.getResponse(); const httpException = exception;
const response = httpException.getResponse();
if (typeof response === 'object' && response !== null) { if (typeof response === 'object' && response !== null) {
const body = response as Record<string, unknown>; const body = response as Record<string, unknown>;
@@ -55,7 +56,7 @@ export class Rfc7807ExceptionFilter implements ExceptionFilter {
if (typeof body['message'] === 'string') { if (typeof body['message'] === 'string') {
return createProblemDetails({ return createProblemDetails({
title: exception.name, title: httpException.name,
status, status,
detail: body['message'], detail: body['message'],
instance, instance,
@@ -68,7 +69,7 @@ export class Rfc7807ExceptionFilter implements ExceptionFilter {
status, status,
detail: 'Request validation failed', detail: 'Request validation failed',
instance, instance,
errors: body['message'].map((message) => ({ errors: body['message'].map((message: unknown) => ({
message: String(message), message: String(message),
})), })),
}); });
@@ -76,9 +77,9 @@ export class Rfc7807ExceptionFilter implements ExceptionFilter {
} }
return createProblemDetails({ return createProblemDetails({
title: exception.name, title: httpException.name,
status, status,
detail: exception.message, detail: httpException.message,
instance, instance,
}); });
} }

View File

@@ -1,26 +1,48 @@
import http from "node:http"; import { randomUUID } from 'node:crypto';
const port = Number(process.env.API_PORT ?? 3001); import { NestFactory } from '@nestjs/core';
const appName = process.env.APP_NAME ?? "HexaHost GameCloud"; import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { Logger } from 'nestjs-pino';
const server = http.createServer((req, res) => { import { validateConfig } from '@hexahost/config';
if (req.url === "/healthz" || req.url === "/health") {
res.writeHead(200, { "Content-Type": "application/json" }); import { AppModule } from './app.module';
res.end( import { Rfc7807ExceptionFilter } from './common/filters/rfc7807-exception.filter';
JSON.stringify({
status: "ok", async function bootstrap(): Promise<void> {
service: "api", const config = validateConfig();
app: appName,
phase: "0", const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter({
genReqId: () => randomUUID(),
requestIdHeader: 'x-request-id',
requestIdLogLabel: 'requestId',
}), }),
{
bufferLogs: true,
},
); );
return;
}
res.writeHead(404, { "Content-Type": "application/json" }); app.useLogger(app.get(Logger));
res.end(JSON.stringify({ status: "not_found" })); app.setGlobalPrefix('api/v1');
}); app.useGlobalFilters(new Rfc7807ExceptionFilter());
server.listen(port, () => { const swaggerConfig = new DocumentBuilder()
console.log(JSON.stringify({ level: "info", msg: "api listening", port })); .setTitle(config.APP_NAME)
}); .setDescription('HexaHost GameCloud REST API')
.setVersion('0.0.0')
.addServer(config.API_URL)
.build();
const document = SwaggerModule.createDocument(app, swaggerConfig);
SwaggerModule.setup('api/v1/docs', app, document);
await app.listen(config.API_PORT, '0.0.0.0');
}
void bootstrap();

View File

@@ -1,13 +1,10 @@
{ {
"extends": "@hexahost/typescript-config/nestjs.json",
"compilerOptions": { "compilerOptions": {
"target": "ES2022", "outDir": "./dist",
"module": "NodeNext", "rootDir": "./src",
"moduleResolution": "NodeNext", "baseUrl": "./"
"outDir": "dist",
"rootDir": "src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}, },
"include": ["src/**/*"] "include": ["src/**/*"],
"exclude": ["node_modules", "dist", "test"]
} }

File diff suppressed because one or more lines are too long

View File

@@ -1,17 +1,27 @@
{ {
"name": "@hexahost/worker", "name": "@hexahost/worker",
"version": "0.1.0", "version": "0.0.0",
"private": true, "private": true,
"main": "./dist/index.js",
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"dev": "tsx watch src/index.ts",
"start": "node dist/index.js",
"lint": "node -e \"process.exit(0)\"", "lint": "node -e \"process.exit(0)\"",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"test": "node --test test/**/*.test.js", "test": "node --test test/**/*.test.js"
"start": "node dist/main.js", },
"dev": "tsc && node dist/main.js" "dependencies": {
"@hexahost/config": "workspace:*",
"@hexahost/database": "workspace:*",
"bullmq": "^5.34.10",
"ioredis": "^5.4.2",
"pino": "^9.6.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.10.2", "@hexahost/typescript-config": "workspace:*",
"typescript": "^5.7.2" "@types/node": "^22.10.7",
"tsx": "^4.19.2",
"typescript": "^5.7.3"
} }
} }

View File

@@ -1,4 +1,4 @@
import { Worker, type ConnectionOptions } from 'bullmq'; import { Job, Worker, type ConnectionOptions } from 'bullmq';
import IORedis from 'ioredis'; import IORedis from 'ioredis';
import { validateConfig } from '@hexahost/config'; import { validateConfig } from '@hexahost/config';
@@ -20,7 +20,7 @@ function createQueueWorker(
): Worker { ): Worker {
return new Worker( return new Worker(
queueName, queueName,
async (job) => { async (job: Job) => {
logger.info( logger.info(
{ queue: queueName, jobId: job.id, jobName: job.name }, { queue: queueName, jobId: job.id, jobName: job.name },
'Processing job', 'Processing job',
@@ -42,11 +42,11 @@ async function bootstrap(): Promise<void> {
); );
for (const worker of workers) { for (const worker of workers) {
worker.on('completed', (job) => { worker.on('completed', (job: Job | undefined) => {
logger.info({ queue: worker.name, jobId: job.id }, 'Job completed'); logger.info({ queue: worker.name, jobId: job?.id }, 'Job completed');
}); });
worker.on('failed', (job, error) => { worker.on('failed', (job: Job | undefined, error: Error) => {
logger.error( logger.error(
{ queue: worker.name, jobId: job?.id, err: error }, { queue: worker.name, jobId: job?.id, err: error },
'Job failed', 'Job failed',

View File

@@ -1,14 +0,0 @@
const redisUrl = process.env.REDIS_URL ?? "redis://localhost:6379";
console.log(
JSON.stringify({
level: "info",
msg: "worker started (phase 0 stub)",
redis: redisUrl.replace(/:[^:@]+@/, ":***@"),
}),
);
// Phase 0: worker process stays alive for compose; BullMQ queues land in Phase 1+
setInterval(() => {
console.log(JSON.stringify({ level: "debug", msg: "worker heartbeat" }));
}, 60_000);

View File

@@ -1,13 +1,9 @@
{ {
"extends": "@hexahost/typescript-config/library.json",
"compilerOptions": { "compilerOptions": {
"target": "ES2022", "outDir": "./dist",
"module": "NodeNext", "rootDir": "./src"
"moduleResolution": "NodeNext",
"outDir": "dist",
"rootDir": "src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}, },
"include": ["src/**/*"] "include": ["src/**/*"],
"exclude": ["node_modules", "dist", "src/main.ts"]
} }

View File

@@ -11,7 +11,10 @@
"build": "turbo run build", "build": "turbo run build",
"lint": "turbo run lint", "lint": "turbo run lint",
"typecheck": "turbo run typecheck", "typecheck": "turbo run typecheck",
"test": "turbo run test" "test": "turbo run test",
"db:generate": "pnpm --filter @hexahost/database db:generate",
"db:migrate": "pnpm --filter @hexahost/database db:migrate",
"db:push": "pnpm --filter @hexahost/database db:push"
}, },
"devDependencies": { "devDependencies": {
"turbo": "^2.3.3", "turbo": "^2.3.3",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

7175
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff