init commit,
This commit is contained in:
61
03_source/api_server.del/src/config/config.ts
Normal file
61
03_source/api_server.del/src/config/config.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import dotenv from 'dotenv';
|
||||
import path from 'path';
|
||||
import Joi from 'joi';
|
||||
|
||||
dotenv.config({ path: path.join(process.cwd(), '.env') });
|
||||
|
||||
const envVarsSchema = Joi.object()
|
||||
.keys({
|
||||
NODE_ENV: Joi.string().valid('production', 'development', 'test').required(),
|
||||
PORT: Joi.number().default(3000),
|
||||
JWT_SECRET: Joi.string().required().description('JWT secret key'),
|
||||
JWT_ACCESS_EXPIRATION_MINUTES: Joi.number()
|
||||
.default(30)
|
||||
.description('minutes after which access tokens expire'),
|
||||
JWT_REFRESH_EXPIRATION_DAYS: Joi.number()
|
||||
.default(30)
|
||||
.description('days after which refresh tokens expire'),
|
||||
JWT_RESET_PASSWORD_EXPIRATION_MINUTES: Joi.number()
|
||||
.default(10)
|
||||
.description('minutes after which reset password token expires'),
|
||||
JWT_VERIFY_EMAIL_EXPIRATION_MINUTES: Joi.number()
|
||||
.default(10)
|
||||
.description('minutes after which verify email token expires'),
|
||||
SMTP_HOST: Joi.string().description('server that will send the emails'),
|
||||
SMTP_PORT: Joi.number().description('port to connect to the email server'),
|
||||
SMTP_USERNAME: Joi.string().description('username for email server'),
|
||||
SMTP_PASSWORD: Joi.string().description('password for email server'),
|
||||
EMAIL_FROM: Joi.string().description('the from field in the emails sent by the app')
|
||||
})
|
||||
.unknown();
|
||||
|
||||
const { value: envVars, error } = envVarsSchema
|
||||
.prefs({ errors: { label: 'key' } })
|
||||
.validate(process.env);
|
||||
|
||||
if (error) {
|
||||
throw new Error(`Config validation error: ${error.message}`);
|
||||
}
|
||||
|
||||
export default {
|
||||
env: envVars.NODE_ENV,
|
||||
port: envVars.PORT,
|
||||
jwt: {
|
||||
secret: envVars.JWT_SECRET,
|
||||
accessExpirationMinutes: envVars.JWT_ACCESS_EXPIRATION_MINUTES,
|
||||
refreshExpirationDays: envVars.JWT_REFRESH_EXPIRATION_DAYS,
|
||||
resetPasswordExpirationMinutes: envVars.JWT_RESET_PASSWORD_EXPIRATION_MINUTES,
|
||||
verifyEmailExpirationMinutes: envVars.JWT_VERIFY_EMAIL_EXPIRATION_MINUTES
|
||||
},
|
||||
email: {
|
||||
smtp: {
|
||||
host: envVars.SMTP_HOST,
|
||||
port: envVars.SMTP_PORT,
|
||||
auth: {
|
||||
user: envVars.SMTP_USERNAME,
|
||||
pass: envVars.SMTP_PASSWORD
|
||||
}
|
||||
},
|
||||
from: envVars.EMAIL_FROM
|
||||
}
|
||||
};
|
26
03_source/api_server.del/src/config/logger.ts
Normal file
26
03_source/api_server.del/src/config/logger.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import winston from 'winston';
|
||||
import config from './config';
|
||||
|
||||
const enumerateErrorFormat = winston.format((info) => {
|
||||
if (info instanceof Error) {
|
||||
Object.assign(info, { message: info.stack });
|
||||
}
|
||||
return info;
|
||||
});
|
||||
|
||||
const logger = winston.createLogger({
|
||||
level: config.env === 'development' ? 'debug' : 'info',
|
||||
format: winston.format.combine(
|
||||
enumerateErrorFormat(),
|
||||
config.env === 'development' ? winston.format.colorize() : winston.format.uncolorize(),
|
||||
winston.format.splat(),
|
||||
winston.format.printf(({ level, message }) => `${level}: ${message}`)
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.Console({
|
||||
stderrLevels: ['error']
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
export default logger;
|
25
03_source/api_server.del/src/config/morgan.ts
Normal file
25
03_source/api_server.del/src/config/morgan.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Response } from 'express';
|
||||
import morgan from 'morgan';
|
||||
import config from './config';
|
||||
import logger from './logger';
|
||||
|
||||
morgan.token('message', (req, res: Response) => res.locals.errorMessage || '');
|
||||
|
||||
const getIpFormat = () => (config.env === 'production' ? ':remote-addr - ' : '');
|
||||
const successResponseFormat = `${getIpFormat()}:method :url :status - :response-time ms`;
|
||||
const errorResponseFormat = `${getIpFormat()}:method :url :status - :response-time ms - message: :message`;
|
||||
|
||||
export const successHandler = morgan(successResponseFormat, {
|
||||
skip: (req, res) => res.statusCode >= 400,
|
||||
stream: { write: (message) => logger.info(message.trim()) }
|
||||
});
|
||||
|
||||
export const errorHandler = morgan(errorResponseFormat, {
|
||||
skip: (req, res) => res.statusCode < 400,
|
||||
stream: { write: (message) => logger.error(message.trim()) }
|
||||
});
|
||||
|
||||
export default {
|
||||
successHandler,
|
||||
errorHandler
|
||||
};
|
33
03_source/api_server.del/src/config/passport.ts
Normal file
33
03_source/api_server.del/src/config/passport.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import prisma from '../client';
|
||||
import { Strategy as JwtStrategy, ExtractJwt, VerifyCallback } from 'passport-jwt';
|
||||
import config from './config';
|
||||
import { TokenType } from '@prisma/client';
|
||||
|
||||
const jwtOptions = {
|
||||
secretOrKey: config.jwt.secret,
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken()
|
||||
};
|
||||
|
||||
const jwtVerify: VerifyCallback = async (payload, done) => {
|
||||
try {
|
||||
if (payload.type !== TokenType.ACCESS) {
|
||||
throw new Error('Invalid token type');
|
||||
}
|
||||
const user = await prisma.user.findUnique({
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true
|
||||
},
|
||||
where: { id: payload.sub }
|
||||
});
|
||||
if (!user) {
|
||||
return done(null, false);
|
||||
}
|
||||
done(null, user);
|
||||
} catch (error) {
|
||||
done(error, false);
|
||||
}
|
||||
};
|
||||
|
||||
export const jwtStrategy = new JwtStrategy(jwtOptions, jwtVerify);
|
9
03_source/api_server.del/src/config/roles.ts
Normal file
9
03_source/api_server.del/src/config/roles.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Role } from '@prisma/client';
|
||||
|
||||
const allRoles = {
|
||||
[Role.USER]: [],
|
||||
[Role.ADMIN]: ['getUsers', 'manageUsers']
|
||||
};
|
||||
|
||||
export const roles = Object.keys(allRoles);
|
||||
export const roleRights = new Map(Object.entries(allRoles));
|
Reference in New Issue
Block a user