feat: scaffold NestJS API application

This commit is contained in:
2026-05-27 14:19:42 +05:30
parent 1b10dda42e
commit ee353762a5
11 changed files with 230 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
moduleFileExtensions: ['js', 'json', 'ts'],
rootDir: 'src',
testRegex: '.*\\.spec\\.ts$',
transform: { '^.+\\.(t|j)s$': 'ts-jest' },
testEnvironment: 'node',
};
+8
View File
@@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
+34
View File
@@ -0,0 +1,34 @@
{
"name": "@tower/api",
"version": "0.0.1",
"scripts": {
"build": "nest build",
"dev": "nest start --watch",
"start": "node dist/main",
"test": "jest",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^11.0.0",
"@nestjs/config": "^4.0.0",
"@nestjs/core": "^11.0.0",
"@nestjs/platform-express": "^11.0.0",
"@prisma/client": "^6.0.0",
"@tower/config": "workspace:*",
"@tower/logger": "workspace:*",
"@tower/types": "workspace:*",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.0"
},
"devDependencies": {
"@nestjs/cli": "^11.0.0",
"@nestjs/schematics": "^11.0.0",
"@nestjs/testing": "^11.0.0",
"@types/jest": "^29.0.0",
"@types/node": "^22.0.0",
"jest": "^29.0.0",
"prisma": "^6.0.0",
"ts-jest": "^29.0.0",
"typescript": "^5.7.0"
}
}
+93
View File
@@ -0,0 +1,93 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Group {
id String @id @default(cuid())
platform String
platformId String
name String
description String?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
messages Message[]
syncRoutesFrom SyncRoute[] @relation("sourceGroup")
syncRoutesTo SyncRoute[] @relation("targetGroup")
consentRecords ConsentRecord[]
@@unique([platform, platformId])
}
model Message {
id String @id @default(cuid())
platform String
platformMsgId String
sourceGroupId String
sourceGroup Group @relation(fields: [sourceGroupId], references: [id])
senderJid String
senderName String?
content String
mediaUrl String?
tags String[]
status MessageStatus @default(PENDING)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
approval Approval?
@@unique([platform, platformMsgId])
}
enum MessageStatus {
PENDING
APPROVED
REJECTED
DISTRIBUTED
ARCHIVED
}
model Approval {
id String @id @default(cuid())
messageId String @unique
message Message @relation(fields: [messageId], references: [id])
adminId String
decision ApprovalDecision
notes String?
decidedAt DateTime @default(now())
}
enum ApprovalDecision {
APPROVED
REJECTED
}
model SyncRoute {
id String @id @default(cuid())
sourceGroupId String
sourceGroup Group @relation("sourceGroup", fields: [sourceGroupId], references: [id])
targetGroupId String
targetGroup Group @relation("targetGroup", fields: [targetGroupId], references: [id])
isActive Boolean @default(true)
createdAt DateTime @default(now())
@@unique([sourceGroupId, targetGroupId])
}
model ConsentRecord {
id String @id @default(cuid())
groupId String
group Group @relation(fields: [groupId], references: [id])
memberJid String
consentType String
grantedAt DateTime @default(now())
revokedAt DateTime?
@@unique([groupId, memberJid, consentType])
}
+13
View File
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { PrismaModule } from './prisma/prisma.module';
import { HealthModule } from './modules/health/health.module';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
PrismaModule,
HealthModule,
],
})
export class AppModule {}
+12
View File
@@ -0,0 +1,12 @@
import 'reflect-metadata';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const port = process.env['API_PORT'] ?? 3001;
await app.listen(port);
console.log(`TOWER API running on port ${port}`);
}
bootstrap();
@@ -0,0 +1,13 @@
import { Controller, Get } from '@nestjs/common';
@Controller('health')
export class HealthController {
@Get()
check() {
return {
status: 'ok',
service: 'tower-api',
timestamp: new Date().toISOString(),
};
}
}
@@ -0,0 +1,7 @@
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
@Module({
controllers: [HealthController],
})
export class HealthModule {}
+9
View File
@@ -0,0 +1,9 @@
import { Global, Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
+16
View File
@@ -0,0 +1,16 @@
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService
extends PrismaClient
implements OnModuleInit, OnModuleDestroy
{
async onModuleInit() {
await this.$connect();
}
async onModuleDestroy() {
await this.$disconnect();
}
}
+18
View File
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "ES2021",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": true,
"noImplicitAny": true
}
}