@open-mercato/core 0.4.5-develop-03023b2707 → 0.4.5-develop-0c30cb4b11
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.
- package/AGENTS.md +66 -0
- package/dist/modules/auth/api/login.js +15 -4
- package/dist/modules/auth/api/login.js.map +2 -2
- package/dist/modules/auth/api/profile/route.js +3 -2
- package/dist/modules/auth/api/profile/route.js.map +2 -2
- package/dist/modules/auth/api/session/refresh.js +69 -2
- package/dist/modules/auth/api/session/refresh.js.map +2 -2
- package/dist/modules/auth/services/sidebarPreferencesService.js +8 -2
- package/dist/modules/auth/services/sidebarPreferencesService.js.map +2 -2
- package/dist/modules/customers/api/people/route.js +1 -0
- package/dist/modules/customers/api/people/route.js.map +2 -2
- package/dist/modules/widgets/lib/injection.js +4 -0
- package/dist/modules/widgets/lib/injection.js.map +2 -2
- package/package.json +2 -2
- package/src/modules/auth/api/login.ts +14 -3
- package/src/modules/auth/api/profile/route.ts +2 -1
- package/src/modules/auth/api/session/refresh.ts +80 -1
- package/src/modules/auth/services/sidebarPreferencesService.ts +9 -2
- package/src/modules/customers/api/people/route.ts +1 -0
- package/src/modules/inbox_ops/migrations/.snapshot-open-mercato.json +1159 -0
- package/src/modules/widgets/lib/injection.ts +3 -0
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
getCoreInjectionTables,
|
|
6
6
|
invalidateInjectionWidgetCache,
|
|
7
7
|
loadAllInjectionWidgets,
|
|
8
|
+
loadInjectionDataWidgetById,
|
|
9
|
+
loadInjectionDataWidgetsForSpot,
|
|
8
10
|
loadInjectionWidgetById,
|
|
9
11
|
loadInjectionWidgetsForSpot
|
|
10
12
|
} from "@open-mercato/shared/modules/widgets/injection-loader";
|
|
@@ -13,6 +15,8 @@ export {
|
|
|
13
15
|
getCoreInjectionWidgets,
|
|
14
16
|
invalidateInjectionWidgetCache,
|
|
15
17
|
loadAllInjectionWidgets,
|
|
18
|
+
loadInjectionDataWidgetById,
|
|
19
|
+
loadInjectionDataWidgetsForSpot,
|
|
16
20
|
loadInjectionWidgetById,
|
|
17
21
|
loadInjectionWidgetsForSpot,
|
|
18
22
|
registerCoreInjectionTables,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/widgets/lib/injection.ts"],
|
|
4
|
-
"sourcesContent": ["// Re-export from shared for backward compatibility\n// New code should import directly from @open-mercato/shared/modules/widgets/injection-loader\nexport {\n registerCoreInjectionWidgets,\n getCoreInjectionWidgets,\n registerCoreInjectionTables,\n getCoreInjectionTables,\n invalidateInjectionWidgetCache,\n loadAllInjectionWidgets,\n loadInjectionWidgetById,\n loadInjectionWidgetsForSpot,\n type LoadedInjectionWidget,\n} from '@open-mercato/shared/modules/widgets/injection-loader'\n"],
|
|
5
|
-
"mappings": "AAEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,
|
|
4
|
+
"sourcesContent": ["// Re-export from shared for backward compatibility\n// New code should import directly from @open-mercato/shared/modules/widgets/injection-loader\nexport {\n registerCoreInjectionWidgets,\n getCoreInjectionWidgets,\n registerCoreInjectionTables,\n getCoreInjectionTables,\n invalidateInjectionWidgetCache,\n loadAllInjectionWidgets,\n loadInjectionDataWidgetById,\n loadInjectionDataWidgetsForSpot,\n loadInjectionWidgetById,\n loadInjectionWidgetsForSpot,\n type LoadedInjectionDataWidget,\n type LoadedInjectionWidget,\n} from '@open-mercato/shared/modules/widgets/injection-loader'\n"],
|
|
5
|
+
"mappings": "AAEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.4.5-develop-
|
|
3
|
+
"version": "0.4.5-develop-0c30cb4b11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -207,7 +207,7 @@
|
|
|
207
207
|
}
|
|
208
208
|
},
|
|
209
209
|
"dependencies": {
|
|
210
|
-
"@open-mercato/shared": "0.4.5-develop-
|
|
210
|
+
"@open-mercato/shared": "0.4.5-develop-0c30cb4b11",
|
|
211
211
|
"@types/semver": "^7.5.8",
|
|
212
212
|
"@xyflow/react": "^12.6.0",
|
|
213
213
|
"ai": "^6.0.0",
|
|
@@ -98,13 +98,23 @@ export async function POST(req: Request) {
|
|
|
98
98
|
email: user.email,
|
|
99
99
|
roles: userRoleNames
|
|
100
100
|
})
|
|
101
|
-
const
|
|
102
|
-
|
|
101
|
+
const responseData: { ok: true; token: string; redirect: string; refreshToken?: string } = {
|
|
102
|
+
ok: true,
|
|
103
|
+
token,
|
|
104
|
+
redirect: '/backend',
|
|
105
|
+
}
|
|
103
106
|
if (remember) {
|
|
104
107
|
const days = Number(process.env.REMEMBER_ME_DAYS || '30')
|
|
105
108
|
const expiresAt = new Date(Date.now() + days * 24 * 60 * 60 * 1000)
|
|
106
109
|
const sess = await auth.createSession(user, expiresAt)
|
|
107
|
-
|
|
110
|
+
responseData.refreshToken = sess.token
|
|
111
|
+
}
|
|
112
|
+
const res = NextResponse.json(responseData)
|
|
113
|
+
res.cookies.set('auth_token', token, { httpOnly: true, path: '/', sameSite: 'lax', secure: process.env.NODE_ENV === 'production', maxAge: 60 * 60 * 8 })
|
|
114
|
+
if (remember && responseData.refreshToken) {
|
|
115
|
+
const days = Number(process.env.REMEMBER_ME_DAYS || '30')
|
|
116
|
+
const expiresAt = new Date(Date.now() + days * 24 * 60 * 60 * 1000)
|
|
117
|
+
res.cookies.set('session_token', responseData.refreshToken, { httpOnly: true, path: '/', sameSite: 'lax', secure: process.env.NODE_ENV === 'production', expires: expiresAt })
|
|
108
118
|
}
|
|
109
119
|
return res
|
|
110
120
|
}
|
|
@@ -118,6 +128,7 @@ const loginSuccessSchema = z.object({
|
|
|
118
128
|
ok: z.literal(true),
|
|
119
129
|
token: z.string().describe('JWT token issued for subsequent API calls'),
|
|
120
130
|
redirect: z.string().nullable().describe('Next location the client should navigate to'),
|
|
131
|
+
refreshToken: z.string().optional().describe('Long-lived refresh token for obtaining new access tokens (only present when remember=true)'),
|
|
121
132
|
})
|
|
122
133
|
|
|
123
134
|
const loginErrorSchema = z.object({
|
|
@@ -15,6 +15,7 @@ import { buildPasswordSchema } from '@open-mercato/shared/lib/auth/passwordPolic
|
|
|
15
15
|
|
|
16
16
|
const profileResponseSchema = z.object({
|
|
17
17
|
email: z.string().email(),
|
|
18
|
+
roles: z.array(z.string()),
|
|
18
19
|
})
|
|
19
20
|
|
|
20
21
|
const passwordSchema = buildPasswordSchema()
|
|
@@ -67,7 +68,7 @@ export async function GET(req: Request) {
|
|
|
67
68
|
if (!user) {
|
|
68
69
|
return NextResponse.json({ error: translate('auth.users.form.errors.notFound', 'User not found') }, { status: 404 })
|
|
69
70
|
}
|
|
70
|
-
return NextResponse.json({ email: String(user.email) })
|
|
71
|
+
return NextResponse.json({ email: String(user.email), roles: auth.roles ?? [] })
|
|
71
72
|
} catch (err) {
|
|
72
73
|
console.error('auth.profile.load failed', err)
|
|
73
74
|
return NextResponse.json({ error: translate('auth.profile.form.errors.load', 'Failed to load profile.') }, { status: 400 })
|
|
@@ -41,25 +41,104 @@ export async function GET(req: Request) {
|
|
|
41
41
|
return res
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
export async function POST(req: Request) {
|
|
45
|
+
let token: string | null = null
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const body = await req.json()
|
|
49
|
+
const parsed = refreshRequestSchema.safeParse(body)
|
|
50
|
+
if (parsed.success) {
|
|
51
|
+
token = parsed.data.refreshToken
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// Invalid JSON
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!token) {
|
|
58
|
+
return NextResponse.json({ ok: false, error: 'Missing or invalid refresh token' }, { status: 400 })
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const c = await createRequestContainer()
|
|
62
|
+
const auth = c.resolve<AuthService>('authService')
|
|
63
|
+
const ctx = await auth.refreshFromSessionToken(token)
|
|
64
|
+
|
|
65
|
+
if (!ctx) {
|
|
66
|
+
return NextResponse.json({ ok: false, error: 'Invalid or expired refresh token' }, { status: 401 })
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const { user, roles } = ctx
|
|
70
|
+
const jwt = signJwt({
|
|
71
|
+
sub: String(user.id),
|
|
72
|
+
tenantId: String(user.tenantId),
|
|
73
|
+
orgId: String(user.organizationId),
|
|
74
|
+
email: user.email,
|
|
75
|
+
roles,
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
const res = NextResponse.json({
|
|
79
|
+
ok: true,
|
|
80
|
+
accessToken: jwt,
|
|
81
|
+
expiresIn: 60 * 60 * 8,
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
res.cookies.set('auth_token', jwt, {
|
|
85
|
+
httpOnly: true,
|
|
86
|
+
path: '/',
|
|
87
|
+
sameSite: 'lax',
|
|
88
|
+
secure: process.env.NODE_ENV === 'production',
|
|
89
|
+
maxAge: 60 * 60 * 8,
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
return res
|
|
93
|
+
}
|
|
94
|
+
|
|
44
95
|
export const metadata = {
|
|
45
96
|
GET: { requireAuth: false },
|
|
97
|
+
POST: { requireAuth: false },
|
|
46
98
|
}
|
|
47
99
|
|
|
48
100
|
const refreshQuerySchema = z.object({
|
|
49
101
|
redirect: z.string().optional().describe('Absolute or relative URL to redirect after refresh'),
|
|
50
102
|
})
|
|
51
103
|
|
|
104
|
+
const refreshRequestSchema = z.object({
|
|
105
|
+
refreshToken: z.string().min(1).describe('The refresh token obtained from login'),
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
const refreshSuccessSchema = z.object({
|
|
109
|
+
ok: z.literal(true),
|
|
110
|
+
accessToken: z.string().describe('New JWT access token'),
|
|
111
|
+
expiresIn: z.number().describe('Token expiration time in seconds'),
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
const refreshErrorSchema = z.object({
|
|
115
|
+
ok: z.literal(false),
|
|
116
|
+
error: z.string(),
|
|
117
|
+
})
|
|
118
|
+
|
|
52
119
|
export const openApi: OpenApiRouteDoc = {
|
|
53
120
|
tag: 'Authentication & Accounts',
|
|
54
121
|
summary: 'Refresh session token',
|
|
55
122
|
methods: {
|
|
56
123
|
GET: {
|
|
57
|
-
summary: 'Refresh auth cookie from session token',
|
|
124
|
+
summary: 'Refresh auth cookie from session token (browser)',
|
|
58
125
|
description: 'Exchanges an existing `session_token` cookie for a fresh JWT auth cookie and redirects the browser.',
|
|
59
126
|
query: refreshQuerySchema,
|
|
60
127
|
responses: [
|
|
61
128
|
{ status: 302, description: 'Redirect to target location when session is valid', mediaType: 'text/html' },
|
|
62
129
|
],
|
|
63
130
|
},
|
|
131
|
+
POST: {
|
|
132
|
+
summary: 'Refresh access token (API/mobile)',
|
|
133
|
+
description: 'Exchanges a refresh token for a new JWT access token. Pass the refresh token obtained from login in the request body.',
|
|
134
|
+
requestBody: { schema: refreshRequestSchema, contentType: 'application/json' },
|
|
135
|
+
responses: [
|
|
136
|
+
{ status: 200, description: 'New access token issued', schema: refreshSuccessSchema },
|
|
137
|
+
],
|
|
138
|
+
errors: [
|
|
139
|
+
{ status: 400, description: 'Missing refresh token', schema: refreshErrorSchema },
|
|
140
|
+
{ status: 401, description: 'Invalid or expired token', schema: refreshErrorSchema },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
64
143
|
},
|
|
65
144
|
}
|
|
@@ -20,6 +20,7 @@ export type RoleSidebarPreferenceScope = {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export type SidebarItemLike<T = Record<string, unknown>> = {
|
|
23
|
+
id?: string
|
|
23
24
|
href: string
|
|
24
25
|
title: string
|
|
25
26
|
defaultTitle: string
|
|
@@ -160,11 +161,17 @@ export function applySidebarPreference<T extends SidebarGroupLike>(
|
|
|
160
161
|
if (!orderIndex.has(id)) orderIndex.set(id, idx)
|
|
161
162
|
})
|
|
162
163
|
const hiddenSet = new Set(normalized.hiddenItems ?? [])
|
|
164
|
+
const resolveItemKey = (item: SidebarItemLike): string => {
|
|
165
|
+
const candidate = item.id?.trim()
|
|
166
|
+
if (candidate && candidate.length > 0) return candidate
|
|
167
|
+
return item.href
|
|
168
|
+
}
|
|
163
169
|
const applyItems = <TI extends SidebarItemLike>(items: TI[]): TI[] => {
|
|
164
170
|
return items.map((item) => {
|
|
165
|
-
const
|
|
171
|
+
const itemKey = resolveItemKey(item)
|
|
172
|
+
const override = normalized.itemLabels?.[itemKey] ?? normalized.itemLabels?.[item.href]
|
|
166
173
|
const nextChildren = item.children ? applyItems(item.children) : undefined
|
|
167
|
-
const hidden = hiddenSet.has(item.href)
|
|
174
|
+
const hidden = hiddenSet.has(itemKey) || hiddenSet.has(item.href)
|
|
168
175
|
const next = {
|
|
169
176
|
...item,
|
|
170
177
|
title: override && override.trim().length > 0 ? override.trim() : item.defaultTitle,
|