✨ Introduce request login
This commit is contained in:
parent
e4a802d5f2
commit
709a7bb6f7
16 changed files with 452 additions and 27 deletions
10
.env.example
10
.env.example
|
|
@ -4,6 +4,7 @@ HOST=localhost
|
||||||
LOG_LEVEL=info
|
LOG_LEVEL=info
|
||||||
APP_KEY=
|
APP_KEY=
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
|
|
||||||
DB_HOST=127.0.0.1
|
DB_HOST=127.0.0.1
|
||||||
DB_PORT=5432
|
DB_PORT=5432
|
||||||
DB_USER=root
|
DB_USER=root
|
||||||
|
|
@ -11,10 +12,15 @@ DB_PASSWORD=root
|
||||||
DB_DATABASE=app
|
DB_DATABASE=app
|
||||||
LIMITER_STORE=redis
|
LIMITER_STORE=redis
|
||||||
DRIVE_DISK=fs
|
DRIVE_DISK=fs
|
||||||
SMTP_HOST=
|
|
||||||
SMTP_PORT=
|
|
||||||
REDIS_HOST=127.0.0.1
|
REDIS_HOST=127.0.0.1
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
REDIS_PASSWORD=
|
REDIS_PASSWORD=
|
||||||
|
|
||||||
MAILGUN_API_KEY=
|
MAILGUN_API_KEY=
|
||||||
MAILGUN_DOMAIN=
|
MAILGUN_DOMAIN=
|
||||||
|
|
||||||
|
TURNSTILE_SITE_KEY=
|
||||||
|
TURNSTILE_SECRET=
|
||||||
|
|
||||||
|
PUBLIC_AUTH_URL=
|
||||||
10
adonisrc.ts
10
adonisrc.ts
|
|
@ -50,7 +50,11 @@ export default defineConfig({
|
||||||
() => import('@adonisjs/cache/cache_provider'),
|
() => import('@adonisjs/cache/cache_provider'),
|
||||||
() => import('@adonisjs/drive/drive_provider'),
|
() => import('@adonisjs/drive/drive_provider'),
|
||||||
() => import('@adonisjs/mail/mail_provider'),
|
() => import('@adonisjs/mail/mail_provider'),
|
||||||
() => import('@adonisjs/redis/redis_provider')
|
() => import('@adonisjs/redis/redis_provider'),
|
||||||
|
() => import('@adonisjs/transmit/transmit_provider'),
|
||||||
|
() => import('@adonisjs/core/providers/vinejs_provider'),
|
||||||
|
() => import('adonis-captcha-guard/providers/captcha_provider'),
|
||||||
|
() => import('@adonisjs/core/providers/edge_provider')
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -87,4 +91,8 @@ export default defineConfig({
|
||||||
],
|
],
|
||||||
forceExit: false,
|
forceExit: false,
|
||||||
},
|
},
|
||||||
|
metaFiles: [{
|
||||||
|
pattern: 'resources/views/**/*.edge',
|
||||||
|
reloadServer: false,
|
||||||
|
}]
|
||||||
})
|
})
|
||||||
|
|
|
||||||
76
app/controllers/auth_controller.ts
Normal file
76
app/controllers/auth_controller.ts
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
import type { HttpContext } from '@adonisjs/core/http'
|
||||||
|
import { requestLoginValidator, verifyCodeValidator } from '#validators/auth'
|
||||||
|
import mail from '@adonisjs/mail/services/main'
|
||||||
|
import { AuthService } from '#services/auth_service'
|
||||||
|
import { inject } from '@adonisjs/core'
|
||||||
|
import app from '@adonisjs/core/services/app'
|
||||||
|
import env from '#start/env'
|
||||||
|
|
||||||
|
@inject()
|
||||||
|
export default class AuthController {
|
||||||
|
constructor(private authService: AuthService) {}
|
||||||
|
|
||||||
|
// POST /auth/request
|
||||||
|
async requestLogin({ request, response, captcha }: HttpContext) {
|
||||||
|
// Validate captcha
|
||||||
|
if (app.inProduction) {
|
||||||
|
const validateResult = await (captcha.use('turnstile') as any).validate()
|
||||||
|
if (!validateResult.success) {
|
||||||
|
return response.badRequest({
|
||||||
|
message: 'Captcha validation failed',
|
||||||
|
error: validateResult.errorCodes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate token
|
||||||
|
const expiresIn = '15 minutes'
|
||||||
|
const { email } = await request.validateUsing(requestLoginValidator)
|
||||||
|
const payload = await this.authService.generateToken(email, expiresIn)
|
||||||
|
|
||||||
|
// Send email
|
||||||
|
await mail.send((message) => {
|
||||||
|
message
|
||||||
|
.from(env.get('MAIL_FROM')!)
|
||||||
|
.to(email)
|
||||||
|
.subject(payload.emailTitle)
|
||||||
|
.htmlView('mails/auth', payload)
|
||||||
|
.textView('mails/auth-fallback', payload)
|
||||||
|
}).then(console.log).catch(console.error)
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
token: payload.token,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /auth/verify
|
||||||
|
async verifyCode({ request }: HttpContext) {
|
||||||
|
// Validate code
|
||||||
|
const { code } = await request.validateUsing(verifyCodeValidator)
|
||||||
|
const email = await this.authService.validateCode(code)
|
||||||
|
if (!email) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: 'Le code est invalide ou a expiré',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TOOD: Login
|
||||||
|
// Find user by email (string similary)
|
||||||
|
}
|
||||||
|
|
||||||
|
magicLink({}: HttpContext) {
|
||||||
|
// Validate signed url (adonis)
|
||||||
|
// + login current device
|
||||||
|
// + SSE to notify other devices (and login)
|
||||||
|
}
|
||||||
|
|
||||||
|
listen({}: HttpContext) {
|
||||||
|
// Listen for SSE events
|
||||||
|
// Need an AUTH token to connect
|
||||||
|
// AUTH token sent to client in requestLogin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import app from '@adonisjs/core/services/app'
|
|
||||||
import { HttpContext, ExceptionHandler } from '@adonisjs/core/http'
|
import { HttpContext, ExceptionHandler } from '@adonisjs/core/http'
|
||||||
|
|
||||||
export default class HttpExceptionHandler extends ExceptionHandler {
|
export default class HttpExceptionHandler extends ExceptionHandler {
|
||||||
|
|
@ -6,21 +5,29 @@ export default class HttpExceptionHandler extends ExceptionHandler {
|
||||||
* In debug mode, the exception handler will display verbose errors
|
* In debug mode, the exception handler will display verbose errors
|
||||||
* with pretty printed stack traces.
|
* with pretty printed stack traces.
|
||||||
*/
|
*/
|
||||||
protected debug = !app.inProduction
|
protected debug = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Status pages are used to display a custom HTML pages for certain error
|
* Status pages are used to display a custom HTML pages for certain error
|
||||||
* codes. You might want to enable them in production only, but feel
|
* codes. You might want to enable them in production only, but feel
|
||||||
* free to enable them in development as well.
|
* free to enable them in development as well.
|
||||||
*/
|
*/
|
||||||
protected renderStatusPages = app.inProduction
|
protected renderStatusPages = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The method is used for handling errors and returning
|
* The method is used for handling errors and returning
|
||||||
* response to the client
|
* response to the client
|
||||||
*/
|
*/
|
||||||
async handle(error: unknown, ctx: HttpContext) {
|
async handle(error: any, ctx: HttpContext) {
|
||||||
return super.handle(error, ctx)
|
const statusCode = error.status || error.statusCode || 500
|
||||||
|
const message = error.message || 'Internal Server Error'
|
||||||
|
const stack = this.debug ? error.stack : undefined
|
||||||
|
const response = {
|
||||||
|
status: statusCode,
|
||||||
|
error: message,
|
||||||
|
stack,
|
||||||
|
}
|
||||||
|
return ctx.response.status(statusCode).json(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
43
app/services/auth_service.ts
Normal file
43
app/services/auth_service.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import encryption from '@adonisjs/core/services/encryption'
|
||||||
|
import cache from '@adonisjs/cache/services/main'
|
||||||
|
import env from '#start/env'
|
||||||
|
|
||||||
|
export class AuthService {
|
||||||
|
async generateToken(email: string, expiresIn: string) {
|
||||||
|
// Generate magic link token
|
||||||
|
const token = encryption.encrypt(email, expiresIn)
|
||||||
|
const magicLink = env.get('PUBLIC_AUTH_URL') + '/success?signature=' + token
|
||||||
|
|
||||||
|
// Generate code
|
||||||
|
const formattedOTP = Math.floor(Math.random() * 1000000)
|
||||||
|
.toString()
|
||||||
|
.padStart(6, '0')
|
||||||
|
await cache.set({
|
||||||
|
key: 'auth:otp:' + formattedOTP,
|
||||||
|
value: email,
|
||||||
|
ttl: expiresIn,
|
||||||
|
})
|
||||||
|
|
||||||
|
const emailTitle = `${formattedOTP} est votre code de vérification pour ${env.get('APP_NAME')}`
|
||||||
|
return {
|
||||||
|
emailTitle,
|
||||||
|
formattedOTP,
|
||||||
|
magicLink,
|
||||||
|
expiresIn,
|
||||||
|
email,
|
||||||
|
token,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateCode(code: string) {
|
||||||
|
// Validate code
|
||||||
|
const key = 'auth:otp:' + code
|
||||||
|
const email = await cache.get({ key })
|
||||||
|
if (!email) return false
|
||||||
|
|
||||||
|
// Delete code from cache
|
||||||
|
await cache.delete({ key })
|
||||||
|
|
||||||
|
return email
|
||||||
|
}
|
||||||
|
}
|
||||||
38
app/validators/auth.ts
Normal file
38
app/validators/auth.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import vine from '@vinejs/vine'
|
||||||
|
|
||||||
|
export const requestLoginValidator = vine.compile(
|
||||||
|
vine.object({
|
||||||
|
email: vine
|
||||||
|
.string()
|
||||||
|
.email({
|
||||||
|
host_whitelist: ['bginette.fr'],
|
||||||
|
})
|
||||||
|
.normalizeEmail({
|
||||||
|
all_lowercase: true,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
export const verifyCodeValidator = vine.compile(
|
||||||
|
vine.object({
|
||||||
|
code: vine.string().minLength(6).maxLength(6),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
export const magicLinkValidator = vine.compile(
|
||||||
|
vine.object({
|
||||||
|
token: vine.string(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
export const listenValidator = vine.compile(
|
||||||
|
vine.object({
|
||||||
|
token: vine.string().uuid(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
export const exchangeTokenValidator = vine.compile(
|
||||||
|
vine.object({
|
||||||
|
token: vine.string().uuid(),
|
||||||
|
})
|
||||||
|
)
|
||||||
19
config/captcha.ts
Normal file
19
config/captcha.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import env from '#start/env'
|
||||||
|
import { defineConfig, services } from 'adonis-captcha-guard'
|
||||||
|
|
||||||
|
const captchaConfig = defineConfig({
|
||||||
|
turnstile: services.turnstile({
|
||||||
|
siteKey: env.get('TURNSTILE_SITE_KEY')!,
|
||||||
|
secret: env.get('TURNSTILE_SECRET')!,
|
||||||
|
}),
|
||||||
|
recaptcha: services.recaptcha({
|
||||||
|
siteKey: env.get('RECAPTCHA_SITE_KEY')!,
|
||||||
|
secret: env.get('RECAPTCHA_SECRET')!,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default captchaConfig
|
||||||
|
|
||||||
|
declare module '@adonisjs/core/types' {
|
||||||
|
interface CaptchaProviders {}
|
||||||
|
}
|
||||||
6
config/transmit.ts
Normal file
6
config/transmit.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { defineConfig } from '@adonisjs/transmit'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
pingInterval: false,
|
||||||
|
transport: null,
|
||||||
|
})
|
||||||
7
docker-compose.yml
Normal file
7
docker-compose.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis
|
||||||
|
container_name: khollise-redis
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
147
package-lock.json
generated
147
package-lock.json
generated
|
|
@ -18,6 +18,10 @@
|
||||||
"@adonisjs/lucid": "^21.6.1",
|
"@adonisjs/lucid": "^21.6.1",
|
||||||
"@adonisjs/mail": "^9.2.2",
|
"@adonisjs/mail": "^9.2.2",
|
||||||
"@adonisjs/redis": "^9.2.0",
|
"@adonisjs/redis": "^9.2.0",
|
||||||
|
"@adonisjs/transmit": "^2.0.2",
|
||||||
|
"@vinejs/vine": "^3.0.1",
|
||||||
|
"adonis-captcha-guard": "^1.0.1",
|
||||||
|
"edge.js": "^6.2.1",
|
||||||
"luxon": "^3.6.1",
|
"luxon": "^3.6.1",
|
||||||
"pg": "^8.16.0",
|
"pg": "^8.16.0",
|
||||||
"reflect-metadata": "^0.2.2"
|
"reflect-metadata": "^0.2.2"
|
||||||
|
|
@ -694,6 +698,21 @@
|
||||||
"node": ">=18.16.0"
|
"node": ">=18.16.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@adonisjs/transmit": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@adonisjs/transmit/-/transmit-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Oyh4S1773N1Ams9mmFM2pplLx8R0IdYwqSTBLRgeDWaR+cyWrL9ZQvF2q7GqIjNzZsXuDgMdPuLlNo+69zIZIg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@boringnode/transmit": "^0.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.11.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@adonisjs/core": "^6.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@adonisjs/tsconfig": {
|
"node_modules/@adonisjs/tsconfig": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@adonisjs/tsconfig/-/tsconfig-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@adonisjs/tsconfig/-/tsconfig-1.4.0.tgz",
|
||||||
|
|
@ -770,6 +789,21 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@boringnode/transmit": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@boringnode/transmit/-/transmit-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-xIBg5PKqqgawNsXffq1P+BpRDjplfwOspwcFH5wfSN6uVcd5hYGC9lJpcXa1oL3wkQZimtiJhrTHhZZtgh2lqA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@boringnode/bus": "^0.7.0",
|
||||||
|
"@poppinss/utils": "^6.8.3",
|
||||||
|
"emittery": "^1.0.3",
|
||||||
|
"matchit": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.11.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@chevrotain/cst-dts-gen": {
|
"node_modules/@chevrotain/cst-dts-gen": {
|
||||||
"version": "11.0.3",
|
"version": "11.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz",
|
||||||
|
|
@ -1461,7 +1495,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/@poppinss/inspect/-/inspect-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@poppinss/inspect/-/inspect-1.0.1.tgz",
|
||||||
"integrity": "sha512-kLeEaBSGhlleyYvKc7c9s3uE6xv7cwyulE0EgHf4jU/CL96h0yC4mkdw1wvC1l1PYYQozCGy46FwMBAAMOobCA==",
|
"integrity": "sha512-kLeEaBSGhlleyYvKc7c9s3uE6xv7cwyulE0EgHf4jU/CL96h0yC4mkdw1wvC1l1PYYQozCGy46FwMBAAMOobCA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
|
|
@ -2027,6 +2060,12 @@
|
||||||
"integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
|
"integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/validator": {
|
||||||
|
"version": "13.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.0.tgz",
|
||||||
|
"integrity": "sha512-nh7nrWhLr6CBq9ldtw0wx+z9wKnnv/uTVLA9g/3/TcOYxbpOSZE+MhKPmWqU+K0NvThjhv12uD8MuqijB0WzEA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.32.1",
|
"version": "8.32.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
|
||||||
|
|
@ -2217,6 +2256,34 @@
|
||||||
"url": "https://opencollective.com/typescript-eslint"
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@vinejs/compiler": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vinejs/compiler/-/compiler-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-v9Lsv59nR56+bmy2p0+czjZxsLHwaibJ+SV5iK9JJfehlJMa501jUJQqqz4X/OqKXrxtE3uTQmSqjUqzF3B2mw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vinejs/vine": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vinejs/vine/-/vine-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-ZtvYkYpZOYdvbws3uaOAvTFuvFXoQGAtmzeiXu+XSMGxi5GVsODpoI9Xu9TplEMuD/5fmAtBbKb9cQHkWkLXDQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@poppinss/macroable": "^1.0.4",
|
||||||
|
"@types/validator": "^13.12.2",
|
||||||
|
"@vinejs/compiler": "^3.0.0",
|
||||||
|
"camelcase": "^8.0.0",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
|
"dlv": "^1.1.3",
|
||||||
|
"normalize-url": "^8.0.1",
|
||||||
|
"validator": "^13.12.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/abstract-logging": {
|
"node_modules/abstract-logging": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
|
||||||
|
|
@ -2292,6 +2359,22 @@
|
||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/adonis-captcha-guard": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/adonis-captcha-guard/-/adonis-captcha-guard-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-+KkQeE1sNEgdfcX+hR+7pzl9Hib1u3XOdOHJ4PEYpYsLc+qD87LaLg8mDElDUSxAoH/Z5AlSeTr0Gsq5n5VXQQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@poppinss/utils": "^6.7.3",
|
||||||
|
"got": "^14.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.6.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@adonisjs/core": "^6.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ajv": {
|
"node_modules/ajv": {
|
||||||
"version": "6.12.6",
|
"version": "6.12.6",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||||
|
|
@ -2396,7 +2479,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz",
|
||||||
"integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==",
|
"integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"astring": "bin/astring"
|
"astring": "bin/astring"
|
||||||
}
|
}
|
||||||
|
|
@ -2655,6 +2737,18 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/camelcase": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001718",
|
"version": "1.0.30001718",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz",
|
||||||
|
|
@ -2804,8 +2898,7 @@
|
||||||
"version": "2.5.1",
|
"version": "2.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
||||||
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
|
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/clean-regexp": {
|
"node_modules/clean-regexp": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
|
@ -3124,6 +3217,12 @@
|
||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dayjs": {
|
||||||
|
"version": "1.11.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
||||||
|
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||||
|
|
@ -3266,6 +3365,12 @@
|
||||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dlv": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/dotenv": {
|
"node_modules/dotenv": {
|
||||||
"version": "16.5.0",
|
"version": "16.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
|
||||||
|
|
@ -3297,7 +3402,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/edge-error/-/edge-error-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/edge-error/-/edge-error-4.0.2.tgz",
|
||||||
"integrity": "sha512-jB76VYn8wapDHKHSOmP3vbKLoa77RJYsTLNmfl8+cuCD69uxZtP3h+kqV+Prw/YkYmN7yHyp4IApE15pDByk0A==",
|
"integrity": "sha512-jB76VYn8wapDHKHSOmP3vbKLoa77RJYsTLNmfl8+cuCD69uxZtP3h+kqV+Prw/YkYmN7yHyp4IApE15pDByk0A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.16.0"
|
"node": ">=18.16.0"
|
||||||
}
|
}
|
||||||
|
|
@ -3307,7 +3411,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/edge-lexer/-/edge-lexer-6.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/edge-lexer/-/edge-lexer-6.0.3.tgz",
|
||||||
"integrity": "sha512-/s15CNnfhZv97bsW+ZgV5rtONULYjhCDYu+usbVLqZ8UQ6b/hQUNvQSIQBXA6Gql9dm72TMBB9sb/eWM2esufg==",
|
"integrity": "sha512-/s15CNnfhZv97bsW+ZgV5rtONULYjhCDYu+usbVLqZ8UQ6b/hQUNvQSIQBXA6Gql9dm72TMBB9sb/eWM2esufg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"edge-error": "^4.0.2"
|
"edge-error": "^4.0.2"
|
||||||
},
|
},
|
||||||
|
|
@ -3320,7 +3423,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/edge-parser/-/edge-parser-9.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/edge-parser/-/edge-parser-9.0.4.tgz",
|
||||||
"integrity": "sha512-vnjzfpqpjM4Mjt9typc1zLoFpC1F6kAObfcdyA6rSy+izIPji2RaQz5jWx5s5iG9hNcuyjtNyGRCLFVfoYhWcA==",
|
"integrity": "sha512-vnjzfpqpjM4Mjt9typc1zLoFpC1F6kAObfcdyA6rSy+izIPji2RaQz5jWx5s5iG9hNcuyjtNyGRCLFVfoYhWcA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "^8.14.0",
|
"acorn": "^8.14.0",
|
||||||
"astring": "^1.9.0",
|
"astring": "^1.9.0",
|
||||||
|
|
@ -3337,7 +3439,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/edge.js/-/edge.js-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/edge.js/-/edge.js-6.2.1.tgz",
|
||||||
"integrity": "sha512-me875zh6YA0V429hywgQIpHgMvQkondv5XHaP6EsL2yIBpLcBWCl7Ba1cai0SwYhp8iD0IyV3KjpxLrnW7S2Ag==",
|
"integrity": "sha512-me875zh6YA0V429hywgQIpHgMvQkondv5XHaP6EsL2yIBpLcBWCl7Ba1cai0SwYhp8iD0IyV3KjpxLrnW7S2Ag==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@poppinss/inspect": "^1.0.1",
|
"@poppinss/inspect": "^1.0.1",
|
||||||
"@poppinss/macroable": "^1.0.4",
|
"@poppinss/macroable": "^1.0.4",
|
||||||
|
|
@ -3510,7 +3611,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz",
|
||||||
"integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==",
|
"integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|
@ -4174,8 +4274,7 @@
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
|
||||||
"integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==",
|
"integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
|
|
@ -4896,8 +4995,7 @@
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz",
|
||||||
"integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==",
|
"integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/js-tokens": {
|
"node_modules/js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
|
|
@ -5300,6 +5398,18 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/matchit": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/matchit/-/matchit-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-+nGYoOlfHmxe5BW5tE0EMJppXEwdSf8uBA1GTZC7Q77kbT35+VKLYJMzVNWCHSsga1ps1tPYFtFyvxvKzWVmMA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@arr/every": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/math-intrinsics": {
|
"node_modules/math-intrinsics": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
|
|
@ -6351,7 +6461,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz",
|
||||||
"integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==",
|
"integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/wooorm"
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
|
|
@ -7317,7 +7426,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/stringify-attributes/-/stringify-attributes-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/stringify-attributes/-/stringify-attributes-4.0.0.tgz",
|
||||||
"integrity": "sha512-6Hq3K153wTTfhEHb4V/viuqmb0DRn08JCrRnmqc4Q/tmoNuvd4DEyqkiiJXtvVz8ZSUhlCQr7zCpCVTgrelesg==",
|
"integrity": "sha512-6Hq3K153wTTfhEHb4V/viuqmb0DRn08JCrRnmqc4Q/tmoNuvd4DEyqkiiJXtvVz8ZSUhlCQr7zCpCVTgrelesg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"escape-goat": "^4.0.0"
|
"escape-goat": "^4.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -7835,6 +7943,15 @@
|
||||||
"spdx-expression-parse": "^3.0.0"
|
"spdx-expression-parse": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/validator": {
|
||||||
|
"version": "13.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.0.tgz",
|
||||||
|
"integrity": "sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vary": {
|
"node_modules/vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,10 @@
|
||||||
"@adonisjs/lucid": "^21.6.1",
|
"@adonisjs/lucid": "^21.6.1",
|
||||||
"@adonisjs/mail": "^9.2.2",
|
"@adonisjs/mail": "^9.2.2",
|
||||||
"@adonisjs/redis": "^9.2.0",
|
"@adonisjs/redis": "^9.2.0",
|
||||||
|
"@adonisjs/transmit": "^2.0.2",
|
||||||
|
"@vinejs/vine": "^3.0.1",
|
||||||
|
"adonis-captcha-guard": "^1.0.1",
|
||||||
|
"edge.js": "^6.2.1",
|
||||||
"luxon": "^3.6.1",
|
"luxon": "^3.6.1",
|
||||||
"pg": "^8.16.0",
|
"pg": "^8.16.0",
|
||||||
"reflect-metadata": "^0.2.2"
|
"reflect-metadata": "^0.2.2"
|
||||||
|
|
|
||||||
19
resources/views/mails/auth-fallback.edge
Normal file
19
resources/views/mails/auth-fallback.edge
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
Bonjour,
|
||||||
|
|
||||||
|
Nous avons reçu une demande de connexion à votre compte Khollisé.
|
||||||
|
|
||||||
|
Cliquez sur ce lien pour vous connecter :
|
||||||
|
{{ magicLink }}
|
||||||
|
|
||||||
|
Vous pouvez également entrer ce code de vérification à 6 chiffres :
|
||||||
|
{{ formattedOTP }}
|
||||||
|
|
||||||
|
Ce lien et ce code expireront dans {{ expiresIn }} et ne peuvent être utilisés qu'une seule fois.
|
||||||
|
|
||||||
|
Si vous n'avez pas demandé cette connexion, vous pouvez ignorer cet e-mail.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Cet e-mail a été envoyé à {{ email }}
|
||||||
|
|
||||||
|
© {{ new Date().getFullYear() }} Khollisé. Tous droits réservés.
|
||||||
48
resources/views/mails/auth.edge
Normal file
48
resources/views/mails/auth.edge
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>{{ emailTitle }}</title>
|
||||||
|
</head>
|
||||||
|
<body style="font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; line-height: 1.6; color: #333; margin: 0; padding: 0; background-color: #f9f9f9;">
|
||||||
|
<div style="max-width: 600px; margin: 0 auto; background-color: #ffffff; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);">
|
||||||
|
<div style="padding: 5px 24px 32px 24px;">
|
||||||
|
<p style="margin-bottom: 16px;">Bonjour,</p>
|
||||||
|
|
||||||
|
<p style="margin-bottom: 16px;">
|
||||||
|
Nous avons reçu une demande de connexion à votre compte Khollisé. Utilisez le bouton ci-dessous pour vous connecter instantanément :
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<a href="{{ magicLink }}" style="display: inline-block; background-color: #4f46e5; color: #ffffff; text-decoration: none; padding: 12px 24px; border-radius: 4px; font-weight: 600; margin: 16px 0; text-align: center;">Se connecter</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr style="margin: 32px 0; border: none; border-top: 1px solid #e5e7eb;" />
|
||||||
|
|
||||||
|
<p style="margin-bottom: 16px;">
|
||||||
|
Vous pouvez également utiliser le code de vérification à 6 chiffres ci-dessous :
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div style="margin: 24px 0; text-align: center;">
|
||||||
|
<div style="font-family: monospace; font-size: 24px; letter-spacing: 4px; background-color: #f3f4f6; padding: 12px 16px; border-radius: 4px; font-weight: bold; color: #1f2937; display: inline-block;">
|
||||||
|
{{ formattedOTP }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p style="font-size: 14px; color: #6b7280; font-style: italic; margin-bottom: 16px;">
|
||||||
|
Le lien et le code ci-dessus expireront dans {{ expiresIn }} et ne peuvent être utilisés qu'une seule fois.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p style="font-size: 14px; color: #6b7280; font-style: italic; margin-bottom: 16px;">
|
||||||
|
Si vous n'avez pas demandé cette connexion, vous pouvez ignorer cet email en toute sécurité.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="background-color: #f3f4f6; padding: 16px 24px; text-align: center; font-size: 12px; color: #6b7280;">
|
||||||
|
<p style="margin: 0 0 4px 0;">Cet email a été envoyé à {{ email }}</p>
|
||||||
|
<p style="margin: 0;">© {{ new Date().getFullYear() }} Khollisé. Tous droits réservés.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -48,8 +48,9 @@ export default await Env.create(new URL('../', import.meta.url), {
|
||||||
| Variables for configuring the mail package
|
| Variables for configuring the mail package
|
||||||
|----------------------------------------------------------
|
|----------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
// MAILGUN_API_KEY: Env.schema.string(),
|
MAILGUN_API_KEY: Env.schema.string(),
|
||||||
// MAILGUN_DOMAIN: Env.schema.string()
|
MAILGUN_DOMAIN: Env.schema.string(),
|
||||||
|
MAIL_FROM: Env.schema.string(),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|----------------------------------------------------------
|
|----------------------------------------------------------
|
||||||
|
|
@ -59,4 +60,7 @@ export default await Env.create(new URL('../', import.meta.url), {
|
||||||
REDIS_HOST: Env.schema.string({ format: 'host' }),
|
REDIS_HOST: Env.schema.string({ format: 'host' }),
|
||||||
REDIS_PORT: Env.schema.number(),
|
REDIS_PORT: Env.schema.number(),
|
||||||
REDIS_PASSWORD: Env.schema.string.optional(),
|
REDIS_PASSWORD: Env.schema.string.optional(),
|
||||||
|
|
||||||
|
TURNSTILE_SITE_KEY: Env.schema.string.optional(),
|
||||||
|
TURNSTILE_SECRET: Env.schema.string.optional(),
|
||||||
})
|
})
|
||||||
|
|
|
||||||
11
start/limiters.ts
Normal file
11
start/limiters.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import limiter from '@adonisjs/limiter/services/main'
|
||||||
|
|
||||||
|
/// For global rate limiting (critical routes)
|
||||||
|
export const throttle = limiter.define('global', () => {
|
||||||
|
return limiter.allowRequests(10).every('1 minute')
|
||||||
|
})
|
||||||
|
|
||||||
|
// For mail sending
|
||||||
|
export const authThrottle = limiter.define('auth', () => {
|
||||||
|
return limiter.allowRequests(3).every('5 minutes')
|
||||||
|
})
|
||||||
|
|
@ -8,5 +8,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import router from '@adonisjs/core/services/router'
|
import router from '@adonisjs/core/services/router'
|
||||||
|
import transmit from '@adonisjs/transmit/services/main'
|
||||||
|
import { authThrottle } from './limiters.js'
|
||||||
|
import { throttle } from './limiter.js'
|
||||||
|
|
||||||
router.get('/', async () => 'It works!')
|
transmit.registerRoutes()
|
||||||
|
|
||||||
|
const AuthController = () => import('#controllers/auth_controller')
|
||||||
|
|
||||||
|
router.group(() => {
|
||||||
|
router.post('/auth/request', [AuthController, 'requestLogin']).use(authThrottle)
|
||||||
|
router.post('/auth/verify', [AuthController, 'verifyCode']).use(throttle)
|
||||||
|
// router.get('/auth/magic-link', 'AuthController.magicLink').use(throttle)
|
||||||
|
// router.get('/auth/listen', 'AuthController.listen')
|
||||||
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue