@astralibx/staff-engine 0.2.0
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/README.md +147 -0
- package/dist/index.cjs +1087 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +262 -0
- package/dist/index.d.ts +262 -0
- package/dist/index.mjs +1049 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +65 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { RequestHandler, Request, Response, Router } from 'express';
|
|
2
|
+
import { Document, Types, Connection, Model } from 'mongoose';
|
|
3
|
+
import { IStaff, IPermissionGroup, LogAdapter, IPermissionGroupCreateInput, IPermissionGroupUpdateInput, StaffAdapters, StaffHooks, IStaffCreateInput, IStaffListFilters, IPaginatedResult, IStaffUpdateInput, StaffEngineConfig } from '@astralibx/staff-types';
|
|
4
|
+
export { DEFAULT_OPTIONS, ResolvedOptions, StaffEngineConfig } from '@astralibx/staff-types';
|
|
5
|
+
import { AlxError } from '@astralibx/core';
|
|
6
|
+
export { sendSuccess } from '@astralibx/core';
|
|
7
|
+
|
|
8
|
+
interface IStaffDocument extends Omit<IStaff, '_id'>, Document {
|
|
9
|
+
_id: Types.ObjectId;
|
|
10
|
+
}
|
|
11
|
+
declare function createStaffModel(connection: Connection, prefix?: string): Model<IStaffDocument>;
|
|
12
|
+
|
|
13
|
+
interface IPermissionGroupDocument extends Omit<IPermissionGroup, '_id'>, Document {
|
|
14
|
+
_id: Types.ObjectId;
|
|
15
|
+
}
|
|
16
|
+
declare function createPermissionGroupModel(connection: Connection, prefix?: string): Model<IPermissionGroupDocument>;
|
|
17
|
+
|
|
18
|
+
declare class PermissionCacheService {
|
|
19
|
+
private StaffModel;
|
|
20
|
+
private ttlMs;
|
|
21
|
+
private redis;
|
|
22
|
+
private keyPrefix;
|
|
23
|
+
private logger;
|
|
24
|
+
private tenantId?;
|
|
25
|
+
private memoryCache;
|
|
26
|
+
constructor(StaffModel: Model<IStaffDocument>, ttlMs: number, redis: unknown | null, keyPrefix: string, logger: LogAdapter, tenantId?: string | undefined);
|
|
27
|
+
get(staffId: string): Promise<string[]>;
|
|
28
|
+
invalidate(staffId: string): Promise<void>;
|
|
29
|
+
invalidateAll(): Promise<void>;
|
|
30
|
+
private getMemory;
|
|
31
|
+
private getRedis;
|
|
32
|
+
private fetchFromDb;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
declare class PermissionService {
|
|
36
|
+
private PermissionGroup;
|
|
37
|
+
private permissionCache;
|
|
38
|
+
private logger;
|
|
39
|
+
private tenantId?;
|
|
40
|
+
constructor(PermissionGroup: Model<IPermissionGroupDocument>, permissionCache: PermissionCacheService, logger: LogAdapter, tenantId?: string | undefined);
|
|
41
|
+
private get tenantFilter();
|
|
42
|
+
listGroups(): Promise<IPermissionGroupDocument[]>;
|
|
43
|
+
createGroup(data: IPermissionGroupCreateInput): Promise<IPermissionGroupDocument>;
|
|
44
|
+
updateGroup(groupId: string, data: IPermissionGroupUpdateInput): Promise<IPermissionGroupDocument>;
|
|
45
|
+
deleteGroup(groupId: string): Promise<void>;
|
|
46
|
+
getAllPermissionKeys(): Promise<string[]>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
declare class RateLimiterService {
|
|
50
|
+
private windowMs;
|
|
51
|
+
private maxAttempts;
|
|
52
|
+
private redis;
|
|
53
|
+
private keyPrefix;
|
|
54
|
+
private logger;
|
|
55
|
+
private memoryStore;
|
|
56
|
+
constructor(windowMs: number, maxAttempts: number, redis: unknown | null, keyPrefix: string, logger: LogAdapter);
|
|
57
|
+
checkLimit(key: string): Promise<{
|
|
58
|
+
allowed: boolean;
|
|
59
|
+
remaining: number;
|
|
60
|
+
retryAfterMs?: number;
|
|
61
|
+
}>;
|
|
62
|
+
recordAttempt(key: string): Promise<void>;
|
|
63
|
+
reset(key: string): Promise<void>;
|
|
64
|
+
private checkLimitMemory;
|
|
65
|
+
private recordAttemptMemory;
|
|
66
|
+
private checkLimitRedis;
|
|
67
|
+
private recordAttemptRedis;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface StaffServiceDeps {
|
|
71
|
+
Staff: Model<IStaffDocument>;
|
|
72
|
+
PermissionGroup: Model<IPermissionGroupDocument>;
|
|
73
|
+
adapters: StaffAdapters;
|
|
74
|
+
hooks: StaffHooks;
|
|
75
|
+
permissionCache: PermissionCacheService;
|
|
76
|
+
rateLimiter: RateLimiterService;
|
|
77
|
+
logger: LogAdapter;
|
|
78
|
+
tenantId?: string;
|
|
79
|
+
jwtSecret: string;
|
|
80
|
+
staffTokenExpiry: string;
|
|
81
|
+
ownerTokenExpiry: string;
|
|
82
|
+
requireEmailUniqueness: boolean;
|
|
83
|
+
allowSelfPasswordChange: boolean;
|
|
84
|
+
}
|
|
85
|
+
declare class StaffService {
|
|
86
|
+
private Staff;
|
|
87
|
+
private PermissionGroup;
|
|
88
|
+
private adapters;
|
|
89
|
+
private hooks;
|
|
90
|
+
private permissionCache;
|
|
91
|
+
private rateLimiter;
|
|
92
|
+
private logger;
|
|
93
|
+
private tenantId?;
|
|
94
|
+
private jwtSecret;
|
|
95
|
+
private staffTokenExpiry;
|
|
96
|
+
private ownerTokenExpiry;
|
|
97
|
+
private requireEmailUniqueness;
|
|
98
|
+
private allowSelfPasswordChange;
|
|
99
|
+
constructor(deps: StaffServiceDeps);
|
|
100
|
+
private get tenantFilter();
|
|
101
|
+
private generateToken;
|
|
102
|
+
setupOwner(data: {
|
|
103
|
+
name: string;
|
|
104
|
+
email: string;
|
|
105
|
+
password: string;
|
|
106
|
+
}): Promise<{
|
|
107
|
+
staff: IStaffDocument;
|
|
108
|
+
token: string;
|
|
109
|
+
}>;
|
|
110
|
+
login(email: string, password: string, ip?: string): Promise<{
|
|
111
|
+
staff: IStaffDocument;
|
|
112
|
+
token: string;
|
|
113
|
+
}>;
|
|
114
|
+
create(data: IStaffCreateInput): Promise<IStaffDocument>;
|
|
115
|
+
list(filters?: IStaffListFilters): Promise<IPaginatedResult<IStaffDocument>>;
|
|
116
|
+
getById(staffId: string): Promise<IStaffDocument>;
|
|
117
|
+
update(staffId: string, data: IStaffUpdateInput): Promise<IStaffDocument>;
|
|
118
|
+
updatePermissions(staffId: string, permissions: string[]): Promise<IStaffDocument>;
|
|
119
|
+
updateStatus(staffId: string, status: string): Promise<IStaffDocument>;
|
|
120
|
+
resetPassword(staffId: string, newPassword: string): Promise<void>;
|
|
121
|
+
changeOwnPassword(staffId: string, oldPassword: string, newPassword: string): Promise<void>;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
interface StaffUser {
|
|
125
|
+
staffId: string;
|
|
126
|
+
role: string;
|
|
127
|
+
permissions: string[];
|
|
128
|
+
}
|
|
129
|
+
interface AuthenticatedRequest extends Request {
|
|
130
|
+
user: StaffUser;
|
|
131
|
+
}
|
|
132
|
+
interface AuthMiddleware {
|
|
133
|
+
verifyToken: RequestHandler;
|
|
134
|
+
resolveStaff: (token: string) => Promise<StaffUser | null>;
|
|
135
|
+
requirePermission: (...keys: string[]) => RequestHandler;
|
|
136
|
+
ownerOnly: RequestHandler;
|
|
137
|
+
requireRole: (...roles: string[]) => RequestHandler;
|
|
138
|
+
}
|
|
139
|
+
declare function createAuthMiddleware(jwtSecret: string, permissionCache: PermissionCacheService, StaffModel: Model<IStaffDocument>, logger: LogAdapter, tenantId?: string): AuthMiddleware;
|
|
140
|
+
|
|
141
|
+
declare const ERROR_CODE: {
|
|
142
|
+
readonly InvalidCredentials: "STAFF_INVALID_CREDENTIALS";
|
|
143
|
+
readonly AccountInactive: "STAFF_ACCOUNT_INACTIVE";
|
|
144
|
+
readonly AccountPending: "STAFF_ACCOUNT_PENDING";
|
|
145
|
+
readonly RateLimited: "STAFF_RATE_LIMITED";
|
|
146
|
+
readonly TokenExpired: "STAFF_TOKEN_EXPIRED";
|
|
147
|
+
readonly TokenInvalid: "STAFF_TOKEN_INVALID";
|
|
148
|
+
readonly InsufficientPermissions: "STAFF_INSUFFICIENT_PERMISSIONS";
|
|
149
|
+
readonly OwnerOnly: "STAFF_OWNER_ONLY";
|
|
150
|
+
readonly StaffNotFound: "STAFF_NOT_FOUND";
|
|
151
|
+
readonly EmailExists: "STAFF_EMAIL_EXISTS";
|
|
152
|
+
readonly SetupAlreadyComplete: "STAFF_SETUP_ALREADY_COMPLETE";
|
|
153
|
+
readonly LastOwnerGuard: "STAFF_LAST_OWNER_GUARD";
|
|
154
|
+
readonly InvalidPermissions: "STAFF_INVALID_PERMISSIONS";
|
|
155
|
+
readonly GroupNotFound: "STAFF_GROUP_NOT_FOUND";
|
|
156
|
+
readonly GroupIdExists: "STAFF_GROUP_ID_EXISTS";
|
|
157
|
+
readonly InvalidConfig: "STAFF_INVALID_CONFIG";
|
|
158
|
+
};
|
|
159
|
+
type ErrorCode = (typeof ERROR_CODE)[keyof typeof ERROR_CODE];
|
|
160
|
+
declare const ERROR_MESSAGE: {
|
|
161
|
+
readonly InvalidCredentials: "Invalid email or password";
|
|
162
|
+
readonly AccountInactive: "Account is deactivated";
|
|
163
|
+
readonly AccountPending: "Account is pending activation";
|
|
164
|
+
readonly RateLimited: "Too many login attempts. Please try again later.";
|
|
165
|
+
readonly TokenExpired: "Token has expired";
|
|
166
|
+
readonly TokenInvalid: "Invalid token";
|
|
167
|
+
readonly InsufficientPermissions: "Insufficient permissions";
|
|
168
|
+
readonly OwnerOnly: "This action requires owner privileges";
|
|
169
|
+
readonly StaffNotFound: "Staff member not found";
|
|
170
|
+
readonly EmailExists: "A staff member with this email already exists";
|
|
171
|
+
readonly SetupAlreadyComplete: "Initial setup has already been completed";
|
|
172
|
+
readonly LastOwnerGuard: "Cannot deactivate the last active owner";
|
|
173
|
+
readonly InvalidPermissions: "Edit permissions require corresponding view permissions";
|
|
174
|
+
readonly GroupNotFound: "Permission group not found";
|
|
175
|
+
readonly GroupIdExists: "A permission group with this ID already exists";
|
|
176
|
+
readonly InvalidConfig: "Invalid engine configuration";
|
|
177
|
+
};
|
|
178
|
+
declare const DEFAULTS: {
|
|
179
|
+
readonly ListPageSize: 20;
|
|
180
|
+
readonly MaxListPageSize: 100;
|
|
181
|
+
readonly PermissionCacheTtlMs: number;
|
|
182
|
+
};
|
|
183
|
+
declare const DEFAULT_AUTH: {
|
|
184
|
+
readonly staffTokenExpiry: "24h";
|
|
185
|
+
readonly ownerTokenExpiry: "30d";
|
|
186
|
+
readonly permissionCacheTtlMs: number;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
declare class AlxStaffError extends AlxError {
|
|
190
|
+
readonly context?: Record<string, unknown> | undefined;
|
|
191
|
+
constructor(message: string, code: string, context?: Record<string, unknown> | undefined);
|
|
192
|
+
}
|
|
193
|
+
declare class AuthenticationError extends AlxStaffError {
|
|
194
|
+
constructor(code?: string, message?: string);
|
|
195
|
+
}
|
|
196
|
+
declare class AuthorizationError extends AlxStaffError {
|
|
197
|
+
constructor(code?: string, message?: string);
|
|
198
|
+
}
|
|
199
|
+
declare class RateLimitError extends AlxStaffError {
|
|
200
|
+
readonly retryAfterMs: number;
|
|
201
|
+
constructor(retryAfterMs: number);
|
|
202
|
+
}
|
|
203
|
+
declare class TokenError extends AlxStaffError {
|
|
204
|
+
constructor(code?: string, message?: string);
|
|
205
|
+
}
|
|
206
|
+
declare class StaffNotFoundError extends AlxStaffError {
|
|
207
|
+
readonly staffId: string;
|
|
208
|
+
constructor(staffId: string);
|
|
209
|
+
}
|
|
210
|
+
declare class DuplicateError extends AlxStaffError {
|
|
211
|
+
constructor(code: string, message: string, context?: Record<string, unknown>);
|
|
212
|
+
}
|
|
213
|
+
declare class SetupError extends AlxStaffError {
|
|
214
|
+
constructor();
|
|
215
|
+
}
|
|
216
|
+
declare class LastOwnerError extends AlxStaffError {
|
|
217
|
+
readonly staffId: string;
|
|
218
|
+
constructor(staffId: string);
|
|
219
|
+
}
|
|
220
|
+
declare class InvalidPermissionError extends AlxStaffError {
|
|
221
|
+
readonly missingViewKeys: string[];
|
|
222
|
+
constructor(missingViewKeys: string[]);
|
|
223
|
+
}
|
|
224
|
+
declare class GroupNotFoundError extends AlxStaffError {
|
|
225
|
+
readonly groupId: string;
|
|
226
|
+
constructor(groupId: string);
|
|
227
|
+
}
|
|
228
|
+
declare class InvalidConfigError extends AlxStaffError {
|
|
229
|
+
readonly field: string;
|
|
230
|
+
readonly reason: string;
|
|
231
|
+
constructor(field: string, reason: string);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Validates that for every edit-type permission, the corresponding view-type permission
|
|
236
|
+
* is also present. Single-level cascade only.
|
|
237
|
+
* Example: 'chat:edit' requires 'chat:view'
|
|
238
|
+
*/
|
|
239
|
+
declare function validatePermissionPairs(permissions: string[], allGroups: IPermissionGroupDocument[]): void;
|
|
240
|
+
|
|
241
|
+
declare function handleStaffError(res: Response, error: unknown, logger: LogAdapter): void;
|
|
242
|
+
|
|
243
|
+
interface RouteServices {
|
|
244
|
+
staff: StaffService;
|
|
245
|
+
permissions: PermissionService;
|
|
246
|
+
}
|
|
247
|
+
declare function createRoutes(services: RouteServices, auth: AuthMiddleware, logger: LogAdapter, allowSelfPasswordChange: boolean): Router;
|
|
248
|
+
|
|
249
|
+
interface StaffEngine {
|
|
250
|
+
routes: Router;
|
|
251
|
+
auth: AuthMiddleware;
|
|
252
|
+
staff: StaffService;
|
|
253
|
+
permissions: PermissionService;
|
|
254
|
+
models: {
|
|
255
|
+
Staff: Model<IStaffDocument>;
|
|
256
|
+
PermissionGroup: Model<IPermissionGroupDocument>;
|
|
257
|
+
};
|
|
258
|
+
destroy: () => Promise<void>;
|
|
259
|
+
}
|
|
260
|
+
declare function createStaffEngine(config: StaffEngineConfig): StaffEngine;
|
|
261
|
+
|
|
262
|
+
export { AlxStaffError, type AuthMiddleware, type AuthenticatedRequest, AuthenticationError, AuthorizationError, DEFAULTS, DEFAULT_AUTH, DuplicateError, ERROR_CODE, ERROR_MESSAGE, type ErrorCode, GroupNotFoundError, type IPermissionGroupDocument, type IStaffDocument, InvalidConfigError, InvalidPermissionError, LastOwnerError, PermissionCacheService, PermissionService, RateLimitError, RateLimiterService, SetupError, type StaffEngine, StaffNotFoundError, StaffService, type StaffUser, TokenError, createAuthMiddleware, createPermissionGroupModel, createRoutes, createStaffEngine, createStaffModel, handleStaffError, validatePermissionPairs };
|