Skip to content
Snippets Groups Projects
Commit d61c76dc authored by Abelangel's avatar Abelangel
Browse files

minikube init

parent cd0eb9a2
No related branches found
No related tags found
No related merge requests found
Showing
with 366 additions and 24924 deletions
Source diff could not be displayed: it is too large. Options to address this: view the blob.
{
"name": "helloworld",
"description": "Template du projet d'architecture web",
"version": "1.0.0",
"license": "",
"author": "Michaël Minelli <michael-jean.minelli@hesge.ch>",
"main": "dist/src/app.js",
"scripts": {
"env:decrypt": "npx dotenvx decrypt",
"env:update": "npx dotenvx encrypt",
"prisma:generate": "npx prisma generate",
"build:project": "npm run prisma:generate && npx tsc --project ./ && cp -R assets dist/assets",
"build": "npm run build:project",
"database:migrate:create": "npx dotenvx run -- npx prisma migrate dev --create-only",
"database:migrate:deploy": "npx dotenvx run -- npx prisma migrate deploy",
"database:seed:dev": "npm run build; npx dotenvx run -- npx prisma db seed",
"database:seed:prod": "npm run build; npx dotenvx run -- NODE_ENV=production npx prisma db seed",
"database:deploy:dev": "npm run database:migrate:deploy && npm run database:seed:dev",
"database:deploy:prod": "npm run database:migrate:deploy && npm run database:seed:prod",
"start:dev": "npm run prisma:generate && npx dotenvx run -- npx nodemon src/app.ts",
"start:prod": "npm run build && npx dotenvx run -- NODE_ENV=production npx node dist/src/app.js",
"clean": "rm -R dist/*",
"test": "jest"
},
"prisma": {
"seed": "node dist/prisma/seed"
},
"dependencies": {
"@dotenvx/dotenvx": "^0.34.0",
"@prisma/client": "^6.3.1",
"axios": "^1.7.2",
"bcryptjs": "^2.4.3",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.19.2",
"express-validator": "^7.0.1",
"form-data": "^4.0.0",
"helmet": "^7.1.0",
"http-status-codes": "^2.3.0",
"jsonwebtoken": "^9.0.2",
"morgan": "^1.10.0",
"multer": "^1.4.5-lts.1",
"winston": "^3.13.0"
},
"devDependencies": {
"@types/bcryptjs": "^2.4.6",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.14",
"@types/jsonwebtoken": "^9.0.6",
"@types/morgan": "^1.9.9",
"@types/multer": "^1.4.11",
"@types/node": "^20.12.7",
"jest": "^29.7.0",
"node": "^20.12.2",
"nodemon": "^3.1.0",
"npm": "^10.5.2",
"prisma": "^6.3.1",
"ts-jest": "^29.2.6",
"ts-node": "^10.9.2",
"tsx": "^4.7.2",
"typescript": "^5.4.5"
}
}
sonar.projectKey=helloworld
sonar.projectName=Helloworld Microservice
sonar.projectVersion=1.0-SNAPSHOT
sonar.sources=src
sonar.tests=test
sonar.language=ts
sonar.typescript.tsconfigPath=tsconfig.json
sonar.sourceEncoding=UTF-8
sonar.exclusions=node_modules/**,target/**,prisma/migrations/**
sonar.test.inclusions=**/*.test.ts
sonar.typescript.lcov.reportPaths=coverage/lcov.info
\ No newline at end of file
import express, { NextFunction } from 'express';
import jwt from 'jsonwebtoken';
declare module 'express' {
export interface Request {
user?: {
id: number;
// Add other properties if needed
};
}
}
export const verifyJWT = (req: express.Request, res: express.Response, next: NextFunction) => {
const token = req.header('Authorization')?.split(' ')[1];
if (!token) {
res.status(401).json({ message: 'No token, authorization denied' });
}
else{
try {
// Décoder et vérifier le token JWT
const decoded = jwt.verify(token, String(process.env.SECRET_JWT));
req.user = decoded as { id: number };
next();
} catch (err) {
res.status(401).json({ message: 'Token is not valid' });
}
}
};
\ No newline at end of file
import Server from './express/Server';
// Test for sonar
new Server().run();
\ No newline at end of file
import db from './helpers/DatabaseHelper.js';
import { randomInt } from 'crypto';
import express from 'express';
export function calcTempsRestant(heureDebut: number, tempsQCM: number) : number
{
const deltaTemps = Math.floor(Date.now() / 1000) - heureDebut;
const tempsRestant = tempsQCM - deltaTemps;
return tempsRestant > 0 ? tempsRestant : -1;
}
export async function calcNbPtsTotalQCM(idQCM: number) : Promise<number>
{
const questions = await db.question.findMany({
where: {
idQCM: idQCM
},
select: {
nbPtsPositif: true
}
});
return questions.reduce((total, question) => total + (question.nbPtsPositif || 0), 0);
}
export function calcNoteBaremeFed(nbPtsObtenus: number, nbPtsTotal: number) : number
{
return nbPtsObtenus / nbPtsTotal * 5 + 1;
}
export function getRandomNumber(min: number, max: number): number {
return randomInt(min, max + 1); // `randomInt` génère un nombre entier aléatoire entre `min` (inclus) et `max` (exclus).
}
export function checkUser(req: express.Request, res: express.Response, userId: number, errorMessage: string)
{
if (req.user) {
const { id } = req.user;
if (id !== userId)
{
res.status(403).send(errorMessage);
}
} else {
res.status(401).send('Unauthorized');
}
}
class Config {
public readonly production: boolean;
public readonly api: {
port: number
};
private constructor() {
this.production = process.env.NODE_ENV === 'production';
this.api = {
port: Number(process.env.API_PORT)
};
}
private static _instance: Config;
public static get instance(): Config {
if ( !Config._instance ) {
Config._instance = new Config();
}
return Config._instance;
}
}
export default Config.instance;
import { Express } from 'express-serve-static-core';
import cors from 'cors';
import morganMiddleware from '../logging/MorganMiddleware';
import logger from '../logging/WinstonLogger';
import { AddressInfo } from 'net';
import http from 'http';
import helmet from 'helmet';
import express from 'express';
import multer from 'multer';
import Config from '../config/Config';
import BaseRoutes from '../routes/BaseRoutes';
import db from '../helpers/DatabaseHelper.js';
import bodyParser from 'body-parser';
import jwt from 'jsonwebtoken';
import axios from 'axios';
export class Server {
private readonly backend: Express;
private readonly server: http.Server;
private readonly redirectUri = 'http://localhost:4200';
constructor() {
this.backend = express();
this.backend.use(multer({
limits: {
fileSize: 8000000
}
}).none()); //Used for extract params from body with format "form-data", The none is for say that we do not wait a file in params
this.backend.use(morganMiddleware); //Log API accesses
this.backend.use(helmet()); //Help to secure express, https://helmetjs.github.io/
this.backend.use(bodyParser.json());
this.backend.use(cors({
origin: '*' ,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Authorization', 'Content-Type']
})); //Allow CORS requests
// Routes
this.backend.use('/' , BaseRoutes);
this.backend.use(express.json());
this.backend.post('/auth/jwt', async (req, res) => {
const { code } = req.body;
if (!code) {
res.status(400).send('Code is required');
}
try {
//Demande access_token user avec le code
const response = await axios.post('https://githepia.hesge.ch/oauth/token', {
client_id: String(process.env.CLIENTID),
client_secret: String(process.env.CLIENTSECRET),
code: code,
grant_type: 'authorization_code',
redirect_uri: this.redirectUri,
});
const { access_token } = response.data;
//Demande du name et de l'email utilisateur
const userResponse = await axios.get('https://githepia.hesge.ch/api/v4/user', {
headers: { Authorization: `Bearer ${access_token}` }
});
const { id, name, email } = userResponse.data;
console.log(id, name, email)
if(name || email || id){
//erreur
}
const infoQcm = await db.user.findFirst({
where: {
name: name,
mail: email
}
});
// Génération d'un token JWT personnalisé
if(!infoQcm){
const createUser = await db.user.create({
data: {
id: id,
gitlabUsername: name,
mail: email,
deleted: false,
name: name,
}
});
if(!createUser){
res.status(500).send({error: 'Error create user'});
}
}
const jwtToken = jwt.sign({ id }, String(process.env.SECRET_JWT), {expiresIn: '1h'});
res.json({
token: jwtToken,
idUser: id
});
} catch (error) {
res.status(500).send('Error exchanging code for token');
}
});
this.server = http.createServer(this.backend);
}
run() {
this.server.listen(Config.api.port, '0.0.0.0', () => {
const {
port,
address
} = this.server.address() as AddressInfo;
logger.info(`Server started on http://${ address }:${ port }`);
});
}
}
export default Server;
import { PrismaClient } from '@prisma/client';
import logger from '../logging/WinstonLogger.js';
const prisma = new PrismaClient({
log: [ {
emit : 'event',
level: 'query'
}, {
emit : 'event',
level: 'info'
}, {
emit : 'event',
level: 'warn'
}, {
emit : 'event',
level: 'error'
} ]
});
prisma.$on('query', e => {
logger.debug(`Prisma => Query (${ e.duration }ms): ${ e.query }`);
logger.debug(`Prisma => Params: ${ e.params }\n`);
});
prisma.$on('info', e => logger.info(`Prisma => ${ e.message }`));
prisma.$on('warn', e => logger.warn(`Prisma => ${ e.message }`));
prisma.$on('error', e => logger.error(`Prisma => ${ e.message }`));
export default prisma;
\ No newline at end of file
import morgan, { StreamOptions } from 'morgan';
import logger from './WinstonLogger';
const stream: StreamOptions = {
write: (message) => logger.http(message)
};
const skip = () => {
return false;
};
const morganMiddleware = morgan(':method :url :status :res[content-length] - :response-time ms', {
stream,
skip
});
export default morganMiddleware;
import winston from 'winston';
import * as Transport from 'winston-transport';
import Config from '../config/Config';
const levels = {
error: 0,
warn : 1,
info : 2,
http : 3,
debug: 4
};
const level = () => {
return Config.production ? 'warn' : 'debug';
};
const colors = {
error: 'red',
warn : 'yellow',
info : 'green',
http : 'magenta',
debug: 'white'
};
winston.addColors(colors);
const format = winston.format.combine(winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }), Config.production ? winston.format.uncolorize() : winston.format.colorize({ all: true }), winston.format.printf((info) => `${ info.timestamp } [${ process.pid }] ${ info.level }: ${ info.message }`));
// Set type of logs (console, file, etc.)
let transports: Array<Transport> = [ new winston.transports.Console() ];
if ( Config.production ) {
transports = transports.concat([ new winston.transports.File({
filename: 'logs/error.log',
level : 'error'
}), new winston.transports.File({ filename: 'logs/all.log' }) ]);
}
// Create logger instance
const logger = winston.createLogger({
level: level(),
levels,
format,
transports
});
export default logger;
import express from 'express';
import { StatusCodes } from 'http-status-codes';
const router: express.Router = express.Router();
router.get('/', (_req: express.Request, res: express.Response) => res.status(StatusCodes.OK).end());
router.get('/helloworld', (req: express.Request, res: express.Response) => {
console.log(req.params);
res.status(StatusCodes.OK).json({ message: 'Hello World' });
});
export default router;
export function sum(a: number, b: number): number {
return a + b;
}
// Jest test for the service
test("Jest is running", () => {
expect(1).toBe(1);
});
{
"compilerOptions": {
"baseUrl" : ".",
"outDir" : "dist",
"strict" : true,
"target" : "ES6",
"module" : "commonjs",
"sourceMap" : true,
"noImplicitAny" : true,
"esModuleInterop" : true,
"moduleResolution": "node",
"paths" : {
"*": [
"node_modules/*"
]
}
},
"include" : [
"src/**/*.ts",
"prisma/seed.ts"
],
"exclude" : [
"node_modules",
"assets/**/*"
]
}
Source diff could not be displayed: it is too large. Options to address this: view the blob.
Source diff could not be displayed: it is too large. Options to address this: view the blob.
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
add_header 'Access-Control-Max-Age' 1728000 always;
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
add_header 'Access-Control-Max-Age' 1728000 always;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
# PVC pour PostgreSQL
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
# Service PostgreSQL
apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
---
# Déploiement PostgreSQL
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:latest
ports:
- containerPort: 5432
env:
- name: POSTGRES_USER
value: "user"
- name: POSTGRES_PASSWORD
value: "super"
- name: POSTGRES_DB
value: "dbqcm"
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: postgres-storage
volumes:
- name: postgres-storage
persistentVolumeClaim:
claimName: postgres-pvc
---
# Déploiement service-auth
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-auth
spec:
replicas: 1
selector:
matchLabels:
app: service-auth
template:
metadata:
labels:
app: service-auth
spec:
containers:
- name: service-auth
image: service-auth
ports:
- containerPort: 8002
env:
- name: POSTGRES_USER
value: "user"
- name: POSTGRES_PASSWORD
value: "super"
- name: POSTGRES_DB
value: "dbqcm"
- name: DATABASE_URL
value: "postgresql://user:super@postgres:5432/dbqcm"
- name: API_PORT
value: "30992"
- name: SECRET_JWT
value: "JECROISQUECEMESSAGEESTSECRET"
- name: CLIENTID
value: "f8b0e14f7eee1a718ad0b3f32c52fe34813d56e9052976f076e039d006e24000"
- name: CLIENTSECRET
value: "gloas-1451c5f206cb04b6b300e6dcbf19a01f1a44bff5e8562741a7efd0ec27eb0855"
---
apiVersion: v1
kind: Service
metadata:
name: service-auth
spec:
selector:
app: service-auth
ports:
- port: 8002
targetPort: 8002
type: ClusterIP
---
# Services restants (sans déploiements)
apiVersion: v1
kind: Service
metadata:
name: service-correction-qcm
spec:
selector:
app: service-correction-qcm
ports:
- port: 8003
targetPort: 8003
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: service-creation-qcm
spec:
selector:
app: service-creation-qcm
ports:
- port: 8004
targetPort: 8004
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: service-realise-qcm
spec:
selector:
app: service-realise-qcm
ports:
- port: 8005
targetPort: 8005
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: service-search-qcm
spec:
selector:
app: service-search-qcm
ports:
- port: 8006
targetPort: 8006
type: ClusterIP
---
# Déploiement nginx
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 30992
volumeMounts:
- name: nginx-conf
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
- name: cors-conf
mountPath: /etc/nginx/cors.conf
subPath: cors.conf
volumes:
- name: nginx-conf
configMap:
name: nginx-conf
- name: cors-conf
configMap:
name: cors-conf
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
selector:
app: nginx
ports:
- port: 30992
targetPort: 30992
type: NodePort
\ No newline at end of file
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:latest
ports:
- containerPort: 5432
env:
- name: POSTGRES_USER
value: "user"
- name: POSTGRES_PASSWORD
value: "super"
- name: POSTGRES_DB
value: "dbqcm"
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: postgres-storage
volumes:
- name: postgres-storage
persistentVolumeClaim:
claimName: postgres-pvc
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-auth
spec:
replicas: 1
selector:
matchLabels:
app: service-auth
template:
metadata:
labels:
app: service-auth
spec:
containers:
- name: service-auth
image: service-auth
ports:
- containerPort: 8002
env:
- name: POSTGRES_USER
value: "user"
- name: POSTGRES_PASSWORD
value: "super"
- name: POSTGRES_DB
value: "dbqcm"
- name: DATABASE_URL
value: "postgresql://user:super@postgres:5432/dbqcm"
---
apiVersion: v1
kind: Service
metadata:
name: service-auth
spec:
selector:
app: service-auth
ports:
- port: 8002
targetPort: 8002
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: service-correction-qcm
spec:
selector:
app: service-correction-qcm
ports:
- port: 8003
targetPort: 8003
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: service-creation-qcm
spec:
selector:
app: service-creation-qcm
ports:
- port: 8004
targetPort: 8004
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: service-realise-qcm
spec:
selector:
app: service-realise-qcm
ports:
- port: 8005
targetPort: 8005
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: service-search-qcm
spec:
selector:
app: service-search-qcm
ports:
- port: 8006
targetPort: 8006
type: ClusterIP
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment