@sqrzro/server 2.0.0-bz.1 → 2.0.0-bz.10

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.
Files changed (91) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +25 -1
  3. package/auth.d.ts +1 -0
  4. package/auth.js +1 -0
  5. package/cache.d.ts +1 -0
  6. package/cache.js +1 -0
  7. package/database.d.ts +1 -0
  8. package/database.js +1 -0
  9. package/dist/auth/AuthService.d.ts +14 -0
  10. package/dist/auth/AuthService.js +135 -0
  11. package/dist/auth/ClientService.d.ts +11 -0
  12. package/dist/auth/ClientService.js +47 -0
  13. package/dist/{LoginRequest.d.ts → auth/LoginRequest.d.ts} +1 -1
  14. package/dist/auth/LoginRequest.js +8 -0
  15. package/dist/auth/MFARequest.d.ts +4 -0
  16. package/dist/auth/MFARequest.js +9 -0
  17. package/dist/auth/MFAService.d.ts +6 -0
  18. package/dist/auth/MFAService.js +105 -0
  19. package/dist/auth/PasswordRequest.d.ts +4 -0
  20. package/dist/auth/PasswordRequest.js +12 -0
  21. package/dist/auth/PasswordResetRequest.d.ts +4 -0
  22. package/dist/auth/PasswordResetRequest.js +13 -0
  23. package/dist/auth/PasswordService.d.ts +8 -0
  24. package/dist/auth/PasswordService.js +54 -0
  25. package/dist/auth/SessionService.d.ts +42 -0
  26. package/dist/auth/SessionService.js +127 -0
  27. package/dist/{index.d.ts → auth/index.d.ts} +3 -4
  28. package/dist/auth/index.js +6 -0
  29. package/dist/auth/interfaces.d.ts +20 -0
  30. package/dist/auth/interfaces.js +1 -0
  31. package/dist/cache/CacheService.d.ts +2 -0
  32. package/dist/cache/CacheService.js +14 -0
  33. package/dist/cache/index.d.ts +1 -0
  34. package/dist/cache/index.js +1 -0
  35. package/dist/database/DatabaseService.d.ts +7 -0
  36. package/dist/database/DatabaseService.js +12 -0
  37. package/dist/database/schema.d.ts +284 -0
  38. package/dist/database/schema.js +42 -0
  39. package/dist/forms/FormService.d.ts +16 -0
  40. package/dist/forms/FormService.js +78 -0
  41. package/dist/forms/ImageService.d.ts +7 -0
  42. package/dist/forms/ImageService.js +19 -0
  43. package/dist/forms/ValidationError.d.ts +4 -0
  44. package/dist/forms/ValidationError.js +7 -0
  45. package/dist/forms/ValidationService.d.ts +20 -0
  46. package/dist/forms/ValidationService.js +59 -0
  47. package/dist/forms/index.d.ts +3 -0
  48. package/dist/forms/index.js +3 -0
  49. package/dist/forms/lang.d.ts +2 -0
  50. package/dist/forms/lang.js +115 -0
  51. package/dist/lists/ListService.d.ts +16 -0
  52. package/dist/lists/ListService.js +28 -0
  53. package/dist/lists/index.d.ts +1 -0
  54. package/dist/lists/index.js +1 -0
  55. package/dist/mail/MailService.d.ts +12 -0
  56. package/dist/mail/MailService.js +55 -0
  57. package/dist/mail/index.d.ts +1 -0
  58. package/dist/mail/index.js +1 -0
  59. package/dist/middleware.d.ts +3 -0
  60. package/dist/middleware.js +30 -0
  61. package/dist/url/URLService.d.ts +26 -0
  62. package/dist/url/URLService.js +48 -0
  63. package/dist/url/index.d.ts +1 -0
  64. package/dist/url/index.js +1 -0
  65. package/forms.d.ts +1 -0
  66. package/forms.js +1 -0
  67. package/lists.d.ts +1 -0
  68. package/lists.js +1 -0
  69. package/mail.d.ts +1 -0
  70. package/mail.js +1 -0
  71. package/middleware.d.ts +1 -0
  72. package/middleware.js +1 -0
  73. package/package.json +68 -44
  74. package/schema.d.ts +1 -0
  75. package/schema.js +1 -0
  76. package/url.d.ts +1 -0
  77. package/url.js +1 -0
  78. package/dist/AuthService.d.ts +0 -10
  79. package/dist/AuthService.js +0 -36
  80. package/dist/DataService.d.ts +0 -29
  81. package/dist/DataService.js +0 -64
  82. package/dist/LoginRequest.js +0 -11
  83. package/dist/PasswordService.d.ts +0 -6
  84. package/dist/PasswordService.js +0 -63
  85. package/dist/RequestService.d.ts +0 -21
  86. package/dist/RequestService.js +0 -121
  87. package/dist/SessionService.d.ts +0 -5
  88. package/dist/SessionService.js +0 -56
  89. package/dist/index.js +0 -10
  90. package/dist/interfaces.d.ts +0 -11
  91. package/dist/interfaces.js +0 -2
@@ -0,0 +1,127 @@
1
+ import { DrizzlePostgreSQLAdapter } from '@lucia-auth/adapter-drizzle';
2
+ import { Lucia, generateId } from 'lucia';
3
+ import { cookies } from 'next/headers';
4
+ import { NextResponse } from 'next/server';
5
+ import { match } from 'path-to-regexp';
6
+ import { getAllowedRoles } from './AuthService';
7
+ import { db } from '../database/DatabaseService';
8
+ import { authSessionTable, authUserTable } from '../database/schema';
9
+ import { getFromCache, setToCache } from '../cache/CacheService';
10
+ import { getOrigin } from '../url/URLService';
11
+ import { getFromObject } from '@sqrzro/utility';
12
+ const DEFAULT_REDIRECT = '/auth/login';
13
+ const ID_LENGTH = 16;
14
+ const DEFAULT_SCOPES = {
15
+ ANON: {
16
+ allowedRoute: '/auth/(login|password)',
17
+ redirectOnUnauth: DEFAULT_REDIRECT,
18
+ },
19
+ MFA: {
20
+ allowedRoute: '/auth/mfa',
21
+ redirectOnUnauth: '/auth/mfa',
22
+ },
23
+ AUTHED: {
24
+ allowedRoute: '*',
25
+ redirectOnAuth: '/',
26
+ },
27
+ };
28
+ const adapter = new DrizzlePostgreSQLAdapter(db, authSessionTable, authUserTable);
29
+ export const lucia = new Lucia(adapter, {
30
+ sessionCookie: {
31
+ attributes: {
32
+ secure: process.env.NODE_ENV === 'production',
33
+ },
34
+ name: process.env.AUTH_COOKIE_NAME || 'auth_session',
35
+ },
36
+ getSessionAttributes: (attributes) => ({
37
+ scope: attributes.scope,
38
+ }),
39
+ getUserAttributes: (attributes) => ({
40
+ email: attributes.email,
41
+ role: attributes.role,
42
+ }),
43
+ });
44
+ export function generateID(length = ID_LENGTH) {
45
+ return generateId(length);
46
+ }
47
+ export async function invalidateSession(id) {
48
+ const cookie = lucia.createBlankSessionCookie();
49
+ cookies().set(cookie.name, cookie.value, cookie.attributes);
50
+ return lucia.invalidateSession(id);
51
+ }
52
+ export async function invalidateUserSessions(id) {
53
+ return lucia.invalidateUserSessions(id);
54
+ }
55
+ export async function createUserSession(id, scope = 'ANON') {
56
+ const session = await lucia.createSession(id, { scope });
57
+ const sessionCookie = lucia.createSessionCookie(session.id);
58
+ cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
59
+ return true;
60
+ }
61
+ export function getSessionID() {
62
+ return cookies().get(lucia.sessionCookieName)?.value ?? null;
63
+ }
64
+ export function checkSessionExists() {
65
+ return Boolean(getSessionID());
66
+ }
67
+ export async function getSessionUser() {
68
+ const sessionID = getSessionID();
69
+ if (!sessionID) {
70
+ return null;
71
+ }
72
+ const { user } = await lucia.validateSession(sessionID);
73
+ if (!user?.role || !getAllowedRoles().includes(user.role)) {
74
+ return null;
75
+ }
76
+ return getFromObject(user, ['id', 'email', 'role']);
77
+ }
78
+ export function checkRouteAllowed(pathname, route) {
79
+ if (!route) {
80
+ return false;
81
+ }
82
+ if (route === '*') {
83
+ return true;
84
+ }
85
+ return match(route)(pathname) !== false;
86
+ }
87
+ export async function getScopes() {
88
+ const scopes = await getFromCache(`${getOrigin()}:scopes`);
89
+ return scopes ? JSON.parse(scopes) : DEFAULT_SCOPES;
90
+ }
91
+ export async function getScopeByID(id) {
92
+ const scopes = await getScopes();
93
+ return scopes[id];
94
+ }
95
+ export async function setScopes(customScopes) {
96
+ const scopes = {
97
+ ANON: {
98
+ ...DEFAULT_SCOPES.ANON,
99
+ ...customScopes?.ANON,
100
+ },
101
+ MFA: {
102
+ ...DEFAULT_SCOPES.MFA,
103
+ ...customScopes?.MFA,
104
+ },
105
+ AUTHED: {
106
+ ...DEFAULT_SCOPES.AUTHED,
107
+ ...customScopes?.AUTHED,
108
+ },
109
+ };
110
+ return setToCache(`${getOrigin()}:scopes`, JSON.stringify(scopes));
111
+ }
112
+ async function validateSessionFromID(sessionID, pathname) {
113
+ const scopes = await getScopes();
114
+ const { session, user } = await lucia.validateSession(sessionID);
115
+ const scope = scopes[session && getAllowedRoles().includes(user?.role) ? session.scope : 'ANON'];
116
+ return checkRouteAllowed(pathname, scope.allowedRoute)
117
+ ? null
118
+ : scope.redirectOnUnauth || DEFAULT_REDIRECT;
119
+ }
120
+ export async function handleSession(request, customScopes) {
121
+ const sessionID = request.nextUrl.searchParams.get('id') || '';
122
+ const pathname = request.nextUrl.searchParams.get('pathname') || '/';
123
+ await setScopes(customScopes);
124
+ return NextResponse.json({
125
+ redirect: await validateSessionFromID(sessionID, pathname),
126
+ });
127
+ }
@@ -1,7 +1,6 @@
1
- export * from './LoginRequest';
2
1
  export * from './AuthService';
3
- export * from './PasswordService';
4
- export * from './DataService';
5
- export * from './RequestService';
2
+ export * from './ClientService';
3
+ export * from './MFAService';
6
4
  export * from './SessionService';
5
+ export * from './PasswordService';
7
6
  export * from './interfaces';
@@ -0,0 +1,6 @@
1
+ export * from './AuthService';
2
+ export * from './ClientService';
3
+ export * from './MFAService';
4
+ export * from './SessionService';
5
+ export * from './PasswordService';
6
+ export * from './interfaces';
@@ -0,0 +1,20 @@
1
+ export interface LoginFormFields {
2
+ email: string;
3
+ password: string;
4
+ }
5
+ export interface MFAFormFields {
6
+ token: string;
7
+ }
8
+ export interface PasswordFormFields {
9
+ email: string;
10
+ }
11
+ export interface PasswordResetFormFields {
12
+ password: string;
13
+ token: string;
14
+ }
15
+ export interface UserObject {
16
+ id: string;
17
+ email: string;
18
+ password?: string | null;
19
+ role?: number;
20
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export declare function getFromCache(key: string): Promise<string | null>;
2
+ export declare function setToCache(key: string, value: string): Promise<void>;
@@ -0,0 +1,14 @@
1
+ import { createClient } from 'redis';
2
+ async function getClient() {
3
+ const client = createClient();
4
+ await client.connect();
5
+ return client;
6
+ }
7
+ export async function getFromCache(key) {
8
+ const client = await getClient();
9
+ return client.get(key);
10
+ }
11
+ export async function setToCache(key, value) {
12
+ const client = await getClient();
13
+ await client.set(key, value);
14
+ }
@@ -0,0 +1 @@
1
+ export * from './CacheService';
@@ -0,0 +1 @@
1
+ export * from './CacheService';
@@ -0,0 +1,7 @@
1
+ import type { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
2
+ declare global {
3
+ var db: ReturnType<typeof createSingleton> | undefined;
4
+ }
5
+ declare function createSingleton(): PostgresJsDatabase;
6
+ export declare const db: PostgresJsDatabase;
7
+ export {};
@@ -0,0 +1,12 @@
1
+ import { drizzle } from 'drizzle-orm/postgres-js';
2
+ import postgres from 'postgres';
3
+ function createSingleton() {
4
+ if (!process.env.DATABASE_URL) {
5
+ throw new Error('DATABASE_URL is not defined');
6
+ }
7
+ return drizzle(postgres(process.env.DATABASE_URL, { prepare: false }));
8
+ }
9
+ export const db = globalThis.db ?? createSingleton();
10
+ if (!process.env.VERCEL_ENV) {
11
+ globalThis.db = db;
12
+ }
@@ -0,0 +1,284 @@
1
+ export declare const mfaType: import("drizzle-orm/pg-core").PgEnum<["TOTP", "HARDWARE"]>;
2
+ export declare const scope: import("drizzle-orm/pg-core").PgEnum<["ANON", "MFA", "AUTHED"]>;
3
+ export type Scope = (typeof scope.enumValues)[number];
4
+ export declare const authSchema: import("drizzle-orm/pg-core").PgSchema<"auth">;
5
+ export declare const authUserTable: import("drizzle-orm/pg-core").PgTableWithColumns<{
6
+ name: "user_credentials";
7
+ schema: "auth";
8
+ columns: {
9
+ id: import("drizzle-orm/pg-core").PgColumn<{
10
+ name: "id";
11
+ tableName: "user_credentials";
12
+ dataType: "string";
13
+ columnType: "PgText";
14
+ data: string;
15
+ driverParam: string;
16
+ notNull: true;
17
+ hasDefault: false;
18
+ enumValues: [string, ...string[]];
19
+ baseColumn: never;
20
+ }, {}, {}>;
21
+ email: import("drizzle-orm/pg-core").PgColumn<{
22
+ name: "email";
23
+ tableName: "user_credentials";
24
+ dataType: "string";
25
+ columnType: "PgText";
26
+ data: string;
27
+ driverParam: string;
28
+ notNull: true;
29
+ hasDefault: false;
30
+ enumValues: [string, ...string[]];
31
+ baseColumn: never;
32
+ }, {}, {}>;
33
+ password: import("drizzle-orm/pg-core").PgColumn<{
34
+ name: "password";
35
+ tableName: "user_credentials";
36
+ dataType: "string";
37
+ columnType: "PgText";
38
+ data: string;
39
+ driverParam: string;
40
+ notNull: false;
41
+ hasDefault: false;
42
+ enumValues: [string, ...string[]];
43
+ baseColumn: never;
44
+ }, {}, {}>;
45
+ role: import("drizzle-orm/pg-core").PgColumn<{
46
+ name: "role";
47
+ tableName: "user_credentials";
48
+ dataType: "number";
49
+ columnType: "PgInteger";
50
+ data: number;
51
+ driverParam: string | number;
52
+ notNull: true;
53
+ hasDefault: true;
54
+ enumValues: undefined;
55
+ baseColumn: never;
56
+ }, {}, {}>;
57
+ };
58
+ dialect: "pg";
59
+ }>;
60
+ export type AuthUser = typeof authUserTable.$inferSelect;
61
+ export declare const authSessionTable: import("drizzle-orm/pg-core").PgTableWithColumns<{
62
+ name: "sessions";
63
+ schema: "auth";
64
+ columns: {
65
+ id: import("drizzle-orm/pg-core").PgColumn<{
66
+ name: "id";
67
+ tableName: "sessions";
68
+ dataType: "string";
69
+ columnType: "PgText";
70
+ data: string;
71
+ driverParam: string;
72
+ notNull: true;
73
+ hasDefault: false;
74
+ enumValues: [string, ...string[]];
75
+ baseColumn: never;
76
+ }, {}, {}>;
77
+ userId: import("drizzle-orm/pg-core").PgColumn<{
78
+ name: "userId";
79
+ tableName: "sessions";
80
+ dataType: "string";
81
+ columnType: "PgText";
82
+ data: string;
83
+ driverParam: string;
84
+ notNull: true;
85
+ hasDefault: false;
86
+ enumValues: [string, ...string[]];
87
+ baseColumn: never;
88
+ }, {}, {}>;
89
+ scope: import("drizzle-orm/pg-core").PgColumn<{
90
+ name: "scope";
91
+ tableName: "sessions";
92
+ dataType: "string";
93
+ columnType: "PgEnumColumn";
94
+ data: "ANON" | "MFA" | "AUTHED";
95
+ driverParam: string;
96
+ notNull: true;
97
+ hasDefault: true;
98
+ enumValues: ["ANON", "MFA", "AUTHED"];
99
+ baseColumn: never;
100
+ }, {}, {}>;
101
+ expiresAt: import("drizzle-orm/pg-core").PgColumn<{
102
+ name: "expiresAt";
103
+ tableName: "sessions";
104
+ dataType: "date";
105
+ columnType: "PgTimestamp";
106
+ data: Date;
107
+ driverParam: string;
108
+ notNull: true;
109
+ hasDefault: false;
110
+ enumValues: undefined;
111
+ baseColumn: never;
112
+ }, {}, {}>;
113
+ };
114
+ dialect: "pg";
115
+ }>;
116
+ export type AuthSession = typeof authSessionTable.$inferSelect;
117
+ export declare const authResetTable: import("drizzle-orm/pg-core").PgTableWithColumns<{
118
+ name: "resets";
119
+ schema: "auth";
120
+ columns: {
121
+ id: import("drizzle-orm/pg-core").PgColumn<{
122
+ name: "id";
123
+ tableName: "resets";
124
+ dataType: "string";
125
+ columnType: "PgText";
126
+ data: string;
127
+ driverParam: string;
128
+ notNull: true;
129
+ hasDefault: false;
130
+ enumValues: [string, ...string[]];
131
+ baseColumn: never;
132
+ }, {}, {}>;
133
+ userId: import("drizzle-orm/pg-core").PgColumn<{
134
+ name: "userId";
135
+ tableName: "resets";
136
+ dataType: "string";
137
+ columnType: "PgText";
138
+ data: string;
139
+ driverParam: string;
140
+ notNull: true;
141
+ hasDefault: false;
142
+ enumValues: [string, ...string[]];
143
+ baseColumn: never;
144
+ }, {}, {}>;
145
+ expiresAt: import("drizzle-orm/pg-core").PgColumn<{
146
+ name: "expiresAt";
147
+ tableName: "resets";
148
+ dataType: "date";
149
+ columnType: "PgTimestamp";
150
+ data: Date;
151
+ driverParam: string;
152
+ notNull: true;
153
+ hasDefault: false;
154
+ enumValues: undefined;
155
+ baseColumn: never;
156
+ }, {}, {}>;
157
+ };
158
+ dialect: "pg";
159
+ }>;
160
+ export type AuthReset = typeof authResetTable.$inferSelect;
161
+ export declare const authMFATable: import("drizzle-orm/pg-core").PgTableWithColumns<{
162
+ name: "mfas";
163
+ schema: "auth";
164
+ columns: {
165
+ id: import("drizzle-orm/pg-core").PgColumn<{
166
+ name: "id";
167
+ tableName: "mfas";
168
+ dataType: "string";
169
+ columnType: "PgText";
170
+ data: string;
171
+ driverParam: string;
172
+ notNull: true;
173
+ hasDefault: false;
174
+ enumValues: [string, ...string[]];
175
+ baseColumn: never;
176
+ }, {}, {}>;
177
+ name: import("drizzle-orm/pg-core").PgColumn<{
178
+ name: "name";
179
+ tableName: "mfas";
180
+ dataType: "string";
181
+ columnType: "PgText";
182
+ data: string;
183
+ driverParam: string;
184
+ notNull: true;
185
+ hasDefault: false;
186
+ enumValues: [string, ...string[]];
187
+ baseColumn: never;
188
+ }, {}, {}>;
189
+ userId: import("drizzle-orm/pg-core").PgColumn<{
190
+ name: "userId";
191
+ tableName: "mfas";
192
+ dataType: "string";
193
+ columnType: "PgText";
194
+ data: string;
195
+ driverParam: string;
196
+ notNull: true;
197
+ hasDefault: false;
198
+ enumValues: [string, ...string[]];
199
+ baseColumn: never;
200
+ }, {}, {}>;
201
+ type: import("drizzle-orm/pg-core").PgColumn<{
202
+ name: "type";
203
+ tableName: "mfas";
204
+ dataType: "string";
205
+ columnType: "PgEnumColumn";
206
+ data: "TOTP" | "HARDWARE";
207
+ driverParam: string;
208
+ notNull: true;
209
+ hasDefault: true;
210
+ enumValues: ["TOTP", "HARDWARE"];
211
+ baseColumn: never;
212
+ }, {}, {}>;
213
+ secret: import("drizzle-orm/pg-core").PgColumn<{
214
+ name: "secret";
215
+ tableName: "mfas";
216
+ dataType: "string";
217
+ columnType: "PgText";
218
+ data: string;
219
+ driverParam: string;
220
+ notNull: true;
221
+ hasDefault: false;
222
+ enumValues: [string, ...string[]];
223
+ baseColumn: never;
224
+ }, {}, {}>;
225
+ verifiedAt: import("drizzle-orm/pg-core").PgColumn<{
226
+ name: "verifiedAt";
227
+ tableName: "mfas";
228
+ dataType: "date";
229
+ columnType: "PgTimestamp";
230
+ data: Date;
231
+ driverParam: string;
232
+ notNull: false;
233
+ hasDefault: false;
234
+ enumValues: undefined;
235
+ baseColumn: never;
236
+ }, {}, {}>;
237
+ };
238
+ dialect: "pg";
239
+ }>;
240
+ export type AuthMFA = typeof authMFATable.$inferSelect;
241
+ export declare const authClientTable: import("drizzle-orm/pg-core").PgTableWithColumns<{
242
+ name: "client_credentials";
243
+ schema: "auth";
244
+ columns: {
245
+ id: import("drizzle-orm/pg-core").PgColumn<{
246
+ name: "id";
247
+ tableName: "client_credentials";
248
+ dataType: "string";
249
+ columnType: "PgText";
250
+ data: string;
251
+ driverParam: string;
252
+ notNull: true;
253
+ hasDefault: false;
254
+ enumValues: [string, ...string[]];
255
+ baseColumn: never;
256
+ }, {}, {}>;
257
+ alias: import("drizzle-orm/pg-core").PgColumn<{
258
+ name: "alias";
259
+ tableName: "client_credentials";
260
+ dataType: "string";
261
+ columnType: "PgText";
262
+ data: string;
263
+ driverParam: string;
264
+ notNull: true;
265
+ hasDefault: false;
266
+ enumValues: [string, ...string[]];
267
+ baseColumn: never;
268
+ }, {}, {}>;
269
+ secret: import("drizzle-orm/pg-core").PgColumn<{
270
+ name: "secret";
271
+ tableName: "client_credentials";
272
+ dataType: "string";
273
+ columnType: "PgText";
274
+ data: string;
275
+ driverParam: string;
276
+ notNull: true;
277
+ hasDefault: false;
278
+ enumValues: [string, ...string[]];
279
+ baseColumn: never;
280
+ }, {}, {}>;
281
+ };
282
+ dialect: "pg";
283
+ }>;
284
+ export type AuthClient = typeof authClientTable.$inferSelect;
@@ -0,0 +1,42 @@
1
+ /* istanbul ignore file */
2
+ import { integer, pgEnum, pgSchema, text, timestamp } from 'drizzle-orm/pg-core';
3
+ const DEFAULT_ROLE = 10;
4
+ export const mfaType = pgEnum('mfaType', ['TOTP', 'HARDWARE']);
5
+ export const scope = pgEnum('scope', ['ANON', 'MFA', 'AUTHED']);
6
+ export const authSchema = pgSchema('auth');
7
+ export const authUserTable = authSchema.table('user_credentials', {
8
+ id: text('id').primaryKey(),
9
+ email: text('email').notNull().unique(),
10
+ password: text('password'),
11
+ role: integer('role').notNull().default(DEFAULT_ROLE),
12
+ });
13
+ export const authSessionTable = authSchema.table('sessions', {
14
+ id: text('id').primaryKey(),
15
+ userId: text('userId')
16
+ .notNull()
17
+ .references(() => authUserTable.id),
18
+ scope: scope('scope').notNull().default('ANON'),
19
+ expiresAt: timestamp('expiresAt').notNull(),
20
+ });
21
+ export const authResetTable = authSchema.table('resets', {
22
+ id: text('id').primaryKey(),
23
+ userId: text('userId')
24
+ .notNull()
25
+ .references(() => authUserTable.id),
26
+ expiresAt: timestamp('expiresAt').notNull(),
27
+ });
28
+ export const authMFATable = authSchema.table('mfas', {
29
+ id: text('id').primaryKey(),
30
+ name: text('name').notNull(),
31
+ userId: text('userId')
32
+ .notNull()
33
+ .references(() => authUserTable.id),
34
+ type: mfaType('type').notNull().default('TOTP'),
35
+ secret: text('secret').notNull(),
36
+ verifiedAt: timestamp('verifiedAt'),
37
+ });
38
+ export const authClientTable = authSchema.table('client_credentials', {
39
+ id: text('id').primaryKey(),
40
+ alias: text('alias').notNull().unique(),
41
+ secret: text('secret').notNull().unique(),
42
+ });
@@ -0,0 +1,16 @@
1
+ import type { Errorable } from '@sqrzro/interfaces';
2
+ import type Joi from 'joi';
3
+ import ValidationError from './ValidationError';
4
+ interface SubmitFormArgs<F extends object> {
5
+ formData: F;
6
+ onSuccess?: (model: F) => Promise<void> | void;
7
+ onValidationError?: (error: ValidationError) => void;
8
+ request?: Joi.ObjectSchema<F>;
9
+ }
10
+ interface SubmitFormArgsWithFn<F extends object, M> extends Omit<SubmitFormArgs<F>, 'onSuccess'> {
11
+ fn: (data: F) => Promise<M | null>;
12
+ onSuccess?: (model: M) => Promise<void> | void;
13
+ }
14
+ export declare function submitForm<F extends object>(args: SubmitFormArgs<F>): Promise<Errorable<F>>;
15
+ export declare function submitForm<F extends object, M>(args: SubmitFormArgsWithFn<F, M>): Promise<Errorable<M>>;
16
+ export {};
@@ -0,0 +1,78 @@
1
+ /* eslint-disable max-statements */
2
+ import ValidationError from './ValidationError';
3
+ import { validateSchema } from './ValidationService';
4
+ function serializeError(err) {
5
+ return {
6
+ cause: err.cause,
7
+ message: err.message,
8
+ name: err.name,
9
+ stack: err.stack,
10
+ };
11
+ }
12
+ function hasFn(args) {
13
+ return Boolean(Object.prototype.hasOwnProperty.call(args, 'fn'));
14
+ }
15
+ export async function submitForm(args) {
16
+ let data = { ...args.formData };
17
+ if (args.request) {
18
+ const [validated, validationError] = await validateSchema(args.formData, args.request);
19
+ if (validationError !== null) {
20
+ if (validationError instanceof ValidationError) {
21
+ args.onValidationError?.(validationError);
22
+ }
23
+ return [null, serializeError(validationError)];
24
+ }
25
+ data = validated;
26
+ }
27
+ if (!hasFn(args)) {
28
+ try {
29
+ await args.onSuccess?.(data);
30
+ }
31
+ catch (err) {
32
+ if (err instanceof Error) {
33
+ return [null, serializeError(err)];
34
+ }
35
+ return [
36
+ null,
37
+ serializeError(new Error('The submitForm onSuccess function encountered an unknown error')),
38
+ ];
39
+ }
40
+ return [data, null];
41
+ }
42
+ let model = null;
43
+ try {
44
+ model = await args.fn(data);
45
+ }
46
+ catch (err) {
47
+ if (err instanceof ValidationError) {
48
+ args.onValidationError?.(err);
49
+ return [null, serializeError(err)];
50
+ }
51
+ if (err instanceof Error) {
52
+ return [null, serializeError(err)];
53
+ }
54
+ return [
55
+ null,
56
+ serializeError(new Error('The function supplied to submitForm encountered an unknown error')),
57
+ ];
58
+ }
59
+ if (!model) {
60
+ return [
61
+ null,
62
+ serializeError(new Error('No model has been returned from the function supplied to submitForm')),
63
+ ];
64
+ }
65
+ try {
66
+ await args.onSuccess?.(model);
67
+ }
68
+ catch (err) {
69
+ if (err instanceof Error) {
70
+ return [null, serializeError(err)];
71
+ }
72
+ return [
73
+ null,
74
+ serializeError(new Error('The submitForm onSuccess function encountered an unknown error')),
75
+ ];
76
+ }
77
+ return [model, null];
78
+ }
@@ -0,0 +1,7 @@
1
+ import type { Errorable } from '@sqrzro/interfaces';
2
+ interface ImageValidationConfig {
3
+ maxSize?: number;
4
+ types?: string[];
5
+ }
6
+ export declare function validateImage(image: File | null, config?: ImageValidationConfig): Promise<Errorable<File>>;
7
+ export {};
@@ -0,0 +1,19 @@
1
+ 'use server';
2
+ const DEFAULT_TYPES = ['image/png', 'image/jpeg', 'image/jpg'];
3
+ // 5MB
4
+ const DEFAULT_MAX_SIZE = 5242880;
5
+ export async function validateImage(image, config) {
6
+ if (!image) {
7
+ return Promise.resolve([null, new Error('IMAGE_UNDEFINED')]);
8
+ }
9
+ if (!(image instanceof File)) {
10
+ return Promise.resolve([null, new Error('IMAGE_NOT_VALID')]);
11
+ }
12
+ if (!(config?.types || DEFAULT_TYPES).includes(image.type)) {
13
+ return Promise.resolve([null, new Error('IMAGE_TYPE_NOT_VALID')]);
14
+ }
15
+ if (image.size > (config?.maxSize || DEFAULT_MAX_SIZE)) {
16
+ return Promise.resolve([null, new Error('IMAGE_TOO_HEAVY')]);
17
+ }
18
+ return Promise.resolve([image, null]);
19
+ }
@@ -0,0 +1,4 @@
1
+ declare class ValidationError extends Error {
2
+ constructor(messages: Record<string, string>);
3
+ }
4
+ export default ValidationError;
@@ -0,0 +1,7 @@
1
+ class ValidationError extends Error {
2
+ constructor(messages) {
3
+ super(JSON.stringify(messages));
4
+ this.name = 'ValidationError';
5
+ }
6
+ }
7
+ export default ValidationError;