This commit is contained in:
2021-10-19 17:21:34 +02:00
parent 6adb941770
commit c9b41cf0d5
13 changed files with 4217 additions and 42 deletions

View File

@@ -23,9 +23,13 @@
"@adonisjs/auth", "@adonisjs/auth",
"@adonisjs/redis", "@adonisjs/redis",
"@adonisjs/lucid", "@adonisjs/lucid",
"@adonisjs/session" "@adonisjs/session",
"@adonisjs/i18n"
], ],
"aceProviders": [ "aceProviders": [
"@adonisjs/repl" "@adonisjs/repl"
],
"metaFiles": [
"resources/lang/**/*.(json|yaml)"
] ]
} }

View File

@@ -136,7 +136,7 @@
"name": "connection", "name": "connection",
"propertyName": "connection", "propertyName": "connection",
"type": "string", "type": "string",
"description": "Define a custom database connection for the migration" "description": "The connection flag is used to lookup the directory for the migration file"
}, },
{ {
"name": "folder", "name": "folder",

View File

@@ -5,7 +5,7 @@ import Link from "App/Models/Link";
export default class LinksController { export default class LinksController {
public async getLink ({params, response}: HttpContextContract) { public async getLink ({params, response, i18n}: HttpContextContract) {
const code = params.id const code = params.id
const link = await Link.findByOrFail('code', code) const link = await Link.findByOrFail('code', code)
@@ -14,40 +14,44 @@ export default class LinksController {
await link.merge({ await link.merge({
visitCount: visitCount visitCount: visitCount
}).save() }).save()
return response.redirect(link.target) return response.status(200).redirect(link.target)
} }
return response.badRequest(`Code does not exist ! (/${code})`) return response.status(404).send({
message: i18n.formatMessage('messages.no_exists', { code: code })
})
} }
public async getAllLinks ({response}: HttpContextContract) { public async getAllLinks ({response}: HttpContextContract) {
const links = await Link.query().orderBy('id', 'asc') const links = await Link.query().orderBy('id', 'asc')
return response.ok(links); return response.status(200).send({
links
})
} }
public async getVisitCount ({params}: HttpContextContract) { public async getVisitCount ({params, response, i18n}: HttpContextContract) {
const code = params.id const code = params.id
const link = await Link.findByOrFail('code', code) const link = await Link.findByOrFail('code', code)
//Check if code exists //Check if code exists
if (link.code === code) { if (link.code === code) {
return { return response.status(200).send({
count: link.visitCount count: link.visitCount
})
} }
} return response.status(404).send({
return { message: i18n.formatMessage('messages.no_exists', { code: code })
message: `Code does not exist ! (${code}` })
}
} }
public async createLink ({request, auth}: HttpContextContract) { public async createLink ({response, request, auth, i18n}: HttpContextContract) {
await auth.authenticate() await auth.authenticate()
const link = await Link.create(await request.validate(StoreValidator)) const link = await Link.create(await request.validate(StoreValidator))
return { return response.status(200).send({
message: `Link successfully created : ${link.code} + ${link.target}` message: i18n.formatMessage('messages.created', { link: link.code, target: link.target })
} })
} }
public async updateLink ({request, auth}: HttpContextContract) { public async updateLink ({response, request, auth}: HttpContextContract) {
await auth.authenticate() await auth.authenticate()
const link = await Link.findByOrFail('code', request.input('code')) const link = await Link.findByOrFail('code', request.input('code'))
const data = await request.validate(UpdateValidator) const data = await request.validate(UpdateValidator)
@@ -55,14 +59,19 @@ export default class LinksController {
target: data.target, target: data.target,
visitCount: 0 visitCount: 0
}).save() }).save()
return link return response.status(200).send({
link
})
} }
public async deleteLink ({request, auth}: HttpContextContract) { public async deleteLink ({i18n, response, request, auth}: HttpContextContract) {
await auth.authenticate() await auth.authenticate()
const code = request.input('code') const code = request.input('code')
const link = await Link.findByOrFail('code', code) const link = await Link.findByOrFail('code', code)
await link.delete() await link.delete()
return response.status(200).send({
message: i18n.formatMessage('messages.deleted', { link: link.code })
})
} }
} }

View File

@@ -2,31 +2,39 @@ import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class UsersController { export default class UsersController {
public async login ({request, auth}: HttpContextContract) { public async login ({request, auth, response}: HttpContextContract) {
const email = request.input('email') const email = request.input('email')
const password = request.input('password') const password = request.input('password')
const token = await auth.use('api').attempt(email, password, { const token = await auth.use('api').attempt(email, password, {
expiresIn: '2 days' expiresIn: '2 days'
}) })
return token.toJSON() return response.status(200).send({
token: token.toJSON()
})
} }
public async createInfiniteToken ({request, auth}: HttpContextContract) { public async createInfiniteToken ({response, request, auth}: HttpContextContract) {
const email = request.input('email') const email = request.input('email')
const password = request.input('password') const password = request.input('password')
const token = await auth.use('api').attempt(email, password) const token = await auth.use('api').attempt(email, password)
return token.toJSON() return response.status(200).send({
token: token.toJSON()
})
} }
public async logout ({auth}: HttpContextContract) { public async logout ({response, auth, i18n}: HttpContextContract) {
await auth.use('api').revoke() await auth.use('api').revoke()
return { message: 'Vous avez été déconnecté' } return response.status(200).send({
message: i18n.formatMessage('messages.logout')
})
} }
public async me ({auth}: HttpContextContract) { public async me ({response, auth}: HttpContextContract) {
await auth.authenticate() await auth.authenticate()
return auth.user return response.status(200).send({
user: auth.user
})
} }
} }

View File

@@ -0,0 +1,45 @@
import I18n from '@ioc:Adonis/Addons/I18n'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
/**
* The middleware detects the user language using the "Accept-language" HTTP header
* or the "lang" query string parameter.
*
* Feel free to change the middleware implementation to what suits your needs. Just
* make sure
*
* - You always ensure the user selected language is supported by your app.
* - Only call "switchLocale" when the detected language is valid string value and
* not "null" or "undefined"
*/
export default class DetectUserLocale {
/**
* Detect user language using "Accept-language" header or
* the "lang" query string parameter.
*
* The user language must be part of the "supportedLocales", otherwise
* this method should return null.
*/
protected getUserLanguage(ctx: HttpContextContract) {
const availableLocales = I18n.supportedLocales()
return ctx.request.language(availableLocales) || ctx.request.input('lang')
}
/**
* Handle method is called by AdonisJS automatically on every middleware
* class.
*/
public async handle(ctx: HttpContextContract, next: () => Promise<void>) {
const language = this.getUserLanguage(ctx)
/**
* Switch locale when we are able to detect the user language and it
* is supported by the application
*/
if (language) {
ctx.i18n.switchLocale(language)
}
await next()
}
}

104
config/i18n.ts Normal file
View File

@@ -0,0 +1,104 @@
/**
* Config source: https://git.io/Jw53K
*
* Feel free to let us know via PR, if you find something broken in this config
* file.
*/
import Application from '@ioc:Adonis/Core/Application'
import { I18nConfig } from '@ioc:Adonis/Addons/I18n'
const i18nConfig: I18nConfig = {
/*
|--------------------------------------------------------------------------
| Translations format
|--------------------------------------------------------------------------
|
| The format in which the translation are written. By default only the
| ICU message syntax is supported. However, you can register custom
| formatters too and please reference the documentation for that.
|
*/
translationsFormat: 'icu',
/*
|--------------------------------------------------------------------------
| Default locale
|--------------------------------------------------------------------------
|
| The default locale represents the language for which all the translations
| are always available.
|
| Having a default locale allows you to incrementally add translations for
| other languages. If a specific language does not have a translation,
| then the default locale translation will be used.
|
| Also, we switch to default locale for HTTP requests where the user language
| is not supported by the your app
|
*/
defaultLocale: 'en',
/*
|--------------------------------------------------------------------------
| Supported locales
|--------------------------------------------------------------------------
|
| Optionally define an array of locales that your application supports. If
| not defined, we will derive this value from the translations stored
| inside the `resources/lang` directory.
|
*/
supportedLocales: [
'fr',
'en'
],
/*
|--------------------------------------------------------------------------
| Fallback locales
|--------------------------------------------------------------------------
|
| Here you can configure per language fallbacks. For example, you can set
| "es" as the fallback locale for the Catalan language.
|
| If not configured, all languages will fallback to the defaultLocale
|
*/
// fallbackLocales: {},
/*
|--------------------------------------------------------------------------
| Provide validator messages
|--------------------------------------------------------------------------
|
| Set the following option to "true" if you want to use "i18n" for defining
| the validation messages.
|
| The validation messages will be loaded from the "validator.shared" prefix.
|
*/
provideValidatorMessages: true,
/*
|--------------------------------------------------------------------------
| Loaders
|--------------------------------------------------------------------------
|
| Loaders from which to load the translations. You can configure multiple
| loaders as well and AdonisJS will merge the translations from all the
| loaders to have a unified collection of messages.
|
| By default, only the "fs" loader is supported. However, you can add custom
| loaders too and please reference the documentation for that.
|
*/
loaders: {
fs: {
enabled: true,
location: Application.resourcesPath('lang'),
},
},
}
export default i18nConfig

View File

@@ -21,24 +21,25 @@
"devDependencies": { "devDependencies": {
"@adonisjs/assembler": "^5.3.2", "@adonisjs/assembler": "^5.3.2",
"adonis-preset-ts": "^2.1.0", "adonis-preset-ts": "^2.1.0",
"pino-pretty": "^5.0.2", "pino-pretty": "^7.1.0",
"typescript": "^4.3.4", "typescript": "^4.4.4",
"youch": "^2.2.2", "youch": "^2.2.2",
"youch-terminal": "^1.1.1" "youch-terminal": "^1.1.1"
}, },
"dependencies": { "dependencies": {
"@adonisjs/auth": "^8.0.6", "@adonisjs/auth": "^8.0.10",
"@adonisjs/core": "^5.1.8", "@adonisjs/core": "^5.4.0",
"@adonisjs/lucid": "^15.0.1", "@adonisjs/i18n": "^1.5.2",
"@adonisjs/redis": "^7.0.6", "@adonisjs/lucid": "^16.2.1",
"@adonisjs/repl": "^3.1.4", "@adonisjs/redis": "^7.0.9",
"@adonisjs/session": "^6.0.6", "@adonisjs/repl": "^3.1.7",
"luxon": "^1.27.0", "@adonisjs/session": "^6.1.2",
"luxon": "^2.0.2",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"phc-argon2": "^1.1.1", "phc-argon2": "^1.1.2",
"proxy-addr": "^2.0.7", "proxy-addr": "^2.0.7",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"source-map-support": "^0.5.19", "source-map-support": "^0.5.20",
"tslib": "^2.3.0" "tslib": "^2.3.1"
} }
} }

View File

@@ -0,0 +1,6 @@
{
"no_exists": "The code '{code}' does not exist!",
"created": "The link '{link}' with target '{target}' was successfully created!",
"deleted": "The link '{link}' was successfully deleted!",
"logout": "You have been disconnected!"
}

View File

@@ -0,0 +1,6 @@
{
"no_exists": "Le code '{code}' n'existe pas!",
"created": "Le lien '{link}' avec pour direction '{target}' a bien été créé!",
"deleted": "Le lien '{link}' a bien été supprimé!",
"logout": "Vous avez été déconnecté !"
}

View File

@@ -22,7 +22,8 @@ import Server from '@ioc:Adonis/Core/Server'
*/ */
Server.middleware.register([ Server.middleware.register([
() => import('@ioc:Adonis/Core/BodyParser'), () => import('@ioc:Adonis/Core/BodyParser'),
() => import('App/Middleware/SilentAuth') () => import('App/Middleware/SilentAuth'),
() => import('App/Middleware/DetectUserLocale')
]) ])
/* /*

View File

@@ -23,7 +23,7 @@ Route.get('/source', async ({response}: HttpContextContract) => {
Route.get('health', async ({response}: HttpContextContract) => { Route.get('health', async ({response}: HttpContextContract) => {
const report = await HealthCheck.getReport() const report = await HealthCheck.getReport()
const isLive = await HealthCheck.isLive() const isLive = await HealthCheck.isLive()
const isReady = await HealthCheck.isReady() const isReady = HealthCheck.isReady()
return report.healthy ? response.ok({ isLive, isReady, report: report.report }) : response.badRequest({ isLive, isReady, report: report.report }) return report.healthy ? response.ok({ isLive, isReady, report: report.report }) : response.badRequest({ isLive, isReady, report: report.report })
}) })

View File

@@ -31,7 +31,8 @@
"@adonisjs/auth", "@adonisjs/auth",
"@adonisjs/redis", "@adonisjs/redis",
"@adonisjs/lucid", "@adonisjs/lucid",
"@adonisjs/session" "@adonisjs/session",
"@adonisjs/i18n"
] ]
} }
} }

3990
yarn.lock Normal file

File diff suppressed because it is too large Load Diff