@tobi2409/fullsty 0.5.4 → 0.5.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tobi2409/fullsty",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "Fullsty is a modular fullstack framework starter built from three core parts: template-engine, layout and proc2rest. It provides a project frame for building fullstack applications with a clear separation of client and server code.",
5
5
  "license": "ISC",
6
6
  "author": "Tobias Hollstein",
@@ -0,0 +1,47 @@
1
+ import { DbConnection } from "../../db-connection/db-connection-wrapper.ts";
2
+ // @ts-ignore kysely is supplied by the server package in generated projects.
3
+ import { KyselyWrapper } from "../../kysely/kysely-wrapper.ts";
4
+ import { authenticationCoreLib } from "../../@tobi2409/authentication-core-lib/authentication-core-lib-wrapper.ts";
5
+ import { DEFAULT_TABLE_NAME, DEFAULT_USER_COLUMN, UserColumn } from "./shared.ts";
6
+
7
+ export namespace AuthenticationCoreKyselyCurrentUser {
8
+ export async function getCurrentUser(
9
+ token: string,
10
+ jwtKey: Parameters<typeof authenticationCoreLib.AuthenticationCoreCurrentUser.getCurrentUser>[1],
11
+ dbConnection: DbConnection,
12
+ tableName: string = DEFAULT_TABLE_NAME,
13
+ columns: UserColumn = DEFAULT_USER_COLUMN,
14
+ verifyOptions: Parameters<typeof authenticationCoreLib.AuthenticationCoreCurrentUser.getCurrentUser>[3] = {}
15
+ ): Promise<string> {
16
+ try {
17
+ const db = KyselyWrapper.buildQueryCompiler(dbConnection.getDriverName());
18
+
19
+ const isActiveCallback = async (uuid: string): Promise<boolean> => {
20
+ const compiledQuery = db
21
+ .selectFrom(tableName)
22
+ .select([
23
+ KyselyWrapper.sql.ref(columns.isActive).as("isActive")
24
+ ])
25
+ .where(KyselyWrapper.sql.ref(columns.uuid), "=", uuid)
26
+ .limit(1)
27
+ .compile();
28
+
29
+ // 'any' is used instead of 'unknown' because calling .query() on an unknown type requires
30
+ // either a type assertion or a separate interface. DbConnection is driver-agnostic, so
31
+ // the concrete pool type (e.g. pg.Pool) is not available here without adding a dependency.
32
+ const connection = await dbConnection.getConnection() as any;
33
+ const result = await connection.query(compiledQuery.sql, compiledQuery.parameters);
34
+
35
+ if (!result.rows?.[0]) {
36
+ return false;
37
+ }
38
+
39
+ return Boolean((result.rows[0] as Record<string, unknown>).isActive);
40
+ };
41
+
42
+ return authenticationCoreLib.AuthenticationCoreCurrentUser.getCurrentUser(token, jwtKey, isActiveCallback, verifyOptions);
43
+ } catch (error) {
44
+ throw error;
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "addToServerPackage": false
3
+ }
@@ -0,0 +1,57 @@
1
+ import { DbConnection } from "../../db-connection/db-connection-wrapper.ts";
2
+ // @ts-ignore kysely is supplied by the server package in generated projects.
3
+ import { KyselyWrapper } from "../../kysely/kysely-wrapper.ts";
4
+ import { authenticationCoreLib } from "../../@tobi2409/authentication-core-lib/authentication-core-lib-wrapper.ts";
5
+ import { DEFAULT_TABLE_NAME, DEFAULT_USER_COLUMN, UserColumn } from "./shared.ts";
6
+
7
+ export namespace AuthenticationCoreKyselyLogin {
8
+ function mapFetchedUser(row: Record<string, unknown> | undefined): authenticationCoreLib.FetchedUser | undefined {
9
+ if (!row) {
10
+ return undefined;
11
+ }
12
+
13
+ return {
14
+ uuid: String(row.uuid),
15
+ mail: String(row.mail),
16
+ password: String(row.password),
17
+ isActive: Boolean(row.isActive)
18
+ };
19
+ }
20
+
21
+ export async function login(
22
+ typedMail: string,
23
+ typedPassword: string,
24
+ jwtKey: Parameters<typeof authenticationCoreLib.AuthenticationCoreLogin.login>[3],
25
+ dbConnection: DbConnection,
26
+ tableName: string = DEFAULT_TABLE_NAME,
27
+ columns: UserColumn = DEFAULT_USER_COLUMN,
28
+ jwtOptions: Parameters<typeof authenticationCoreLib.AuthenticationCoreLogin.login>[4] = {}
29
+ ): Promise<string> {
30
+ try {
31
+ const db = KyselyWrapper.buildQueryCompiler(dbConnection.getDriverName());
32
+
33
+ const compiledQuery = db
34
+ .selectFrom(tableName)
35
+ .select([
36
+ KyselyWrapper.sql.ref(columns.uuid).as("uuid"),
37
+ KyselyWrapper.sql.ref(columns.mail).as("mail"),
38
+ KyselyWrapper.sql.ref(columns.password).as("password"),
39
+ KyselyWrapper.sql.ref(columns.isActive).as("isActive")
40
+ ])
41
+ .where(KyselyWrapper.sql.ref(columns.mail), "=", typedMail)
42
+ .limit(1)
43
+ .compile();
44
+
45
+ // 'any' is used instead of 'unknown' because calling .query() on an unknown type requires
46
+ // either a type assertion or a separate interface. DbConnection is driver-agnostic, so
47
+ // the concrete pool type (e.g. pg.Pool) is not available here without adding a dependency.
48
+ const connection = await dbConnection.getConnection() as any;
49
+ const result = await connection.query(compiledQuery.sql, compiledQuery.parameters);
50
+
51
+ const fetchedUser = mapFetchedUser(result.rows?.[0] as Record<string, unknown> | undefined);
52
+ return authenticationCoreLib.AuthenticationCoreLogin.login(typedMail, typedPassword, fetchedUser, jwtKey, jwtOptions);
53
+ } catch (error) {
54
+ throw error;
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,108 @@
1
+ import { DbConnection } from "../../db-connection/db-connection-wrapper.ts";
2
+ // @ts-ignore kysely is supplied by the server package in generated projects.
3
+ import { KyselyWrapper } from "../../kysely/kysely-wrapper.ts";
4
+ import { authenticationCoreLib } from "../../@tobi2409/authentication-core-lib/authentication-core-lib-wrapper.ts";
5
+ import { DEFAULT_TABLE_NAME, DEFAULT_USER_COLUMN, UserColumn } from "./shared.ts";
6
+
7
+ export namespace AuthenticationCoreKyselyRegister {
8
+ function mapFetchedUser(row: Record<string, unknown> | undefined): authenticationCoreLib.FetchedUser | undefined {
9
+ if (!row) {
10
+ return undefined;
11
+ }
12
+
13
+ return {
14
+ uuid: String(row.uuid),
15
+ mail: String(row.mail),
16
+ password: String(row.password),
17
+ isActive: Boolean(row.isActive)
18
+ };
19
+ }
20
+
21
+ export async function register(
22
+ registrationInputData: authenticationCoreLib.RegistrationInputData,
23
+ customInputData: Record<string, unknown>,
24
+ verificationMail: authenticationCoreLib.VerificationMail,
25
+ dbConnection: DbConnection,
26
+ tableName: string = DEFAULT_TABLE_NAME,
27
+ columns: UserColumn = DEFAULT_USER_COLUMN,
28
+ mailTransportConfig?: Parameters<typeof authenticationCoreLib.AuthenticationCoreRegister.register>[5],
29
+ hashOptions?: Parameters<typeof authenticationCoreLib.AuthenticationCoreRegister.register>[6],
30
+ ): Promise<authenticationCoreLib.FetchedUser> {
31
+ const db = KyselyWrapper.buildQueryCompiler(dbConnection.getDriverName());
32
+
33
+ // 'any' is used instead of 'unknown' because calling pool/client APIs on an unknown type requires
34
+ // either a type assertion or a separate interface. DbConnection is driver-agnostic, so
35
+ // the concrete pool/client type (e.g. pg.Pool/PoolClient) is not available here without adding a dependency.
36
+ const pool = await dbConnection.getConnection() as any;
37
+ const transactionConnection = await pool.connect();
38
+
39
+ const mailExistsRoutine = async (mail: string): Promise<boolean> => {
40
+ const compiledQuery = db
41
+ .selectFrom(tableName)
42
+ .select((eb: any) => [eb.val(1).as("exists")])
43
+ .where(KyselyWrapper.sql.ref(columns.mail), "=", mail)
44
+ .limit(1)
45
+ .compile();
46
+
47
+ const result = await transactionConnection.query(compiledQuery.sql, compiledQuery.parameters);
48
+
49
+ return (result.rows?.length ?? 0) > 0;
50
+ };
51
+
52
+ const dataProcessing = async (
53
+ identification: string,
54
+ hashedPassword: string,
55
+ inputData: Record<string, unknown>
56
+ ): Promise<authenticationCoreLib.FetchedUser | undefined> => {
57
+ const userData: Record<string, unknown> = {
58
+ [columns.mail]: identification,
59
+ [columns.password]: hashedPassword,
60
+ [columns.isActive]: false,
61
+ ...inputData
62
+ };
63
+
64
+ const compiledQuery = db
65
+ .insertInto(tableName)
66
+ .values(userData as any)
67
+ .returning([
68
+ KyselyWrapper.sql.ref(columns.uuid).as("uuid"),
69
+ KyselyWrapper.sql.ref(columns.mail).as("mail"),
70
+ KyselyWrapper.sql.ref(columns.password).as("password"),
71
+ KyselyWrapper.sql.ref(columns.isActive).as("isActive")
72
+ ])
73
+ .compile();
74
+
75
+ const result = await transactionConnection.query(compiledQuery.sql, compiledQuery.parameters);
76
+ return mapFetchedUser(result.rows?.[0] as Record<string, unknown> | undefined);
77
+ };
78
+
79
+ try {
80
+ await transactionConnection.query("BEGIN");
81
+
82
+ const registeredUser = await authenticationCoreLib.AuthenticationCoreRegister.register(
83
+ registrationInputData,
84
+ mailExistsRoutine,
85
+ customInputData,
86
+ dataProcessing,
87
+ verificationMail,
88
+ mailTransportConfig,
89
+ hashOptions
90
+ );
91
+
92
+ await transactionConnection.query("COMMIT");
93
+ return registeredUser;
94
+ } catch (error) {
95
+ try {
96
+ await transactionConnection.query("ROLLBACK");
97
+ } catch {
98
+ // Ignore rollback errors, original error is more relevant.
99
+ }
100
+
101
+ throw error;
102
+ } finally {
103
+ if (typeof transactionConnection.release === "function") {
104
+ transactionConnection.release();
105
+ }
106
+ }
107
+ }
108
+ }
@@ -0,0 +1,15 @@
1
+ export type UserColumn = {
2
+ uuid: string;
3
+ mail: string;
4
+ password: string;
5
+ isActive: string;
6
+ };
7
+
8
+ export const DEFAULT_TABLE_NAME = "users";
9
+
10
+ export const DEFAULT_USER_COLUMN: UserColumn = {
11
+ uuid: "uuid",
12
+ mail: "mail",
13
+ password: "password",
14
+ isActive: "isActive"
15
+ };