diff --git a/.adonisrc.json b/.adonisrc.json old mode 100644 new mode 100755 index 46f29c9..a34ea65 --- a/.adonisrc.json +++ b/.adonisrc.json @@ -5,7 +5,8 @@ "@adonisjs/core/build/commands", "@adonisjs/repl/build/commands", "@adonisjs/lucid/build/commands", - "@adonisjs/mail/build/commands" + "@adonisjs/mail/build/commands", + "@adonisjs/bouncer/build/commands" ], "exceptionHandlerNamespace": "App/Exceptions/Handler", "aliases": { @@ -16,18 +17,19 @@ }, "preloads": [ "./start/routes", - "./start/kernel" + "./start/kernel", + "./start/bouncer" ], "providers": [ "./providers/AppProvider", "@adonisjs/core", - "@adonisjs/redis", "@adonisjs/session", "@adonisjs/auth", "@adonisjs/lucid", "@adonisjs/mail", "@adonisjs/view", - "@adonisjs/ally" + "@adonisjs/bouncer", + "@adonisjs/redis" ], "aceProviders": [ "@adonisjs/repl" diff --git a/.dockerignore b/.dockerignore old mode 100644 new mode 100755 index 3c3629e..37d7e73 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,2 @@ node_modules +.env diff --git a/.editorconfig b/.editorconfig old mode 100644 new mode 100755 diff --git a/.env.example b/.env.example old mode 100644 new mode 100755 index 92e298b..0a5a677 --- a/.env.example +++ b/.env.example @@ -3,6 +3,12 @@ HOST= NODE_ENV= APP_KEY= APP_NAME= +BASE_URL= +API_VERSION= + +GITHUB_TOKEN= +GITHUB_SOURCE= +DISCORD_ID= DB_CONNECTION= @@ -20,21 +26,13 @@ REDIS_DB= REDIS_HOST= REDIS_PASSWORD= -GITHUB_TOKEN= -GITHUB_SOURCE= -BASE_URL= -API_VERSION= - CACHE_VIEWS= -MAILGUN_API_KEY= -MAILGUN_URL= +SMTP_HOST= +SMTP_PORT= +SMTP_USERNAME= +SMTP_PASSWORD= -GITHUB_CLIENT_ID= -GITHUB_CLIENT_SECRET= - -GOOGLE_CLIENT_ID= -GOOGLE_CLIENT_SECRET= - -TWITTER_CLIENT_ID= -TWITTER_CLIENT_SECRET= +WAKATIME_USER= +WAKATIME_KEY= +WAKATIME_ID= diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..9f3f79e --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +nodes_modules +.env +build diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..09c2e42 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "@antfu" +} diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index a0b3fd6..70d871d --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,111 @@ -node_modules -build +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul coverage -.vscode -.DS_STORE +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file .env -tmp +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and *not* Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Jetbrains +.idea + +# AdonisJS database/seeders -.idea/ -/database/seeders/ +build diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index b58b603..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/.idea/artapi.iml b/.idea/artapi.iml deleted file mode 100644 index 24643cc..0000000 --- a/.idea/artapi.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index a55e7a1..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index e6356c6..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Dockerfile b/Dockerfile old mode 100644 new mode 100755 index d463aae..31fdca8 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,16 @@ -FROM node:15.8.0-alpine3.10 +FROM node:16-alpine3.11 -RUN mkdir -p /usr/src/artapi -WORKDIR /usr/src/artapi +RUN mkdir -p /usr/src/athena -COPY . /usr/src/artapi +WORKDIR /usr/src/athena -RUN apk update && \ - apk add git +COPY . /usr/src/athena RUN yarn install RUN yarn build -RUN cp .env build - -WORKDIR /usr/src/artapi/build +WORKDIR /usr/src/athena/build RUN yarn install --production diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 33b6989..ab2a052 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# ArtApi 🧠 +# Athena 🧠 -ArtAPi is my personnal api connected to my instances +Athena is my personal api connected to my instances ## Features ✨ @@ -26,4 +26,4 @@ ArtAPi is my personnal api connected to my instances ## License 📑 Copyright © 2020 - [@ArthurDanj](https://arthurdanjou.fr) \ -This project is [MIT](https://github.com/ArthurDanjou/artapi/blob/master/LICENSE) Licensed. +This project is [MIT](https://github.com/ArthurDanjou/athena/blob/master/LICENSE) Licensed. diff --git a/ace b/ace old mode 100644 new mode 100755 diff --git a/ace-manifest.json b/ace-manifest.json old mode 100644 new mode 100755 index 77bd59c..149a594 --- a/ace-manifest.json +++ b/ace-manifest.json @@ -2,7 +2,7 @@ "commands": { "dump:rcfile": { "settings": {}, - "commandPath": "@adonisjs/core/build/commands/DumpRc", + "commandPath": "@adonisjs/core/commands/DumpRc", "commandName": "dump:rcfile", "description": "Dump contents of .adonisrc.json file along with defaults", "args": [], @@ -13,7 +13,7 @@ "settings": { "loadApp": true }, - "commandPath": "@adonisjs/core/build/commands/ListRoutes", + "commandPath": "@adonisjs/core/commands/ListRoutes", "commandName": "list:routes", "description": "List application routes", "args": [], @@ -29,7 +29,7 @@ }, "generate:key": { "settings": {}, - "commandPath": "@adonisjs/core/build/commands/GenerateKey", + "commandPath": "@adonisjs/core/commands/GenerateKey", "commandName": "generate:key", "description": "Generate a new APP_KEY secret", "args": [], @@ -136,7 +136,7 @@ "name": "connection", "propertyName": "connection", "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", @@ -278,6 +278,42 @@ ], "aliases": [], "flags": [] + }, + "make:policy": { + "settings": {}, + "commandPath": "@adonisjs/bouncer/build/commands/MakePolicy", + "commandName": "make:policy", + "description": "Make a new bouncer policy", + "args": [ + { + "type": "string", + "propertyName": "name", + "name": "name", + "required": true, + "description": "Name of the policy to create" + } + ], + "aliases": [], + "flags": [ + { + "name": "resource-model", + "propertyName": "resourceModel", + "type": "string", + "description": "Name of the resource model to authorize" + }, + { + "name": "user-model", + "propertyName": "userModel", + "type": "string", + "description": "Name of the user model to be authorized" + }, + { + "name": "actions", + "propertyName": "actions", + "type": "array", + "description": "Actions to implement" + } + ] } }, "aliases": {} diff --git a/app/Controllers/Http/AnnouncesController.ts b/app/Controllers/Http/AnnouncesController.ts new file mode 100644 index 0000000..6715271 --- /dev/null +++ b/app/Controllers/Http/AnnouncesController.ts @@ -0,0 +1,36 @@ +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import Announce from 'App/Models/Announce' +import AnnounceUpdateValidator from 'App/Validators/announce/AnnounceUpdateValidator' +import File from 'App/Models/File' +import { getTranslation } from 'App/Utils/TranslationsUtils' + +export default class AnnouncesController { + public async index({ response }: HttpContextContract) { + const announce = await Announce + .query() + .orderBy('created_at', 'desc') + .preload('message') + .preload('cover') + .first() + return response.status(200).send({ + announce, + }) + } + + public async update({ request, params, response }: HttpContextContract) { + const data = await request.validate(AnnounceUpdateValidator) + const announce = await Announce.findOrFail(params.id) + + if (data.code) + await announce.related('message').associate(await getTranslation(data.code)) + + const cover = await File.findBy('label', data.cover) + if (cover) await announce.related('cover').associate(cover) + + await announce.merge(data).save() + + return response.status(200).send({ + announce, + }) + } +} diff --git a/app/Controllers/Http/AuthController.ts b/app/Controllers/Http/AuthController.ts old mode 100644 new mode 100755 index c5fa42e..27f7ef1 --- a/app/Controllers/Http/AuthController.ts +++ b/app/Controllers/Http/AuthController.ts @@ -1,136 +1,52 @@ -import {HttpContextContract} from '@ioc:Adonis/Core/HttpContext' -import User from "App/Models/User"; -import AuthValidator from "App/Validators/AuthValidator"; -import {AllyUserContract} from "@ioc:Adonis/Addons/Ally"; +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import AuthValidator from 'App/Validators/AuthValidator' export default class AuthController { - - public async loginWeb ({request, auth}: HttpContextContract) { - const data = await request.validate(AuthValidator) - const {email, password, remember_me } = data - - try { - await auth.attempt(email, password, remember_me) - const user = await User.query() - .where('id', auth.user!.id) - .firstOrFail() - if (!remember_me) { - await user.merge({ - rememberMeToken: '' - }).save() - } - return { user } - } catch (error) { - if (error.code === 'E_INVALID_AUTH_UID') return { error: "L'utilisateur n'a pas été trouvé" } - if (error.code === 'E_INVALID_AUTH_PASSWORD') return { error: "L'identifiant ou le mot de passe est incorrect" } - } - } - - public async loginApi ({request, auth}: HttpContextContract) { - const email = request.input('email') - const password = request.input('password') - + public async loginApi({ request, auth, response }: HttpContextContract) { + const { email, password } = await request.validate(AuthValidator) const token = await auth.use('api').attempt(email, password, { - expiresIn: '2 days' + expiresIn: '2 days', + }) + return response.status(200).send({ + token: token.toJSON(), }) - return token.toJSON() } - public async createInfiniteToken ({request, auth}: HttpContextContract) { - const email = request.input('email') - const password = request.input('password') + public async loginWeb({ request, auth, response }: HttpContextContract) { + const { email, password, remember } = await request.validate(AuthValidator) + await auth.use('web').attempt(email, password, remember) + + return response.status(200).send({ + user: auth.use('web').user, + }) + } + + public async createInfiniteToken({ request, auth, response }: HttpContextContract) { + const { email, password } = await request.validate(AuthValidator) const token = await auth.use('api').attempt(email, password) - return token.toJSON() - } - - public async logoutWeb ({auth}: HttpContextContract) { - await auth.logout() - return { message: 'Vous avez été déconnecté' } - } - - public async logoutApi ({auth}: HttpContextContract) { - await auth.use('api').logout() - return { message: 'Vous avez été déconnecté' } - } - - public async user ({auth}: HttpContextContract) { - await auth.authenticate() - return await User.query() - .where('id', auth.user!.id) - .firstOrFail() - } - - public async twitter ({ally, auth}: HttpContextContract) { - const twitter = ally.use('twitter') - - if (twitter.accessDenied()) { - return 'Access Denied' - } - - if (twitter.stateMisMatch()) { - return 'Request expired. Retry again' - } - - if (twitter.hasError()) { - return twitter.getError() - } - - const twitterUser = await twitter.user() - const user = await this.createUser(twitterUser) - await auth.use('web').login(user) - return user - } - - public async github ({ally, auth}: HttpContextContract) { - const github = ally.use('github') - - if (github.accessDenied()) { - return 'Access Denied' - } - - if (github.stateMisMatch()) { - return 'Request expired. Retry again' - } - - if (github.hasError()) { - return github.getError() - } - - const githubUser = await github.user() - const user = await this.createUser(githubUser) - await auth.use('web').login(user) - return user - } - - public async google ({ally, auth}: HttpContextContract) { - const google = ally.use('google') - - if (google.accessDenied()) { - return 'Access Denied' - } - - if (google.stateMisMatch()) { - return 'Request expired. Retry again' - } - - if (google.hasError()) { - return google.getError() - } - - const googleUser = await google.user() - const user = await this.createUser(googleUser) - await auth.use('web').login(user) - return user - } - - public async createUser(allyUser: AllyUserContract): Promise { - return await User.firstOrCreate({ - email: allyUser.email!, - }, { - email: allyUser.email!, - username: allyUser.name, - isConfirmed: allyUser.emailVerificationState === 'verified' + return response.status(200).send({ + token: token.toJSON(), }) } + public async logoutApi({ auth, response }: HttpContextContract) { + await auth.use('api').revoke() + return response.status(200).send({ + message: 'You have been disconnected!', + }) + } + + public async logoutWeb({ auth, response }: HttpContextContract) { + await auth.use('web').logout() + return response.status(200).send({ + message: 'You have been disconnected!', + }) + } + + public async user({ auth, response }: HttpContextContract) { + const user = await auth.use('web').authenticate() || await auth.use('api').authenticate() + return response.status(200).send({ + user, + }) + } } diff --git a/app/Controllers/Http/ExperiencesController.ts b/app/Controllers/Http/ExperiencesController.ts new file mode 100644 index 0000000..42e4c08 --- /dev/null +++ b/app/Controllers/Http/ExperiencesController.ts @@ -0,0 +1,57 @@ +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import Experience from 'App/Models/Experience' +import ExperienceStoreValidator from 'App/Validators/experience/ExperienceStoreValidator' +import ExperienceUpdateValidator from 'App/Validators/experience/ExperienceUpdateValidator' +import { getTranslation } from 'App/Utils/TranslationsUtils' + +export default class ExperiencesController { + public async index({ response }: HttpContextContract) { + const experiences = await Experience + .query() + .orderBy('begin_date', 'desc') + .preload('title') + return response.status(200).send({ + experiences, + }) + } + + public async store({ request, response }: HttpContextContract) { + const data = await request.validate(ExperienceStoreValidator) + const experience = await Experience.create(data) + await experience.related('title').associate(await getTranslation(data.title)) + + return response.status(200).send({ + experience, + }) + } + + public async show({ params, response }: HttpContextContract) { + const experience = await Experience.findOrFail(params.id) + experience.load('title') + return response.status(200).send({ + experience, + }) + } + + public async update({ request, params, response }: HttpContextContract) { + const data = await request.validate(ExperienceUpdateValidator) + const experience = await Experience.findOrFail(params.id) + + if (data.title) + await experience.related('title').associate(await getTranslation(data.title)) + + await experience.merge(data).save() + + return response.status(200).send({ + experience, + }) + } + + public async destroy({ response, params }: HttpContextContract) { + const experience = await Experience.findOrFail(params.id) + await experience.delete() + return response.status(200).send({ + message: 'Experience successfully deleted!', + }) + } +} diff --git a/app/Controllers/Http/FileController.ts b/app/Controllers/Http/FileController.ts deleted file mode 100644 index 8acea6a..0000000 --- a/app/Controllers/Http/FileController.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' -import Application from "@ioc:Adonis/Core/Application"; -import File from "App/Models/File"; - -export default class FileController { - - public async index () { - return File.query() - } - - public async store ({request}: HttpContextContract) { - const file = await request.file('file', { - extnames: ['jpg', 'png', 'jpeg'] - }) - const label = request.input('label') - - if (!file) { - return 'Please upload file' - } - if (file.hasErrors) { - return file.errors - } - - await file.move(Application.makePath('storage'), { - name: `${label}.${file.extname}` - }) - - return await File.create({ - fileName: `${label}.${file.extname}`, - label: label - }) - } - - public async destroy({ params }: HttpContextContract) { - const file = await File.findOrFail(params.id) - await file.delete() - return { message: "Le fichier a bien été supprimée" } - } - -} diff --git a/app/Controllers/Http/FilesController.ts b/app/Controllers/Http/FilesController.ts new file mode 100755 index 0000000..94931ef --- /dev/null +++ b/app/Controllers/Http/FilesController.ts @@ -0,0 +1,46 @@ +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import Application from '@ioc:Adonis/Core/Application' +import File from 'App/Models/File' + +export default class FilesController { + public async index({ response }: HttpContextContract) { + return response.status(200).send({ + files: await File.all(), + }) + } + + public async store({ request, response }: HttpContextContract) { + const file = await request.file('file', { + extnames: ['jpg', 'png', 'jpeg'], + }) + const label = request.input('label') + + if (!file) + return 'Please upload file!' + + if (file.hasErrors) + return file.errors + + await file.move(Application.makePath('storage'), { + name: `${label}.${file.extname}`, + overwrite: true, + }) + + return response.status(200).send({ + file: await File.firstOrCreate({ + label, + }, { + fileName: `${label}.${file.extname}`, + label, + }), + }) + } + + public async destroy({ params, response }: HttpContextContract) { + const file = await File.findOrFail(params.id) + await file.delete() + return response.status(200).send({ + message: 'File successfully deleted!', + }) + } +} diff --git a/app/Controllers/Http/FormationsController.ts b/app/Controllers/Http/FormationsController.ts new file mode 100644 index 0000000..d391c49 --- /dev/null +++ b/app/Controllers/Http/FormationsController.ts @@ -0,0 +1,64 @@ +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import FormationStoreValidator from 'App/Validators/formation/FormationStoreValidator' +import FormationUpdateValidator from 'App/Validators/formation/FormationUpdateValidator' +import Formation from 'App/Models/Formation' +import { getTranslation } from 'App/Utils/TranslationsUtils' + +export default class FormationsController { + public async index({ response }: HttpContextContract) { + const formations = await Formation + .query() + .orderBy('begin_date', 'desc') + .preload('title') + .preload('description') + return response.status(200).send({ + formations, + }) + } + + public async store({ request, response }: HttpContextContract) { + const data = await request.validate(FormationStoreValidator) + const formation = await Formation.create(data) + + await formation.related('title').associate(await getTranslation(data.title)) + await formation.related('description').associate(await getTranslation(data.description)) + + return response.status(200).send({ + formation, + }) + } + + public async show({ params, response }: HttpContextContract) { + const formation = await Formation.findOrFail(params.id) + formation.load('title') + formation.load('description') + return response.status(200).send({ + formation, + }) + } + + public async update({ request, params, response }: HttpContextContract) { + const data = await request.validate(FormationUpdateValidator) + const formation = await Formation.findOrFail(params.id) + + if (data.title) + await formation.related('title').associate(await getTranslation(data.title)) + + if (data.description) + await formation.related('description').associate(await getTranslation(data.description)) + + await formation.merge(data).save() + + return response.status(200).send({ + formation, + }) + } + + public async destroy({ response, params }: HttpContextContract) { + const formation = await Formation.findOrFail(params.id) + await formation.delete() + return response.status(200).send({ + message: 'Formation successfully deleted!', + }) + } +} diff --git a/app/Controllers/Http/FormsController.ts b/app/Controllers/Http/FormsController.ts old mode 100644 new mode 100755 index c6424bd..882dc27 --- a/app/Controllers/Http/FormsController.ts +++ b/app/Controllers/Http/FormsController.ts @@ -1,20 +1,33 @@ import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' -import FormValidator from "App/Validators/FormValidator"; -import Form from "App/Models/Form"; -//import FormConfirmation from "App/Mailers/FormConfirmation"; +import FormStoreValidator from 'App/Validators/form/FormStoreValidator' +import Form from 'App/Models/Form' export default class FormsController { - - public async send({ request, response }: HttpContextContract) { - const data = await request.validate(FormValidator) - - await Form.create(data) - - //await new FormConfirmation(data.name, data.email).sendLater() - //todo send confirmation email + email to me + public async index({ response }: HttpContextContract) { return response.status(200).send({ - status: 200 + forms: Form.query().orderBy('created_at', 'asc'), }) } + public async store({ request, response }: HttpContextContract) { + const data = await request.validate(FormStoreValidator) + // todo send confirmation email + email to me with FormConfirmation + return response.status(200).send({ + form: await Form.create(data), + }) + } + + public async show({ params, response }: HttpContextContract) { + return response.status(200).send({ + form: await Form.findOrFail(params.id), + }) + } + + public async destroy({ response, params }: HttpContextContract) { + const form = await Form.findOrFail(params.id) + await form.delete() + return response.status(200).send({ + message: 'Form successfully deleted!', + }) + } } diff --git a/app/Controllers/Http/GuestBookController.ts b/app/Controllers/Http/GuestBookController.ts deleted file mode 100644 index b7f80e7..0000000 --- a/app/Controllers/Http/GuestBookController.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {HttpContextContract} from "@ioc:Adonis/Core/HttpContext"; -import GuestBookMessage from "../../Models/GuestBookMessage"; -import StoreValidator from "../../Validators/guestbook/StoreValidator"; - -export default class GuestBookController { - - public async index () { - return GuestBookMessage.query().orderBy('created_at', 'desc') - } - - public async store ({request}: HttpContextContract) { - const data = await request.validate(StoreValidator) - return await GuestBookMessage.create(data) - } - -} diff --git a/app/Controllers/Http/InformationsController.ts b/app/Controllers/Http/InformationsController.ts new file mode 100644 index 0000000..bad1f21 --- /dev/null +++ b/app/Controllers/Http/InformationsController.ts @@ -0,0 +1,31 @@ +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import Information from 'App/Models/Information' +import InformationUpdateValidator from 'App/Validators/information/InformationUpdateValidator' +import { getTranslation } from 'App/Utils/TranslationsUtils' + +export default class InformationsController { + public async index({ response }: HttpContextContract) { + return response.status(200).send({ + informations: await Information + .query() + .preload('translation') + .first(), + }) + } + + public async update({ response, request }: HttpContextContract) { + const information = await Information.firstOrFail() + const data = await request.validate(InformationUpdateValidator) + + if (data.code) { + const translation = await getTranslation(data.code) + await information.related('translation').associate(translation) + } + + await information.merge(data).save() + + return response.status(200).send({ + information, + }) + } +} diff --git a/app/Controllers/Http/LocationsController.ts b/app/Controllers/Http/LocationsController.ts old mode 100644 new mode 100755 index 29ffb5c..2bece3a --- a/app/Controllers/Http/LocationsController.ts +++ b/app/Controllers/Http/LocationsController.ts @@ -1,24 +1,31 @@ import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' -import Location from "App/Models/Location"; -import LocationValidator from "App/Validators/location/LocationValidator"; +import Location from 'App/Models/Location' +import LocationValidator from 'App/Validators/location/LocationValidator' export default class LocationsController { - - public async get ({ response }: HttpContextContract) { - const location = await Location.query().orderBy('since', 'desc').firstOrFail() - return response.status(200).send({ - place: location.place, - left: location.left, - since: location.since - }) + public async index({ response }: HttpContextContract) { + const location = await Location.query().orderBy('since', 'desc').first() + if (location) { + return response.status(200).send({ + location: { + place: location.place, + left: location.left, + since: location.since, + }, + }) + } + else { + return response.status(200).send({ + location: 'Location is unknown...', + }) + } } - public async add ({ request, response }: HttpContextContract) { + public async store({ request, response }: HttpContextContract) { const data = await request.validate(LocationValidator) - await Location.create(data) + const location = await Location.create(data) return response.status(200).send({ - message: 'Location successfully added !' + location, }) } - } diff --git a/app/Controllers/Http/MaintenancesController.ts b/app/Controllers/Http/MaintenancesController.ts new file mode 100644 index 0000000..751d6c3 --- /dev/null +++ b/app/Controllers/Http/MaintenancesController.ts @@ -0,0 +1,31 @@ +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import Maintenance from 'App/Models/Maintenance' +import MaintenanceUpdateValidator from 'App/Validators/maintenance/MaintenanceUpdateValidator' +import { getTranslation } from 'App/Utils/TranslationsUtils' + +export default class MaintenancesController { + public async index({ response }: HttpContextContract) { + const maintenance = await Maintenance + .query() + .orderBy('created_at', 'desc') + .preload('reason') + .first() + return response.status(200).send({ + maintenance, + }) + } + + public async update({ request, params, response }: HttpContextContract) { + const data = await request.validate(MaintenanceUpdateValidator) + const maintenance = await Maintenance.findOrFail(params.id) + + if (data.reason) + await maintenance.related('reason').associate(await getTranslation(data.reason)) + + await maintenance.merge(data).save() + + return response.status(200).send({ + maintenance, + }) + } +} diff --git a/app/Controllers/Http/PostColorsController.ts b/app/Controllers/Http/PostColorsController.ts new file mode 100644 index 0000000..2566f4f --- /dev/null +++ b/app/Controllers/Http/PostColorsController.ts @@ -0,0 +1,44 @@ +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import PostColor from 'App/Models/PostColor' +import PostColorStoreValidator from 'App/Validators/postColor/PostColorStoreValidator' +import PostColorUpdateValidator from 'App/Validators/postColor/PostColorUpdateValidator' + +export default class PostColorsController { + public async index({ response }: HttpContextContract) { + return response.status(200).send({ + post_colors: await PostColor.all(), + }) + } + + public async store({ request, response }: HttpContextContract) { + const data = await request.validate(PostColorStoreValidator) + const postColor = await PostColor.create(data) + return response.status(200).send({ + post_color: postColor, + }) + } + + public async show({ params, response }: HttpContextContract) { + const postColor = await PostColor.findOrFail(params.id) + return response.status(200).send({ + post_color: postColor, + }) + } + + public async update({ request, params, response }: HttpContextContract) { + const data = await request.validate(PostColorUpdateValidator) + const postColor = await PostColor.findOrFail(params.id) + await postColor.merge(data).save() + return response.status(200).send({ + post_color: postColor, + }) + } + + public async destroy({ response, params }: HttpContextContract) { + const postColor = await PostColor.findOrFail(params.id) + await postColor.delete() + return response.status(200).send({ + message: 'PostColor successfully deleted!', + }) + } +} diff --git a/app/Controllers/Http/PostsController.ts b/app/Controllers/Http/PostsController.ts old mode 100644 new mode 100755 index 66ce3fa..f19c8d3 --- a/app/Controllers/Http/PostsController.ts +++ b/app/Controllers/Http/PostsController.ts @@ -1,54 +1,137 @@ -import Post from "App/Models/Post"; -import {HttpContextContract} from "@ioc:Adonis/Core/HttpContext"; +import Post from 'App/Models/Post' +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import PostUpdateValidator from 'App/Validators/post/PostUpdateValidator' +import File from 'App/Models/File' +import PostStoreValidator from 'App/Validators/post/PostStoreValidator' +import PostColor from 'App/Models/PostColor' +import { getTranslation } from 'App/Utils/TranslationsUtils' export default class PostsController { - - public async getLikes ({params}: HttpContextContract) { - let post = await Post.findBy('slug', params.slug) - - if (!post) { - post = await Post.create({ - slug: params.slug, - likes: 0 - }) - } - - return post.likes - } - - public async like ({params, response}: HttpContextContract) { - let post = await Post.findBy('slug', params.slug) - - if (!post) { - post = await Post.create({ - slug: params.slug, - likes: 0 - }) - } - - const getLikes = post.likes + 1 - - await post.merge({ - likes: getLikes - }).save() + public async index({ response }: HttpContextContract) { return response.status(200).send({ - status: 200, - post + posts: await Post.query() + .orderBy('id', 'desc') + .preload('tags', (tags) => { + tags.preload('label') + }) + .preload('cover') + .preload('color') + .preload('content') + .preload('title') + .preload('description'), }) } - public async unlike ({params, response}: HttpContextContract) { - let post = await Post.findByOrFail('slug', params.slug) + public async store({ request, response }: HttpContextContract) { + const data = await request.validate(PostStoreValidator) + const post = await Post.create(data) - const getLikes = post.likes - 1 + const cover = await File.findByOrFail('label', data.cover) + const color = await PostColor.findByOrFail('name', data.color) + + await post.related('cover').associate(cover) + await post.related('color').associate(color) + + await post.related('description').associate(await getTranslation(data.description)) + await post.related('title').associate(await getTranslation(data.title)) + await post.related('content').associate(await getTranslation(data.content)) + + await post.related('tags').sync(data.tags!) - await post.merge({ - likes: getLikes - }).save() return response.status(200).send({ - status: 200, - post + post, }) } + public async show({ params, response }: HttpContextContract) { + const post = await Post.findOrFail(params.id) + await post.load('cover') + await post.load('title') + await post.load('description') + await post.load('content') + await post.load('color') + await post.load('tags', (tags) => { + tags.preload('label') + }) + return response.status(200).send({ + post, + }) + } + + public async get({ params, response }: HttpContextContract) { + const post = await Post.firstOrCreate({ + slug: params.slug, + }, { + slug: params.slug, + likes: 0, + }) + await post.load('tags', (tags) => { + tags.preload('label') + }) + await post.load('cover') + await post.load('description') + await post.load('title') + await post.load('content') + await post.load('color') + return response.status(200).send({ + post, + }) + } + + public async update({ request, params, response }: HttpContextContract) { + const post = await Post.findOrFail(params.id) + const data = await request.validate(PostUpdateValidator) + + await post.merge(data).save() + + await post.related('tags').sync(data.tags!) + await post.related('description').associate(await getTranslation(data.description!)) + await post.related('title').associate(await getTranslation(data.title!)) + await post.related('content').associate(await getTranslation(data.content!)) + + const cover = await File.findBy('label', data.cover) + if (cover) await post.related('cover').associate(cover) + + const color = await PostColor.findBy('name', data.color) + if (color) await post.related('color').associate(color) + + return response.status(200).send({ + post, + }) + } + + public async destroy({ response, params }: HttpContextContract) { + const post = await Post.findOrFail(params.id) + await post.delete() + return response.status(200).send({ + message: 'Post successfully deleted!', + }) + } + + public async like({ params, response }: HttpContextContract) { + const post = await Post.firstOrCreate({ + slug: params.slug, + }, { + slug: params.slug, + likes: 0, + }) + const getLikes = post.likes + await post.merge({ + likes: getLikes + 1, + }).save() + return response.status(200).send({ + post, + }) + } + + public async unlike({ params, response }: HttpContextContract) { + const post = await Post.findByOrFail('slug', params.slug) + const getLikes = post.likes + await post.merge({ + likes: getLikes - 1, + }).save() + return response.status(200).send({ + post, + }) + } } diff --git a/app/Controllers/Http/ProfileController.ts b/app/Controllers/Http/ProfileController.ts old mode 100644 new mode 100755 index 78eedaf..db85d7f --- a/app/Controllers/Http/ProfileController.ts +++ b/app/Controllers/Http/ProfileController.ts @@ -1,46 +1,46 @@ -import {HttpContextContract} from "@ioc:Adonis/Core/HttpContext"; +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' export default class ProfileController { - - public me ({ response }: HttpContextContract) { + public me({ response }: HttpContextContract) { return response.status(200).send({ - pronouns: "Arthur", - home: ["Paris", "France"], + pronouns: 'Arthur', + home: ['Paris', 'France'], passions: [ - "Dev", - "DevOps", - "New technologies", - "Gaming" + 'Dev', + 'DevOps', + 'New technologies', + 'Gaming', + 'Cloud', ], code: [ - "Javascript", - "Typescript", - "HTML", - "CSS", - "GoLang", - "Java" + 'Javascript', + 'Typescript', + 'HTML', + 'CSS', + 'GoLang', + 'Java', ], ask_me_about: [ - "Web dev", - "Tech", - "Consulting", - "Cloud computing", - "DevOps", - "Software dev" + 'Web dev', + 'Tech', + 'Consulting', + 'Cloud computing', + 'DevOps', + 'Software dev', ], technologies: { - web_app: ["VueJs", "NuxtJs", "Sass", "Tailwind"], - desktop_app: ["ElectronJs"], - mobile_app: ["React Native"], + web_app: ['VueJs', 'NuxtJs', 'Sass', 'TailwindCss', 'WindiCss'], + desktop_app: ['ElectronJs'], + mobile_app: ['React Native', 'Vue Native'], back_end: { - typescript: ["AdonisJs"], - java: ["Spring"] + typescript: ['AdonisJs'], + java: ['Spring'], }, - databases: ["MongoDB", "MariaDB", "Redis"], - messaging: ["RabbitMq"], - other: ["Docker", "Git"], - architecture: ["microservices", "event-driven", "design system pattern"], - operating_systems: ['Windows', 'Linux'] + databases: ['MongoDB', 'MariaDB', 'Redis'], + messaging: ['RabbitMQ'], + other: ['Docker', 'Git'], + architecture: ['microservices', 'event-driven', 'design system pattern'], + operating_systems: ['MacOS', 'Linux'], }, }) } diff --git a/app/Controllers/Http/ProjectsController.ts b/app/Controllers/Http/ProjectsController.ts old mode 100644 new mode 100755 index 7b031e0..9fc7745 --- a/app/Controllers/Http/ProjectsController.ts +++ b/app/Controllers/Http/ProjectsController.ts @@ -1,22 +1,69 @@ import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' -import Project from "App/Models/Project"; -import ProjectValidator from "App/Validators/project/ProjectValidator"; +import Project from 'App/Models/Project' +import ProjectStoreValidator from 'App/Validators/project/ProjectStoreValidator' +import ProjectUpdateValidator from 'App/Validators/project/ProjectUpdateValidator' +import File from 'App/Models/File' +import { getTranslation } from 'App/Utils/TranslationsUtils' export default class ProjectsController { - - public async get ({ response }: HttpContextContract) { - const projects = await Project.query().orderBy('id', 'asc') + public async index({ response }: HttpContextContract) { return response.status(200).send({ - projects + projects: await Project.query() + .orderBy('id', 'asc') + .preload('cover') + .preload('description') + .preload('tags', (tags) => { + tags.preload('label') + }), }) } - public async add ({ request, response}: HttpContextContract) { - const data = await request.validate(ProjectValidator) - await Project.create(data) + public async store({ request, response }: HttpContextContract) { + const data = await request.validate(ProjectStoreValidator) + const project = await Project.create(data) + const cover = await File.findByOrFail('label', data.cover) + + await project.related('cover').associate(cover) + await project.related('description').associate(await getTranslation(data.description)) + await project.related('tags').sync(data.tags!) return response.status(200).send({ - message: 'Project successfully created' + project, }) } + public async show({ params, response }: HttpContextContract) { + const project = await Project.findOrFail(params.id) + await project.load('cover') + await project.load('description') + await project.load('tags', (tags) => { + tags.preload('label') + }) + return response.status(200).send({ + project, + }) + } + + public async update({ request, params, response }: HttpContextContract) { + const project = await Project.findOrFail(params.id) + const data = await request.validate(ProjectUpdateValidator) + const cover = await File.findBy('label', data.cover) + + await project.merge(data).save() + if (cover) await project.related('cover').associate(cover) + + if (data.description) await project.related('description').associate(await getTranslation(data.description)) + + await project.related('tags').sync(data.tags!) + return response.status(200).send({ + project, + }) + } + + public async destroy({ response, params }: HttpContextContract) { + const project = await Project.findOrFail(params.id) + await project.delete() + return response.status(200).send({ + message: 'Project successfully deleted!', + }) + } } diff --git a/app/Controllers/Http/SkillsController.ts b/app/Controllers/Http/SkillsController.ts new file mode 100644 index 0000000..59eff78 --- /dev/null +++ b/app/Controllers/Http/SkillsController.ts @@ -0,0 +1,57 @@ +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import File from 'App/Models/File' +import Skill from 'App/Models/Skill' +import SkillStoreValidator from 'App/Validators/skill/SkillStoreValidator' +import SkillUpdateValidator from 'App/Validators/skill/SkillUpdateValidator' + +export default class SkillsController { + public async index({ response }: HttpContextContract) { + const skills = await Skill + .query() + .preload('file') + return response.status(200).send({ + skills, + }) + } + + public async store({ request, response }: HttpContextContract) { + const data = await request.validate(SkillStoreValidator) + const skill = await Skill.create(data) + + const cover = await File.findBy('label', data.cover) + if (cover) await skill.related('file').associate(cover) + + return response.status(200).send({ + skill, + }) + } + + public async show({ params, response }: HttpContextContract) { + const skill = await Skill.findOrFail(params.id) + skill.load('file') + return response.status(200).send({ + skill, + }) + } + + public async update({ request, params, response }: HttpContextContract) { + const data = await request.validate(SkillUpdateValidator) + const skill = await Skill.findOrFail(params.id) + + const cover = await File.findBy('label', data.cover) + if (cover) await skill.related('file').associate(cover) + await skill.merge(data).save() + + return response.status(200).send({ + skill, + }) + } + + public async destroy({ response, params }: HttpContextContract) { + const skill = await Skill.findOrFail(params.id) + await skill.delete() + return response.status(200).send({ + message: 'Skill successfully deleted!', + }) + } +} diff --git a/app/Controllers/Http/StatesController.ts b/app/Controllers/Http/StatesController.ts index b6b2d63..ae60764 100644 --- a/app/Controllers/Http/StatesController.ts +++ b/app/Controllers/Http/StatesController.ts @@ -1,63 +1,31 @@ -import {HttpContextContract} from "@ioc:Adonis/Core/HttpContext"; -import Redis from "@ioc:Adonis/Addons/Redis"; -import {UpdateGitHubReadme} from "App/tasks/UpdateGithubReadme"; -import Logger from "@ioc:Adonis/Core/Logger"; +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import Redis from '@ioc:Adonis/Addons/Redis' +import StateSleepingValidator from 'App/Validators/states/StateSleepingValidator' export default class StatesController { + // Listening Music - public async get ({response}: HttpContextContract) { - const is_sleeping = await Redis.get('states:sleeping') - const is_listening_music = await Redis.get('states:listening') - const is_developing = await Redis.get('states:developing') - const is_learning = await Redis.get('states:learning') - + public async index({ response }: HttpContextContract) { + const sleeping = this.formatValue(await Redis.get('states:sleeping')) + const developing = this.formatValue(await Redis.get('states:developing')) return response.status(200).send({ - is_learning: this.getStatus(is_learning), - is_sleeping: this.getStatus(is_sleeping), - is_developing: this.getStatus(is_developing), - is_listening_music: this.getStatus(is_listening_music) + sleeping, + developing, + listening_music: 'Soon', }) } - public async set ({request, response, params}: HttpContextContract) { - const state = params.state - const value = await request.input('value') - - if (state && value) { - await Redis.set(`states:${state}`, value) - - if (value === 'true') { - switch (state) { - case 'learning': - await Redis.set(`states:developing`, 'false') - await Redis.set(`states:sleeping`, 'false') - break - case 'developing': - await Redis.set(`states:learning`, 'false') - await Redis.set(`states:sleeping`, 'false') - break - case 'listening': - await Redis.set(`states:sleeping`, 'false') - break - case 'sleeping': - await Redis.set(`states:developing`, 'false') - await Redis.set(`states:listening`, 'false') - await Redis.set(`states:learning`, 'false') - break - } - } - - await UpdateGitHubReadme() - return response.status(200).send({ - message: 'State successfully updated !' - }) - } - Logger.info("Finish") + public async setSleeping({ request, response }: HttpContextContract) { + const { value } = await request.validate(StateSleepingValidator) + await Redis.set('states:sleeping', String(value)) + await Redis.set('states:developing', String(!value)) + return response.status(200).send({ + message: 'State was successfully set!', + value: this.formatValue(String(value)), + }) } - public getStatus(state: string | null): string { - if (state === null) return "No" - return state === 'true' ? "Yes" : "No" + public formatValue(value: string | null): string { + return value === 'true' ? 'Yes' : 'No' } - } diff --git a/app/Controllers/Http/StatsController.ts b/app/Controllers/Http/StatsController.ts new file mode 100644 index 0000000..0582442 --- /dev/null +++ b/app/Controllers/Http/StatsController.ts @@ -0,0 +1,68 @@ +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import CommandsRun from 'App/Models/CommandsRun' +import BuildsRun from 'App/Models/BuildsRun' +import { + fetchDailyStatistics, + fetchMonthlyStatistics, + fetchStatistics, + fetchWeeklyStatistics, + NOW, +} from 'App/Utils/StatsUtils' + +export default class StatsController { + public async index({ response }: HttpContextContract) { + const daily = await fetchDailyStatistics() + const weekly = await fetchWeeklyStatistics() + const monthly = await fetchMonthlyStatistics() + const total = await fetchStatistics() + + return response.status(200).send({ + daily, + weekly, + monthly, + total: { + development_time: total.development_time, + commands_run: total.commands_ran, + builds_run: total.builds_ran, + }, + }) + } + + public async incrementCommandCount({ response }: HttpContextContract) { + const current_commands = await CommandsRun.firstOrCreate( + { + date: NOW, + }, + { + date: NOW, + commands: 0, + }, + ) + + current_commands.commands++ + await current_commands.save() + + return response.status(200).send({ + message: 'Commands Count successfully incremented!', + }) + } + + public async incrementBuildCount({ response }: HttpContextContract) { + const current_builds = await BuildsRun.firstOrCreate( + { + date: NOW, + }, + { + date: NOW, + builds: 0, + }, + ) + + current_builds.builds++ + await current_builds.save() + + return response.status(200).send({ + message: 'Builds Count successfully incremented!', + }) + } +} diff --git a/app/Controllers/Http/SubscribersController.ts b/app/Controllers/Http/SubscribersController.ts new file mode 100755 index 0000000..59cf81f --- /dev/null +++ b/app/Controllers/Http/SubscribersController.ts @@ -0,0 +1,28 @@ +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import Subscriber from 'App/Models/Subscriber' +import SubscriberStoreValidator from 'App/Validators/subscriber/SubscriberStoreValidator' + +export default class SubscribersController { + public async index({ response }: HttpContextContract) { + const subscribers = await Subscriber.query() + return response.status(200).send({ + count: subscribers.length, + subscribers, + }) + } + + public async store({ request, response }: HttpContextContract) { + const data = await request.validate(SubscriberStoreValidator) + return response.status(200).send({ + subscriber: await Subscriber.create(data), + }) + } + + public async destroy({ params, response }: HttpContextContract) { + const subscriber = await Subscriber.findOrFail(params.id) + await subscriber.delete() + return response.status(200).send({ + message: 'Subscriber successfully deleted!', + }) + } +} diff --git a/app/Controllers/Http/TagsController.ts b/app/Controllers/Http/TagsController.ts new file mode 100644 index 0000000..cc21ae5 --- /dev/null +++ b/app/Controllers/Http/TagsController.ts @@ -0,0 +1,55 @@ +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import TagStoreValidator from 'App/Validators/tag/TagStoreValidator' +import TagUpdateValidator from 'App/Validators/tag/TagUpdateValidator' +import Tag from 'App/Models/Tag' +import { getTranslation } from 'App/Utils/TranslationsUtils' + +export default class TagsController { + public async index({ response }: HttpContextContract) { + const tags = await Tag + .query() + .preload('label') + return response.status(200).send({ + tags, + }) + } + + public async store({ request, response }: HttpContextContract) { + const data = await request.validate(TagStoreValidator) + const tag = await Tag.create({}) + + await tag.related('label').associate(await getTranslation(data.label)) + + return response.status(200).send({ + tag, + }) + } + + public async show({ params, response }: HttpContextContract) { + const tag = await Tag.findOrFail(params.id) + tag.load('label') + return response.status(200).send({ + tag, + }) + } + + public async update({ request, params, response }: HttpContextContract) { + const data = await request.validate(TagUpdateValidator) + const tag = await Tag.findOrFail(params.id) + + if (data.label) + await tag.related('label').associate(await getTranslation(data.label)) + + return response.status(200).send({ + tag, + }) + } + + public async destroy({ response, params }: HttpContextContract) { + const tag = await Tag.findOrFail(params.id) + await tag.delete() + return response.status(200).send({ + message: 'Tag successfully deleted!', + }) + } +} diff --git a/app/Controllers/Http/TranslationsController.ts b/app/Controllers/Http/TranslationsController.ts new file mode 100644 index 0000000..ca59522 --- /dev/null +++ b/app/Controllers/Http/TranslationsController.ts @@ -0,0 +1,43 @@ +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import Translation from 'App/Models/Translation' +import TranslationStoreValidator from 'App/Validators/translation/TranslationStoreValidator' +import TranslationUpdateValidator from 'App/Validators/translation/TranslationUpdateValidator' + +export default class TranslationsController { + public async index({ response }: HttpContextContract) { + return response.status(200).send({ + translations: await Translation.query().orderBy('id', 'asc'), + }) + } + + public async store({ request, response }: HttpContextContract) { + const data = await request.validate(TranslationStoreValidator) + return response.status(200).send({ + translation: await Translation.create(data), + }) + } + + public async show({ params, response }: HttpContextContract) { + return response.status(200).send({ + translation: await Translation.findOrFail(params.id), + }) + } + + public async update({ request, params, response }: HttpContextContract) { + const translation = await Translation.findOrFail(params.id) + const data = await request.validate(TranslationUpdateValidator) + await translation.merge(data).save() + + return response.status(200).send({ + translation, + }) + } + + public async destroy({ response, params }: HttpContextContract) { + const translation = await Translation.findOrFail(params.id) + await translation.delete() + return response.status(200).send({ + message: 'Translation successfully deleted!', + }) + } +} diff --git a/app/Controllers/Http/UsersController.ts b/app/Controllers/Http/UsersController.ts old mode 100644 new mode 100755 index 166182f..3f77d54 --- a/app/Controllers/Http/UsersController.ts +++ b/app/Controllers/Http/UsersController.ts @@ -1,50 +1,48 @@ -import {HttpContextContract} from '@ioc:Adonis/Core/HttpContext' -import User from "App/Models/User"; -import StoreValidator from "App/Validators/users/StoreValidator"; -import UpdateValidator from "App/Validators/users/UpdateValidator"; +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import User from 'App/Models/User' +import UserStoreValidator from 'App/Validators/user/UserStoreValidator' +import UserUpdateValidator from 'App/Validators/user/UserUpdateValidator' export default class UsersController { - - public async index () { - return User.query() + public async index({ response }: HttpContextContract) { + return response.status(200).send({ + users: await User.all(), + }) } - public async store ({request}: HttpContextContract) { - const data = await request.validate(StoreValidator) - return await User.create(data) + public async store({ request, response }: HttpContextContract) { + const data = await request.validate(UserStoreValidator) + return response.status(200).send({ + user: await User.create(data), + }) } - public async show ({params}: HttpContextContract) { - return await User.findOrFail(params.id) + public async show({ params, response }: HttpContextContract) { + return response.status(200).send({ + user: await User.findOrFail(params.id), + }) } public async update({ request, params, response }: HttpContextContract) { const user = await User.findOrFail(params.id) - const data = await request.validate(UpdateValidator) - const { email } = data - const user2 = await User.findBy('email', email) - - if (user2 !== null && user.email !== email) { - return response.abort({ - message: 'L\' adresse mail n\'est pas unique !' - }) - } - + const data = await request.validate(UserUpdateValidator) await user.merge(data).save() - return { message: 'Le compte a été mis à jour' } + return response.status(200).send({ + user, + }) } public async destroy({ response, params, auth }: HttpContextContract) { const user = await User.findOrFail(params.id) const admin = await User.findBy('email', 'arthurdanjou@outlook.fr') - if (auth.user?.id != admin?.id) { + if (auth.user?.id !== admin?.id) return response.unauthorized() - } await user.delete() - return { message: "L'utilisateur a bien été supprimé" } + return response.status(200).send({ + message: 'User successfully deleted!', + }) } - } diff --git a/app/Exceptions/Handler.ts b/app/Exceptions/Handler.ts old mode 100644 new mode 100755 index 79593c6..ac6541c --- a/app/Exceptions/Handler.ts +++ b/app/Exceptions/Handler.ts @@ -17,7 +17,7 @@ import Logger from '@ioc:Adonis/Core/Logger' import HttpExceptionHandler from '@ioc:Adonis/Core/HttpExceptionHandler' export default class ExceptionHandler extends HttpExceptionHandler { - constructor () { + constructor() { super(Logger) } } diff --git a/app/Mailers/FormConfirmation.ts b/app/Mailers/FormConfirmation.ts old mode 100644 new mode 100755 index c3c3c77..14cb843 --- a/app/Mailers/FormConfirmation.ts +++ b/app/Mailers/FormConfirmation.ts @@ -1,22 +1,23 @@ -import mjml from 'mjml' import { BaseMailer, MessageContract } from '@ioc:Adonis/Addons/Mail' -import View from "@ioc:Adonis/Core/View"; export default class FormConfirmation extends BaseMailer { - - constructor (private name: string, private email: string) { + constructor(private name: string, private email: string) { super() } - public html = mjml(View.render('emails/confirmation_form', { + /* public html = mjml(View.render('emails/confirmation_form', { name: this.name - })).html + })).html */ public prepare(message: MessageContract) { message - .from('contact@arthurdanjou.fr') + .from('no-reply@arthurdanjou.fr') + .replyTo('contact@arthurdanjou.fr') .to(this.email) - .subject('Confirmation Form') - .html(this.html) + .subject('Thank you for contacting !') + .htmlView('emails/confirmation_form', { + name: this.name, + url: 'https://arthurdanjou.fr', + }) } } diff --git a/app/Middleware/Auth.ts b/app/Middleware/Auth.ts old mode 100644 new mode 100755 index 262c309..21205ea --- a/app/Middleware/Auth.ts +++ b/app/Middleware/Auth.ts @@ -22,24 +22,23 @@ export default class AuthMiddleware { * of the mentioned guards and that guard will be used by the rest of the code * during the current request. */ - protected async authenticate (auth: HttpContextContract['auth'], guards: any[]) { - /** - * Hold reference to the guard last attempted within the for loop. We pass - * the reference of the guard to the "AuthenticationException", so that - * it can decide the correct response behavior based upon the guard - * driver - */ - let guardLastAttempted: string | undefined - - for (let guard of guards) { - guardLastAttempted = guard + protected async authenticate(auth: HttpContextContract['auth'], guards: any[]) { + /** + * Hold reference to the guard last attempted within the for loop. We pass + * the reference of the guard to the "AuthenticationException", so that + * it can decide the correct response behavior based upon the guard + * driver + */ + let guardLastAttempted: string | undefined + for (const guard of guards) { + guardLastAttempted = guard if (await auth.use(guard).check()) { /** - * Instruct auth to use the given guard as the default guard for - * the rest of the request, since the user authenticated - * succeeded here - */ + * Instruct auth to use the given guard as the default guard for + * the rest of the request, since the user authenticated + * succeeded here + */ auth.defaultGuard = guard return true } @@ -59,7 +58,7 @@ export default class AuthMiddleware { /** * Handle request */ - public async handle ({ auth }: HttpContextContract, next: () => Promise, customGuards: string[]) { + public async handle({ auth }: HttpContextContract, next: () => Promise, customGuards: string[]) { /** * Uses the user defined guards or the default guard mentioned in * the config file diff --git a/app/Middleware/SilentAuth.ts b/app/Middleware/SilentAuth.ts old mode 100644 new mode 100755 index a30e106..e285e3a --- a/app/Middleware/SilentAuth.ts +++ b/app/Middleware/SilentAuth.ts @@ -10,12 +10,13 @@ export default class SilentAuthMiddleware { /** * Handle request */ - public async handle ({ auth }: HttpContextContract, next: () => Promise) { + public async handle({ auth }: HttpContextContract, next: () => Promise) { /** * Check if user is logged-in or not. If yes, then `ctx.auth.user` will be * set to the instance of the currently logged in user. */ - await auth.check() + await auth.use('api').check() + await auth.use('web').check() await next() } } diff --git a/app/Models/Announce.ts b/app/Models/Announce.ts new file mode 100644 index 0000000..911bf78 --- /dev/null +++ b/app/Models/Announce.ts @@ -0,0 +1,37 @@ +import { DateTime } from 'luxon' +import { BaseModel, BelongsTo, belongsTo, column } from '@ioc:Adonis/Lucid/Orm' +import File from 'App/Models/File' +import Translation from 'App/Models/Translation' + +export default class Announce extends BaseModel { + @column({ isPrimary: true }) + public id: number + + @column() + public color: string + + @column() + public hoverColor: string + + @belongsTo(() => Translation, { + foreignKey: 'messageId', + }) + public message: BelongsTo + + @column() + public messageId: number + + @belongsTo(() => File, { + foreignKey: 'coverId', + }) + public cover: BelongsTo + + @column() + public coverId: number + + @column.dateTime({ autoCreate: true }) + public createdAt: DateTime + + @column.dateTime({ autoCreate: true, autoUpdate: true }) + public updatedAt: DateTime +} diff --git a/app/Models/BuildsRun.ts b/app/Models/BuildsRun.ts new file mode 100644 index 0000000..bec157c --- /dev/null +++ b/app/Models/BuildsRun.ts @@ -0,0 +1,12 @@ +import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm' + +export default class BuildsRun extends BaseModel { + @column({ isPrimary: true }) + public id: number + + @column() + public builds: number + + @column() + public date: string +} diff --git a/app/Models/CommandsRun.ts b/app/Models/CommandsRun.ts new file mode 100644 index 0000000..c4137b8 --- /dev/null +++ b/app/Models/CommandsRun.ts @@ -0,0 +1,12 @@ +import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm' + +export default class CommandsRun extends BaseModel { + @column({ isPrimary: true }) + public id: number + + @column() + public commands: number + + @column() + public date: string +} diff --git a/app/Models/DevelopmentHour.ts b/app/Models/DevelopmentHour.ts new file mode 100644 index 0000000..0415d32 --- /dev/null +++ b/app/Models/DevelopmentHour.ts @@ -0,0 +1,12 @@ +import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm' + +export default class DevelopmentHour extends BaseModel { + @column({ isPrimary: true }) + public id: number + + @column() + public seconds: number + + @column() + public date: string +} diff --git a/app/Models/Experience.ts b/app/Models/Experience.ts new file mode 100644 index 0000000..605c590 --- /dev/null +++ b/app/Models/Experience.ts @@ -0,0 +1,34 @@ +import { DateTime } from 'luxon' +import { BaseModel, BelongsTo, belongsTo, column } from '@ioc:Adonis/Lucid/Orm' +import Translation from 'App/Models/Translation' + +export default class Experience extends BaseModel { + @column({ isPrimary: true }) + public id: number + + @belongsTo(() => Translation, { + foreignKey: 'titleId', + }) + public title: BelongsTo + + @column() + public titleId: number + + @column() + public company: string + + @column() + public location: string + + @column() + public beginDate: string + + @column() + public endDate: string + + @column.dateTime({ autoCreate: true }) + public createdAt: DateTime + + @column.dateTime({ autoCreate: true, autoUpdate: true }) + public updatedAt: DateTime +} diff --git a/app/Models/File.ts b/app/Models/File.ts old mode 100644 new mode 100755 index 721e8bc..c8b25af --- a/app/Models/File.ts +++ b/app/Models/File.ts @@ -1,5 +1,5 @@ import { DateTime } from 'luxon' -import {BaseModel, column} from '@ioc:Adonis/Lucid/Orm' +import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm' export default class File extends BaseModel { @column({ isPrimary: true }) diff --git a/app/Models/Form.ts b/app/Models/Form.ts old mode 100644 new mode 100755 diff --git a/app/Models/Formation.ts b/app/Models/Formation.ts new file mode 100644 index 0000000..7d02e0c --- /dev/null +++ b/app/Models/Formation.ts @@ -0,0 +1,39 @@ +import { DateTime } from 'luxon' +import { BaseModel, BelongsTo, belongsTo, column } from '@ioc:Adonis/Lucid/Orm' +import Translation from 'App/Models/Translation' + +export default class Formation extends BaseModel { + @column({ isPrimary: true }) + public id: number + + @belongsTo(() => Translation, { + foreignKey: 'titleId', + }) + public title: BelongsTo + + @column() + public titleId: number + + @belongsTo(() => Translation, { + foreignKey: 'descriptionId', + }) + public description: BelongsTo + + @column() + public descriptionId: number + + @column() + public location: string + + @column() + public beginDate: string + + @column() + public endDate: string + + @column.dateTime({ autoCreate: true }) + public createdAt: DateTime + + @column.dateTime({ autoCreate: true, autoUpdate: true }) + public updatedAt: DateTime +} diff --git a/app/Models/GuestBookMessage.ts b/app/Models/GuestBookMessage.ts deleted file mode 100644 index 1a4e261..0000000 --- a/app/Models/GuestBookMessage.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { DateTime } from 'luxon' -import {BaseModel, BelongsTo, belongsTo, column} from '@ioc:Adonis/Lucid/Orm' -import User from "./User"; - -export default class GuestBookMessage extends BaseModel { - @column({ isPrimary: true }) - public id: number - - @belongsTo(() => User) - public user: BelongsTo - - @column() - public userId: number - - @column() - public message: string - - @column.dateTime({ autoCreate: true }) - public createdAt: DateTime - - @column.dateTime({ autoCreate: true, autoUpdate: true }) - public updatedAt: DateTime -} diff --git a/app/Models/Information.ts b/app/Models/Information.ts new file mode 100644 index 0000000..ff65000 --- /dev/null +++ b/app/Models/Information.ts @@ -0,0 +1,23 @@ +import { DateTime } from 'luxon' +import { BaseModel, BelongsTo, belongsTo, column } from '@ioc:Adonis/Lucid/Orm' +import Translation from 'App/Models/Translation' + +export default class Information extends BaseModel { + @column({ isPrimary: true }) + public id: number + + @column() + public age: number + + @belongsTo(() => Translation) + public translation: BelongsTo + + @column() + public translationId: number + + @column.dateTime({ autoCreate: true }) + public createdAt: DateTime + + @column.dateTime({ autoCreate: true, autoUpdate: true }) + public updatedAt: DateTime +} diff --git a/app/Models/Location.ts b/app/Models/Location.ts old mode 100644 new mode 100755 diff --git a/app/Models/Maintenance.ts b/app/Models/Maintenance.ts new file mode 100644 index 0000000..7cff52a --- /dev/null +++ b/app/Models/Maintenance.ts @@ -0,0 +1,25 @@ +import { DateTime } from 'luxon' +import { BaseModel, BelongsTo, belongsTo, column } from '@ioc:Adonis/Lucid/Orm' +import Translation from 'App/Models/Translation' + +export default class Maintenance extends BaseModel { + @column({ isPrimary: true }) + public id: number + + @column() + public active: boolean + + @belongsTo(() => Translation, { + foreignKey: 'reasonId', + }) + public reason: BelongsTo + + @column() + public reasonId: number + + @column.dateTime({ autoCreate: true }) + public createdAt: DateTime + + @column.dateTime({ autoCreate: true, autoUpdate: true }) + public updatedAt: DateTime +} diff --git a/app/Models/Post.ts b/app/Models/Post.ts old mode 100644 new mode 100755 index 465c912..ea02d33 --- a/app/Models/Post.ts +++ b/app/Models/Post.ts @@ -1,16 +1,69 @@ import { DateTime } from 'luxon' -import {BaseModel, column} from '@ioc:Adonis/Lucid/Orm' +import { BaseModel, BelongsTo, belongsTo, column, manyToMany, ManyToMany } from '@ioc:Adonis/Lucid/Orm' +import Tag from 'App/Models/Tag' +import Translation from 'App/Models/Translation' +import File from 'App/Models/File' +import PostColor from 'App/Models/PostColor' export default class Post extends BaseModel { @column({ isPrimary: true }) public id: number + @manyToMany(() => Tag) + public tags: ManyToMany + @column() public slug: string @column() public likes: number + @belongsTo(() => Translation, { + foreignKey: 'titleId', + }) + public title: BelongsTo + + @column() + public titleId: number + + @belongsTo(() => Translation, { + foreignKey: 'descriptionId', + }) + public description: BelongsTo + + @column() + public descriptionId: number + + @belongsTo(() => File, { + foreignKey: 'coverId', + }) + public cover: BelongsTo + + @column() + public coverId: number + + @belongsTo(() => Translation, { + foreignKey: 'contentId', + }) + public content: BelongsTo + + @column() + public contentId: number + + @belongsTo(() => PostColor, { + foreignKey: 'colorId', + }) + public color: BelongsTo + + @column() + public colorId: number + + @column() + public readingTime: number + + @column() + public date: string + @column.dateTime({ autoCreate: true }) public createdAt: DateTime diff --git a/app/Models/PostColor.ts b/app/Models/PostColor.ts new file mode 100644 index 0000000..3a2d32f --- /dev/null +++ b/app/Models/PostColor.ts @@ -0,0 +1,16 @@ +import { DateTime } from 'luxon' +import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm' + +export default class PostColor extends BaseModel { + @column({ isPrimary: true }) + public id: number + + @column() + public name: string + + @column.dateTime({ autoCreate: true }) + public createdAt: DateTime + + @column.dateTime({ autoCreate: true, autoUpdate: true }) + public updatedAt: DateTime +} diff --git a/app/Models/Project.ts b/app/Models/Project.ts old mode 100644 new mode 100755 index 041d0f6..815dffa --- a/app/Models/Project.ts +++ b/app/Models/Project.ts @@ -1,5 +1,8 @@ import { DateTime } from 'luxon' -import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm' +import { BaseModel, BelongsTo, belongsTo, column, ManyToMany, manyToMany } from '@ioc:Adonis/Lucid/Orm' +import File from 'App/Models/File' +import Tag from 'App/Models/Tag' +import Translation from 'App/Models/Translation' export default class Project extends BaseModel { @column({ isPrimary: true }) @@ -8,15 +11,28 @@ export default class Project extends BaseModel { @column() public name: string - @column() - public description: string + @belongsTo(() => Translation, { + foreignKey: 'descriptionId', + }) + public description: BelongsTo @column() - public progress: number + public descriptionId: number @column() public url: string + @belongsTo(() => File, { + foreignKey: 'coverId', + }) + public cover: BelongsTo + + @column() + public coverId: number + + @manyToMany(() => Tag) + public tags: ManyToMany + @column.dateTime({ autoCreate: true }) public createdAt: DateTime diff --git a/app/Models/Skill.ts b/app/Models/Skill.ts new file mode 100644 index 0000000..7ca9cf2 --- /dev/null +++ b/app/Models/Skill.ts @@ -0,0 +1,26 @@ +import { DateTime } from 'luxon' +import { BaseModel, BelongsTo, belongsTo, column } from '@ioc:Adonis/Lucid/Orm' +import File from 'App/Models/File' + +export default class Skill extends BaseModel { + @column({ isPrimary: true }) + public id: number + + @column() + public name: string + + @belongsTo(() => File) + public file: BelongsTo + + @column() + public fileId: number + + @column() + public color: string + + @column.dateTime({ autoCreate: true }) + public createdAt: DateTime + + @column.dateTime({ autoCreate: true, autoUpdate: true }) + public updatedAt: DateTime +} diff --git a/app/Models/Subscriber.ts b/app/Models/Subscriber.ts new file mode 100755 index 0000000..8464704 --- /dev/null +++ b/app/Models/Subscriber.ts @@ -0,0 +1,16 @@ +import { DateTime } from 'luxon' +import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm' + +export default class Subscriber extends BaseModel { + @column({ isPrimary: true }) + public id: number + + @column() + public email: string + + @column.dateTime({ autoCreate: true }) + public createdAt: DateTime + + @column.dateTime({ autoCreate: true, autoUpdate: true }) + public updatedAt: DateTime +} diff --git a/app/Models/Tag.ts b/app/Models/Tag.ts new file mode 100644 index 0000000..8919a1c --- /dev/null +++ b/app/Models/Tag.ts @@ -0,0 +1,22 @@ +import { DateTime } from 'luxon' +import { BaseModel, BelongsTo, belongsTo, column } from '@ioc:Adonis/Lucid/Orm' +import Translation from 'App/Models/Translation' + +export default class Tag extends BaseModel { + @column({ isPrimary: true }) + public id: number + + @belongsTo(() => Translation, { + foreignKey: 'labelId', + }) + public label: BelongsTo + + @column() + public labelId: number + + @column.dateTime({ autoCreate: true }) + public createdAt: DateTime + + @column.dateTime({ autoCreate: true, autoUpdate: true }) + public updatedAt: DateTime +} diff --git a/app/Models/Song.ts b/app/Models/Translation.ts similarity index 55% rename from app/Models/Song.ts rename to app/Models/Translation.ts index bb65730..d462ffa 100644 --- a/app/Models/Song.ts +++ b/app/Models/Translation.ts @@ -1,30 +1,18 @@ import { DateTime } from 'luxon' import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm' -export default class Song extends BaseModel { +export default class Translation extends BaseModel { @column({ isPrimary: true }) public id: number @column() - public author: string + public code: string @column() - public album: string + public french: string @column() - public title: string - - @column() - public type: string - - @column() - public device: string - - @column() - public duration: number - - @column.date() - public releaseDate: DateTime + public english: string @column.dateTime({ autoCreate: true }) public createdAt: DateTime diff --git a/app/Models/User.ts b/app/Models/User.ts old mode 100644 new mode 100755 index d92ef39..a60ba77 --- a/app/Models/User.ts +++ b/app/Models/User.ts @@ -1,10 +1,6 @@ import { DateTime } from 'luxon' import Hash from '@ioc:Adonis/Core/Hash' -import { - column, - beforeSave, - BaseModel, -} from '@ioc:Adonis/Lucid/Orm' +import { BaseModel, beforeSave, column } from '@ioc:Adonis/Lucid/Orm' export default class User extends BaseModel { @column({ isPrimary: true }) @@ -35,9 +31,8 @@ export default class User extends BaseModel { public updatedAt: DateTime @beforeSave() - public static async hashPassword (user: User) { - if (user.$dirty.password) { + public static async hashPassword(user: User) { + if (user.$dirty.password) user.password = await Hash.make(user.password) - } } } diff --git a/app/Tasks/SongsTask.ts b/app/Tasks/SongsTask.ts new file mode 100644 index 0000000..18698f2 --- /dev/null +++ b/app/Tasks/SongsTask.ts @@ -0,0 +1,13 @@ +import Logger from '@ioc:Adonis/Core/Logger' + +const MS = 1000 + +export async function getCurrentPlayingMusic(): Promise { + // Fetch from deezer +} + +export async function Activate(): Promise { + Logger.info(`Starting task runner for watching deezer current playing [${MS} ms]`) + await getCurrentPlayingMusic() + setInterval(getCurrentPlayingMusic, MS) +} diff --git a/app/Tasks/StatesTask.ts b/app/Tasks/StatesTask.ts new file mode 100644 index 0000000..49b8d53 --- /dev/null +++ b/app/Tasks/StatesTask.ts @@ -0,0 +1,49 @@ +import { btoa } from 'buffer' +import axios from 'axios' +import Env from '@ioc:Adonis/Core/Env' +import Logger from '@ioc:Adonis/Core/Logger' +import Redis from '@ioc:Adonis/Addons/Redis' + +const MS = 1000 * 2 * 60 // 2 min +let taskId + +interface StatesResponse { + time: number +} + +async function getCurrentTime(): Promise { + const response = await axios.get<{ data: StatesResponse[]}>(`https://wakatime.com/api/v1/users/${Env.get('WAKATIME_USER')}/heartbeats`, { + headers: { + Authorization: `Basic ${btoa(Env.get('WAKATIME_KEY'))}`, + }, + params: { + date: new Date(), + }, + }) + + if (response.status === 200) { + const heartbeat = response.data.data[response.data.data.length - 1] + const current_time = new Date(Date.now()).getTime() / 1000 + + if (heartbeat.time) { + const active = current_time - heartbeat.time <= 60 * 5 // Less than 5 min. + const redis_state = await Redis.get('states:developing') === 'true' + + if (redis_state !== active) { + await Redis.set('states:developing', String(active)) + if (redis_state) await Redis.set('states:sleeping', 'false') + } + } + } +} + +export async function Activate(): Promise { + Logger.info(`Starting task runner for getting current developing state [every ${MS} ms]`) + await getCurrentTime() + taskId = setInterval(getCurrentTime, MS) +} + +export function ShutDown(): void { + clearInterval(taskId) + Logger.info('Shutdown task runner for getting current developing state') +} diff --git a/app/Tasks/StatsTask.ts b/app/Tasks/StatsTask.ts new file mode 100644 index 0000000..ce9fb52 --- /dev/null +++ b/app/Tasks/StatsTask.ts @@ -0,0 +1,47 @@ +import Logger from '@ioc:Adonis/Core/Logger' +import Env from '@ioc:Adonis/Core/Env' +import axios from 'axios' +import DevelopmentHour from 'App/Models/DevelopmentHour' + +const MS = 1000 * 5 * 60 // 5 min +let taskId + +interface StatsResponse { + grand_total: { + total_seconds: number + } + range: { + date: string + } +} + +async function getDevelopmentHours(): Promise { + const response = await axios.get<{ data: StatsResponse[]}>(`https://wakatime.com/share/@${Env.get('WAKATIME_USER')}/${Env.get('WAKATIME_ID')}.json`) + if (response.status === 200) { + const mapped_stats = response.data.data.map((item: StatsResponse) => { + return { + seconds: item.grand_total.total_seconds, date: item.range.date, + } + }) + + for (const data of mapped_stats) { + await DevelopmentHour.updateOrCreate({ + date: data.date.split('T')[0], + }, { + date: data.date.split('T')[0], + seconds: data.seconds, + }) + } + } +} + +export async function Activate(): Promise { + Logger.info(`Starting task runner for getting development hours [every ${MS} ms]`) + await getDevelopmentHours() + taskId = setInterval(getDevelopmentHours, MS) +} + +export function ShutDown(): void { + clearInterval(taskId) + Logger.info('Shutdown task runner for getting development hours') +} diff --git a/app/Utils/SongUtils.ts b/app/Utils/SongUtils.ts new file mode 100644 index 0000000..1f3bfb9 --- /dev/null +++ b/app/Utils/SongUtils.ts @@ -0,0 +1,11 @@ +export async function getHistory(range: 'day' | 'week' | 'month') { + return range +} + +export async function getTopTrack() { + return 0 +} + +export async function GetCurrentPlaying() { + return null +} diff --git a/app/Utils/StatsUtils.ts b/app/Utils/StatsUtils.ts new file mode 100644 index 0000000..5eeea16 --- /dev/null +++ b/app/Utils/StatsUtils.ts @@ -0,0 +1,153 @@ +import DevelopmentHour from 'App/Models/DevelopmentHour' +import CommandsRun from 'App/Models/CommandsRun' +import BuildsRun from 'App/Models/BuildsRun' + +interface Time { + total_hours: number + total_minutes: number + total_seconds: number +} + +interface Stats { + range: { + start: string + end: string + } + + development_time: Time + commands_ran: number + builds_ran: number +} + +function formatDate(date: Date): string { + return date.toISOString().split('T')[0] +} + +export const NOW = formatDate(new Date()) + +export async function getDevelopmentHours(start: string, end: string): Promise