chore(repo): restructure as monorepo with server and client workspaces

- move NestJS app into server/ subdirectory
- add client/ React+TypeScript (Vite) app with Hello World
- update docker-compose to build and run both services
- add root package.json declaring npm workspaces
- update .gitignore to cover node_modules and dist at all depths
This commit is contained in:
2026-04-08 21:28:01 +03:00
parent afc4627353
commit 5cc16725fb
88 changed files with 12637 additions and 405 deletions
+40
View File
@@ -0,0 +1,40 @@
import { ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer";
import { IsIn, IsInt, IsOptional, IsString, Min } from "class-validator";
export class PaginationQueryDto<TOrderBy extends string = string> {
@ApiPropertyOptional({ example: 1, default: 1 })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
page?: number = 1;
@ApiPropertyOptional({ example: 20, default: 20 })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
limit?: number = 20;
@ApiPropertyOptional({
example: "id",
default: "id",
description: "Field to order by",
})
@IsOptional()
@IsString()
orderBy?: TOrderBy;
@ApiPropertyOptional({ enum: ["ASC", "DESC"], default: "ASC" })
@IsOptional()
@IsIn(["ASC", "DESC"])
orderDir?: "ASC" | "DESC" = "ASC";
}
export interface PaginatedResult<T> {
data: T[];
total: number;
page: number;
limit: number;
}
+11
View File
@@ -0,0 +1,11 @@
import { AsyncLocalStorage } from "async_hooks";
export interface TraceStore {
traceId: string;
}
export const traceStorage = new AsyncLocalStorage<TraceStore>();
export function getTraceId(): string | undefined {
return traceStorage.getStore()?.traceId;
}
+31
View File
@@ -0,0 +1,31 @@
import { ConsoleLogger, ConsoleLoggerOptions, LogLevel } from "@nestjs/common";
import { getTraceId } from "./trace-context";
export class TraceLogger extends ConsoleLogger {
constructor(context?: string, options: ConsoleLoggerOptions = {}) {
super(context as string, options);
}
protected override getTimestamp(): string {
return new Date().toISOString();
}
protected override formatMessage(
logLevel: LogLevel,
message: unknown,
_pidMessage: string,
_formattedLogLevel: string,
contextMessage: string,
timestampDiff: string,
): string {
const output = this.stringifyMessage(message, logLevel);
const level = this.colorize(logLevel.toUpperCase(), logLevel);
return `${this.getTimestamp()} ${level} ${contextMessage}${output}${timestampDiff}\n`;
}
protected override formatContext(context: string): string {
const traceId = getTraceId();
const traced = traceId ? `${context}:${traceId}` : context;
return super.formatContext(traced);
}
}