feat: add Drizzle ORM, Supabase admin client, Swagger, Helmet, Pino logging, rate limiting
This commit is contained in:
parent
ce5cf134ae
commit
3fe8d24179
9 changed files with 104 additions and 10 deletions
26
.env.example
26
.env.example
|
|
@ -1,9 +1,27 @@
|
|||
PORT=3000
|
||||
NODE_ENV=development
|
||||
PORT=3001
|
||||
|
||||
# Supabase (CEO-OS project, Coolify)
|
||||
DATABASE_URL=postgresql://postgres:CHANGE_ME@supabase-db:5432/postgres
|
||||
SUPABASE_URL=http://supabasekong-to6mg3yxqit4ac9nl0otnvav.152.53.112.35.sslip.io:8000
|
||||
DATABASE_URL=
|
||||
SUPABASE_URL=
|
||||
SUPABASE_ANON_KEY=
|
||||
SUPABASE_SERVICE_ROLE_KEY=
|
||||
|
||||
# Redis (ceo-os-redis, Coolify)
|
||||
REDIS_URL=redis://default:CHANGE_ME@ceo-os-redis:6379/0
|
||||
REDIS_URL=
|
||||
|
||||
APP_ENCRYPTION_KEY=
|
||||
JWT_SECRET=
|
||||
COOKIE_SECRET=
|
||||
|
||||
AI_GATEWAY_URL=
|
||||
AI_GATEWAY_API_KEY=
|
||||
|
||||
N8N_BASE_URL=
|
||||
N8N_WEBHOOK_SECRET=
|
||||
|
||||
SMTP_HOST=
|
||||
SMTP_PORT=
|
||||
SMTP_USER=
|
||||
SMTP_PASSWORD=
|
||||
SMTP_FROM=
|
||||
|
|
|
|||
13
README.md
13
README.md
|
|
@ -10,4 +10,15 @@ cp .env.example .env
|
|||
npm run start:dev
|
||||
```
|
||||
|
||||
`GET /health` returneaza statusul serviciului.
|
||||
`GET /health` returneaza statusul serviciului. `GET /docs` expune Swagger UI.
|
||||
|
||||
## Stack
|
||||
|
||||
NestJS, Drizzle ORM (`src/db`), Supabase Admin SDK (`src/supabase`), BullMQ/Redis, class-validator/Zod, Helmet, rate limiting (Throttler), logging Pino.
|
||||
|
||||
## Migratii
|
||||
|
||||
```bash
|
||||
npm run db:generate
|
||||
npm run db:migrate
|
||||
```
|
||||
|
|
|
|||
8
drizzle.config.ts
Normal file
8
drizzle.config.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { defineConfig } from 'drizzle-kit';
|
||||
|
||||
export default defineConfig({
|
||||
schema: './src/db/schema.ts',
|
||||
out: './drizzle',
|
||||
dialect: 'postgresql',
|
||||
dbCredentials: { url: process.env.DATABASE_URL! },
|
||||
});
|
||||
22
package.json
22
package.json
|
|
@ -9,19 +9,37 @@
|
|||
"start": "node dist/main",
|
||||
"start:dev": "nest start --watch",
|
||||
"lint": "eslint \"src/**/*.ts\"",
|
||||
"test": "jest"
|
||||
"test": "jest",
|
||||
"db:generate": "drizzle-kit generate",
|
||||
"db:migrate": "drizzle-kit migrate"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/bullmq": "^11.0.0",
|
||||
"@nestjs/common": "^11.0.0",
|
||||
"@nestjs/config": "^4.0.0",
|
||||
"@nestjs/core": "^11.0.0",
|
||||
"@nestjs/platform-express": "^11.0.0",
|
||||
"@nestjs/swagger": "^8.0.0",
|
||||
"@nestjs/throttler": "^6.2.0",
|
||||
"@supabase/supabase-js": "^2.47.0",
|
||||
"bullmq": "^5.28.0",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"drizzle-orm": "^0.36.0",
|
||||
"helmet": "^8.0.0",
|
||||
"ioredis": "^5.4.1",
|
||||
"nestjs-pino": "^4.1.0",
|
||||
"pg": "^8.13.0",
|
||||
"pino-http": "^10.3.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1"
|
||||
"rxjs": "^7.8.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^11.0.0",
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/pg": "^8.11.10",
|
||||
"drizzle-kit": "^0.28.0",
|
||||
"typescript": "^5.6.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { ThrottlerModule } from '@nestjs/throttler';
|
||||
import { LoggerModule } from 'nestjs-pino';
|
||||
import { HealthController } from './health/health.controller';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule.forRoot({ isGlobal: true })],
|
||||
imports: [
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
LoggerModule.forRoot(),
|
||||
ThrottlerModule.forRoot([{ ttl: 60000, limit: 100 }]),
|
||||
],
|
||||
controllers: [HealthController],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
|
|
|||
7
src/db/client.ts
Normal file
7
src/db/client.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||
import { Pool } from 'pg';
|
||||
import * as schema from './schema';
|
||||
|
||||
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
||||
|
||||
export const db = drizzle(pool, { schema });
|
||||
7
src/db/schema.ts
Normal file
7
src/db/schema.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { pgTable, uuid, text, timestamp } from 'drizzle-orm/pg-core';
|
||||
|
||||
export const organizations = pgTable('organizations', {
|
||||
id: uuid('id').defaultRandom().primaryKey(),
|
||||
name: text('name').notNull(),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
});
|
||||
17
src/main.ts
17
src/main.ts
|
|
@ -1,11 +1,24 @@
|
|||
import 'reflect-metadata';
|
||||
import helmet from 'helmet';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { Logger } from 'nestjs-pino';
|
||||
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
const app = await NestFactory.create(AppModule, { bufferLogs: true });
|
||||
app.useLogger(app.get(Logger));
|
||||
app.use(helmet());
|
||||
app.enableCors();
|
||||
const port = process.env.PORT ?? 3000;
|
||||
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('CEO OS API')
|
||||
.setVersion('0.1.0')
|
||||
.build();
|
||||
const document = SwaggerModule.createDocument(app, config);
|
||||
SwaggerModule.setup('docs', app, document);
|
||||
|
||||
const port = process.env.PORT ?? 3001;
|
||||
await app.listen(port);
|
||||
}
|
||||
|
||||
|
|
|
|||
6
src/supabase/supabase.client.ts
Normal file
6
src/supabase/supabase.client.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import { createClient } from '@supabase/supabase-js';
|
||||
|
||||
export const supabaseAdmin = createClient(
|
||||
process.env.SUPABASE_URL ?? '',
|
||||
process.env.SUPABASE_SERVICE_ROLE_KEY ?? '',
|
||||
);
|
||||
Loading…
Reference in a new issue