diff --git a/.adonisrc.json b/.adonisrc.json index 46f29c9..567be11 100755 --- 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,7 +17,8 @@ }, "preloads": [ "./start/routes", - "./start/kernel" + "./start/kernel", + "./start/bouncer" ], "providers": [ "./providers/AppProvider", @@ -27,7 +29,8 @@ "@adonisjs/lucid", "@adonisjs/mail", "@adonisjs/view", - "@adonisjs/ally" + "@adonisjs/ally", + "@adonisjs/bouncer" ], "aceProviders": [ "@adonisjs/repl" diff --git a/ace-manifest.json b/ace-manifest.json index b53470f..0b1c0f2 100755 --- a/ace-manifest.json +++ b/ace-manifest.json @@ -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/SubscribersController.ts b/app/Controllers/Http/SubscribersController.ts index e26c4b7..0d4c8d8 100755 --- a/app/Controllers/Http/SubscribersController.ts +++ b/app/Controllers/Http/SubscribersController.ts @@ -6,7 +6,7 @@ export default class SubscribersController { public async get ({ response }: HttpContextContract) { return response.status(200).send({ - count: Subscriber.query().count + count: Subscriber.query().count('* as total') }) } diff --git a/contracts/bouncer.ts b/contracts/bouncer.ts new file mode 100644 index 0000000..ae896e7 --- /dev/null +++ b/contracts/bouncer.ts @@ -0,0 +1,19 @@ +/** + * Contract source: https://git.io/Jte3v + * + * Feel free to let us know via PR, if you find something broken in this config + * file. + */ + +import {actions, policies} from '../start/bouncer' + +declare module '@ioc:Adonis/Addons/Bouncer' { + type ApplicationActions = ExtractActionsTypes + type ApplicationPolicies = ExtractPoliciesTypes + + interface ActionsList extends ApplicationActions { + } + + interface PoliciesList extends ApplicationPolicies { + } +} diff --git a/package.json b/package.json index b715ca9..75b811d 100755 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "dependencies": { "@adonisjs/ally": "^4.0.2", "@adonisjs/auth": "^8.0.6", + "@adonisjs/bouncer": "^2.2.4", "@adonisjs/core": "~5.1.8", "@adonisjs/lucid": "^15.0.1", "@adonisjs/mail": "^7.2.1", diff --git a/start/bouncer.ts b/start/bouncer.ts new file mode 100644 index 0000000..b5f9cbb --- /dev/null +++ b/start/bouncer.ts @@ -0,0 +1,57 @@ +/** + * Contract source: https://git.io/Jte3T + * + * Feel free to let us know via PR, if you find something broken in this config + * file. + */ + +import Bouncer from '@ioc:Adonis/Addons/Bouncer' + +/* +|-------------------------------------------------------------------------- +| Bouncer Actions +|-------------------------------------------------------------------------- +| +| Actions allows you to separate your application business logic from the +| authorization logic. Feel free to make use of policies when you find +| yourself creating too many actions +| +| You can define an action using the `.define` method on the Bouncer object +| as shown in the following example +| +| ``` +| Bouncer.define('deletePost', (user: User, post: Post) => { +| return post.user_id === user.id +| }) +| ``` +| +|**************************************************************** +| NOTE: Always export the "actions" const from this file +|**************************************************************** +*/ +export const {actions} = Bouncer + +/* +|-------------------------------------------------------------------------- +| Bouncer Policies +|-------------------------------------------------------------------------- +| +| Policies are self contained actions for a given resource. For example: You +| can create a policy for a "User" resource, one policy for a "Post" resource +| and so on. +| +| The "registerPolicies" accepts a unique policy name and a function to lazy +| import the policy +| +| ``` +| Bouncer.registerPolicies({ +| UserPolicy: () => import('App/Policies/User'), +| PostPolicy: () => import('App/Policies/Post') +| }) +| ``` +| +|**************************************************************** +| NOTE: Always export the "policies" const from this file +|**************************************************************** +*/ +export const {policies} = Bouncer.registerPolicies({}) diff --git a/start/routes.ts b/start/routes.ts deleted file mode 100755 index 8c6f083..0000000 --- a/start/routes.ts +++ /dev/null @@ -1,95 +0,0 @@ -import Application from "@ioc:Adonis/Core/Application"; -import Route from "@ioc:Adonis/Core/Route"; -import {HttpContextContract} from "@ioc:Adonis/Core/HttpContext"; -import HealthCheck from "@ioc:Adonis/Core/HealthCheck"; -import Env from '@ioc:Adonis/Core/Env' - -const BASE_URL = Env.get('BASE_URL') - -Route.get('/', async ({response}: HttpContextContract) => { - return response.status(200).send({ - domain: BASE_URL, - version: Env.get('API_VERSION'), - source: `${BASE_URL}/source`, - healthCheck: `${BASE_URL}/health`, - routes: { - profile: `${BASE_URL}/profile`, - //stats: `${BASE_URL}/stats`, - states: `${BASE_URL}/states`, - locations: `${BASE_URL}/locations`, - projects: `${BASE_URL}/projects` - } - }) -}) - -Route.get('/source', async ({response}: HttpContextContract) => { - return response.redirect(Env.get('GITHUB_SOURCE')) -}) - -Route.get('/health', async ({response}: HttpContextContract) => { - const report = await HealthCheck.getReport() - const isLive = await HealthCheck.isLive() - const isReady = await HealthCheck.isReady() - return report.healthy ? response.ok({ isLive, isReady, report: report.report }) : response.badRequest({ isLive, isReady, report: report.report }) -}) - -// ArtAPI -Route.get('/profile', 'ProfileController.me') -Route.get('/locations', 'LocationsController.get') -Route.get('/stats', 'StatsController.get') -Route.get('/states', 'StatesController.get') -Route.get('/projects', 'ProjectsController.get') - -Route.group(() => { - Route.get('/discord', 'ProfileController.discord') - Route.post('/form', 'FormsController.send') - Route.post('/states/:state', 'StatesController.set') - - Route.resource('/users', 'UsersController') - Route.resource('/files', 'FileController').only(['store', 'destroy']) - - Route.post('/locations', 'LocationsController.store') - Route.post('/projects', 'ProjectsController.store') - - Route.group(() => { - Route.get('/:slug', 'PostsController.getLikes') - Route.post('/:slug/like', 'PostsController.like') - Route.post('/:slug/unlike', 'PostsController.unlike') - }).prefix('/posts') - - Route.get('/subscribers', 'SubscribersController.get') - Route.post('/subscribers', 'SubscribersController.store') - - Route.get('/guestbook', 'GuestBookController.get') - Route.post('/guestbook', 'GuestBookController.store') - - Route.group(() => { - Route.get('/', 'FileController.index') - Route.get('/:filename', async ({response, params}) => { - response.download(Application.makePath('storage', params.filename)) - }) - }).prefix('/files') - -}).middleware('auth') - -Route.group(() => { - Route.get('/me', 'AuthController.user').middleware('auth') - Route.post('/token', 'AuthController.createInfiniteToken') - - Route.post('/login', 'AuthController.login') - Route.post('/logout', 'AuthController.logout') - - Route.get('/twitter/callback', 'AuthController.twitter') - Route.get('/github/callback', 'AuthController.github') - Route.get('/google/callback', 'AuthController.google') - - Route.get('/twitter', async ({ally}) => { - return ally.use('twitter').redirect() - }) - Route.get('/github', async ({ally}) => { - return ally.use('github').redirect() - }) - Route.get('/google', async ({ally}) => { - return ally.use('google').redirect() - }) -}).prefix('/auth') diff --git a/start/routes/artapi.ts b/start/routes/artapi.ts new file mode 100644 index 0000000..1ca8923 --- /dev/null +++ b/start/routes/artapi.ts @@ -0,0 +1,17 @@ +import Route from "@ioc:Adonis/Core/Route"; +import Application from "@ioc:Adonis/Core/Application"; + +Route.group(() => { + Route.get('/discord', 'ProfileController.discord') + Route.post('/states/:state', 'StatesController.set') + Route.resource('/users', 'UsersController') + Route.post('/locations', 'LocationsController.store') + Route.post('/projects', 'ProjectsController.store') + Route.resource('/files', 'FileController').only(['store', 'destroy']) + Route.group(() => { + Route.get('/', 'FileController.index') + Route.get('/:filename', async ({response, params}) => { + response.download(Application.makePath('storage', params.filename)) + }) + }).prefix('/files') +}).middleware('auth') diff --git a/start/routes/artsite.ts b/start/routes/artsite.ts new file mode 100644 index 0000000..620277f --- /dev/null +++ b/start/routes/artsite.ts @@ -0,0 +1,15 @@ +import Route from "@ioc:Adonis/Core/Route"; + +Route.group(() => { + Route.post('/form', 'FormsController.send') + Route.group(() => { + Route.get('/:slug', 'PostsController.getLikes') + Route.post('/:slug/like', 'PostsController.like') + Route.post('/:slug/unlike', 'PostsController.unlike') + }).prefix('/posts') + Route.get('/subscribers', 'SubscribersController.get') + Route.post('/subscribers', 'SubscribersController.store') + Route.delete('/subscribers', 'SubscribersController.delete') + Route.get('/guestbook', 'GuestBookController.get') + Route.post('/guestbook', 'GuestBookController.store') +}).middleware('auth') diff --git a/start/routes/auth.ts b/start/routes/auth.ts new file mode 100644 index 0000000..add1705 --- /dev/null +++ b/start/routes/auth.ts @@ -0,0 +1,23 @@ +import Route from "@ioc:Adonis/Core/Route"; + +Route.group(() => { + Route.get('/me', 'AuthController.user').middleware('auth') + Route.post('/token', 'AuthController.createInfiniteToken') + + Route.post('/login', 'AuthController.login') + Route.post('/logout', 'AuthController.logout') + + Route.get('/twitter/callback', 'AuthController.twitter') + Route.get('/github/callback', 'AuthController.github') + Route.get('/google/callback', 'AuthController.google') + + Route.get('/twitter', async ({ally}) => { + return ally.use('twitter').redirect() + }) + Route.get('/github', async ({ally}) => { + return ally.use('github').redirect() + }) + Route.get('/google', async ({ally}) => { + return ally.use('google').redirect() + }) +}).prefix('/auth') diff --git a/start/routes/home.ts b/start/routes/home.ts new file mode 100644 index 0000000..45fd8c1 --- /dev/null +++ b/start/routes/home.ts @@ -0,0 +1,43 @@ +import Env from "@ioc:Adonis/Core/Env"; +import Route from "@ioc:Adonis/Core/Route"; +import {HttpContextContract} from "@ioc:Adonis/Core/HttpContext"; +import HealthCheck from "@ioc:Adonis/Core/HealthCheck"; + +const BASE_URL = Env.get('BASE_URL') + +Route.get('/', async ({response}: HttpContextContract) => { + return response.status(200).send({ + domain: BASE_URL, + version: Env.get('API_VERSION'), + source: `${BASE_URL}/source`, + healthCheck: `${BASE_URL}/health`, + routes: { + profile: `${BASE_URL}/profile`, + //stats: `${BASE_URL}/stats`, + states: `${BASE_URL}/states`, + locations: `${BASE_URL}/locations`, + projects: `${BASE_URL}/projects` + } + }) +}) + +Route.get('/source', async ({response}: HttpContextContract) => { + return response.redirect(Env.get('GITHUB_SOURCE')) +}) + +Route.get('/health', async ({response}: HttpContextContract) => { + const report = await HealthCheck.getReport() + const isLive = await HealthCheck.isLive() + const isReady = await HealthCheck.isReady() + return report.healthy ? response.ok({isLive, isReady, report: report.report}) : response.badRequest({ + isLive, + isReady, + report: report.report + }) +}) + +Route.get('/profile', 'ProfileController.me') +Route.get('/locations', 'LocationsController.get') +Route.get('/stats', 'StatsController.get') +Route.get('/states', 'StatesController.get') +Route.get('/projects', 'ProjectsController.get') diff --git a/start/routes/index.ts b/start/routes/index.ts new file mode 100644 index 0000000..f1efa6f --- /dev/null +++ b/start/routes/index.ts @@ -0,0 +1,4 @@ +import './artsite' +import './artapi' +import './auth' +import './home' diff --git a/tsconfig.json b/tsconfig.json index c350d7c..1194e95 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,6 +35,7 @@ "@adonisjs/mail", "@adonisjs/view", "@adonisjs/ally", + "@adonisjs/bouncer" ] } } diff --git a/yarn.lock b/yarn.lock index fea65ad..ba4b044 100644 --- a/yarn.lock +++ b/yarn.lock @@ -83,6 +83,13 @@ fs-extra "^10.0.0" media-typer "^1.1.0" +"@adonisjs/bouncer@^2.2.4": + version "2.2.4" + resolved "https://registry.yarnpkg.com/@adonisjs/bouncer/-/bouncer-2.2.4.tgz#651428140f71b087ad71850d5f5d4bbc405e953d" + integrity sha512-b8DRojf12qeJ2cdY84fUC/Ab2nhMkL/5+V9JBWP8/pnkbhQA1YjkvjTKLFWeGKSb+o4mKeK6OBW0W5e8v/R1Zw== + dependencies: + "@poppinss/utils" "^3.1.3" + "@adonisjs/config@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@adonisjs/config/-/config-3.0.3.tgz#881ef1cd4d7e85050b474288a0eb1ebde9eb4625"