diff --git a/.env.example b/.env.example index 48210b0..0e5d8e7 100644 --- a/.env.example +++ b/.env.example @@ -12,3 +12,4 @@ DB_PASSWORD=my-links-pwd DB_DATABASE=my-links GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= +GOOGLE_CLIENT_CALLBACK_URL=http://localhost:3333/auth/callback diff --git a/adonisrc.ts b/adonisrc.ts index 31db80a..5bc6446 100644 --- a/adonisrc.ts +++ b/adonisrc.ts @@ -38,6 +38,7 @@ export default defineConfig({ () => import('@adonisjs/lucid/database_provider'), () => import('@adonisjs/auth/auth_provider'), () => import('@adonisjs/inertia/inertia_provider'), + () => import('@adonisjs/ally/ally_provider'), ], /* diff --git a/app/controllers/apps_controller.ts b/app/controllers/apps_controller.ts new file mode 100644 index 0000000..93ff812 --- /dev/null +++ b/app/controllers/apps_controller.ts @@ -0,0 +1,7 @@ +import type { HttpContext } from '@adonisjs/core/http'; + +export default class AppsController { + index({ inertia }: HttpContext) { + return inertia.render('home'); + } +} diff --git a/app/controllers/users_controller.ts b/app/controllers/users_controller.ts new file mode 100644 index 0000000..ee9c056 --- /dev/null +++ b/app/controllers/users_controller.ts @@ -0,0 +1,56 @@ +import User from '#models/user'; +import type { HttpContext } from '@adonisjs/core/http'; +import logger from '@adonisjs/core/services/logger'; + +export default class UsersController { + private redirectTo = '/'; + + google = ({ ally }: HttpContext) => ally.use('google').redirect(); + + async callbackAuth({ ally, auth, response, session }: HttpContext) { + const google = ally.use('google'); + if (google.accessDenied()) { + // TODO: translate error messages + show them in UI + session.flash('flash', 'Access was denied'); + return response.redirect(this.redirectTo); + } + + if (google.stateMisMatch()) { + session.flash('flash', 'Request expired. Retry again'); + return response.redirect(this.redirectTo); + } + + if (google.hasError()) { + session.flash('flash', google.getError() || 'Something went wrong'); + return response.redirect(this.redirectTo); + } + + const { email, id: providerId, name, nickName, avatarUrl, token } = await google.user(); + const user = await User.updateOrCreate( + { + email, + }, + { + email, + providerId, + name, + nickName, + avatarUrl, + token, + } + ); + + await auth.use('web').login(user); + session.flash('flash', 'Successfully authenticated'); + logger.info(`[${user.email}] auth success`); + + response.redirect('/'); + } + + async logout({ auth, response, session }: HttpContext) { + await auth.use('web').logout(); + session.flash('flash', 'Successfully disconnected'); + logger.info(`[${auth.user?.email}] disconnected successfully`); + response.redirect('/'); + } +} diff --git a/app/models/app_base_model.ts b/app/models/app_base_model.ts new file mode 100644 index 0000000..ae28a5b --- /dev/null +++ b/app/models/app_base_model.ts @@ -0,0 +1,5 @@ +import { BaseModel, CamelCaseNamingStrategy } from '@adonisjs/lucid/orm'; + +export default class AppBaseModel extends BaseModel { + static namingStrategy = new CamelCaseNamingStrategy(); +} diff --git a/app/models/user.ts b/app/models/user.ts index c2e5206..c72b832 100644 --- a/app/models/user.ts +++ b/app/models/user.ts @@ -1,30 +1,53 @@ +import type { GoogleToken } from '@adonisjs/ally/types'; +import { beforeCreate, column } from '@adonisjs/lucid/orm'; import { DateTime } from 'luxon'; -import hash from '@adonisjs/core/services/hash'; -import { compose } from '@adonisjs/core/helpers'; -import { BaseModel, column } from '@adonisjs/lucid/orm'; -import { withAuthFinder } from '@adonisjs/auth/mixins/lucid'; +import { v4 as uuidv4 } from 'uuid'; +import AppBaseModel from './app_base_model.js'; -const AuthFinder = withAuthFinder(() => hash.use('scrypt'), { - uids: ['email'], - passwordColumnName: 'password', -}); +export default class User extends AppBaseModel { + static selfAssignPrimaryKey = true; -export default class User extends compose(BaseModel, AuthFinder) { @column({ isPrimary: true }) - declare id: number; - - @column() - declare fullName: string | null; + declare id: string; // UUID @column() declare email: string; @column() - declare password: string; + declare name: string; - @column.dateTime({ autoCreate: true }) + @column({ serializeAs: 'nickName' }) + declare nickName: string; // public username + + @column({ serializeAs: 'avatarUrl' }) + declare avatarUrl: string; + + declare isAdmin: boolean; + + @column({ serializeAs: null }) + declare token: GoogleToken; + + @column({ serializeAs: null }) + declare providerId: string; + + @column.dateTime({ + autoCreate: true, + serialize: (value: DateTime) => value.toISODate(), + serializeAs: 'createdAt', + }) declare createdAt: DateTime; - @column.dateTime({ autoCreate: true, autoUpdate: true }) - declare updatedAt: DateTime | null; + @column.dateTime({ + autoCreate: true, + autoUpdate: true, + serialize: (value: DateTime) => value.toISODate(), + serializeAs: 'updatedAt', + }) + declare updatedAt: DateTime; + + @beforeCreate() + static assignUuid(user: User) { + user.id = uuidv4(); + user.isAdmin = false; + } } diff --git a/config/ally.ts b/config/ally.ts new file mode 100644 index 0000000..a14282d --- /dev/null +++ b/config/ally.ts @@ -0,0 +1,19 @@ +import env from '#start/env'; +import { defineConfig, services } from '@adonisjs/ally'; + +const allyConfig = defineConfig({ + google: services.google({ + clientId: env.get('GOOGLE_CLIENT_ID'), + clientSecret: env.get('GOOGLE_CLIENT_SECRET'), + callbackUrl: env.get('GOOGLE_CLIENT_CALLBACK_URL'), + prompt: 'select_account', + display: 'page', + scopes: ['userinfo.email', 'userinfo.profile'], + }), +}); + +export default allyConfig; + +declare module '@adonisjs/ally/types' { + interface SocialProviders extends InferSocialProviders {} +} diff --git a/config/inertia.ts b/config/inertia.ts index 8123631..3c44d89 100644 --- a/config/inertia.ts +++ b/config/inertia.ts @@ -11,6 +11,13 @@ export default defineConfig({ */ sharedData: { errors: (ctx) => ctx.session?.flashMessages.get('errors'), + auth: async (ctx) => { + await ctx.auth.check(); + return { + user: ctx.auth.user, + isAuthenticated: ctx.auth.isAuthenticated, + }; + }, }, /** diff --git a/database/migrations/1714218548323_create_users_table.ts b/database/migrations/1714218548323_create_users_table.ts index d89073f..a4f3f24 100644 --- a/database/migrations/1714218548323_create_users_table.ts +++ b/database/migrations/1714218548323_create_users_table.ts @@ -5,10 +5,15 @@ export default class extends BaseSchema { async up() { this.schema.createTable(this.tableName, (table) => { - table.increments('id').notNullable(); - table.string('full_name').nullable(); + table.string('id').notNullable(); table.string('email', 254).notNullable().unique(); - table.string('password').notNullable(); + table.string('name', 254).notNullable(); + table.string('nick_name', 254).notNullable(); + table.text('avatar_url').notNullable(); + table.boolean('is_admin').defaultTo(0).notNullable(); + + table.json('token').notNullable(); + table.string('provider_id').notNullable(); table.timestamp('created_at').notNullable(); table.timestamp('updated_at').nullable(); diff --git a/inertia/app/app.tsx b/inertia/app/app.tsx index f6f3fcc..6374b4b 100644 --- a/inertia/app/app.tsx +++ b/inertia/app/app.tsx @@ -1,7 +1,9 @@ -import '../css/app.css'; -import { hydrateRoot } from 'react-dom/client'; -import { createInertiaApp } from '@inertiajs/react'; +/// + import { resolvePageComponent } from '@adonisjs/inertia/helpers'; +import { createInertiaApp } from '@inertiajs/react'; +import { hydrateRoot } from 'react-dom/client'; +import '../css/app.css'; const appName = import.meta.env.VITE_APP_NAME || 'AdonisJS'; diff --git a/inertia/hooks/use_user.tsx b/inertia/hooks/use_user.tsx new file mode 100644 index 0000000..2d1a50e --- /dev/null +++ b/inertia/hooks/use_user.tsx @@ -0,0 +1,5 @@ +import { usePage } from '@inertiajs/react'; +import type { InertiaPage } from '~/types/inertia'; + +const useUser = () => usePage().props.auth; +export default useUser; diff --git a/inertia/pages/home.tsx b/inertia/pages/home.tsx index 90e9102..b7260c2 100644 --- a/inertia/pages/home.tsx +++ b/inertia/pages/home.tsx @@ -1,17 +1,28 @@ +import { InferPageProps } from '@adonisjs/inertia/types'; import { Head } from '@inertiajs/react'; +import useUser from '~/hooks/use_user'; +import type AppsController from '../../app/controllers/apps_controller'; + +export default function Home(_: InferPageProps) { + const { isAuthenticated, user } = useUser(); -export default function Home(props: { version: number }) { return ( <>
-
AdonisJS {props.version} x Inertia x React
+
AdonisJS x Inertia x React
Learn more about AdonisJS and Inertia.js by visiting the{' '} AdonisJS documentation. + + {isAuthenticated ? 'Authenticated' : 'Not authenticated'} + + {isAuthenticated ? Logout : Login} + +
{JSON.stringify(user, null, 2)}
); diff --git a/inertia/types/app.d.ts b/inertia/types/app.d.ts new file mode 100644 index 0000000..53c9b0d --- /dev/null +++ b/inertia/types/app.d.ts @@ -0,0 +1,4 @@ +import { Serialize } from '@tuyau/utils/types'; +import type UserModel from '../../app/models/user'; + +type User = Serialize; diff --git a/inertia/types/inertia.d.ts b/inertia/types/inertia.d.ts new file mode 100644 index 0000000..2e9fb14 --- /dev/null +++ b/inertia/types/inertia.d.ts @@ -0,0 +1,8 @@ +import type { User } from './app'; + +export type InertiaPage = Record> = T & { + auth: { + user?: User; + isAuthenticated: boolean; + }; +}; diff --git a/package-lock.json b/package-lock.json index f6053b2..cd2cfc5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "license": "UNLICENSED", "dependencies": { + "@adonisjs/ally": "^5.0.2", "@adonisjs/auth": "^9.2.0", "@adonisjs/core": "^6.8.0", "@adonisjs/cors": "^2.2.1", @@ -25,7 +26,8 @@ "pg": "^8.11.5", "react": "^18.3.1", "react-dom": "^18.3.1", - "reflect-metadata": "^0.2.2" + "reflect-metadata": "^0.2.2", + "uuid": "^9.0.1" }, "devDependencies": { "@adonisjs/assembler": "^7.5.1", @@ -39,6 +41,7 @@ "@types/node": "^20.12.7", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.0", + "@types/uuid": "^9.0.8", "@vitejs/plugin-react": "^4.2.1", "eslint": "^8.57.0", "hot-hook": "^0.2.1", @@ -70,6 +73,21 @@ "node": ">=18.16.0" } }, + "node_modules/@adonisjs/ally": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@adonisjs/ally/-/ally-5.0.2.tgz", + "integrity": "sha512-/A53dXiFwwwi9SM8U5dIffesCcNbklHqoME8zHYYO4KQ+dzg9CnubBtgcvH1XRJV8v52FgyUnPFFHDd8J3zI9A==", + "dependencies": { + "@poppinss/oauth-client": "^5.1.2", + "@poppinss/utils": "^6.7.1" + }, + "engines": { + "node": ">=18.16.0" + }, + "peerDependencies": { + "@adonisjs/core": "^6.2.0" + } + }, "node_modules/@adonisjs/application": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/@adonisjs/application/-/application-8.2.2.tgz", @@ -436,6 +454,11 @@ } } }, + "node_modules/@adonisjs/inertia/node_modules/@tuyau/utils": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@tuyau/utils/-/utils-0.0.1.tgz", + "integrity": "sha512-Vi2uT9yuy0jZkaW0c1nMgja266DcIoF+/UK0HGMlTH/FUrmKiQLtu64PKBKZtibkezN4nlw3po4SZKvH37SklA==" + }, "node_modules/@adonisjs/logger": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@adonisjs/logger/-/logger-6.0.3.tgz", @@ -2111,6 +2134,18 @@ "uid-safe": "2.1.5" } }, + "node_modules/@poppinss/oauth-client": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@poppinss/oauth-client/-/oauth-client-5.1.3.tgz", + "integrity": "sha512-cjgac/0pwk/J+x1Jei/cAHTzstCuZE7KRfmdVhClb9zMUskw477rZPZ86dg1LBghw+IGaPtnAgmxTV9pNWG9ZQ==", + "dependencies": { + "@poppinss/utils": "^6.7.3", + "got": "^14.2.1" + }, + "engines": { + "node": ">=18.16.0" + } + }, "node_modules/@poppinss/prompts": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@poppinss/prompts/-/prompts-3.1.3.tgz", @@ -2560,6 +2595,17 @@ "@swc/counter": "^0.1.3" } }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, "node_modules/@tokenizer/token": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", @@ -2601,11 +2647,6 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, - "node_modules/@tuyau/utils": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@tuyau/utils/-/utils-0.0.1.tgz", - "integrity": "sha512-Vi2uT9yuy0jZkaW0c1nMgja266DcIoF+/UK0HGMlTH/FUrmKiQLtu64PKBKZtibkezN4nlw3po4SZKvH37SklA==" - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -2668,6 +2709,11 @@ "resolved": "https://registry.npmjs.org/@types/he/-/he-1.2.3.tgz", "integrity": "sha512-q67/qwlxblDzEDvzHhVkwc1gzVWxaNxeyHUBF4xElrvjL11O+Ytze+1fGpBHlr/H9myiBUaUXNnNPmBHxxfAcA==" }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -2760,6 +2806,12 @@ "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, "node_modules/@types/validator": { "version": "13.11.9", "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.9.tgz", @@ -3585,6 +3637,42 @@ "node": ">=8" } }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -4272,6 +4360,31 @@ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", "dev": true }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dedent": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", @@ -4330,6 +4443,14 @@ "node": ">=0.8" } }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "engines": { + "node": ">=10" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -5481,6 +5602,14 @@ "node": ">= 6" } }, + "node_modules/form-data-encoder": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.0.2.tgz", + "integrity": "sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==", + "engines": { + "node": ">= 18" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -5636,7 +5765,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "devOptional": true, "engines": { "node": ">=16" }, @@ -5789,6 +5917,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/got": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/got/-/got-14.2.1.tgz", + "integrity": "sha512-KOaPMremmsvx6l9BLC04LYE6ZFW4x7e4HkTe3LwBmtuYYQwpeS4XKqzhubTIkaQ1Nr+eXxeori0zuwupXMovBQ==", + "dependencies": { + "@sindresorhus/is": "^6.1.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.14", + "decompress-response": "^6.0.0", + "form-data-encoder": "^4.0.2", + "get-stream": "^8.0.1", + "http2-wrapper": "^2.2.1", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^4.0.1", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -5939,6 +6091,11 @@ } ] }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -5954,6 +6111,18 @@ "node": ">= 0.8" } }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", @@ -6673,8 +6842,7 @@ "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -6767,7 +6935,6 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, "dependencies": { "json-buffer": "3.0.1" } @@ -7029,6 +7196,17 @@ "get-func-name": "^2.0.1" } }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lru-cache": { "version": "10.2.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.1.tgz", @@ -7184,6 +7362,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -7462,6 +7651,14 @@ "node": ">= 0.8.0" } }, + "node_modules/p-cancelable": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz", + "integrity": "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==", + "engines": { + "node": ">=14.16" + } + }, "node_modules/p-event": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/p-event/-/p-event-6.0.1.tgz", @@ -8150,6 +8347,17 @@ "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", @@ -8580,6 +8788,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -8589,6 +8802,20 @@ "node": ">=4" } }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/restore-cursor": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", @@ -9909,6 +10136,18 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", diff --git a/package.json b/package.json index 8d395fb..38a64f5 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "@types/node": "^20.12.7", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.0", + "@types/uuid": "^9.0.8", "@vitejs/plugin-react": "^4.2.1", "eslint": "^8.57.0", "hot-hook": "^0.2.1", @@ -53,6 +54,7 @@ "vite": "^5.2.10" }, "dependencies": { + "@adonisjs/ally": "^5.0.2", "@adonisjs/auth": "^9.2.0", "@adonisjs/core": "^6.8.0", "@adonisjs/cors": "^2.2.1", @@ -69,7 +71,8 @@ "pg": "^8.11.5", "react": "^18.3.1", "react-dom": "^18.3.1", - "reflect-metadata": "^0.2.2" + "reflect-metadata": "^0.2.2", + "uuid": "^9.0.1" }, "hotHook": { "boundaries": [ diff --git a/start/env.ts b/start/env.ts index bf5f36c..ff6009b 100644 --- a/start/env.ts +++ b/start/env.ts @@ -35,4 +35,13 @@ export default await Env.create(new URL('../', import.meta.url), { DB_USER: Env.schema.string(), DB_PASSWORD: Env.schema.string.optional(), DB_DATABASE: Env.schema.string(), + + /* + |---------------------------------------------------------- + | Variables for configuring ally package + |---------------------------------------------------------- + */ + GOOGLE_CLIENT_ID: Env.schema.string(), + GOOGLE_CLIENT_SECRET: Env.schema.string(), + GOOGLE_CLIENT_CALLBACK_URL: Env.schema.string(), }); diff --git a/start/routes.ts b/start/routes.ts index d071125..7b52ef3 100644 --- a/start/routes.ts +++ b/start/routes.ts @@ -1,11 +1,10 @@ -/* -|-------------------------------------------------------------------------- -| Routes file -|-------------------------------------------------------------------------- -| -| The routes file is used for defining the HTTP routes. -| -*/ - import router from '@adonisjs/core/services/router'; -router.on('/inertia').renderInertia('home', { version: 6 }); + +const UsersController = () => import('#controllers/users_controller'); +const AppsController = () => import('#controllers/apps_controller'); + +router.get('/', [AppsController, 'index']); + +router.get('/auth/login', [UsersController, 'google']); +router.get('/auth/callback', [UsersController, 'callbackAuth']); +router.get('/auth/logout', [UsersController, 'logout']);