webspresso 0.0.98 → 0.0.100
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/ADMIN.md +11 -1
- package/.agents/AGENTS.md +2 -2
- package/.agents/AUTH.md +415 -0
- package/.agents/CLI.md +47 -0
- package/.agents/ERRORS.md +59 -0
- package/.agents/ORM.md +178 -4
- package/.agents/PLUGINS.md +113 -8
- package/.agents/QUEUE.md +92 -0
- package/.agents/ROUTING.md +160 -7
- package/.agents/STREAMING.md +35 -0
- package/AGENTS.md +2 -0
- package/README.md +145 -93
- package/bin/commands/add-docker.js +467 -0
- package/bin/commands/deploy-check.js +339 -0
- package/bin/webspresso.js +4 -0
- package/core/auth/index.js +123 -1
- package/core/auth/magic-link.js +170 -0
- package/core/auth/manager.js +477 -3
- package/core/auth/middleware.js +118 -5
- package/core/auth/oauth.js +517 -0
- package/core/auth/qrcode.js +499 -0
- package/core/auth/session-manager.js +255 -0
- package/core/auth/sessions.js +351 -0
- package/core/auth/tokens.js +1 -0
- package/core/auth/totp.js +507 -0
- package/core/auth/user-agent.js +161 -0
- package/core/orm/eager-loader.js +144 -10
- package/core/orm/index.js +10 -1
- package/core/orm/model.js +55 -1
- package/core/orm/query-builder.js +90 -4
- package/core/orm/repository.js +327 -16
- package/core/orm/scopes.js +3 -2
- package/core/orm/search.js +213 -0
- package/core/orm/types.js +6 -1
- package/core/queue/cron.js +544 -0
- package/core/queue/index.js +8 -1
- package/core/queue/manager.js +100 -1
- package/core/queue/scheduler.js +415 -0
- package/core/sse/index.js +19 -0
- package/core/sse/stream.js +430 -0
- package/core/ssr/stream.js +62 -2
- package/index.d.ts +794 -6
- package/index.js +48 -0
- package/package.json +7 -5
- package/plugins/admin-panel/client/parts/05-rich-text-file-helpers.js +323 -31
- package/plugins/admin-panel/client/parts/08-record-list.js +5 -0
- package/plugins/admin-panel/client/verify-spa-parts.js +2 -0
- package/plugins/admin-panel/field-renderers/file-upload.js +187 -104
- package/plugins/admin-panel/field-renderers/index.js +2 -0
- package/plugins/admin-panel/modules/admin-users.js +1 -1
- package/plugins/api-key/crypto.js +101 -0
- package/plugins/api-key/index.js +288 -0
- package/plugins/api-key/middleware.js +461 -0
- package/plugins/api-key/migration-template.js +48 -0
- package/plugins/api-key/model.js +50 -0
- package/plugins/api-key/scopes.js +125 -0
- package/plugins/audit-log/admin-component.js +487 -70
- package/plugins/audit-log/api-handlers.js +114 -0
- package/plugins/audit-log/index.js +24 -0
- package/plugins/content/client/inline-edit.js +2 -46
- package/plugins/cors.js +13 -2
- package/plugins/email/auth-bridge.js +66 -0
- package/plugins/email/bundled-templates.js +15 -0
- package/plugins/email/templates/auth-magic-link.mjml +13 -0
- package/plugins/file-manager/admin-component.js +69 -53
- package/plugins/file-manager/index.js +25 -8
- package/plugins/index.js +8 -0
- package/plugins/oauth/index.js +338 -0
- package/plugins/queue/index.js +4 -0
- package/plugins/webhooks/admin-component.js +608 -0
- package/plugins/webhooks/api-handlers.js +237 -0
- package/plugins/webhooks/crypto.js +144 -0
- package/plugins/webhooks/dispatcher.js +708 -0
- package/plugins/webhooks/index.js +263 -0
- package/plugins/webhooks/matcher.js +113 -0
- package/plugins/webhooks/migration-template.js +75 -0
- package/plugins/webhooks/models.js +98 -0
- package/plugins/webhooks/sender.js +176 -0
- package/src/api/api-loader.js +1 -0
- package/src/bundle/index.js +2 -1
- package/src/discovery/file-route-parser.js +44 -1
- package/src/discovery/scan-routes.js +2 -2
- package/src/file-router.js +310 -104
- package/src/helpers.js +16 -1
- package/src/njk-frontmatter.js +3 -0
- package/src/routing/cascading.js +490 -0
- package/src/routing/middleware-resolver.js +92 -0
- package/src/server.js +6 -0
package/index.d.ts
CHANGED
|
@@ -541,8 +541,70 @@ export function mountPages(
|
|
|
541
541
|
registerDynamicFileRoutes: () => void;
|
|
542
542
|
};
|
|
543
543
|
|
|
544
|
+
export interface CascadingLayoutInfo {
|
|
545
|
+
layoutRelPath: string;
|
|
546
|
+
fullPath: string;
|
|
547
|
+
configPath: string | null;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
export interface CascadingErrorBoundaryInfo {
|
|
551
|
+
dirSegment: string;
|
|
552
|
+
errorRelPath: string;
|
|
553
|
+
fullPath: string;
|
|
554
|
+
configPath: string | null;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
export interface CascadingLoadingInfo {
|
|
558
|
+
dirSegment: string;
|
|
559
|
+
loadingRelPath: string;
|
|
560
|
+
fullPath: string;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
export namespace cascading {
|
|
564
|
+
export function getDirectoryChain(relativeFilePath: string): string[];
|
|
565
|
+
export function templateHasExtends(templateBody?: string | null, fullPath?: string): boolean;
|
|
566
|
+
export function findCascadingMiddlewareFiles(baseDir: string, relativeFilePath: string, isDev?: boolean): string[];
|
|
567
|
+
export function loadCascadingMiddlewares(
|
|
568
|
+
baseDir: string,
|
|
569
|
+
relativeFilePath: string,
|
|
570
|
+
middlewareRegistry?: Record<string, unknown>,
|
|
571
|
+
isDev?: boolean
|
|
572
|
+
): Array<(req: any, res: any, next: (err?: any) => void) => void>;
|
|
573
|
+
export function runMiddleware(mw: Function, req: any, res: any): Promise<void>;
|
|
574
|
+
export function runMiddlewareChain(chain: Function[], req: any, res: any): Promise<void>;
|
|
575
|
+
export function findCascadingLayouts(baseDir: string, relativeFilePath: string, isDev?: boolean): CascadingLayoutInfo[];
|
|
576
|
+
export function executeCascadingLayoutLoads(layouts: CascadingLayoutInfo[], req: any, ctx: any, isDev?: boolean): Promise<void>;
|
|
577
|
+
export function applyCascadingLayouts(
|
|
578
|
+
nunjucksEnv: any,
|
|
579
|
+
layouts: CascadingLayoutInfo[],
|
|
580
|
+
pageHtml: string,
|
|
581
|
+
renderContext: Record<string, unknown>,
|
|
582
|
+
isDev?: boolean
|
|
583
|
+
): string;
|
|
584
|
+
export function findNearestErrorBoundary(
|
|
585
|
+
baseDir: string,
|
|
586
|
+
relativeFilePath: string,
|
|
587
|
+
isDev?: boolean
|
|
588
|
+
): CascadingErrorBoundaryInfo | null;
|
|
589
|
+
export function findNearestLoadingTemplate(
|
|
590
|
+
baseDir: string,
|
|
591
|
+
relativeFilePath: string,
|
|
592
|
+
isDev?: boolean
|
|
593
|
+
): CascadingLoadingInfo | null;
|
|
594
|
+
export function clearCascadingCaches(): void;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
export function resolveMiddlewares(
|
|
598
|
+
middlewareConfig: unknown[] | null | undefined,
|
|
599
|
+
middlewareRegistry?: Record<string, unknown>
|
|
600
|
+
): Array<(req: any, res: any, next: (err?: any) => void) => void>;
|
|
601
|
+
|
|
544
602
|
export function filePathToRoute(filePath: string, pagesDir: string): string;
|
|
545
603
|
|
|
604
|
+
export function isRouteGroupSegment(segment: string): boolean;
|
|
605
|
+
|
|
606
|
+
export function stripRouteGroups(route: string): string;
|
|
607
|
+
|
|
546
608
|
export function extractMethodFromFilename(filename: string): {
|
|
547
609
|
method: string;
|
|
548
610
|
baseName: string;
|
|
@@ -706,11 +768,20 @@ export interface ScopeContext {
|
|
|
706
768
|
|
|
707
769
|
export type RelationType = 'belongsTo' | 'hasMany' | 'hasOne';
|
|
708
770
|
|
|
771
|
+
export type RelationCascadeOption =
|
|
772
|
+
| boolean
|
|
773
|
+
| 'delete'
|
|
774
|
+
| 'restore'
|
|
775
|
+
| 'all'
|
|
776
|
+
| ('delete' | 'restore' | 'all')[]
|
|
777
|
+
| { delete?: boolean; restore?: boolean };
|
|
778
|
+
|
|
709
779
|
export interface RelationDefinition {
|
|
710
780
|
type: RelationType;
|
|
711
|
-
model: () => ModelDefinition;
|
|
781
|
+
model: string | (() => ModelDefinition<any> | string);
|
|
712
782
|
foreignKey: string;
|
|
713
783
|
localKey?: string;
|
|
784
|
+
cascade?: RelationCascadeOption;
|
|
714
785
|
}
|
|
715
786
|
|
|
716
787
|
export interface AdminPermissions {
|
|
@@ -864,6 +935,7 @@ export interface ModelOptions<TSchema extends ZodObject<any> = ZodObject<Record<
|
|
|
864
935
|
maxIncludes?: number;
|
|
865
936
|
maxFilterConditions?: number;
|
|
866
937
|
};
|
|
938
|
+
searchable?: string[];
|
|
867
939
|
cache?: boolean | 'auto' | 'smart' | { strategy: 'auto' | 'smart' };
|
|
868
940
|
}
|
|
869
941
|
|
|
@@ -898,6 +970,7 @@ export interface ModelDefinition<TModel = Record<string, any>> {
|
|
|
898
970
|
maxIncludes?: number;
|
|
899
971
|
maxFilterConditions?: number;
|
|
900
972
|
};
|
|
973
|
+
searchable?: string[];
|
|
901
974
|
hooks: Record<string, unknown>;
|
|
902
975
|
cache?: boolean | 'auto' | 'smart' | { strategy: 'auto' | 'smart' };
|
|
903
976
|
}
|
|
@@ -914,6 +987,8 @@ export function hasModel(name: string): boolean;
|
|
|
914
987
|
|
|
915
988
|
export function clearRegistry(): void;
|
|
916
989
|
|
|
990
|
+
export function isCascadeEnabled(relation: RelationDefinition, operation: 'delete' | 'restore'): boolean;
|
|
991
|
+
|
|
917
992
|
// --- ORM: schema & columns ---
|
|
918
993
|
|
|
919
994
|
/** zdb column helpers (id, string, timestamp, …) + `schema()` */
|
|
@@ -934,7 +1009,10 @@ export function getColumnMeta(schema: ZodTypeAny): unknown;
|
|
|
934
1009
|
|
|
935
1010
|
export interface FindOptions {
|
|
936
1011
|
with?: string[];
|
|
1012
|
+
include?: string[];
|
|
937
1013
|
select?: string[];
|
|
1014
|
+
limit?: number;
|
|
1015
|
+
offset?: number;
|
|
938
1016
|
}
|
|
939
1017
|
|
|
940
1018
|
export interface PaginatedResult<T = Record<string, any>> {
|
|
@@ -945,6 +1023,16 @@ export interface PaginatedResult<T = Record<string, any>> {
|
|
|
945
1023
|
totalPages: number;
|
|
946
1024
|
}
|
|
947
1025
|
|
|
1026
|
+
export interface SearchOptions {
|
|
1027
|
+
mode?: 'auto' | 'fts' | 'like';
|
|
1028
|
+
language?: string;
|
|
1029
|
+
matchAllWords?: boolean;
|
|
1030
|
+
exact?: boolean;
|
|
1031
|
+
rank?: boolean;
|
|
1032
|
+
ftsTable?: string;
|
|
1033
|
+
boolean?: 'and' | 'or';
|
|
1034
|
+
}
|
|
1035
|
+
|
|
948
1036
|
export interface QueryBuilder<T = Record<string, any>> {
|
|
949
1037
|
where(column: keyof T | string, value: unknown): this;
|
|
950
1038
|
where(column: keyof T | string, operator: string, value: unknown): this;
|
|
@@ -954,14 +1042,19 @@ export interface QueryBuilder<T = Record<string, any>> {
|
|
|
954
1042
|
whereBetween(column: keyof T | string, range: [unknown, unknown]): this;
|
|
955
1043
|
select(...columns: (keyof T | string)[]): this;
|
|
956
1044
|
orderBy(column: keyof T | string, direction?: 'asc' | 'desc'): this;
|
|
1045
|
+
orderByRaw(sql: string, bindings?: unknown[]): this;
|
|
1046
|
+
search(term: string, columns?: string | string[], options?: SearchOptions): this;
|
|
1047
|
+
orSearch(term: string, columns?: string | string[], options?: SearchOptions): this;
|
|
957
1048
|
limit(n: number): this;
|
|
958
1049
|
offset(n: number): this;
|
|
959
1050
|
with(...relations: string[]): this;
|
|
1051
|
+
include(...relations: string[]): this;
|
|
960
1052
|
withTrashed(): this;
|
|
961
1053
|
onlyTrashed(): this;
|
|
962
1054
|
first(): Promise<T | null>;
|
|
963
1055
|
list(): Promise<T[]>;
|
|
964
1056
|
get(): Promise<T[]>;
|
|
1057
|
+
find(): Promise<T[]>;
|
|
965
1058
|
count(): Promise<number>;
|
|
966
1059
|
paginate(page?: number, perPage?: number): Promise<PaginatedResult<T>>;
|
|
967
1060
|
exists(): Promise<boolean>;
|
|
@@ -976,18 +1069,21 @@ export interface Repository<T = Record<string, any>> {
|
|
|
976
1069
|
findById(id: string | number, options?: FindOptions): Promise<T | null>;
|
|
977
1070
|
findOne(conditions: Partial<T> | Record<string, unknown>, options?: FindOptions): Promise<T | null>;
|
|
978
1071
|
findAll(options?: FindOptions): Promise<T[]>;
|
|
1072
|
+
find(conditions?: Partial<T> | Record<string, unknown>, options?: FindOptions): Promise<T[]>;
|
|
979
1073
|
create(data: Partial<T> | Record<string, unknown>): Promise<T>;
|
|
980
1074
|
createMany(data: (Partial<T> | Record<string, unknown>)[]): Promise<T[]>;
|
|
981
1075
|
update(id: string | number, data: Partial<T> | Record<string, unknown>): Promise<T | null>;
|
|
982
1076
|
updateWhere(conditions: Partial<T> | Record<string, unknown>, data: Partial<T> | Record<string, unknown>): Promise<number>;
|
|
983
|
-
delete(id: string | number): Promise<boolean>;
|
|
984
|
-
forceDelete(id: string | number): Promise<boolean>;
|
|
985
|
-
restore(id: string | number): Promise<T | null>;
|
|
1077
|
+
delete(id: string | number, options?: { cascade?: boolean; deletedAt?: Date }): Promise<boolean>;
|
|
1078
|
+
forceDelete(id: string | number, options?: { cascade?: boolean }): Promise<boolean>;
|
|
1079
|
+
restore(id: string | number, options?: { cascade?: boolean; allChildren?: boolean }): Promise<T | null>;
|
|
986
1080
|
query(): QueryBuilder<T>;
|
|
987
1081
|
raw(sql: string, bindings?: unknown[]): Promise<unknown>;
|
|
988
1082
|
count(conditions?: Partial<T> | Record<string, unknown>): Promise<number>;
|
|
989
1083
|
exists(conditions: Partial<T> | Record<string, unknown>): Promise<boolean>;
|
|
990
1084
|
with(...relations: string[]): QueryBuilder<T>;
|
|
1085
|
+
include(...relations: string[]): QueryBuilder<T>;
|
|
1086
|
+
search(term: string, columns?: string | string[], options?: SearchOptions): QueryBuilder<T>;
|
|
991
1087
|
model: ModelDefinition<T>;
|
|
992
1088
|
}
|
|
993
1089
|
|
|
@@ -1208,6 +1304,24 @@ export function validateQueryComplexity(
|
|
|
1208
1304
|
includes: string[];
|
|
1209
1305
|
};
|
|
1210
1306
|
|
|
1307
|
+
export function buildSearchClause(params: {
|
|
1308
|
+
knex: any;
|
|
1309
|
+
model: any;
|
|
1310
|
+
term: string;
|
|
1311
|
+
columns?: string | string[];
|
|
1312
|
+
options?: SearchOptions;
|
|
1313
|
+
}): {
|
|
1314
|
+
raw: true;
|
|
1315
|
+
sql: string;
|
|
1316
|
+
bindings: any[];
|
|
1317
|
+
boolean: 'and' | 'or';
|
|
1318
|
+
rankClause: { sql: string; bindings: any[] } | null;
|
|
1319
|
+
} | null;
|
|
1320
|
+
|
|
1321
|
+
export function tokenizeSearchTerm(term: string): string[];
|
|
1322
|
+
|
|
1323
|
+
export function detectDialect(knex: any): 'sqlite' | 'postgres' | 'mysql' | 'generic';
|
|
1324
|
+
|
|
1211
1325
|
export interface SchemaExplorerPluginOptions {
|
|
1212
1326
|
/** Raw JSON API endpoint path (default: '/_schema') */
|
|
1213
1327
|
path?: string;
|
|
@@ -1374,6 +1488,7 @@ export interface AuditLogPluginOptions {
|
|
|
1374
1488
|
tableName?: string;
|
|
1375
1489
|
includeDefaultPage?: boolean;
|
|
1376
1490
|
apiPrefix?: string;
|
|
1491
|
+
registerRecordAction?: boolean;
|
|
1377
1492
|
retentionDays?: number;
|
|
1378
1493
|
cleanupIntervalMs?: number;
|
|
1379
1494
|
autoCleanup?: boolean;
|
|
@@ -1391,6 +1506,8 @@ export function auditLogPlugin(options?: AuditLogPluginOptions): WebspressoPlugi
|
|
|
1391
1506
|
stopCleanup(): void;
|
|
1392
1507
|
getRetentionConfig(): AuditLogRetentionConfig;
|
|
1393
1508
|
queryLogs(filters?: Record<string, unknown>): Promise<any[]>;
|
|
1509
|
+
queryTimeline(model: string, recordId: string | number): Promise<any[]>;
|
|
1510
|
+
getRecordTimeline(model: string, recordId: string | number): Promise<any[]>;
|
|
1394
1511
|
getMigrationTemplate(tableName?: string): string;
|
|
1395
1512
|
};
|
|
1396
1513
|
dispose?(): void;
|
|
@@ -1632,6 +1749,162 @@ export interface BasicAuthPluginOptions {
|
|
|
1632
1749
|
|
|
1633
1750
|
export function basicAuthPlugin(options?: BasicAuthPluginOptions): WebspressoPlugin;
|
|
1634
1751
|
|
|
1752
|
+
export interface ApiKeyRecord {
|
|
1753
|
+
id?: number | string;
|
|
1754
|
+
name: string;
|
|
1755
|
+
key_hash: string;
|
|
1756
|
+
key_hint: string;
|
|
1757
|
+
prefix?: string;
|
|
1758
|
+
user_id?: number | string | null;
|
|
1759
|
+
scopes?: string[] | string | null;
|
|
1760
|
+
rate_limit?: number | null;
|
|
1761
|
+
metadata?: any;
|
|
1762
|
+
expires_at?: Date | string | null;
|
|
1763
|
+
last_used_at?: Date | string | null;
|
|
1764
|
+
revoked_at?: Date | string | null;
|
|
1765
|
+
created_at?: Date | string;
|
|
1766
|
+
updated_at?: Date | string;
|
|
1767
|
+
user?: any;
|
|
1768
|
+
[key: string]: any;
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
export interface ApiKeyPluginOptions {
|
|
1772
|
+
/** Database instance or Knex instance. */
|
|
1773
|
+
db?: any;
|
|
1774
|
+
/** Knex instance. */
|
|
1775
|
+
knex?: any;
|
|
1776
|
+
/** Table name for API keys. Default 'api_keys'. */
|
|
1777
|
+
tableName?: string;
|
|
1778
|
+
/** Default key prefix. Default 'wsp_live_'. */
|
|
1779
|
+
prefix?: string;
|
|
1780
|
+
/** Random byte length for key generation. Default 24. */
|
|
1781
|
+
byteLength?: number;
|
|
1782
|
+
/** Protect all matching routes globally. Default false. */
|
|
1783
|
+
global?: boolean;
|
|
1784
|
+
/** Route prefixes to protect when global: true. Default true (all routes). */
|
|
1785
|
+
routes?: string | string[] | boolean;
|
|
1786
|
+
/** Path prefixes to bypass authentication. */
|
|
1787
|
+
skipPaths?: string[];
|
|
1788
|
+
/** Custom predicate function to skip authentication. */
|
|
1789
|
+
skip?: (req: Request) => boolean;
|
|
1790
|
+
/** Static in-memory keys for testing, serverless, or microservices. */
|
|
1791
|
+
keys?: Array<{ key?: string; key_hash?: string; name?: string; scopes?: string[] | string; [key: string]: any }> | Record<string, any>;
|
|
1792
|
+
/** Automatically update last_used_at timestamp. Default true. */
|
|
1793
|
+
trackUsage?: boolean;
|
|
1794
|
+
/** Throttle interval in milliseconds for last_used_at updates. Default 60000. */
|
|
1795
|
+
updateLastUsedThrottleMs?: number;
|
|
1796
|
+
/** Allow query parameter extraction (?apiKey=...). Default false. */
|
|
1797
|
+
allowQueryParam?: boolean;
|
|
1798
|
+
/** Query parameter name. Default 'apiKey'. */
|
|
1799
|
+
queryParamName?: string;
|
|
1800
|
+
/** Custom key extraction function. */
|
|
1801
|
+
extractKey?: (req: Request) => string | null;
|
|
1802
|
+
/** Custom key lookup by SHA-256 hash. */
|
|
1803
|
+
findKeyByHash?: (hash: string, req?: Request) => Promise<ApiKeyRecord | null> | ApiKeyRecord | null;
|
|
1804
|
+
/** Custom verification hook. */
|
|
1805
|
+
verify?: (rawKey: string, req?: Request) => Promise<ApiKeyRecord | null> | ApiKeyRecord | null;
|
|
1806
|
+
/** Custom user loader for key. */
|
|
1807
|
+
getUser?: (record: ApiKeyRecord, req?: Request) => Promise<any> | any;
|
|
1808
|
+
/** Callback hook on successful authentication. */
|
|
1809
|
+
onAuthenticated?: (req: Request, record: ApiKeyRecord) => void | Promise<void>;
|
|
1810
|
+
/** Custom 401 Unauthorized response handler, string, or object. */
|
|
1811
|
+
unauthorizedResponse?: ((req: Request, res: Response, info?: any) => unknown) | string | Record<string, unknown>;
|
|
1812
|
+
/** Custom 403 Forbidden response handler, string, or object. */
|
|
1813
|
+
forbiddenResponse?: ((req: Request, res: Response, info?: any) => unknown) | string | Record<string, unknown>;
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
export interface ApiKeyRouteOptions {
|
|
1817
|
+
/** Required scope(s) for this route. Supports wildcards ('*', 'reports:*'). */
|
|
1818
|
+
scopes?: string | string[];
|
|
1819
|
+
/** Whether all required scopes must be present (true) or any scope (false). Default true. */
|
|
1820
|
+
requireAllScopes?: boolean;
|
|
1821
|
+
/** If true, allows requests without API key to pass through without error. Default false. */
|
|
1822
|
+
optional?: boolean;
|
|
1823
|
+
/** Custom 401 Unauthorized response. */
|
|
1824
|
+
unauthorizedResponse?: ((req: Request, res: Response, info?: any) => unknown) | string | Record<string, unknown>;
|
|
1825
|
+
/** Custom 403 Forbidden response. */
|
|
1826
|
+
forbiddenResponse?: ((req: Request, res: Response, info?: any) => unknown) | string | Record<string, unknown>;
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
export function apiKeyPlugin(options?: ApiKeyPluginOptions): WebspressoPlugin;
|
|
1830
|
+
export function createApiKeyMiddleware(options?: ApiKeyPluginOptions & ApiKeyRouteOptions): RequestHandler;
|
|
1831
|
+
|
|
1832
|
+
export interface WebhookEndpoint {
|
|
1833
|
+
id?: number | string;
|
|
1834
|
+
name: string;
|
|
1835
|
+
url: string;
|
|
1836
|
+
secret?: string;
|
|
1837
|
+
events?: string[] | string;
|
|
1838
|
+
is_active?: boolean;
|
|
1839
|
+
description?: string | null;
|
|
1840
|
+
headers?: Record<string, string> | string | null;
|
|
1841
|
+
metadata?: Record<string, any> | string | null;
|
|
1842
|
+
created_at?: Date | string;
|
|
1843
|
+
updated_at?: Date | string;
|
|
1844
|
+
[key: string]: any;
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
export interface WebhookDelivery {
|
|
1848
|
+
id?: number | string;
|
|
1849
|
+
endpoint_id: number | string;
|
|
1850
|
+
event: string;
|
|
1851
|
+
payload: string;
|
|
1852
|
+
status_code?: number | null;
|
|
1853
|
+
success: boolean;
|
|
1854
|
+
duration_ms?: number | null;
|
|
1855
|
+
error?: string | null;
|
|
1856
|
+
response_body?: string | null;
|
|
1857
|
+
attempt?: number;
|
|
1858
|
+
max_attempts?: number;
|
|
1859
|
+
next_retry_at?: Date | string | null;
|
|
1860
|
+
created_at?: Date | string;
|
|
1861
|
+
updated_at?: Date | string;
|
|
1862
|
+
[key: string]: any;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
export interface WebhooksPluginOptions {
|
|
1866
|
+
db?: any;
|
|
1867
|
+
knex?: any;
|
|
1868
|
+
endpointsTable?: string;
|
|
1869
|
+
deliveriesTable?: string;
|
|
1870
|
+
maxAttempts?: number;
|
|
1871
|
+
backoffBaseMs?: number;
|
|
1872
|
+
timeoutMs?: number;
|
|
1873
|
+
wrapPayload?: boolean;
|
|
1874
|
+
queue?: any;
|
|
1875
|
+
endpoints?: WebhookEndpoint[];
|
|
1876
|
+
apiPrefix?: string;
|
|
1877
|
+
includeDefaultPage?: boolean;
|
|
1878
|
+
pageTitle?: string;
|
|
1879
|
+
menuOrder?: number;
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
export interface WebhookDispatchResult {
|
|
1883
|
+
dispatched: number;
|
|
1884
|
+
deliveries: WebhookDelivery[];
|
|
1885
|
+
results?: Array<{ success?: boolean; statusCode?: number; error?: string; [key: string]: any }>;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
export interface WebhookDispatcherInstance {
|
|
1889
|
+
dispatch(event: string, payload: any, options?: { wait?: boolean; endpointId?: number | string }): Promise<WebhookDispatchResult>;
|
|
1890
|
+
pingEndpoint(endpointId: number | string): Promise<any>;
|
|
1891
|
+
retryDelivery(deliveryId: number | string): Promise<any>;
|
|
1892
|
+
createEndpoint(data: Partial<WebhookEndpoint>): Promise<WebhookEndpoint>;
|
|
1893
|
+
getEndpoint(id: number | string): Promise<WebhookEndpoint | null>;
|
|
1894
|
+
updateEndpoint(id: number | string, data: Partial<WebhookEndpoint>): Promise<WebhookEndpoint | null>;
|
|
1895
|
+
deleteEndpoint(id: number | string): Promise<boolean>;
|
|
1896
|
+
listEndpoints(filters?: { active?: boolean; search?: string; limit?: number; offset?: number }): Promise<WebhookEndpoint[]>;
|
|
1897
|
+
listDeliveries(filters?: { endpointId?: number | string; event?: string; success?: boolean | string; page?: number; limit?: number }): Promise<{ items: WebhookDelivery[]; total: number; page: number; limit: number; pages: number }>;
|
|
1898
|
+
getDelivery(id: number | string): Promise<WebhookDelivery | null>;
|
|
1899
|
+
purgeDeliveries(options?: { days?: number }): Promise<number>;
|
|
1900
|
+
dispose(): void;
|
|
1901
|
+
on(event: string, listener: (...args: any[]) => void): this;
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
export function webhooksPlugin(options?: WebhooksPluginOptions): WebspressoPlugin & {
|
|
1905
|
+
dispatcher: WebhookDispatcherInstance;
|
|
1906
|
+
};
|
|
1907
|
+
|
|
1635
1908
|
export interface ContentPluginOptions {
|
|
1636
1909
|
db: import('./index').Database;
|
|
1637
1910
|
adminPath?: string;
|
|
@@ -2117,6 +2390,82 @@ export interface QueueRetentionConfig {
|
|
|
2117
2390
|
runOnStart?: boolean;
|
|
2118
2391
|
}
|
|
2119
2392
|
|
|
2393
|
+
export interface ScheduleOptions {
|
|
2394
|
+
id?: string;
|
|
2395
|
+
overlap?: boolean;
|
|
2396
|
+
preventOverlap?: boolean;
|
|
2397
|
+
maxRuns?: number;
|
|
2398
|
+
utc?: boolean;
|
|
2399
|
+
timezone?: string;
|
|
2400
|
+
tz?: string;
|
|
2401
|
+
runImmediately?: boolean;
|
|
2402
|
+
delay?: number | Date;
|
|
2403
|
+
attempts?: number;
|
|
2404
|
+
priority?: number;
|
|
2405
|
+
timeout?: number;
|
|
2406
|
+
backoff?: number | { type?: 'exponential' | 'fixed'; delay?: number };
|
|
2407
|
+
[key: string]: unknown;
|
|
2408
|
+
}
|
|
2409
|
+
|
|
2410
|
+
export interface ScheduleInfo {
|
|
2411
|
+
id: string;
|
|
2412
|
+
name: string;
|
|
2413
|
+
cronExpression: string;
|
|
2414
|
+
cron?: CronExpression;
|
|
2415
|
+
data: unknown;
|
|
2416
|
+
options: ScheduleOptions;
|
|
2417
|
+
runsCount: number;
|
|
2418
|
+
lastRunAt: Date | null;
|
|
2419
|
+
nextRunAt: Date | null;
|
|
2420
|
+
isRunning: boolean;
|
|
2421
|
+
activeJobId: string | number | null;
|
|
2422
|
+
activeJobIds?: (string | number)[];
|
|
2423
|
+
registeredAt: Date;
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
export class CronExpression {
|
|
2427
|
+
rawExpression: string;
|
|
2428
|
+
resolvedExpression: string;
|
|
2429
|
+
utc: boolean;
|
|
2430
|
+
timezone: string | null;
|
|
2431
|
+
minutes: Set<number>;
|
|
2432
|
+
hours: Set<number>;
|
|
2433
|
+
doms: Set<number>;
|
|
2434
|
+
months: Set<number>;
|
|
2435
|
+
dows: Set<number>;
|
|
2436
|
+
constructor(expression: string, options?: { utc?: boolean; timezone?: string; tz?: string });
|
|
2437
|
+
match(date?: Date | string | number): boolean;
|
|
2438
|
+
getNextDate(fromDate?: Date | string | number): Date | null;
|
|
2439
|
+
getNextDates(count?: number, fromDate?: Date | string | number): Date[];
|
|
2440
|
+
static isValid(expression: string, options?: { utc?: boolean; timezone?: string; tz?: string }): boolean;
|
|
2441
|
+
static parse(expression: string, options?: { utc?: boolean; timezone?: string; tz?: string }): CronExpression;
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
export function parseCron(expression: string, options?: { utc?: boolean; timezone?: string; tz?: string }): CronExpression;
|
|
2445
|
+
export function isValidCron(expression: string, options?: { utc?: boolean; timezone?: string; tz?: string }): boolean;
|
|
2446
|
+
export function resolveCronAlias(str: string): string;
|
|
2447
|
+
export const CRON_ALIASES: Record<string, string>;
|
|
2448
|
+
|
|
2449
|
+
export class TaskScheduler {
|
|
2450
|
+
queue: QueueManager;
|
|
2451
|
+
maxIntervalMs: number;
|
|
2452
|
+
isRunning: boolean;
|
|
2453
|
+
schedules: Map<string, any>;
|
|
2454
|
+
constructor(queue: QueueManager, options?: { autoStart?: boolean; maxIntervalMs?: number });
|
|
2455
|
+
schedule(cronExpression: string | CronExpression, jobName: string, data?: unknown, options?: ScheduleOptions): ScheduleInfo;
|
|
2456
|
+
cron(cronExpression: string | CronExpression, jobName: string, data?: unknown, options?: ScheduleOptions): ScheduleInfo;
|
|
2457
|
+
unschedule(idOrJobName: string): boolean;
|
|
2458
|
+
getSchedules(): ScheduleInfo[];
|
|
2459
|
+
getSchedule(idOrJobName: string): ScheduleInfo | null;
|
|
2460
|
+
trigger(idOrJobName: string): Promise<Job | null>;
|
|
2461
|
+
start(): this;
|
|
2462
|
+
stop(): this;
|
|
2463
|
+
pause(): this;
|
|
2464
|
+
resume(): this;
|
|
2465
|
+
clear(): void;
|
|
2466
|
+
destroy(): void;
|
|
2467
|
+
}
|
|
2468
|
+
|
|
2120
2469
|
export class QueueManager extends EventEmitter {
|
|
2121
2470
|
adapter: QueueAdapter;
|
|
2122
2471
|
concurrency: number;
|
|
@@ -2125,6 +2474,7 @@ export class QueueManager extends EventEmitter {
|
|
|
2125
2474
|
isPaused: boolean;
|
|
2126
2475
|
isDraining: boolean;
|
|
2127
2476
|
retentionConfig: QueueRetentionConfig;
|
|
2477
|
+
scheduler: TaskScheduler;
|
|
2128
2478
|
constructor(options?: {
|
|
2129
2479
|
adapter?: QueueAdapter;
|
|
2130
2480
|
concurrency?: number;
|
|
@@ -2141,6 +2491,13 @@ export class QueueManager extends EventEmitter {
|
|
|
2141
2491
|
define(name: string, handler: (job: Job, ctx: { queue: QueueManager }) => Promise<any> | any, options?: DispatchOptions): this;
|
|
2142
2492
|
has(name: string): boolean;
|
|
2143
2493
|
dispatch(name: string, data?: unknown, options?: DispatchOptions): Promise<Job>;
|
|
2494
|
+
schedule(cronExpression: string | CronExpression, name: string, data?: unknown, options?: ScheduleOptions): ScheduleInfo;
|
|
2495
|
+
cron(cronExpression: string | CronExpression, name: string, data?: unknown, options?: ScheduleOptions): ScheduleInfo;
|
|
2496
|
+
unschedule(idOrJobName: string): boolean;
|
|
2497
|
+
getSchedules(): ScheduleInfo[];
|
|
2498
|
+
getSchedule(idOrJobName: string): ScheduleInfo | null;
|
|
2499
|
+
triggerSchedule(idOrJobName: string): Promise<Job | null>;
|
|
2500
|
+
triggerCron(idOrJobName: string): Promise<Job | null>;
|
|
2144
2501
|
start(): this;
|
|
2145
2502
|
pause(): this;
|
|
2146
2503
|
resume(): this;
|
|
@@ -2202,6 +2559,10 @@ export function queuePlugin(options?: {
|
|
|
2202
2559
|
olderThan?: Date | string | number;
|
|
2203
2560
|
limit?: number;
|
|
2204
2561
|
}): Promise<number>;
|
|
2562
|
+
schedule(cronExpression: string | CronExpression, name: string, data?: unknown, options?: ScheduleOptions): ScheduleInfo | undefined;
|
|
2563
|
+
cron(cronExpression: string | CronExpression, name: string, data?: unknown, options?: ScheduleOptions): ScheduleInfo | undefined;
|
|
2564
|
+
unschedule(idOrJobName: string): boolean | undefined;
|
|
2565
|
+
getSchedules(): ScheduleInfo[];
|
|
2205
2566
|
};
|
|
2206
2567
|
};
|
|
2207
2568
|
|
|
@@ -2211,14 +2572,20 @@ export interface HtmlStreamOptions {
|
|
|
2211
2572
|
env: import('nunjucks').Environment;
|
|
2212
2573
|
templatePath: string;
|
|
2213
2574
|
context?: Record<string, unknown>;
|
|
2214
|
-
defer?: Record<string, Promise<unknown> | { promise: Promise<unknown>; template?: string }>;
|
|
2575
|
+
defer?: Record<string, Promise<unknown> | { promise: Promise<unknown>; template?: string; loadingTemplate?: string }>;
|
|
2576
|
+
nonce?: string;
|
|
2577
|
+
loadingTemplate?: string | null;
|
|
2578
|
+
layouts?: CascadingLayoutInfo[];
|
|
2215
2579
|
}
|
|
2216
2580
|
|
|
2217
2581
|
export interface RenderStreamOptions {
|
|
2218
2582
|
env?: import('nunjucks').Environment;
|
|
2219
|
-
defer?: Record<string, Promise<unknown> | { promise: Promise<unknown>; template?: string }>;
|
|
2583
|
+
defer?: Record<string, Promise<unknown> | { promise: Promise<unknown>; template?: string; loadingTemplate?: string }>;
|
|
2220
2584
|
status?: number;
|
|
2221
2585
|
flushImmediately?: boolean;
|
|
2586
|
+
nonce?: string;
|
|
2587
|
+
loadingTemplate?: string | null;
|
|
2588
|
+
layouts?: CascadingLayoutInfo[];
|
|
2222
2589
|
}
|
|
2223
2590
|
|
|
2224
2591
|
export function createHtmlStream(options: HtmlStreamOptions): import('stream').Readable;
|
|
@@ -2234,6 +2601,54 @@ export const ssr: {
|
|
|
2234
2601
|
renderStream: typeof renderStream;
|
|
2235
2602
|
};
|
|
2236
2603
|
|
|
2604
|
+
// --- Server-Sent Events (SSE) Streaming ---
|
|
2605
|
+
|
|
2606
|
+
export interface SseOptions {
|
|
2607
|
+
status?: number;
|
|
2608
|
+
headers?: Record<string, string>;
|
|
2609
|
+
heartbeat?: number | boolean;
|
|
2610
|
+
heartbeatComment?: string;
|
|
2611
|
+
retry?: number;
|
|
2612
|
+
map?: (chunk: any) => any;
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
export interface SseEventObject {
|
|
2616
|
+
data?: any;
|
|
2617
|
+
event?: string;
|
|
2618
|
+
id?: string | number;
|
|
2619
|
+
retry?: number;
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2622
|
+
export class ServerSentEventsStream extends EventEmitter {
|
|
2623
|
+
req: import('http').IncomingMessage;
|
|
2624
|
+
res: import('http').ServerResponse;
|
|
2625
|
+
options: SseOptions;
|
|
2626
|
+
isOpen: boolean;
|
|
2627
|
+
isClosed: boolean;
|
|
2628
|
+
|
|
2629
|
+
constructor(req: import('http').IncomingMessage, res: import('http').ServerResponse, options?: SseOptions);
|
|
2630
|
+
send(payloadOrData: any | SseEventObject, event?: string, id?: string | number): Promise<boolean>;
|
|
2631
|
+
comment(text: string): boolean;
|
|
2632
|
+
heartbeat(comment?: string): boolean;
|
|
2633
|
+
retry(ms: number): boolean;
|
|
2634
|
+
pipe(source: AsyncIterable<any> | Iterable<any> | import('stream').Readable, mapFn?: (chunk: any) => any): Promise<void>;
|
|
2635
|
+
stream(generatorFn: (stream: ServerSentEventsStream) => any): Promise<void>;
|
|
2636
|
+
close(): void;
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
export function createSseStream(
|
|
2640
|
+
req: import('http').IncomingMessage,
|
|
2641
|
+
res: import('http').ServerResponse,
|
|
2642
|
+
sourceOrOptions?: SseOptions | AsyncIterable<any> | Iterable<any> | import('stream').Readable | ((stream: ServerSentEventsStream) => any),
|
|
2643
|
+
maybeOptions?: SseOptions
|
|
2644
|
+
): ServerSentEventsStream | Promise<void>;
|
|
2645
|
+
|
|
2646
|
+
export const sseStream: {
|
|
2647
|
+
ServerSentEventsStream: typeof ServerSentEventsStream;
|
|
2648
|
+
createSseStream: typeof createSseStream;
|
|
2649
|
+
formatSseFrame: (data?: any, event?: string, id?: string | number, retry?: number) => string;
|
|
2650
|
+
};
|
|
2651
|
+
|
|
2237
2652
|
// --- Model Context Protocol (MCP) Plugin ---
|
|
2238
2653
|
|
|
2239
2654
|
export interface McpPluginOptions {
|
|
@@ -2629,8 +3044,381 @@ export interface GenerateTypesResult {
|
|
|
2629
3044
|
|
|
2630
3045
|
export function generateTypes(options?: GenerateTypesOptions): GenerateTypesResult;
|
|
2631
3046
|
|
|
3047
|
+
// --- OAuth 2.0 & Social Login Plugin ---
|
|
2632
3048
|
|
|
3049
|
+
export class OAuthError extends Error {
|
|
3050
|
+
code: string;
|
|
3051
|
+
details?: any;
|
|
3052
|
+
constructor(message: string, code?: string, details?: any);
|
|
3053
|
+
}
|
|
2633
3054
|
|
|
3055
|
+
export interface OAuthUserProfile {
|
|
3056
|
+
provider: string;
|
|
3057
|
+
id: string;
|
|
3058
|
+
email?: string | null;
|
|
3059
|
+
emailVerified?: boolean;
|
|
3060
|
+
name?: string | null;
|
|
3061
|
+
firstName?: string | null;
|
|
3062
|
+
lastName?: string | null;
|
|
3063
|
+
username?: string | null;
|
|
3064
|
+
avatarUrl?: string | null;
|
|
3065
|
+
bio?: string | null;
|
|
3066
|
+
location?: string | null;
|
|
3067
|
+
locale?: string | null;
|
|
3068
|
+
raw: Record<string, any>;
|
|
3069
|
+
[key: string]: any;
|
|
3070
|
+
}
|
|
3071
|
+
|
|
3072
|
+
export interface OAuthProviderConfig {
|
|
3073
|
+
name?: string;
|
|
3074
|
+
clientId: string;
|
|
3075
|
+
clientSecret: string;
|
|
3076
|
+
redirectUri?: string;
|
|
3077
|
+
authorizationUrl?: string;
|
|
3078
|
+
tokenUrl?: string;
|
|
3079
|
+
userInfoUrl?: string;
|
|
3080
|
+
scopes?: string[] | string;
|
|
3081
|
+
defaultScopes?: string[];
|
|
3082
|
+
params?: Record<string, any>;
|
|
3083
|
+
normalizeProfile?: (raw: any, extra?: any) => OAuthUserProfile;
|
|
3084
|
+
fetchExtra?: (tokens: any) => Promise<any>;
|
|
3085
|
+
}
|
|
3086
|
+
|
|
3087
|
+
export interface OAuthClientConfig {
|
|
3088
|
+
secret?: string;
|
|
3089
|
+
stateTtlMs?: number;
|
|
3090
|
+
providers?: Record<string, OAuthProviderConfig>;
|
|
3091
|
+
}
|
|
3092
|
+
|
|
3093
|
+
export class OAuthClient {
|
|
3094
|
+
secret: string;
|
|
3095
|
+
stateTtlMs: number;
|
|
3096
|
+
providers: Record<string, OAuthProviderConfig>;
|
|
3097
|
+
constructor(config?: OAuthClientConfig);
|
|
3098
|
+
registerProvider(name: string, options?: OAuthProviderConfig): this;
|
|
3099
|
+
getProvider(name: string): OAuthProviderConfig;
|
|
3100
|
+
getProviderNames(): string[];
|
|
3101
|
+
getAuthorizationUrl(providerName: string, options?: {
|
|
3102
|
+
redirectUri?: string;
|
|
3103
|
+
scopes?: string[] | string;
|
|
3104
|
+
stateData?: Record<string, any>;
|
|
3105
|
+
pkce?: boolean;
|
|
3106
|
+
extraParams?: Record<string, any>;
|
|
3107
|
+
}): { url: string; state: string; codeVerifier?: string };
|
|
3108
|
+
exchangeCode(providerName: string, params: {
|
|
3109
|
+
code: string;
|
|
3110
|
+
redirectUri?: string;
|
|
3111
|
+
codeVerifier?: string;
|
|
3112
|
+
}): Promise<Record<string, any>>;
|
|
3113
|
+
fetchUserProfile(providerName: string, tokens: Record<string, any>): Promise<OAuthUserProfile>;
|
|
3114
|
+
handleCallback(providerName: string, params: {
|
|
3115
|
+
code: string;
|
|
3116
|
+
state: string;
|
|
3117
|
+
redirectUri?: string;
|
|
3118
|
+
}): Promise<{ provider: string; user: OAuthUserProfile; tokens: Record<string, any>; stateData: Record<string, any> }>;
|
|
3119
|
+
}
|
|
3120
|
+
|
|
3121
|
+
export function createOAuthClient(config?: OAuthClientConfig): OAuthClient;
|
|
3122
|
+
export function generatePKCE(): { codeVerifier: string; codeChallenge: string; codeChallengeMethod: 'S256' };
|
|
3123
|
+
export function createSignedState(payload: Record<string, any>, secret: string): string;
|
|
3124
|
+
export function verifySignedState(stateString: string, secret: string, maxAgeMs?: number): Record<string, any>;
|
|
3125
|
+
|
|
3126
|
+
export interface OAuthPluginOptions {
|
|
3127
|
+
client?: OAuthClient;
|
|
3128
|
+
providers?: Record<string, OAuthProviderConfig>;
|
|
3129
|
+
secret?: string;
|
|
3130
|
+
pathPrefix?: string;
|
|
3131
|
+
routes?: boolean;
|
|
3132
|
+
pkce?: boolean;
|
|
3133
|
+
stateTtlMs?: number;
|
|
3134
|
+
defaultReturnTo?: string;
|
|
3135
|
+
failureRedirect?: string;
|
|
3136
|
+
findOrCreateUser?: (params: {
|
|
3137
|
+
provider: string;
|
|
3138
|
+
profile: OAuthUserProfile;
|
|
3139
|
+
tokens: Record<string, any>;
|
|
3140
|
+
stateData: Record<string, any>;
|
|
3141
|
+
req: any;
|
|
3142
|
+
res: any;
|
|
3143
|
+
db?: any;
|
|
3144
|
+
}) => Promise<any>;
|
|
3145
|
+
onSuccess?: (params: {
|
|
3146
|
+
provider: string;
|
|
3147
|
+
user: any;
|
|
3148
|
+
profile: OAuthUserProfile;
|
|
3149
|
+
tokens: Record<string, any>;
|
|
3150
|
+
stateData: Record<string, any>;
|
|
3151
|
+
req: any;
|
|
3152
|
+
res: any;
|
|
3153
|
+
client: OAuthClient;
|
|
3154
|
+
}) => Promise<void | any>;
|
|
3155
|
+
onError?: (params: {
|
|
3156
|
+
error: Error | OAuthError;
|
|
3157
|
+
provider: string;
|
|
3158
|
+
req: any;
|
|
3159
|
+
res: any;
|
|
3160
|
+
client: OAuthClient;
|
|
3161
|
+
}) => Promise<void | any>;
|
|
3162
|
+
db?: any;
|
|
3163
|
+
}
|
|
2634
3164
|
|
|
3165
|
+
export function oauthPlugin(options?: OAuthPluginOptions): WebspressoPlugin;
|
|
3166
|
+
|
|
3167
|
+
// ==========================================
|
|
3168
|
+
// 2FA / TOTP (RFC 6238, RFC 4226, RFC 4648)
|
|
3169
|
+
// ==========================================
|
|
3170
|
+
|
|
3171
|
+
export interface Base32EncodeOptions {
|
|
3172
|
+
padding?: boolean;
|
|
3173
|
+
}
|
|
3174
|
+
|
|
3175
|
+
export interface TotpSecretOptions extends Base32EncodeOptions {
|
|
3176
|
+
length?: number;
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3179
|
+
export interface HotpOptions {
|
|
3180
|
+
digits?: number;
|
|
3181
|
+
algorithm?: 'sha1' | 'sha256' | 'sha512' | string;
|
|
3182
|
+
encoding?: 'base32' | 'ascii' | 'utf8' | 'hex';
|
|
3183
|
+
}
|
|
2635
3184
|
|
|
3185
|
+
export interface TotpOptions extends HotpOptions {
|
|
3186
|
+
time?: number | Date;
|
|
3187
|
+
step?: number;
|
|
3188
|
+
epoch?: number;
|
|
3189
|
+
}
|
|
3190
|
+
|
|
3191
|
+
export interface TotpVerifyOptions extends TotpOptions {
|
|
3192
|
+
window?: number;
|
|
3193
|
+
detailed?: boolean;
|
|
3194
|
+
}
|
|
3195
|
+
|
|
3196
|
+
export interface TotpUriOptions {
|
|
3197
|
+
secret: string;
|
|
3198
|
+
account?: string;
|
|
3199
|
+
issuer?: string;
|
|
3200
|
+
label?: string;
|
|
3201
|
+
algorithm?: string;
|
|
3202
|
+
digits?: number;
|
|
3203
|
+
period?: number;
|
|
3204
|
+
}
|
|
3205
|
+
|
|
3206
|
+
export interface QrSvgOptions {
|
|
3207
|
+
size?: number;
|
|
3208
|
+
margin?: number;
|
|
3209
|
+
darkColor?: string;
|
|
3210
|
+
lightColor?: string;
|
|
3211
|
+
version?: number;
|
|
3212
|
+
ecc?: 'L' | 'M' | 'Q' | 'H';
|
|
3213
|
+
mask?: number;
|
|
3214
|
+
}
|
|
3215
|
+
|
|
3216
|
+
export interface RecoveryCodeOptions {
|
|
3217
|
+
count?: number;
|
|
3218
|
+
length?: number;
|
|
3219
|
+
hyphen?: boolean;
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3222
|
+
export interface TotpSetupOptions extends TotpSecretOptions {
|
|
3223
|
+
user?: any;
|
|
3224
|
+
account?: string;
|
|
3225
|
+
issuer?: string;
|
|
3226
|
+
recoveryCodeCount?: number;
|
|
3227
|
+
qrOptions?: QrSvgOptions;
|
|
3228
|
+
algorithm?: string;
|
|
3229
|
+
digits?: number;
|
|
3230
|
+
period?: number;
|
|
3231
|
+
}
|
|
3232
|
+
|
|
3233
|
+
export interface TotpSetupResult {
|
|
3234
|
+
secret: string;
|
|
3235
|
+
uri: string;
|
|
3236
|
+
qrSvg: string;
|
|
3237
|
+
qrDataUri: string;
|
|
3238
|
+
recoveryCodes: string[];
|
|
3239
|
+
recoveryCodesHashed: string[];
|
|
3240
|
+
}
|
|
3241
|
+
|
|
3242
|
+
export function base32Encode(buffer: Buffer | string, options?: Base32EncodeOptions): string;
|
|
3243
|
+
export function base32Decode(input: string): Buffer;
|
|
3244
|
+
export function generateTotpSecret(options?: TotpSecretOptions): string;
|
|
3245
|
+
export function generateHotp(secret: string | Buffer, counter: number | bigint, options?: HotpOptions): string;
|
|
3246
|
+
export function verifyHotp(token: string, secret: string | Buffer, counter: number | bigint, options?: HotpOptions): boolean;
|
|
3247
|
+
export function generateTotp(secret: string | Buffer, options?: TotpOptions): string;
|
|
3248
|
+
export function verifyTotp(token: string, secret: string | Buffer, options?: TotpVerifyOptions): boolean | { valid: boolean; delta: number | null };
|
|
3249
|
+
export function generateTotpUri(options: TotpUriOptions): string;
|
|
3250
|
+
export function generateTotpQrSvg(uriOrOptions: string | TotpUriOptions, svgOptions?: QrSvgOptions): string;
|
|
3251
|
+
export function generateTotpQrDataUri(uriOrOptions: string | TotpUriOptions, svgOptions?: QrSvgOptions): string;
|
|
3252
|
+
export function generateRecoveryCodes(options?: RecoveryCodeOptions): string[];
|
|
3253
|
+
export function hashRecoveryCode(code: string): string;
|
|
3254
|
+
export function verifyRecoveryCode(code: string, hashedCodes: string[]): { valid: boolean; matchedIndex: number; remainingHashedCodes: string[] };
|
|
3255
|
+
export function createTotpSetup(options?: TotpSetupOptions): TotpSetupResult;
|
|
3256
|
+
|
|
3257
|
+
export function createQrMatrix(text: string, options?: QrSvgOptions): { matrix: boolean[][]; size: number; version: number };
|
|
3258
|
+
export function generateQrSvg(text: string, options?: QrSvgOptions): string;
|
|
3259
|
+
export function generateQrDataUri(text: string, options?: QrSvgOptions): string;
|
|
3260
|
+
|
|
3261
|
+
export namespace totp {
|
|
3262
|
+
export {
|
|
3263
|
+
base32Encode,
|
|
3264
|
+
base32Decode,
|
|
3265
|
+
generateTotpSecret,
|
|
3266
|
+
generateHotp,
|
|
3267
|
+
verifyHotp,
|
|
3268
|
+
generateTotp,
|
|
3269
|
+
verifyTotp,
|
|
3270
|
+
generateTotpUri,
|
|
3271
|
+
generateTotpQrSvg,
|
|
3272
|
+
generateTotpQrDataUri,
|
|
3273
|
+
generateRecoveryCodes,
|
|
3274
|
+
hashRecoveryCode,
|
|
3275
|
+
verifyRecoveryCode,
|
|
3276
|
+
createTotpSetup,
|
|
3277
|
+
createQrMatrix,
|
|
3278
|
+
generateQrSvg,
|
|
3279
|
+
generateQrDataUri,
|
|
3280
|
+
};
|
|
3281
|
+
}
|
|
3282
|
+
|
|
3283
|
+
// --- Magic Link / Passwordless Authentication ---
|
|
3284
|
+
|
|
3285
|
+
export interface BuildMagicLinkUrlOptions {
|
|
3286
|
+
baseUrl?: string;
|
|
3287
|
+
path?: string;
|
|
3288
|
+
token: string;
|
|
3289
|
+
}
|
|
3290
|
+
|
|
3291
|
+
export interface FormatMagicLinkHtmlOptions {
|
|
3292
|
+
name?: string;
|
|
3293
|
+
magicLinkUrl: string;
|
|
3294
|
+
expiresMinutes?: number;
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
export interface MagicLinkVerifyMiddlewareOptions {
|
|
3298
|
+
tokenParam?: string;
|
|
3299
|
+
redirectTo?: string;
|
|
3300
|
+
failureRedirect?: string;
|
|
3301
|
+
markEmailVerified?: boolean;
|
|
3302
|
+
jwt?: boolean;
|
|
3303
|
+
}
|
|
3304
|
+
|
|
3305
|
+
export interface MagicLinkRequestOptions {
|
|
3306
|
+
baseUrl?: string;
|
|
3307
|
+
path?: string;
|
|
3308
|
+
tokenTtlMs?: number;
|
|
3309
|
+
hideUserNotFound?: boolean;
|
|
3310
|
+
buildMagicLinkUrl?: (token: string) => string;
|
|
3311
|
+
}
|
|
3312
|
+
|
|
3313
|
+
export interface MagicLinkRequestResult {
|
|
3314
|
+
sent: boolean;
|
|
3315
|
+
userFound: boolean;
|
|
3316
|
+
expiresAt?: Date;
|
|
3317
|
+
token?: string;
|
|
3318
|
+
magicLinkUrl?: string;
|
|
3319
|
+
}
|
|
3320
|
+
|
|
3321
|
+
export interface MagicLinkVerifyResult {
|
|
3322
|
+
ok: true;
|
|
3323
|
+
user: any;
|
|
3324
|
+
userId: string | number;
|
|
3325
|
+
accessToken?: string;
|
|
3326
|
+
refreshToken?: string;
|
|
3327
|
+
}
|
|
3328
|
+
|
|
3329
|
+
export function buildMagicLinkUrl(options: BuildMagicLinkUrlOptions): string;
|
|
3330
|
+
export function formatMagicLinkHtml(options: FormatMagicLinkHtmlOptions): string;
|
|
3331
|
+
export function createMagicLinkVerifyMiddleware(authManager: any, options?: MagicLinkVerifyMiddlewareOptions): (req: any, res: any, next: any) => Promise<any>;
|
|
3332
|
+
export function createMagicLinkRequestHandler(authManager: any, options?: MagicLinkRequestOptions): (req: any, res: any) => Promise<any>;
|
|
3333
|
+
|
|
3334
|
+
export namespace magicLink {
|
|
3335
|
+
export {
|
|
3336
|
+
buildMagicLinkUrl,
|
|
3337
|
+
formatMagicLinkHtml,
|
|
3338
|
+
createMagicLinkVerifyMiddleware,
|
|
3339
|
+
createMagicLinkRequestHandler,
|
|
3340
|
+
};
|
|
3341
|
+
}
|
|
3342
|
+
|
|
3343
|
+
// Active Devices & Session Management
|
|
3344
|
+
export interface DeviceInfo {
|
|
3345
|
+
browser: string;
|
|
3346
|
+
os: string;
|
|
3347
|
+
device: 'desktop' | 'mobile' | 'tablet' | 'bot' | 'unknown';
|
|
3348
|
+
isMobile: boolean;
|
|
3349
|
+
isTablet: boolean;
|
|
3350
|
+
isDesktop: boolean;
|
|
3351
|
+
isBot: boolean;
|
|
3352
|
+
}
|
|
3353
|
+
|
|
3354
|
+
export interface UserSession {
|
|
3355
|
+
id?: number | string;
|
|
3356
|
+
session_id: string;
|
|
3357
|
+
user_id: number | string;
|
|
3358
|
+
ip_address?: string | null;
|
|
3359
|
+
user_agent?: string | null;
|
|
3360
|
+
browser?: string | null;
|
|
3361
|
+
os?: string | null;
|
|
3362
|
+
device?: string | null;
|
|
3363
|
+
last_active_at: Date;
|
|
3364
|
+
created_at?: Date;
|
|
3365
|
+
is_current?: boolean;
|
|
3366
|
+
}
|
|
3367
|
+
|
|
3368
|
+
export interface SessionTracker {
|
|
3369
|
+
track(sessionId: string, userId: string | number, metadata?: { ip?: string; userAgent?: string; browser?: string; os?: string; device?: string }): Promise<UserSession | null>;
|
|
3370
|
+
touch(sessionId: string): Promise<boolean>;
|
|
3371
|
+
get(sessionId: string): Promise<UserSession | null>;
|
|
3372
|
+
list(userId: string | number, currentSessionId?: string | null): Promise<UserSession[]>;
|
|
3373
|
+
isSessionValid(sessionId: string, userId?: string | number | null): Promise<boolean>;
|
|
3374
|
+
revoke(sessionId: string): Promise<boolean>;
|
|
3375
|
+
revokeOthers(userId: string | number, currentSessionId?: string | null): Promise<number>;
|
|
3376
|
+
revokeAll(userId: string | number): Promise<number>;
|
|
3377
|
+
purgeExpired(maxAgeMs: number): Promise<number>;
|
|
3378
|
+
}
|
|
3379
|
+
|
|
3380
|
+
export interface SessionManagerOptions {
|
|
3381
|
+
tracker?: SessionTracker;
|
|
3382
|
+
sessionStore?: any;
|
|
3383
|
+
touchThrottleMs?: number;
|
|
3384
|
+
emitter?: any;
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3387
|
+
export class SessionManager {
|
|
3388
|
+
constructor(options?: SessionManagerOptions);
|
|
3389
|
+
tracker: SessionTracker;
|
|
3390
|
+
sessionStore?: any;
|
|
3391
|
+
touchThrottleMs: number;
|
|
3392
|
+
emitter?: any;
|
|
3393
|
+
track(sessionId: string, userId: string | number, req?: any): Promise<UserSession | null>;
|
|
3394
|
+
touch(sessionId: string): Promise<boolean>;
|
|
3395
|
+
isSessionValid(sessionId: string, userId?: string | number | null): Promise<boolean>;
|
|
3396
|
+
listSessions(userId: string | number, currentSessionId?: string | null): Promise<UserSession[]>;
|
|
3397
|
+
revokeSession(sessionId: string, options?: { userId?: string | number; revokedBy?: string }): Promise<boolean>;
|
|
3398
|
+
revokeOtherSessions(userId: string | number, currentSessionId: string): Promise<number>;
|
|
3399
|
+
revokeAllSessions(userId: string | number): Promise<number>;
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3402
|
+
export function parseUserAgent(userAgent?: string): DeviceInfo;
|
|
3403
|
+
export function getClientIp(req?: any): string;
|
|
3404
|
+
export function createUserSessionsTable(knex: any): Promise<void>;
|
|
3405
|
+
export function dropUserSessionsTable(knex: any): Promise<void>;
|
|
3406
|
+
export function createKnexSessionTracker(db: any, options?: { tableName?: string }): SessionTracker;
|
|
3407
|
+
export function createMemorySessionTracker(): SessionTracker;
|
|
3408
|
+
export function createListSessionsHandler(authManager: any): (req: any, res: any) => Promise<any>;
|
|
3409
|
+
export function createRevokeSessionHandler(authManager: any): (req: any, res: any) => Promise<any>;
|
|
3410
|
+
|
|
3411
|
+
export namespace sessions {
|
|
3412
|
+
export {
|
|
3413
|
+
SessionManager,
|
|
3414
|
+
createUserSessionsTable,
|
|
3415
|
+
dropUserSessionsTable,
|
|
3416
|
+
createKnexSessionTracker,
|
|
3417
|
+
createMemorySessionTracker,
|
|
3418
|
+
getClientIp,
|
|
3419
|
+
parseUserAgent,
|
|
3420
|
+
createListSessionsHandler,
|
|
3421
|
+
createRevokeSessionHandler,
|
|
3422
|
+
};
|
|
3423
|
+
}
|
|
2636
3424
|
|