@stacksjs/defaults 0.74.31 → 0.74.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -66,7 +66,7 @@ globalThis.toggleDark = toggleDark
66
66
  - **Custom Functions**: From `resources/functions/` (counter, dark mode, GPX, geo utilities)
67
67
 
68
68
  ### Server Auto-Imports (100+)
69
- - **All ORM Models**: User, Post, Author, Product, Order, Payment, Customer, etc. (98 models)
69
+ - **All ORM Models**: User, Post, Author, Product, Order, Payment, Customer, etc. (100 models)
70
70
  - **Request Models**: UserRequest, PostRequest, OrderRequest, etc.
71
71
  - **Actions**: Action types and helpers
72
72
  - **Schema**: validation schema builder
@@ -11,7 +11,7 @@ allowed-tools: Read Edit Write Bash Grep Glob
11
11
  ## Key Paths
12
12
  - Core ORM package: `storage/framework/core/orm/src/`
13
13
  - ORM implementation: `storage/framework/orm/`
14
- - Model definitions: `storage/framework/defaults/app/Models/` (98 models)
14
+ - Model definitions: `storage/framework/defaults/app/Models/` (100 models)
15
15
  - Application models: `app/Models/`
16
16
  - Default model templates: `storage/framework/defaults/app/Models/`
17
17
  - ORM type globals: `storage/framework/types/orm-globals.d.ts`
@@ -0,0 +1,13 @@
1
+ import { Action } from '@stacksjs/actions'
2
+ import { createReferralCode } from '@stacksjs/auth'
3
+ import { response } from '@stacksjs/router'
4
+
5
+ export default new Action({
6
+ name: 'CreateReferralCodeAction',
7
+ method: 'POST',
8
+ async handle(request: RequestInstance) {
9
+ const user = await request.user()
10
+ if (!user?.id) return response.error('Unauthenticated', 401)
11
+ return response.json({ code: await createReferralCode(Number(user.id)) })
12
+ },
13
+ })
@@ -0,0 +1,13 @@
1
+ import { Action } from '@stacksjs/actions'
2
+ import { referralSummary } from '@stacksjs/auth'
3
+ import { response } from '@stacksjs/router'
4
+
5
+ export default new Action({
6
+ name: 'ReferralSummaryAction',
7
+ method: 'GET',
8
+ async handle(request: RequestInstance) {
9
+ const user = await request.user()
10
+ if (!user?.id) return response.error('Unauthenticated', 401)
11
+ return response.json(await referralSummary(Number(user.id)))
12
+ },
13
+ })
@@ -30,7 +30,8 @@ export default new Action({
30
30
  const password = request.get('password')
31
31
  const name = request.get('name')
32
32
 
33
- const result = await register({ email, password, name })
33
+ const referralCode = request.get('referralCode')
34
+ const result = await register({ email, password, name, referralCode: typeof referralCode === 'string' ? referralCode : undefined })
34
35
 
35
36
  if (result) {
36
37
  const user = await Auth.getUserFromToken(result.token)
@@ -3,6 +3,8 @@ import { authenticatedUser } from '@stacksjs/auth/middleware'
3
3
  import { HttpError } from '@stacksjs/error-handling'
4
4
  import { Middleware, resolveRouteModel, setRouteModelFallback } from '@stacksjs/router'
5
5
 
6
+ let ormModule: typeof import('@stacksjs/orm') | undefined
7
+
6
8
  /**
7
9
  * Convention binding: parameter `site` resolves through the `Site` model
8
10
  * (stacksjs/stacks#2231).
@@ -24,7 +26,7 @@ setRouteModelFallback(async (value, { param }) => {
24
26
  // touched: lowercasing the rest would turn `blogPost` into `Blogpost`.
25
27
  const modelName = param.charAt(0).toUpperCase() + param.slice(1)
26
28
 
27
- const orm = await import('@stacksjs/orm') as Record<string, any>
29
+ const orm = (ormModule ??= await import('@stacksjs/orm')) as Record<string, any>
28
30
  const model = orm[modelName]
29
31
 
30
32
  // No model of that name — decline, so the raw string passes through exactly
@@ -2,6 +2,8 @@ import { authenticatedUser } from '@stacksjs/auth/middleware'
2
2
  import { HttpError } from '@stacksjs/error-handling'
3
3
  import { Middleware } from '@stacksjs/router'
4
4
 
5
+ let rbacModule: typeof import('@stacksjs/auth/rbac') | undefined
6
+
5
7
  /**
6
8
  * Permission Middleware
7
9
  *
@@ -32,7 +34,7 @@ export default new Middleware({
32
34
  throw new HttpError(401, 'Unauthenticated.')
33
35
  }
34
36
 
35
- const { hasAnyPermission } = await import('@stacksjs/auth/rbac')
37
+ const { hasAnyPermission } = rbacModule ??= await import('@stacksjs/auth/rbac')
36
38
 
37
39
  const hasRequired = await hasAnyPermission(user, requiredPermissions)
38
40
 
@@ -2,6 +2,8 @@ import { authenticatedUser } from '@stacksjs/auth/middleware'
2
2
  import { HttpError } from '@stacksjs/error-handling'
3
3
  import { Middleware } from '@stacksjs/router'
4
4
 
5
+ let rbacModule: typeof import('@stacksjs/auth/rbac') | undefined
6
+
5
7
  /**
6
8
  * Role Middleware
7
9
  *
@@ -32,7 +34,7 @@ export default new Middleware({
32
34
  }
33
35
 
34
36
  // Dynamically import to avoid circular dependency
35
- const { hasAnyRole } = await import('@stacksjs/auth/rbac')
37
+ const { hasAnyRole } = rbacModule ??= await import('@stacksjs/auth/rbac')
36
38
 
37
39
  const hasRequired = await hasAnyRole(user, requiredRoles)
38
40
 
@@ -0,0 +1,16 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ export default defineModel({
5
+ name: 'Referral',
6
+ table: 'referrals',
7
+ traits: { useTimestamps: true },
8
+ indexes: [{ name: 'referrals_referrer_status', columns: ['referrer_id', 'status'] }],
9
+ attributes: {
10
+ referrerId: { required: true, validation: { rule: schema.number().integer().min(1) } },
11
+ referredUserId: { required: true, unique: true, validation: { rule: schema.number().integer().min(1) } },
12
+ code: { required: true, validation: { rule: schema.string().max(24) } },
13
+ status: { required: true, default: 'registered', validation: { rule: schema.enum(['registered', 'qualified']) } },
14
+ qualifiedAt: { nullable: true, validation: { rule: schema.date() } },
15
+ },
16
+ } as const)
@@ -0,0 +1,12 @@
1
+ import { defineModel } from '@stacksjs/orm'
2
+ import { schema } from '@stacksjs/validation'
3
+
4
+ export default defineModel({
5
+ name: 'ReferralCode',
6
+ table: 'referral_codes',
7
+ traits: { useTimestamps: true },
8
+ attributes: {
9
+ userId: { required: true, unique: true, validation: { rule: schema.number().integer().min(1) } },
10
+ code: { required: true, unique: true, validation: { rule: schema.string().max(24) } },
11
+ },
12
+ } as const)
@@ -2,7 +2,7 @@
2
2
  "publisher": "Stacks",
3
3
  "name": "vscode-stacks",
4
4
  "displayName": "Stacks",
5
- "version": "0.74.31",
5
+ "version": "0.74.32",
6
6
  "description": "A modern Stacks development environment.",
7
7
  "license": "MIT",
8
8
  "funding": "https://github.com/sponsors/chrisbbreuer",
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/defaults",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.74.31",
5
+ "version": "0.74.32",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/stacksjs/stacks.git",
@@ -55,7 +55,7 @@
55
55
  "dependencies": {
56
56
  "@iconify-json/f7": "^1.2.2",
57
57
  "@iconify-json/hugeicons": "^1.2.27",
58
- "@stacksjs/mobile": "^0.74.31",
58
+ "@stacksjs/mobile": "^0.74.32",
59
59
  "@stacksjs/sanitizer": "^0.2.113",
60
60
  "ts-qr-codes": "^0.1.8"
61
61
  }
package/routes/auth.ts CHANGED
@@ -74,6 +74,8 @@ route.group({ prefix: '/auth' }, () => {
74
74
  })
75
75
 
76
76
  route.group({ middleware: 'auth' }, () => {
77
+ route.get('/referrals', 'Actions/Auth/ReferralSummaryAction').rateLimit(60, 'minute')
78
+ route.post('/referrals/code', 'Actions/Auth/CreateReferralCodeAction').rateLimit(10, 'minute')
77
79
  route.get('/me', 'Actions/Auth/AuthUserAction')
78
80
  route.post('/logout', 'Actions/Auth/LogoutAction')
79
81
  // Sign out everywhere: revoke every access/refresh token AND destroy