Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
92e5026
chore(database): add pg driver dependency
EdgarEldy Aug 24, 2026
f781a7c
chore(database): switch default Lucid connection to PostgreSQL
EdgarEldy Aug 24, 2026
8096acf
refactor(auth): drop the access tokens guard from the default scaffold
EdgarEldy Aug 24, 2026
0de0f73
refactor(auth): remove the scaffold signup controller
EdgarEldy Aug 24, 2026
4c3faf8
refactor(auth): remove the scaffold access tokens controller
EdgarEldy Aug 24, 2026
4c4debf
refactor(auth): remove the scaffold profile controller
EdgarEldy Aug 24, 2026
dade20d
refactor(auth): remove the scaffold signup and login validators
EdgarEldy Aug 24, 2026
8cbc27b
refactor(auth): remove the scaffold user transformer
EdgarEldy Aug 24, 2026
33a9f22
chore(database): remove the scaffold users migration
EdgarEldy Aug 24, 2026
5740e22
chore(database): remove the scaffold access tokens migration
EdgarEldy Aug 24, 2026
76126d8
chore(database): remove the stale generated schema snapshot
EdgarEldy Aug 24, 2026
8d891b8
refactor(routes): remove the scaffold auth and account route group
EdgarEldy Aug 24, 2026
6185b74
chore(codegen): regenerate the controllers registry
EdgarEldy Aug 24, 2026
1b83f9c
feat(data-modeling): add Category Lucid model
EdgarEldy Aug 24, 2026
49a5347
feat(data-modeling): add Product Lucid model
EdgarEldy Aug 24, 2026
83b9f5f
feat(data-modeling): add Customer Lucid model
EdgarEldy Aug 24, 2026
e7cbf52
feat(data-modeling): add Order Lucid model
EdgarEldy Aug 24, 2026
5cc4aab
refactor(data-modeling): rewrite User model to the EER_AUTH shape
EdgarEldy Aug 24, 2026
4e8c514
feat(data-modeling): add Role Lucid model
EdgarEldy Aug 24, 2026
08c8645
feat(data-modeling): add Permission Lucid model
EdgarEldy Aug 24, 2026
9e7a84d
feat(data-modeling): add ActivationToken Lucid model
EdgarEldy Aug 24, 2026
98aa99e
feat(data-modeling): add BlacklistedToken Lucid model
EdgarEldy Aug 24, 2026
baef6bc
feat(data-modeling): add PasswordResetToken Lucid model
EdgarEldy Aug 24, 2026
8f876ab
feat(database): add core schema migration for catalog and sales tables
EdgarEldy Aug 24, 2026
89cfa77
feat(database): add auth schema migration for EER_AUTH tables
EdgarEldy Aug 24, 2026
e43c5ee
feat(data-modeling): expose test seed credentials in typed env schema
EdgarEldy Aug 24, 2026
8d9e59c
feat(database): add CategorySeeder with sample catalog categories
EdgarEldy Aug 24, 2026
fe80ed6
feat(database): add ProductSeeder with sample products per category
EdgarEldy Aug 24, 2026
8242f04
feat(database): add CustomerSeeder with sample customers
EdgarEldy Aug 24, 2026
180bab0
feat(database): add UserSeeder with ADMIN and USER test accounts
EdgarEldy Aug 24, 2026
761ef89
feat(database): add MainSeeder orchestrating all domain seeders
EdgarEldy Aug 24, 2026
8121fa9
fix(database): correct the typed env import path
EdgarEldy Aug 24, 2026
a94b38b
chore(database): commit the regenerated schema snapshot
EdgarEldy Aug 24, 2026
e8790d4
test(category): verify hasMany products relation loads associated rows
EdgarEldy Aug 24, 2026
91c465f
test(order): verify total column round trips through storage and queries
EdgarEldy Aug 24, 2026
61c336e
test(user): verify beforeSave hook hashes password only when it changes
EdgarEldy Aug 24, 2026
69dcc1c
test(database): verify migrations run and roll back without error
EdgarEldy Aug 24, 2026
1bb5fc2
chore(database): add pg type declarations
EdgarEldy Aug 24, 2026
e3f0bc7
fix(auth): use serializeAs instead of serializeUsing on User.password
EdgarEldy Aug 24, 2026
930e2c3
fix(database): parse BIGINT columns as numbers
EdgarEldy Aug 24, 2026
aa90950
ci: run migrations before the test step
EdgarEldy Aug 24, 2026
5b4e5a8
ci: run migrations before the test step
EdgarEldy Aug 24, 2026
c6dff4a
style(database): format the product seeder with Prettier
EdgarEldy Aug 24, 2026
d66a902
style(database): format the migrations spec with Prettier
EdgarEldy Aug 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .adonisjs/client/data.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@

/// <reference path="./manifest.d.ts" />
import type { InferData, InferVariants } from '@adonisjs/core/types/transformers'
import type UserTransformer from '#transformers/user_transformer'

export namespace Data {
export type User = InferData<UserTransformer>
export namespace User {
export type Variants = InferVariants<UserTransformer>
}
}
4 changes: 1 addition & 3 deletions .adonisjs/server/controllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,5 @@
*/

export const controllers = {
AccessTokens: () => import('#controllers/access_tokens_controller'),
NewAccount: () => import('#controllers/new_account_controller'),
Profile: () => import('#controllers/profile_controller'),
Health: () => import('#controllers/health_controller'),
}
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,8 @@ jobs:
- name: Lint
run: npm run lint

- name: Run migrations
run: node ace migration:run --force

- name: Run tests
run: npm test
6 changes: 6 additions & 0 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ jobs:
- name: Lint
run: npm run lint

# Migrations must run against the fresh service container before any
# test touches the database, unit specs under tests/unit/database and
# tests/unit/models assume the schema already exists.
- name: Run migrations
run: node ace migration:run --force

# Functional tests start the AdonisJS HTTP server and issue real requests.
# Future browser / E2E suites will use @japa/browser-client with Playwright
# in headless-chromium mode; install steps will be added when that suite
Expand Down
29 changes: 0 additions & 29 deletions app/controllers/access_tokens_controller.ts

This file was deleted.

18 changes: 0 additions & 18 deletions app/controllers/new_account_controller.ts

This file was deleted.

8 changes: 0 additions & 8 deletions app/controllers/profile_controller.ts

This file was deleted.

30 changes: 30 additions & 0 deletions app/models/activation_token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { BaseModel, belongsTo, column } from '@adonisjs/lucid/orm'
import type { BelongsTo } from '@adonisjs/lucid/types/relations'
import { DateTime } from 'luxon'
import User from '#models/user'

export default class ActivationToken extends BaseModel {
@column({ isPrimary: true })
declare id: number

@column()
declare userId: number

@column()
declare token: string

// NOT NULL per the README's Auth Model table. Uses Lucid's autoCreate so
// every row gets a creation timestamp without the issuing service (owned
// by feature/auth) having to set it manually.
@column.dateTime({ autoCreate: true })
declare createdAt: DateTime

@column.dateTime()
declare expiresAt: DateTime | null

@column.dateTime()
declare validatedAt: DateTime | null

@belongsTo(() => User)
declare user: BelongsTo<typeof User>
}
40 changes: 40 additions & 0 deletions app/models/blacklisted_token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { BaseModel, belongsTo, column } from '@adonisjs/lucid/orm'
import type { BelongsTo } from '@adonisjs/lucid/types/relations'
import { DateTime } from 'luxon'
import User from '#models/user'

/**
* Backs the JWT revocation list. `jti` is the JWT ID claim of the token
* being blacklisted and is unique so the same token cannot be blacklisted
* twice; feature/auth's AuthMiddleware queries this table by `jti` on
* every authenticated request to reject revoked tokens before the guard
* even looks at the signature's expiry.
*/
export default class BlacklistedToken extends BaseModel {
@column({ isPrimary: true })
declare id: number

@column()
declare userId: number

@column()
declare token: string

@column()
declare jti: string

@column.dateTime()
declare blacklistedAt: DateTime | null

@column.dateTime({ autoCreate: true })
declare createdAt: DateTime

@column.dateTime()
declare expiresAt: DateTime | null

@column.dateTime()
declare validatedAt: DateTime | null

@belongsTo(() => User)
declare user: BelongsTo<typeof User>
}
20 changes: 20 additions & 0 deletions app/models/category.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { BaseModel, column, hasMany } from '@adonisjs/lucid/orm'
import type { HasMany } from '@adonisjs/lucid/types/relations'
import Product from '#models/product'

// NOTE: the README's Data Model "Column Details" table for `categories`
// lists only `id` and `category_name`, with no `created_at` / `updated_at`
// columns, unlike the generic Lucid model example in the patterns skill.
// This model and its migration follow the README's explicit column list
// rather than the generic pattern, since the README is the authoritative
// spec for this branch.
export default class Category extends BaseModel {
@column({ isPrimary: true })
declare id: number

@column()
declare categoryName: string

@hasMany(() => Product)
declare products: HasMany<typeof Product>
}
32 changes: 32 additions & 0 deletions app/models/customer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { BaseModel, column, hasMany } from '@adonisjs/lucid/orm'
import type { HasMany } from '@adonisjs/lucid/types/relations'
import Order from '#models/order'

export default class Customer extends BaseModel {
@column({ isPrimary: true })
declare id: number

@column()
declare firstName: string

@column()
declare lastName: string

@column()
declare telephone: string

@column()
declare email: string

@column()
declare address: string

// NOTE: the README's Data Model diagram shows customers 1 -- N orders,
// and the Column Details table only defines orders.customer_id as the FK
// side. The inverse hasMany here is not explicitly requested by the
// Tasks checklist (which only lists scalar columns for Customer) but is
// added because the diagram documents the relation and orders.customer_id
// would otherwise be unreachable from the Customer side.
@hasMany(() => Order)
declare orders: HasMany<typeof Order>
}
27 changes: 27 additions & 0 deletions app/models/order.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { BaseModel, belongsTo, column } from '@adonisjs/lucid/orm'
import type { BelongsTo } from '@adonisjs/lucid/types/relations'
import Customer from '#models/customer'
import Product from '#models/product'

export default class Order extends BaseModel {
@column({ isPrimary: true })
declare id: number

@column()
declare customerId: number

@column()
declare productId: number

@column()
declare quantity: number

@column()
declare total: number

@belongsTo(() => Customer)
declare customer: BelongsTo<typeof Customer>

@belongsTo(() => Product)
declare product: BelongsTo<typeof Product>
}
28 changes: 28 additions & 0 deletions app/models/password_reset_token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { BaseModel, belongsTo, column } from '@adonisjs/lucid/orm'
import type { BelongsTo } from '@adonisjs/lucid/types/relations'
import { DateTime } from 'luxon'
import User from '#models/user'

export default class PasswordResetToken extends BaseModel {
@column({ isPrimary: true })
declare id: number

@column()
declare userId: number

@column()
declare token: string

// NOTE: the README does not enumerate the values for `type`. feature/auth
// owns the actual reset flow and will decide the concrete values (for
// example 'password_reset' vs 'account_activation'); this model keeps
// the column as a plain string to stay unopinionated about that choice.
@column()
declare type: string

@column.dateTime()
declare expiryDate: DateTime

@belongsTo(() => User)
declare user: BelongsTo<typeof User>
}
22 changes: 22 additions & 0 deletions app/models/permission.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { BaseModel, column, manyToMany } from '@adonisjs/lucid/orm'
import type { ManyToMany } from '@adonisjs/lucid/types/relations'
import Role from '#models/role'

export default class Permission extends BaseModel {
@column({ isPrimary: true })
declare id: number

@column()
declare resource: string

@column()
declare action: string

// NOTE: the README only specifies the Role -> Permission direction
// (`@manyToMany(() => Permission, { pivotTable: 'role_permission' })` on
// Role). The inverse is declared here so a Permission can be traced back
// to the roles that grant it, which the `role_permission` FK pair
// supports without any extra schema.
@manyToMany(() => Role, { pivotTable: 'role_permission' })
declare roles: ManyToMany<typeof Role>
}
20 changes: 20 additions & 0 deletions app/models/product.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { BaseModel, belongsTo, column } from '@adonisjs/lucid/orm'
import type { BelongsTo } from '@adonisjs/lucid/types/relations'
import Category from '#models/category'

export default class Product extends BaseModel {
@column({ isPrimary: true })
declare id: number

@column()
declare categoryId: number

@column()
declare productName: string

@column()
declare unitPrice: number

@belongsTo(() => Category)
declare category: BelongsTo<typeof Category>
}
18 changes: 18 additions & 0 deletions app/models/role.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { BaseModel, column, manyToMany } from '@adonisjs/lucid/orm'
import type { ManyToMany } from '@adonisjs/lucid/types/relations'
import User from '#models/user'
import Permission from '#models/permission'

export default class Role extends BaseModel {
@column({ isPrimary: true })
declare id: number

@column()
declare roleName: string

@manyToMany(() => User, { pivotTable: 'role_user' })
declare users: ManyToMany<typeof User>

@manyToMany(() => Permission, { pivotTable: 'role_permission' })
declare permissions: ManyToMany<typeof Permission>
}
Loading
Loading