@mohasinac/appkit 2.6.5 → 2.6.6
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/dist/_internal/client/features/layout/DashboardLayoutClient.d.ts +8 -1
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +18 -2
- package/dist/_internal/server/features/auth/capabilities.d.ts +17 -0
- package/dist/_internal/server/features/auth/capabilities.js +31 -0
- package/dist/_internal/server/features/auth/index.d.ts +2 -0
- package/dist/_internal/server/features/auth/index.js +2 -0
- package/dist/_internal/server/features/auth/permissions.d.ts +49 -0
- package/dist/_internal/server/features/auth/permissions.js +76 -0
- package/dist/configs/next.d.ts +6 -1
- package/dist/configs/next.js +45 -18
- package/dist/constants/api-endpoints.d.ts +69 -0
- package/dist/constants/api-endpoints.js +34 -0
- package/dist/contracts/registry.d.ts +7 -0
- package/dist/features/admin/components/AdminEmployeeEditorView.d.ts +10 -0
- package/dist/features/admin/components/AdminEmployeeEditorView.js +168 -0
- package/dist/features/admin/components/AdminSidebar.d.ts +2 -0
- package/dist/features/admin/components/AdminStoreEditorView.d.ts +2 -1
- package/dist/features/admin/components/AdminStoreEditorView.js +55 -3
- package/dist/features/admin/components/AdminStoresView.js +3 -1
- package/dist/features/admin/components/AdminSupportTicketDetailView.d.ts +23 -0
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +83 -0
- package/dist/features/admin/components/AdminSupportTicketsView.d.ts +4 -0
- package/dist/features/admin/components/AdminSupportTicketsView.js +151 -0
- package/dist/features/admin/components/AdminTeamView.d.ts +4 -0
- package/dist/features/admin/components/AdminTeamView.js +139 -0
- package/dist/features/admin/components/AdminUserEditorView.d.ts +14 -1
- package/dist/features/admin/components/AdminUserEditorView.js +116 -14
- package/dist/features/admin/components/AdminUsersView.js +39 -12
- package/dist/features/admin/components/index.d.ts +8 -0
- package/dist/features/admin/components/index.js +4 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +37 -0
- package/dist/features/admin/constants/filter-tabs.js +17 -0
- package/dist/features/auth/server/checkSoftBan.d.ts +15 -0
- package/dist/features/auth/server/checkSoftBan.js +36 -0
- package/dist/features/auth/server.d.ts +1 -0
- package/dist/features/auth/server.js +1 -0
- package/dist/features/media/types/index.js +2 -1
- package/dist/features/stores/repository/store.repository.d.ts +5 -1
- package/dist/features/stores/repository/store.repository.js +27 -2
- package/dist/features/support/repository/support.repository.d.ts +18 -0
- package/dist/features/support/repository/support.repository.js +117 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/next/api/routeHandler.d.ts +8 -0
- package/dist/next/api/routeHandler.js +24 -3
- package/dist/next/routing/route-map.d.ts +2 -0
- package/dist/next/routing/route-map.js +1 -0
- package/dist/repositories/index.d.ts +2 -0
- package/dist/repositories/index.js +1 -0
- package/dist/security/pii-schemas.d.ts +2 -0
- package/dist/security/pii-schemas.js +2 -0
- package/dist/seed/stores-seed-data.js +8 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.js +6 -0
- package/dist/tailwind-utilities.css +1 -1
- package/dist/utils/media-url.d.ts +9 -0
- package/dist/utils/media-url.js +29 -0
- package/package.json +1 -1
|
@@ -8,10 +8,35 @@ import { BaseRepository, prepareForFirestore, applySieveToFirestore, } from "../
|
|
|
8
8
|
import { increment } from "../../../contracts/field-ops";
|
|
9
9
|
import { STORE_COLLECTION, STORE_FIELDS, DEFAULT_STORE_DATA, StoreStatusValues, } from "../schemas";
|
|
10
10
|
import { DatabaseError } from "../../../errors";
|
|
11
|
+
import { encryptSecret, decryptSecret, } from "../../../security/settings-encryption";
|
|
11
12
|
export class StoreRepository extends BaseRepository {
|
|
12
13
|
constructor() {
|
|
13
14
|
super(STORE_COLLECTION);
|
|
14
15
|
}
|
|
16
|
+
encryptSecrets(data) {
|
|
17
|
+
const token = data.whatsappConfig?.accessToken;
|
|
18
|
+
if (!token)
|
|
19
|
+
return data;
|
|
20
|
+
return {
|
|
21
|
+
...data,
|
|
22
|
+
whatsappConfig: { ...data.whatsappConfig, accessToken: encryptSecret(token) },
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
decryptSecrets(data) {
|
|
26
|
+
const token = data.whatsappConfig?.accessToken;
|
|
27
|
+
if (!token)
|
|
28
|
+
return data;
|
|
29
|
+
return {
|
|
30
|
+
...data,
|
|
31
|
+
whatsappConfig: { ...data.whatsappConfig, accessToken: decryptSecret(token) },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
mapDoc(snap) {
|
|
35
|
+
return this.decryptSecrets(super.mapDoc(snap));
|
|
36
|
+
}
|
|
37
|
+
async update(id, data) {
|
|
38
|
+
return super.update(id, this.encryptSecrets(data));
|
|
39
|
+
}
|
|
15
40
|
/**
|
|
16
41
|
* Create a new store.
|
|
17
42
|
* The document ID is set to storeSlug for easy URL-based lookups.
|
|
@@ -37,7 +62,7 @@ export class StoreRepository extends BaseRepository {
|
|
|
37
62
|
await this.db
|
|
38
63
|
.collection(this.collection)
|
|
39
64
|
.doc(input.storeSlug)
|
|
40
|
-
.create(prepareForFirestore(storeData));
|
|
65
|
+
.create(prepareForFirestore(this.encryptSecrets(storeData)));
|
|
41
66
|
}
|
|
42
67
|
catch (err) {
|
|
43
68
|
// gRPC ALREADY_EXISTS = code 6
|
|
@@ -210,7 +235,7 @@ export class StoreRepository extends BaseRepository {
|
|
|
210
235
|
const batch = this.db.batch();
|
|
211
236
|
const newRef = this.db.collection(this.collection).doc(newSlug);
|
|
212
237
|
const oldRef = this.db.collection(this.collection).doc(currentSlug);
|
|
213
|
-
batch.create(newRef, prepareForFirestore(newDoc));
|
|
238
|
+
batch.create(newRef, prepareForFirestore(this.encryptSecrets(newDoc)));
|
|
214
239
|
batch.delete(oldRef);
|
|
215
240
|
await batch.commit();
|
|
216
241
|
return { id: newSlug, ...newDoc };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BaseRepository } from "../../../providers/db-firebase";
|
|
2
|
+
import type { FirebaseSieveFields, FirebaseSieveResult, SieveModel } from "../../../providers/db-firebase";
|
|
3
|
+
import { type SupportTicketDocument, type SupportTicketCreateInput, type SupportTicketUpdateInput, type TicketMessage, type TicketStatus } from "../schemas/firestore";
|
|
4
|
+
export declare class SupportRepository extends BaseRepository<SupportTicketDocument> {
|
|
5
|
+
static readonly SIEVE_FIELDS: FirebaseSieveFields;
|
|
6
|
+
constructor();
|
|
7
|
+
createTicket(input: SupportTicketCreateInput): Promise<SupportTicketDocument>;
|
|
8
|
+
getUserTickets(userId: string, page?: number, pageSize?: number): Promise<FirebaseSieveResult<SupportTicketDocument>>;
|
|
9
|
+
getTicketById(ticketId: string): Promise<SupportTicketDocument | null>;
|
|
10
|
+
countActiveTickets(userId: string): Promise<number>;
|
|
11
|
+
getActiveOrderTicket(userId: string, orderId: string): Promise<SupportTicketDocument | null>;
|
|
12
|
+
getActiveCategoryTicket(userId: string, category: string): Promise<SupportTicketDocument | null>;
|
|
13
|
+
updateTicketStatus(ticketId: string, update: SupportTicketUpdateInput): Promise<SupportTicketDocument>;
|
|
14
|
+
addMessage(ticketId: string, message: TicketMessage, newStatus?: TicketStatus): Promise<void>;
|
|
15
|
+
assignTicket(ticketId: string, assignedTo: string, assignedToName: string): Promise<SupportTicketDocument>;
|
|
16
|
+
listAll(model: SieveModel): Promise<FirebaseSieveResult<SupportTicketDocument>>;
|
|
17
|
+
}
|
|
18
|
+
export declare const supportRepository: SupportRepository;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { firebaseFieldOps } from "../../../providers/db-firebase";
|
|
2
|
+
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
3
|
+
import { DatabaseError } from "../../../errors";
|
|
4
|
+
import { SUPPORT_TICKET_COLLECTION, ACTIVE_TICKET_STATUSES, } from "../schemas/firestore";
|
|
5
|
+
export class SupportRepository extends BaseRepository {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(SUPPORT_TICKET_COLLECTION);
|
|
8
|
+
}
|
|
9
|
+
async createTicket(input) {
|
|
10
|
+
try {
|
|
11
|
+
const now = new Date();
|
|
12
|
+
const data = {
|
|
13
|
+
...input,
|
|
14
|
+
status: "open",
|
|
15
|
+
priority: "normal",
|
|
16
|
+
messages: [],
|
|
17
|
+
createdAt: now,
|
|
18
|
+
updatedAt: now,
|
|
19
|
+
};
|
|
20
|
+
return this.create(data);
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
throw new DatabaseError("Failed to create support ticket", err);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async getUserTickets(userId, page = 1, pageSize = 20) {
|
|
27
|
+
const model = {
|
|
28
|
+
filters: `userId==${userId}`,
|
|
29
|
+
sorts: "-createdAt",
|
|
30
|
+
page,
|
|
31
|
+
pageSize,
|
|
32
|
+
};
|
|
33
|
+
return this.sieveQuery(model, SupportRepository.SIEVE_FIELDS, { defaultPageSize: pageSize, maxPageSize: 50 });
|
|
34
|
+
}
|
|
35
|
+
async getTicketById(ticketId) {
|
|
36
|
+
return this.findById(ticketId);
|
|
37
|
+
}
|
|
38
|
+
async countActiveTickets(userId) {
|
|
39
|
+
const col = this.db.collection(SUPPORT_TICKET_COLLECTION);
|
|
40
|
+
const snaps = await Promise.all(ACTIVE_TICKET_STATUSES.map((s) => col
|
|
41
|
+
.where("userId", "==", userId)
|
|
42
|
+
.where("status", "==", s)
|
|
43
|
+
.select()
|
|
44
|
+
.get()));
|
|
45
|
+
return snaps.reduce((total, snap) => total + snap.size, 0);
|
|
46
|
+
}
|
|
47
|
+
async getActiveOrderTicket(userId, orderId) {
|
|
48
|
+
const col = this.db.collection(SUPPORT_TICKET_COLLECTION);
|
|
49
|
+
const snap = await col
|
|
50
|
+
.where("userId", "==", userId)
|
|
51
|
+
.where("orderId", "==", orderId)
|
|
52
|
+
.where("status", "in", ACTIVE_TICKET_STATUSES)
|
|
53
|
+
.limit(1)
|
|
54
|
+
.get();
|
|
55
|
+
if (snap.empty)
|
|
56
|
+
return null;
|
|
57
|
+
const doc = snap.docs[0];
|
|
58
|
+
return { id: doc.id, ...doc.data() };
|
|
59
|
+
}
|
|
60
|
+
async getActiveCategoryTicket(userId, category) {
|
|
61
|
+
const col = this.db.collection(SUPPORT_TICKET_COLLECTION);
|
|
62
|
+
const snap = await col
|
|
63
|
+
.where("userId", "==", userId)
|
|
64
|
+
.where("category", "==", category)
|
|
65
|
+
.where("status", "==", "waiting_on_user")
|
|
66
|
+
.limit(1)
|
|
67
|
+
.get();
|
|
68
|
+
if (snap.empty)
|
|
69
|
+
return null;
|
|
70
|
+
const doc = snap.docs[0];
|
|
71
|
+
return { id: doc.id, ...doc.data() };
|
|
72
|
+
}
|
|
73
|
+
async updateTicketStatus(ticketId, update) {
|
|
74
|
+
return this.update(ticketId, {
|
|
75
|
+
...update,
|
|
76
|
+
updatedAt: new Date(),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
async addMessage(ticketId, message, newStatus) {
|
|
80
|
+
try {
|
|
81
|
+
const ref = this.db
|
|
82
|
+
.collection(SUPPORT_TICKET_COLLECTION)
|
|
83
|
+
.doc(ticketId);
|
|
84
|
+
const updateData = {
|
|
85
|
+
messages: firebaseFieldOps.arrayUnion(prepareForFirestore(message)),
|
|
86
|
+
updatedAt: new Date(),
|
|
87
|
+
};
|
|
88
|
+
if (newStatus)
|
|
89
|
+
updateData.status = newStatus;
|
|
90
|
+
await ref.update(updateData);
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
throw new DatabaseError("Failed to add message to ticket", err);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async assignTicket(ticketId, assignedTo, assignedToName) {
|
|
97
|
+
return this.update(ticketId, {
|
|
98
|
+
assignedTo,
|
|
99
|
+
assignedToName,
|
|
100
|
+
updatedAt: new Date(),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
async listAll(model) {
|
|
104
|
+
return this.sieveQuery(model, SupportRepository.SIEVE_FIELDS, { defaultPageSize: 25, maxPageSize: 100 });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
SupportRepository.SIEVE_FIELDS = {
|
|
108
|
+
userId: { canFilter: true, canSort: false },
|
|
109
|
+
status: { canFilter: true, canSort: false },
|
|
110
|
+
category: { canFilter: true, canSort: false },
|
|
111
|
+
priority: { canFilter: true, canSort: false },
|
|
112
|
+
assignedTo: { canFilter: true, canSort: false },
|
|
113
|
+
orderId: { canFilter: true, canSort: false },
|
|
114
|
+
createdAt: { canFilter: true, canSort: true },
|
|
115
|
+
updatedAt: { canFilter: true, canSort: true },
|
|
116
|
+
};
|
|
117
|
+
export const supportRepository = new SupportRepository();
|
package/dist/index.d.ts
CHANGED
|
@@ -526,6 +526,9 @@ export { siteSettingsRepository } from "./repositories/index";
|
|
|
526
526
|
export { smsCounterRepository } from "./repositories/index";
|
|
527
527
|
export { storeRepository } from "./repositories/index";
|
|
528
528
|
export { scammerRepository } from "./repositories/index";
|
|
529
|
+
export { supportRepository, SupportRepository } from "./repositories/index";
|
|
530
|
+
export type { SupportTicketDocument, SupportTicketCreateInput, SupportTicketUpdateInput, TicketMessage, TicketCategory, TicketStatus, TicketPriority, } from "./repositories/index";
|
|
531
|
+
export { ELIGIBLE_ORDER_STATUSES_FOR_TICKET } from "./features/support/schemas/firestore";
|
|
529
532
|
export { productFeaturesRepository } from "./repositories/index";
|
|
530
533
|
export type { ProductFeatureListFilter } from "./repositories/index";
|
|
531
534
|
export { loadProductFeaturesForStore } from "./repositories/index";
|
|
@@ -1134,6 +1137,10 @@ export { DEFAULT_SITE_SETTINGS_DATA } from "./features/admin/index";
|
|
|
1134
1137
|
export { DEFAULT_TRUST_BAR_ITEMS } from "./features/admin/index";
|
|
1135
1138
|
export { DashboardStatsGrid } from "./features/admin/index";
|
|
1136
1139
|
export { DemoSeedView } from "./features/admin/index";
|
|
1140
|
+
export { AdminTeamView } from "./features/admin/index";
|
|
1141
|
+
export { AdminEmployeeEditorView } from "./features/admin/index";
|
|
1142
|
+
export { AdminSupportTicketsView } from "./features/admin/index";
|
|
1143
|
+
export { AdminSupportTicketDetailView } from "./features/admin/index";
|
|
1137
1144
|
export { DrawerFormFooter } from "./features/admin/index";
|
|
1138
1145
|
export { FEATURE_FLAG_META } from "./features/admin/index";
|
|
1139
1146
|
export { NOTIFICATIONS_COLLECTION } from "./features/admin/index";
|
|
@@ -1208,6 +1215,10 @@ export type { ChatRoomDocument } from "./features/admin/index";
|
|
|
1208
1215
|
export type { ChatRoomUpdateInput } from "./features/admin/index";
|
|
1209
1216
|
export type { DashboardStats } from "./features/admin/index";
|
|
1210
1217
|
export type { DemoSeedViewProps } from "./features/admin/index";
|
|
1218
|
+
export type { AdminTeamViewProps } from "./features/admin/index";
|
|
1219
|
+
export type { AdminEmployeeEditorViewProps } from "./features/admin/index";
|
|
1220
|
+
export type { AdminSupportTicketsViewProps } from "./features/admin/index";
|
|
1221
|
+
export type { AdminSupportTicketDetailViewProps } from "./features/admin/index";
|
|
1211
1222
|
export type { DrawerFormFooterProps } from "./features/admin/index";
|
|
1212
1223
|
export type { FeatureFlagKey } from "./features/admin/index";
|
|
1213
1224
|
export type { FeatureFlagMeta } from "./features/admin/index";
|
|
@@ -2973,6 +2984,7 @@ export { formatFileSize } from "./utils/number.formatter";
|
|
|
2973
2984
|
export { formatMonthYear } from "./utils/date.formatter";
|
|
2974
2985
|
export { generateMediaFilename } from "./utils/id-generators";
|
|
2975
2986
|
export type { MediaFilenameContext } from "./utils/id-generators";
|
|
2987
|
+
export { resolveMediaUrl } from "./utils/media-url";
|
|
2976
2988
|
export { generateFAQId } from "./utils/id-generators";
|
|
2977
2989
|
export type { GenerateFAQIdInput } from "./utils/id-generators";
|
|
2978
2990
|
export { generateCroppedImageFilename } from "./utils/id-generators";
|
package/dist/index.js
CHANGED
|
@@ -1082,6 +1082,9 @@ export { storeRepository } from "./repositories/index";
|
|
|
1082
1082
|
// [DB]-Database layer — uses firebase-admin or another server-side DB SDK; can only run in a trusted server environment.
|
|
1083
1083
|
// scammerRepository - Shared export for scammer profiles repository.
|
|
1084
1084
|
export { scammerRepository } from "./repositories/index";
|
|
1085
|
+
// supportRepository - Server-only repository for support tickets (BAN1).
|
|
1086
|
+
export { supportRepository, SupportRepository } from "./repositories/index";
|
|
1087
|
+
export { ELIGIBLE_ORDER_STATUSES_FOR_TICKET } from "./features/support/schemas/firestore";
|
|
1085
1088
|
// SB-UNI-B — sublistingCategoriesRepository + SublistingCategoryDocument deleted.
|
|
1086
1089
|
// Use categoriesRepository.findBySlugAndType(slug, "sublisting") and CategoryDocument with categoryType:"sublisting".
|
|
1087
1090
|
// [DB]-Database layer — uses firebase-admin; server-only.
|
|
@@ -2234,6 +2237,14 @@ export { DashboardStatsGrid } from "./features/admin/index";
|
|
|
2234
2237
|
// [CLIENT-SSR]-Runs in both SSR and browser — React component or hook that does not depend on browser-only APIs.
|
|
2235
2238
|
// DemoSeedView - Component for demo seed view.
|
|
2236
2239
|
export { DemoSeedView } from "./features/admin/index";
|
|
2240
|
+
// AdminTeamView - Employee management list view.
|
|
2241
|
+
export { AdminTeamView } from "./features/admin/index";
|
|
2242
|
+
// AdminEmployeeEditorView - Invite/edit employee permissions SideDrawer.
|
|
2243
|
+
export { AdminEmployeeEditorView } from "./features/admin/index";
|
|
2244
|
+
// AdminSupportTicketsView - Admin support ticket list view.
|
|
2245
|
+
export { AdminSupportTicketsView } from "./features/admin/index";
|
|
2246
|
+
// AdminSupportTicketDetailView - Admin support ticket detail/reply SideDrawer.
|
|
2247
|
+
export { AdminSupportTicketDetailView } from "./features/admin/index";
|
|
2237
2248
|
// [CLIENT-SSR]-Runs in both SSR and browser — React component or hook that does not depend on browser-only APIs.
|
|
2238
2249
|
// DrawerFormFooter - Shared export for drawer form footer.
|
|
2239
2250
|
export { DrawerFormFooter } from "./features/admin/index";
|
|
@@ -5433,6 +5444,7 @@ export { buildSieveFilters } from "./utils/filter.helper";
|
|
|
5433
5444
|
export { formatFileSize } from "./utils/number.formatter";
|
|
5434
5445
|
export { formatMonthYear } from "./utils/date.formatter";
|
|
5435
5446
|
export { generateMediaFilename } from "./utils/id-generators"; // generateProductImageFilename already exported from "./utils/id-generators";
|
|
5447
|
+
export { resolveMediaUrl } from "./utils/media-url";
|
|
5436
5448
|
export { generateFAQId } from "./utils/id-generators";
|
|
5437
5449
|
export { generateCroppedImageFilename } from "./utils/id-generators";
|
|
5438
5450
|
// Messages / Conversations feature schemas
|
|
@@ -48,6 +48,14 @@ interface RouteHandlerOptions<TInput = unknown, TParams = Record<string, string>
|
|
|
48
48
|
* Implies `auth: true`.
|
|
49
49
|
*/
|
|
50
50
|
roles?: string[];
|
|
51
|
+
/**
|
|
52
|
+
* Fine-grained permission required for this route.
|
|
53
|
+
* When set: also allows `"employee"` role through the role check, then
|
|
54
|
+
* verifies the permission via the registered `rbac` provider.
|
|
55
|
+
* Admin role always bypasses. Requires `rbac` provider to be registered.
|
|
56
|
+
* Implies `auth: true`.
|
|
57
|
+
*/
|
|
58
|
+
permission?: string;
|
|
51
59
|
/** Zod schema to validate + parse the JSON request body. */
|
|
52
60
|
schema?: ParseableSchema<TInput>;
|
|
53
61
|
/**
|
|
@@ -68,7 +68,9 @@ export function createRouteHandler(options) {
|
|
|
68
68
|
try {
|
|
69
69
|
// -- Auth --------------------------------------------------------------
|
|
70
70
|
let user;
|
|
71
|
-
const needsAuth = options.auth ||
|
|
71
|
+
const needsAuth = options.auth ||
|
|
72
|
+
(options.roles && options.roles.length > 0) ||
|
|
73
|
+
!!options.permission;
|
|
72
74
|
if (needsAuth) {
|
|
73
75
|
user = await verifySession(request);
|
|
74
76
|
}
|
|
@@ -80,8 +82,27 @@ export function createRouteHandler(options) {
|
|
|
80
82
|
// No valid session — continue as anonymous
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
// Role check — when `permission` is set, also allow "employee" through
|
|
86
|
+
const effectiveRoles = options.roles
|
|
87
|
+
? options.permission
|
|
88
|
+
? [...new Set([...options.roles, "employee"])]
|
|
89
|
+
: options.roles
|
|
90
|
+
: options.permission
|
|
91
|
+
? ["admin", "employee"]
|
|
92
|
+
: [];
|
|
93
|
+
if (effectiveRoles.length > 0) {
|
|
94
|
+
if (!user || !effectiveRoles.includes(user.role ?? "")) {
|
|
95
|
+
return NextResponse.json({ success: false, error: "Insufficient permissions" }, { status: 403 });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// -- Permission check (employee fine-grained) --------------------------
|
|
99
|
+
if (options.permission && user && user.role !== "admin") {
|
|
100
|
+
const { rbac } = getProviders();
|
|
101
|
+
if (!rbac) {
|
|
102
|
+
return NextResponse.json({ success: false, error: "RBAC provider not configured" }, { status: 503 });
|
|
103
|
+
}
|
|
104
|
+
const perms = await rbac.getPermissions(user.uid);
|
|
105
|
+
if (!perms.includes(options.permission)) {
|
|
85
106
|
return NextResponse.json({ success: false, error: "Insufficient permissions" }, { status: 403 });
|
|
86
107
|
}
|
|
87
108
|
}
|
|
@@ -230,6 +230,7 @@ export declare const DEFAULT_ROUTE_MAP: {
|
|
|
230
230
|
readonly BUNDLES_EDIT: (id: string) => string;
|
|
231
231
|
readonly PRIZE_DRAWS: "/admin/prize-draws";
|
|
232
232
|
readonly PRIZE_DRAWS_EDIT: (id: string) => string;
|
|
233
|
+
readonly TEAM: "/admin/team";
|
|
233
234
|
};
|
|
234
235
|
readonly DEMO: {
|
|
235
236
|
readonly SEED: "/demo/seed";
|
|
@@ -459,6 +460,7 @@ export declare const ROUTES: {
|
|
|
459
460
|
readonly BUNDLES_EDIT: (id: string) => string;
|
|
460
461
|
readonly PRIZE_DRAWS: "/admin/prize-draws";
|
|
461
462
|
readonly PRIZE_DRAWS_EDIT: (id: string) => string;
|
|
463
|
+
readonly TEAM: "/admin/team";
|
|
462
464
|
};
|
|
463
465
|
readonly DEMO: {
|
|
464
466
|
readonly SEED: "/demo/seed";
|
|
@@ -221,6 +221,7 @@ export const DEFAULT_ROUTE_MAP = {
|
|
|
221
221
|
BUNDLES_EDIT: (id) => `/admin/bundles/${id}/edit`,
|
|
222
222
|
PRIZE_DRAWS: "/admin/prize-draws",
|
|
223
223
|
PRIZE_DRAWS_EDIT: (id) => `/admin/prize-draws/${id}/edit`,
|
|
224
|
+
TEAM: "/admin/team",
|
|
224
225
|
},
|
|
225
226
|
DEMO: {
|
|
226
227
|
SEED: "/demo/seed",
|
|
@@ -37,6 +37,8 @@ export type { NewsletterSubscriberDocument, NewsletterSubscriberCreateInput, New
|
|
|
37
37
|
export { CopilotLogRepository, copilotLogRepository } from "../core";
|
|
38
38
|
export type { CopilotFeedback, CopilotLogDocument, CopilotLogCreateInput, } from "../core";
|
|
39
39
|
export { ScammerRepository, scammerRepository, } from "../features/scams/repository/scammer.repository";
|
|
40
|
+
export { SupportRepository, supportRepository, } from "../features/support/repository/support.repository";
|
|
41
|
+
export type { SupportTicketDocument, SupportTicketCreateInput, SupportTicketUpdateInput, TicketMessage, TicketCategory, TicketStatus, TicketPriority, } from "../features/support/schemas/firestore";
|
|
40
42
|
export { ProductTemplateRepository, productTemplateRepository, } from "../features/products/repository/product-templates.repository";
|
|
41
43
|
export type { ProductTemplateDocument, ProductTemplateCreateInput, ProductTemplateUpdateInput, } from "../features/products/schemas/product-templates";
|
|
42
44
|
export { ProductFeaturesRepository, productFeaturesRepository, } from "../features/products/repository/product-features.repository";
|
|
@@ -43,6 +43,7 @@ export { EventEntryRepository, EventEntriesRepository, eventEntryRepository, } f
|
|
|
43
43
|
export { NewsletterRepository, newsletterRepository, } from "../core/newsletter.repository";
|
|
44
44
|
export { CopilotLogRepository, copilotLogRepository } from "../core";
|
|
45
45
|
export { ScammerRepository, scammerRepository, } from "../features/scams/repository/scammer.repository";
|
|
46
|
+
export { SupportRepository, supportRepository, } from "../features/support/repository/support.repository";
|
|
46
47
|
// SB-UNI-B — SublistingCategoriesRepository deleted; sublistings now live on
|
|
47
48
|
// categoriesRepository with categoryType:"sublisting".
|
|
48
49
|
export { ProductTemplateRepository, productTemplateRepository, } from "../features/products/repository/product-templates.repository";
|
|
@@ -36,3 +36,5 @@ export declare const OFFER_PII_FIELDS: readonly ["buyerName", "buyerEmail", "sel
|
|
|
36
36
|
export declare const CHAT_PII_FIELDS: readonly ["buyerName", "sellerName"];
|
|
37
37
|
/** PII fields in event entries */
|
|
38
38
|
export declare const EVENT_ENTRY_PII_FIELDS: readonly ["userDisplayName", "userEmail", "ipAddress"];
|
|
39
|
+
/** Store OAuth bearer tokens — encrypted via SETTINGS_ENCRYPTION_KEY */
|
|
40
|
+
export declare const STORE_SECRET_FIELDS: readonly ["whatsappConfig.accessToken"];
|
|
@@ -59,6 +59,7 @@ export const storesSeedData = [
|
|
|
59
59
|
totalReviews: 0,
|
|
60
60
|
averageRating: 0,
|
|
61
61
|
},
|
|
62
|
+
capabilities: ["host_auctions", "host_preorders", "suggest_brands", "create_coupons", "bulk_listing_import", "verified_seller", "featured_placement", "promotional_banner", "advanced_analytics", "api_access", "multiple_stores", "early_access_features", "whatsapp_catalog_sync"],
|
|
62
63
|
createdAt: daysAgo(400),
|
|
63
64
|
updatedAt: daysAgo(1),
|
|
64
65
|
},
|
|
@@ -119,6 +120,7 @@ export const storesSeedData = [
|
|
|
119
120
|
totalReviews: 42,
|
|
120
121
|
averageRating: 4.8,
|
|
121
122
|
},
|
|
123
|
+
capabilities: ["host_auctions", "host_preorders", "suggest_brands", "create_coupons", "verified_seller", "featured_placement", "extended_return_window"],
|
|
122
124
|
createdAt: daysAgo(380),
|
|
123
125
|
updatedAt: daysAgo(2),
|
|
124
126
|
},
|
|
@@ -148,6 +150,7 @@ export const storesSeedData = [
|
|
|
148
150
|
totalReviews: 31,
|
|
149
151
|
averageRating: 4.6,
|
|
150
152
|
},
|
|
153
|
+
capabilities: ["suggest_brands", "create_coupons", "verified_seller", "extended_return_window"],
|
|
151
154
|
createdAt: daysAgo(350),
|
|
152
155
|
updatedAt: daysAgo(3),
|
|
153
156
|
},
|
|
@@ -178,6 +181,7 @@ export const storesSeedData = [
|
|
|
178
181
|
totalReviews: 58,
|
|
179
182
|
averageRating: 4.9,
|
|
180
183
|
},
|
|
184
|
+
capabilities: ["suggest_brands", "create_coupons"],
|
|
181
185
|
createdAt: daysAgo(320),
|
|
182
186
|
updatedAt: daysAgo(1),
|
|
183
187
|
},
|
|
@@ -207,6 +211,7 @@ export const storesSeedData = [
|
|
|
207
211
|
totalReviews: 47,
|
|
208
212
|
averageRating: 4.7,
|
|
209
213
|
},
|
|
214
|
+
capabilities: ["host_auctions", "suggest_brands", "create_coupons", "extended_return_window"],
|
|
210
215
|
createdAt: daysAgo(290),
|
|
211
216
|
updatedAt: daysAgo(2),
|
|
212
217
|
},
|
|
@@ -237,6 +242,7 @@ export const storesSeedData = [
|
|
|
237
242
|
totalReviews: 28,
|
|
238
243
|
averageRating: 4.8,
|
|
239
244
|
},
|
|
245
|
+
capabilities: ["suggest_brands", "create_coupons", "whatsapp_catalog_sync"],
|
|
240
246
|
createdAt: daysAgo(260),
|
|
241
247
|
updatedAt: daysAgo(1),
|
|
242
248
|
},
|
|
@@ -267,6 +273,7 @@ export const storesSeedData = [
|
|
|
267
273
|
totalReviews: 22,
|
|
268
274
|
averageRating: 4.7,
|
|
269
275
|
},
|
|
276
|
+
capabilities: ["host_preorders", "suggest_brands", "create_coupons", "extended_return_window"],
|
|
270
277
|
createdAt: daysAgo(230),
|
|
271
278
|
updatedAt: daysAgo(2),
|
|
272
279
|
},
|
|
@@ -296,6 +303,7 @@ export const storesSeedData = [
|
|
|
296
303
|
totalReviews: 19,
|
|
297
304
|
averageRating: 4.9,
|
|
298
305
|
},
|
|
306
|
+
capabilities: ["suggest_brands", "create_coupons", "featured_placement"],
|
|
299
307
|
createdAt: daysAgo(200),
|
|
300
308
|
updatedAt: daysAgo(3),
|
|
301
309
|
},
|
package/dist/server.d.ts
CHANGED
|
@@ -477,3 +477,9 @@ export { applyMediaContextGuards, CONTEXT_LIMITS, } from "./_internal/server/fea
|
|
|
477
477
|
export type { GuardResult, GuardError, GuardSuccess } from "./_internal/server/features/media/contextGuards";
|
|
478
478
|
export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, } from "./_internal/shared/media/limits";
|
|
479
479
|
export type { MediaKind, AllowedImageMime, AllowedVideoMime, AllowedDocMime, AllowedMime, } from "./_internal/shared/media/limits";
|
|
480
|
+
export { getServerPermissions, checkPermission, checkAnyPermission, makeAdminSectionLayout, } from "./_internal/server/features/auth/permissions";
|
|
481
|
+
export type { ResolvedPermissions, AdminSectionLayoutOpts, } from "./_internal/server/features/auth/permissions";
|
|
482
|
+
export { getStoreCapabilities, storeHasCapability, } from "./_internal/server/features/auth/capabilities";
|
|
483
|
+
export { isSoftBanned, getBanSummary } from "./features/auth/server/checkSoftBan";
|
|
484
|
+
export type { UserSoftBan } from "./features/auth/schemas/firestore";
|
|
485
|
+
export type { BannedAction } from "./features/auth/permissions/constants";
|
package/dist/server.js
CHANGED
|
@@ -1350,3 +1350,9 @@ export { LISTING_TYPE_REGISTRY, pluginFor } from "./_internal/shared/listing-typ
|
|
|
1350
1350
|
export { applyMediaContextGuards, CONTEXT_LIMITS, } from "./_internal/server/features/media/contextGuards";
|
|
1351
1351
|
// Media upload limits — shared by /api/media/sign + /api/media/finalize + /api/media/upload.
|
|
1352
1352
|
export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, } from "./_internal/shared/media/limits";
|
|
1353
|
+
// ── RBAC server helpers ───────────────────────────────────────────────────────
|
|
1354
|
+
// getServerPermissions, checkPermission, makeAdminSectionLayout
|
|
1355
|
+
export { getServerPermissions, checkPermission, checkAnyPermission, makeAdminSectionLayout, } from "./_internal/server/features/auth/permissions";
|
|
1356
|
+
export { getStoreCapabilities, storeHasCapability, } from "./_internal/server/features/auth/capabilities";
|
|
1357
|
+
// ── Soft ban helpers ──────────────────────────────────────────────────────────
|
|
1358
|
+
export { isSoftBanned, getBanSummary } from "./features/auth/server/checkSoftBan";
|