ajout app
This commit is contained in:
3
Voltaserve/idp/src/infra/date-time.ts
Normal file
3
Voltaserve/idp/src/infra/date-time.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function newDateTime() {
|
||||
return new Date().toISOString()
|
||||
}
|
8
Voltaserve/idp/src/infra/env.ts
Normal file
8
Voltaserve/idp/src/infra/env.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import fs from 'fs'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
if (fs.existsSync('.env.local')) {
|
||||
dotenv.config({ path: '.env.local' })
|
||||
} else {
|
||||
dotenv.config()
|
||||
}
|
119
Voltaserve/idp/src/infra/error.ts
Normal file
119
Voltaserve/idp/src/infra/error.ts
Normal file
@ -0,0 +1,119 @@
|
||||
import { Request, Response, NextFunction } from 'express'
|
||||
|
||||
export enum ErrorCode {
|
||||
InternalServerError = 'internal_server_error',
|
||||
RequestValidationError = 'request_validation_error',
|
||||
UsernameUnavailable = 'username_unavailable',
|
||||
ResourceNotFound = 'resource_not_found',
|
||||
InvalidUsernameOrPassword = 'invalid_username_or_password',
|
||||
InvalidPassword = 'invalid_password',
|
||||
InvalidJwt = 'invalid_jwt',
|
||||
EmailNotConfimed = 'email_not_confirmed',
|
||||
RefreshTokenExpired = 'refresh_token_expired',
|
||||
InvalidRequest = 'invalid_request',
|
||||
UnsupportedGrantType = 'unsupported_grant_type',
|
||||
PasswordValidationFailed = 'password_validation_failed',
|
||||
}
|
||||
|
||||
const statuses: { [key: string]: number } = {
|
||||
[ErrorCode.InternalServerError]: 500,
|
||||
[ErrorCode.RequestValidationError]: 400,
|
||||
[ErrorCode.UsernameUnavailable]: 409,
|
||||
[ErrorCode.ResourceNotFound]: 404,
|
||||
[ErrorCode.InvalidUsernameOrPassword]: 401,
|
||||
[ErrorCode.InvalidPassword]: 401,
|
||||
[ErrorCode.InvalidJwt]: 401,
|
||||
[ErrorCode.EmailNotConfimed]: 401,
|
||||
[ErrorCode.RefreshTokenExpired]: 401,
|
||||
[ErrorCode.InvalidRequest]: 400,
|
||||
[ErrorCode.UnsupportedGrantType]: 400,
|
||||
[ErrorCode.PasswordValidationFailed]: 400,
|
||||
}
|
||||
|
||||
const userMessages: { [key: string]: string } = {
|
||||
[ErrorCode.UsernameUnavailable]: 'Email belongs to an existing user.',
|
||||
[ErrorCode.EmailNotConfimed]: 'Email not confirmed.',
|
||||
[ErrorCode.InvalidPassword]: 'Invalid password.',
|
||||
[ErrorCode.InvalidUsernameOrPassword]: 'Invalid username or password.',
|
||||
}
|
||||
|
||||
export type ErrorData = {
|
||||
code: string
|
||||
status: number
|
||||
message: string
|
||||
userMessage: string
|
||||
moreInfo: string
|
||||
error?: any
|
||||
}
|
||||
|
||||
export type ErrorResponse = {
|
||||
code: string
|
||||
status: number
|
||||
message: string
|
||||
userMessage: string
|
||||
moreInfo: string
|
||||
}
|
||||
|
||||
export type ErrorOptions = {
|
||||
code: ErrorCode
|
||||
message?: string
|
||||
userMessage?: string
|
||||
error?: any
|
||||
}
|
||||
|
||||
export function newError(options: ErrorOptions): ErrorData {
|
||||
const userMessage =
|
||||
options.userMessage ||
|
||||
userMessages[options.code] ||
|
||||
'Oops! something went wrong'
|
||||
return {
|
||||
code: options.code,
|
||||
status: statuses[options.code],
|
||||
message: options.message || userMessage,
|
||||
userMessage,
|
||||
moreInfo: `https://voltaserve.com/docs/idp/errors/${options.code}`,
|
||||
error: options.error,
|
||||
}
|
||||
}
|
||||
|
||||
export function newResponse(data: ErrorData): ErrorResponse {
|
||||
return {
|
||||
code: data.code,
|
||||
status: data.status,
|
||||
message: data.message,
|
||||
userMessage: data.userMessage,
|
||||
moreInfo: data.moreInfo,
|
||||
}
|
||||
}
|
||||
|
||||
export function errorHandler(
|
||||
error: any,
|
||||
_: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
if (error.code && Object.values(ErrorCode).includes(error.code)) {
|
||||
const data = error as ErrorData
|
||||
if (data.error) {
|
||||
console.error(data.error)
|
||||
}
|
||||
res.status(data.status).json(newResponse(data))
|
||||
} else {
|
||||
console.error(error)
|
||||
res
|
||||
.status(500)
|
||||
.json(newResponse(newError({ code: ErrorCode.InternalServerError })))
|
||||
}
|
||||
next(error)
|
||||
return
|
||||
}
|
||||
|
||||
export function parseValidationError(result: any): ErrorData {
|
||||
let message: string
|
||||
if (result.errors) {
|
||||
message = result.errors
|
||||
.map((e: any) => `${e.msg} for ${e.type} ${e.path} in ${e.location}.`)
|
||||
.join(' ')
|
||||
}
|
||||
return newError({ code: ErrorCode.RequestValidationError, message })
|
||||
}
|
10
Voltaserve/idp/src/infra/id.ts
Normal file
10
Voltaserve/idp/src/infra/id.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import hashids from 'hashids'
|
||||
|
||||
export function newHashId(): string {
|
||||
return new hashids(uuidv4()).encode(Date.now())
|
||||
}
|
||||
|
||||
export function newHyphenlessUuid(): string {
|
||||
return uuidv4().replaceAll('-', '')
|
||||
}
|
59
Voltaserve/idp/src/infra/mail.ts
Normal file
59
Voltaserve/idp/src/infra/mail.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import fs from 'fs'
|
||||
import Handlebars from 'handlebars'
|
||||
import yaml from 'js-yaml'
|
||||
import nodemailer from 'nodemailer'
|
||||
import path from 'path'
|
||||
import { getConfig } from '@/config/config'
|
||||
|
||||
type MessageParams = {
|
||||
subject: string
|
||||
}
|
||||
|
||||
const config = getConfig().smtp
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: config.host,
|
||||
port: config.port,
|
||||
secure: config.secure,
|
||||
auth:
|
||||
config.username || config.password
|
||||
? {
|
||||
user: config.username,
|
||||
pass: config.password,
|
||||
}
|
||||
: null,
|
||||
})
|
||||
|
||||
export function sendTemplateMail(
|
||||
template: string,
|
||||
address: string,
|
||||
variables: Record<string, any>
|
||||
) {
|
||||
const params = yaml.load(
|
||||
fs.readFileSync(path.join('templates', template, 'params.yml'), 'utf8')
|
||||
) as MessageParams
|
||||
const html = Handlebars.compile(
|
||||
fs.readFileSync(path.join('templates', template, 'template.hbs'), 'utf8')
|
||||
)(variables)
|
||||
const text = Handlebars.compile(
|
||||
fs.readFileSync(path.join('templates', template, 'template.txt'), 'utf8')
|
||||
)(variables)
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
transporter.sendMail(
|
||||
{
|
||||
from: `"${config.senderName}" <${config.senderAddress}>`,
|
||||
to: address,
|
||||
subject: params.subject,
|
||||
text,
|
||||
html,
|
||||
},
|
||||
(err) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
6
Voltaserve/idp/src/infra/passport-request.ts
Normal file
6
Voltaserve/idp/src/infra/passport-request.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { Request } from 'express'
|
||||
import { User } from '@/user/repo'
|
||||
|
||||
export interface PassportRequest extends Request {
|
||||
user: User
|
||||
}
|
13
Voltaserve/idp/src/infra/password.ts
Normal file
13
Voltaserve/idp/src/infra/password.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { scryptSync, randomBytes } from 'crypto'
|
||||
|
||||
export function hashPassword(password: string): string {
|
||||
const salt = randomBytes(16).toString('hex')
|
||||
const key = scryptSync(password, salt, 64).toString('hex')
|
||||
return `${key}:${salt}`
|
||||
}
|
||||
|
||||
export function verifyPassword(password: string, hash: string): boolean {
|
||||
const [key, salt] = hash.split(':')
|
||||
const newKey = scryptSync(password, salt, 64).toString('hex')
|
||||
return newKey === key
|
||||
}
|
6
Voltaserve/idp/src/infra/postgres.ts
Normal file
6
Voltaserve/idp/src/infra/postgres.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { Client } from 'pg'
|
||||
import { getConfig } from '@/config/config'
|
||||
|
||||
export const client = new Client({
|
||||
connectionString: getConfig().databaseURL,
|
||||
})
|
9
Voltaserve/idp/src/infra/search.ts
Normal file
9
Voltaserve/idp/src/infra/search.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { MeiliSearch } from 'meilisearch'
|
||||
import { getConfig } from '@/config/config'
|
||||
|
||||
export const USER_SEARCH_INDEX = 'user'
|
||||
|
||||
const client = new MeiliSearch({ host: getConfig().search.url })
|
||||
client.createIndex(USER_SEARCH_INDEX, { primaryKey: 'id' })
|
||||
|
||||
export default client
|
Reference in New Issue
Block a user