chore(backend): bootstrap Express + TypeScript + Prisma
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "eme-backend",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "EME — Emprunt Matériel ENSUP — API REST",
|
||||||
|
"main": "dist/app.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"start": "node dist/app.js",
|
||||||
|
"dev": "nodemon --exec ts-node src/app.ts",
|
||||||
|
"prisma:generate": "prisma generate",
|
||||||
|
"prisma:migrate": "prisma migrate dev",
|
||||||
|
"prisma:studio": "prisma studio"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"type": "commonjs",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/cors": "^2.8.19",
|
||||||
|
"@types/express": "^5.0.6",
|
||||||
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
|
"@types/node": "^25.9.1",
|
||||||
|
"nodemon": "^3.1.14",
|
||||||
|
"prisma": "^7.8.0",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^6.0.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/adapter-mssql": "^7.8.0",
|
||||||
|
"@prisma/client": "^7.8.0",
|
||||||
|
"cors": "^2.8.6",
|
||||||
|
"dotenv": "^17.4.2",
|
||||||
|
"express": "^5.2.1",
|
||||||
|
"jsonwebtoken": "^9.0.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import 'dotenv/config';
|
||||||
|
import { defineConfig, env } from 'prisma/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
schema: 'prisma/schema.prisma',
|
||||||
|
migrations: {
|
||||||
|
path: 'prisma/migrations',
|
||||||
|
},
|
||||||
|
datasource: {
|
||||||
|
url: env('DATABASE_URL'),
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import express from 'express';
|
||||||
|
import cors from 'cors';
|
||||||
|
import { env } from './config/env';
|
||||||
|
import { connectDatabase, disconnectDatabase } from './db/prisma';
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
// Middlewares globaux
|
||||||
|
app.use(cors());
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
// Route de test
|
||||||
|
app.get('/health', (_req, res) => {
|
||||||
|
res.json({ status: 'ok', app: 'EME API' });
|
||||||
|
});
|
||||||
|
|
||||||
|
async function bootstrap(): Promise<void> {
|
||||||
|
await connectDatabase();
|
||||||
|
|
||||||
|
const server = app.listen(env.port, () => {
|
||||||
|
console.log(`[EME API] Serveur demarre sur http://localhost:${env.port}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('SIGINT', async () => {
|
||||||
|
await disconnectDatabase();
|
||||||
|
server.close(() => process.exit(0));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bootstrap().catch((error: unknown) => {
|
||||||
|
console.error('[EME API] Echec du demarrage', error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import dotenv from 'dotenv';
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
function required(name: string): string {
|
||||||
|
const value = process.env[name];
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
throw new Error(`Variable d'environnement manquante: ${name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function numberFromEnv(name: string, defaultValue: number): number {
|
||||||
|
const value = process.env[name];
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsed = Number(value);
|
||||||
|
|
||||||
|
if (Number.isNaN(parsed)) {
|
||||||
|
throw new Error(`Variable d'environnement invalide: ${name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const env = {
|
||||||
|
port: numberFromEnv('PORT', 3000),
|
||||||
|
nodeEnv: process.env.NODE_ENV || 'development',
|
||||||
|
frontendUrl: required('FRONTEND_URL'),
|
||||||
|
databaseUrl: required('DATABASE_URL'),
|
||||||
|
db: {
|
||||||
|
server: required('DB_SERVER'),
|
||||||
|
port: numberFromEnv('DB_PORT', 1433),
|
||||||
|
name: required('DB_NAME'),
|
||||||
|
user: required('DB_USER'),
|
||||||
|
password: required('DB_PASSWORD'),
|
||||||
|
},
|
||||||
|
azure: {
|
||||||
|
tenantId: required('AZURE_TENANT_ID'),
|
||||||
|
clientId: required('AZURE_CLIENT_ID'),
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
import { PrismaMssql } from '@prisma/adapter-mssql';
|
||||||
|
import { env } from '../config/env';
|
||||||
|
|
||||||
|
const adapter = new PrismaMssql({
|
||||||
|
server: env.db.server,
|
||||||
|
port: env.db.port,
|
||||||
|
database: env.db.name,
|
||||||
|
user: env.db.user,
|
||||||
|
password: env.db.password,
|
||||||
|
options: {
|
||||||
|
encrypt: true,
|
||||||
|
trustServerCertificate: true,
|
||||||
|
},
|
||||||
|
pool: {
|
||||||
|
max: 10,
|
||||||
|
min: 0,
|
||||||
|
idleTimeoutMillis: 30000,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
|
export async function connectDatabase(): Promise<void> {
|
||||||
|
await prisma.$connect();
|
||||||
|
console.log('[DB] Connexion SQL Server via Prisma etablie');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function disconnectDatabase(): Promise<void> {
|
||||||
|
await prisma.$disconnect();
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
export const STATUT_MATERIEL = [
|
||||||
|
'DISPONIBLE',
|
||||||
|
'EMPRUNTE',
|
||||||
|
'NON_CONFORME',
|
||||||
|
'DETERIORE',
|
||||||
|
'MAINTENANCE',
|
||||||
|
'INDISPONIBLE',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type StatutMateriel = (typeof STATUT_MATERIEL)[number];
|
||||||
|
|
||||||
|
export const STATUT_EMPRUNT = [
|
||||||
|
'EN_COURS',
|
||||||
|
'EN_RETARD',
|
||||||
|
'CLOTURE',
|
||||||
|
'RETOUR_NON_CONFORME',
|
||||||
|
'ANNULE',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type StatutEmprunt = (typeof STATUT_EMPRUNT)[number];
|
||||||
|
|
||||||
|
export const TYPE_CHECKLIST = ['DEPART', 'RETOUR'] as const;
|
||||||
|
|
||||||
|
export type TypeChecklist = (typeof TYPE_CHECKLIST)[number];
|
||||||
|
|
||||||
|
export const ETAT_CHECKLIST_ELEMENT = ['PRESENT', 'ABSENT', 'DETERIORE'] as const;
|
||||||
|
|
||||||
|
export type EtatChecklistElement = (typeof ETAT_CHECKLIST_ELEMENT)[number];
|
||||||
|
|
||||||
|
export const STATUT_ANOMALIE = [
|
||||||
|
'DETECTEE',
|
||||||
|
'EN_COURS_TRAITEMENT',
|
||||||
|
'RESOLUE',
|
||||||
|
'CLOTUREE',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type StatutAnomalie = (typeof STATUT_ANOMALIE)[number];
|
||||||
|
|
||||||
|
export const MODE_IDENTIFICATION = ['QR_CODE', 'MAIL_ENSUP'] as const;
|
||||||
|
|
||||||
|
export type ModeIdentification = (typeof MODE_IDENTIFICATION)[number];
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "commonjs",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user