simpledi-app-generator 0.0.1
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 +96 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +53 -0
- package/dist/cli.js.map +1 -0
- package/dist/create_module.d.ts +2 -0
- package/dist/create_module.d.ts.map +1 -0
- package/dist/create_module.js +439 -0
- package/dist/create_module.js.map +1 -0
- package/dist/generate_skeleton.d.ts +2 -0
- package/dist/generate_skeleton.d.ts.map +1 -0
- package/dist/generate_skeleton.js +249 -0
- package/dist/generate_skeleton.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/.env.development +3 -0
- package/dist/templates/AppModule.ts +7 -0
- package/dist/templates/Config.ts +98 -0
- package/dist/templates/UseCaseModule.ts +5 -0
- package/dist/templates/config/getConfigModule.ts +12 -0
- package/dist/templates/db/DbService.ts +18 -0
- package/dist/templates/db/getDb.ts +11 -0
- package/dist/templates/db/getDbModule.ts +15 -0
- package/dist/templates/db/initDb.ts +11 -0
- package/dist/templates/main.routes.ts +10 -0
- package/dist/templates/main.ts +91 -0
- package/dist/templates/package.json +51 -0
- package/dist/templates/schema.ts +1 -0
- package/dist/templates/src/BaseRepository.ts +136 -0
- package/dist/templates/src/BaseService.ts +78 -0
- package/dist/templates/src/IRepository.ts +11 -0
- package/dist/templates/src/IService.ts +6 -0
- package/dist/templates/src/core/baseSchema.ts +13 -0
- package/dist/templates/src/lib/AuthenticationUtils.ts +49 -0
- package/dist/templates/src/lib/errors/BadRequestException.ts +11 -0
- package/dist/templates/src/lib/errors/ConflictException.ts +9 -0
- package/dist/templates/src/lib/errors/ForbiddenException.ts +11 -0
- package/dist/templates/src/lib/errors/HttpException.ts +22 -0
- package/dist/templates/src/lib/errors/InternalServerException.ts +13 -0
- package/dist/templates/src/lib/errors/NotFoundException.ts +11 -0
- package/dist/templates/src/lib/errors/UnauthorizedException.ts +11 -0
- package/dist/templates/src/lib/functions/getEnvFile.ts +19 -0
- package/dist/templates/src/lib/functions/getHostname.ts +18 -0
- package/dist/templates/src/lib/functions/isNotNullNorUndefined.ts +5 -0
- package/dist/templates/src/lib/functions/tableName.ts +5 -0
- package/dist/templates/src/lib/functions/withBaseSchema.ts +30 -0
- package/dist/templates/src/lib/types/BaseEntityInterface.ts +9 -0
- package/dist/templates/src/lib/types/EnvFileNames.ts +7 -0
- package/dist/templates/src/lib/types/Envs.ts +7 -0
- package/dist/templates/src/lib/types/FailedOperation.ts +9 -0
- package/dist/templates/src/lib/types/OperationResult.ts +5 -0
- package/dist/templates/src/lib/types/SharedStubs.ts +16 -0
- package/dist/templates/src/lib/types/SuccessfullOperation.ts +9 -0
- package/dist/templates/src/lib/types/TableNameToken.ts +1 -0
- package/dist/templates/src/lib/types/TokenPayload.ts +6 -0
- package/dist/templates/src/lib/types/UserInterface.ts +10 -0
- package/dist/templates/src/lib/types/getCorsOrigin.ts +20 -0
- package/dist/templates/src/main.routes.ts +11 -0
- package/dist/templates/src/use-case/IUseCase.ts +3 -0
- package/dist/templates/src/use-case/UseCaseModule.ts +6 -0
- package/dist/templates/src/use-case/health-check/HealthCheck.ts +24 -0
- package/dist/templates/src/use-case/health-check/healthCheckRoutes.ts +23 -0
- package/dist/templates/src/use-case/health-check/outputs/HealthCheckSuccess.ts +14 -0
- package/dist/templates/tsconfig.json +46 -0
- package/package.json +41 -0
- package/src/templates/.env.development +3 -0
- package/src/templates/package.json +51 -0
- package/src/templates/tsconfig.json +46 -0
- package/tsconfig.json +40 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { IRepository } from './IRepository';
|
|
2
|
+
import type { NeonHttpDatabase } from 'drizzle-orm/neon-http';
|
|
3
|
+
import type { PgTableWithColumns } from 'drizzle-orm/pg-core';
|
|
4
|
+
import { and, eq, sql } from 'drizzle-orm';
|
|
5
|
+
import { getDb } from 'db/getDb';
|
|
6
|
+
import type { BaseEntityInterface } from '@root/lib';
|
|
7
|
+
import * as schema from './schema';
|
|
8
|
+
import type { DbService } from 'db/DbService';
|
|
9
|
+
export abstract class BaseRepository<
|
|
10
|
+
T extends BaseEntityInterface,
|
|
11
|
+
TSchema extends PgTableWithColumns<any> = PgTableWithColumns<any>,
|
|
12
|
+
> implements IRepository<T> {
|
|
13
|
+
private readonly _db!: NeonHttpDatabase<typeof schema>;
|
|
14
|
+
private readonly _schema!: TSchema;
|
|
15
|
+
|
|
16
|
+
constructor(schema: TSchema, dbService: DbService) {
|
|
17
|
+
this._db = dbService.getDb();
|
|
18
|
+
this._schema = schema;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get schema() {
|
|
22
|
+
return this._schema;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get db() {
|
|
26
|
+
return this._db;
|
|
27
|
+
}
|
|
28
|
+
async create(entity: Partial<T>): Promise<Partial<T>> {
|
|
29
|
+
// Remove undefined values
|
|
30
|
+
const filteredValues = Object.fromEntries(
|
|
31
|
+
Object.entries(entity).filter(([_, v]) => v !== undefined),
|
|
32
|
+
);
|
|
33
|
+
try {
|
|
34
|
+
const [insertedEntity] = await this._db
|
|
35
|
+
.insert(this._schema)
|
|
36
|
+
.values(filteredValues as any)
|
|
37
|
+
.returning();
|
|
38
|
+
|
|
39
|
+
return insertedEntity as Partial<T>;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('Error creating entity:', error);
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async findById(id: string, withDeleted = false): Promise<T | null> {
|
|
47
|
+
const conditions = [
|
|
48
|
+
eq(this._schema.id, id),
|
|
49
|
+
withDeleted ? undefined : eq(this._schema.deleted, false),
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
const result = await this._db
|
|
53
|
+
.select()
|
|
54
|
+
.from(this._schema)
|
|
55
|
+
.where(and(...conditions));
|
|
56
|
+
return result.length > 0 ? (result[0] as unknown as T) : null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async findAll(withDeleted = false): Promise<T[]> {
|
|
60
|
+
const conditions = withDeleted
|
|
61
|
+
? undefined
|
|
62
|
+
: eq(this._schema.deleted, false);
|
|
63
|
+
|
|
64
|
+
return (await this._db
|
|
65
|
+
.select()
|
|
66
|
+
.from(this._schema)
|
|
67
|
+
.where(conditions)) as unknown as T[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async update(id: string, entity: Partial<T>): Promise<T | null> {
|
|
71
|
+
try {
|
|
72
|
+
// Remove undefined values
|
|
73
|
+
const filteredValues = Object.fromEntries(
|
|
74
|
+
Object.entries(entity).filter(
|
|
75
|
+
([_, v]) => v !== undefined || v === null,
|
|
76
|
+
),
|
|
77
|
+
);
|
|
78
|
+
const result = await this._db
|
|
79
|
+
.update(this._schema)
|
|
80
|
+
.set(filteredValues as any)
|
|
81
|
+
.where(eq(this._schema.id, id))
|
|
82
|
+
.returning();
|
|
83
|
+
const updated = Array.isArray(result) ? result[0] : null;
|
|
84
|
+
return updated as unknown as T | null;
|
|
85
|
+
} catch (e) {
|
|
86
|
+
console.error('Error updating entity:', e);
|
|
87
|
+
throw e;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async delete(id: string, soft = true): Promise<boolean> {
|
|
92
|
+
try {
|
|
93
|
+
if (soft) {
|
|
94
|
+
const updatedAt = new Date();
|
|
95
|
+
const [result] = await this._db
|
|
96
|
+
.update(this._schema)
|
|
97
|
+
.set({
|
|
98
|
+
updatedAt,
|
|
99
|
+
deletedAt: updatedAt,
|
|
100
|
+
deleted: true,
|
|
101
|
+
})
|
|
102
|
+
.where(eq(this._schema.id, id))
|
|
103
|
+
.returning({ id: this._schema.id });
|
|
104
|
+
|
|
105
|
+
return !!result;
|
|
106
|
+
} else {
|
|
107
|
+
const result = await this._db
|
|
108
|
+
.delete(this._schema)
|
|
109
|
+
.where(eq(this._schema.id, id));
|
|
110
|
+
|
|
111
|
+
return result.rowCount > 0;
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error('Error deleting entity:', error);
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
*
|
|
121
|
+
* @param config - The configuration object for the query.
|
|
122
|
+
* @returns A promise that resolves to an array of entities.
|
|
123
|
+
* @description This method is used to find multiple entities in the database.
|
|
124
|
+
* It can take a configuration object that specifies the query parameters.
|
|
125
|
+
* The configuration object can include the following properties:
|
|
126
|
+
* - where: The conditions to filter the results.
|
|
127
|
+
* - orderBy: The column to order the results by.
|
|
128
|
+
* - limit: The maximum number of results to return.
|
|
129
|
+
* - offset: The number of results to skip.
|
|
130
|
+
* - with: The related entities to include in the results.
|
|
131
|
+
* - withDeleted: Whether to include deleted entities in the results.
|
|
132
|
+
*/
|
|
133
|
+
abstract find(config?: any): Promise<T[]>;
|
|
134
|
+
|
|
135
|
+
abstract findOne(config?: any): Promise<T>;
|
|
136
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { IRepository } from './IRepository';
|
|
2
|
+
import { randomUUIDv7 } from 'bun';
|
|
3
|
+
import type { IService } from './IService';
|
|
4
|
+
import type { ZodObject } from 'zod';
|
|
5
|
+
import { type BaseEntityInterface } from '@root/lib';
|
|
6
|
+
|
|
7
|
+
export abstract class BaseService<
|
|
8
|
+
T extends BaseEntityInterface,
|
|
9
|
+
U extends ZodObject<any> = ZodObject<any>,
|
|
10
|
+
> implements IService<T> {
|
|
11
|
+
constructor(
|
|
12
|
+
private readonly repository: IRepository<T>,
|
|
13
|
+
private readonly insertSchema: U,
|
|
14
|
+
private readonly updateSchema: U,
|
|
15
|
+
private readonly selectSchema: U,
|
|
16
|
+
) {}
|
|
17
|
+
|
|
18
|
+
async create(entity: Partial<T>): Promise<Partial<T>> {
|
|
19
|
+
try {
|
|
20
|
+
if (entity.id === undefined || entity.id === null) {
|
|
21
|
+
entity.id = randomUUIDv7();
|
|
22
|
+
}
|
|
23
|
+
entity.createdAt = new Date();
|
|
24
|
+
const result = this.insertSchema.safeParse(entity);
|
|
25
|
+
if (!result.success) {
|
|
26
|
+
return Promise.reject(result.error);
|
|
27
|
+
}
|
|
28
|
+
return await this.repository.create(entity);
|
|
29
|
+
} catch (e) {
|
|
30
|
+
console.error(e);
|
|
31
|
+
throw e;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async find(config?: any): Promise<T[]> {
|
|
36
|
+
return await this.repository.find(config);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async findOne(config?: any): Promise<T> {
|
|
40
|
+
return await this.repository.findOne(config);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async findById(id: string, withDeleted?: boolean): Promise<T | null> {
|
|
44
|
+
try {
|
|
45
|
+
return await this.repository.findById(id, withDeleted);
|
|
46
|
+
} catch (e) {
|
|
47
|
+
console.error(e);
|
|
48
|
+
throw e;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async findAll(withDeleted?: boolean): Promise<Partial<T>[]> {
|
|
53
|
+
try {
|
|
54
|
+
return await this.repository.findAll(withDeleted);
|
|
55
|
+
} catch (e) {
|
|
56
|
+
console.error(e);
|
|
57
|
+
throw e;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async update(id: string, entity: Partial<T>): Promise<Partial<T> | null> {
|
|
62
|
+
try {
|
|
63
|
+
entity.updatedAt = new Date();
|
|
64
|
+
const result = this.updateSchema.safeParse(entity);
|
|
65
|
+
if (!result.success) {
|
|
66
|
+
return Promise.reject(result.error);
|
|
67
|
+
}
|
|
68
|
+
return await this.repository.update(id, entity);
|
|
69
|
+
} catch (e) {
|
|
70
|
+
console.error(e);
|
|
71
|
+
throw e;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async delete(id: string, soft: boolean): Promise<boolean> {
|
|
76
|
+
return await this.repository.delete(id, soft);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BaseEntityInterface } from '@root/lib';
|
|
2
|
+
|
|
3
|
+
export interface IRepository<T extends BaseEntityInterface> {
|
|
4
|
+
create(entity: Partial<T>): Promise<Partial<T>>;
|
|
5
|
+
findById(id: string, withDeleted?: boolean): Promise<T | null>;
|
|
6
|
+
findAll(withDeleted?: boolean): Promise<Partial<T>[]>;
|
|
7
|
+
update(id: string, entity: Partial<T>): Promise<Partial<T> | null>;
|
|
8
|
+
delete(id: string, soft: boolean): Promise<boolean>;
|
|
9
|
+
find(config?: any): Promise<T[]>;
|
|
10
|
+
findOne(config?: any): Promise<T>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { boolean, timestamp, uuid } from 'drizzle-orm/pg-core';
|
|
2
|
+
|
|
3
|
+
// Base entity schema with common fields
|
|
4
|
+
export const baseSchema = {
|
|
5
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
6
|
+
createdAt: timestamp('created_at').defaultNow(),
|
|
7
|
+
updatedAt: timestamp('updated_at').defaultNow(),
|
|
8
|
+
deleted: boolean('deleted').default(false),
|
|
9
|
+
deletedAt: timestamp('deleted_at').defaultNow(),
|
|
10
|
+
createdBy: uuid('created_by'),
|
|
11
|
+
updatedBy: uuid('updated_by'),
|
|
12
|
+
};
|
|
13
|
+
export type BaseSchemaType = typeof baseSchema;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import jwt from 'jsonwebtoken';
|
|
2
|
+
import type {
|
|
3
|
+
PhoneNumberInterface,
|
|
4
|
+
TokenPayload,
|
|
5
|
+
UserInterface,
|
|
6
|
+
} from './types';
|
|
7
|
+
|
|
8
|
+
export class AuthenticationUtils {
|
|
9
|
+
// Secure password hashing using Bun.password.hash
|
|
10
|
+
static async hashPassword(password: string): Promise<string> {
|
|
11
|
+
return Bun.password.hash(password);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Password verification method using Bun.password.verify
|
|
15
|
+
static async verifyPassword(
|
|
16
|
+
inputPassword: string,
|
|
17
|
+
storedHash: string,
|
|
18
|
+
): Promise<boolean> {
|
|
19
|
+
return Bun.password.verify(inputPassword, storedHash);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static async generateToken(user: Partial<UserInterface>): Promise<string> {
|
|
23
|
+
// Replace with your actual secret key
|
|
24
|
+
const secretKey = process.env.JWT_SECRET || 'your-secret-key'; // NEVER hardcode secrets in production!
|
|
25
|
+
|
|
26
|
+
const token = jwt.sign(
|
|
27
|
+
{
|
|
28
|
+
userId: user.id,
|
|
29
|
+
firstName: user.firstName,
|
|
30
|
+
lastName: user.lastName,
|
|
31
|
+
phoneNumber: user.phoneNumber as PhoneNumberInterface,
|
|
32
|
+
roles: user.roles,
|
|
33
|
+
email: user.email,
|
|
34
|
+
inviteCode: user.inviteCode,
|
|
35
|
+
exp: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60,
|
|
36
|
+
iat: Math.floor(Date.now() / 1000),
|
|
37
|
+
},
|
|
38
|
+
secretKey,
|
|
39
|
+
);
|
|
40
|
+
return token;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static verifyToken(token: string): string | TokenPayload {
|
|
44
|
+
// Replace with your actual secret key
|
|
45
|
+
const secretKey = process.env.JWT_SECRET || 'your-secret-key'; // NEVER hardcode secrets in production!
|
|
46
|
+
const verified: TokenPayload = jwt.verify(token, secretKey) as TokenPayload;
|
|
47
|
+
return verified;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { HttpException } from './HttpException';
|
|
2
|
+
import { ReasonPhrases, StatusCodes } from 'http-status-codes';
|
|
3
|
+
|
|
4
|
+
export class BadRequestException extends HttpException {
|
|
5
|
+
readonly name = 'BadRequestException';
|
|
6
|
+
constructor(
|
|
7
|
+
response: string | Record<string, any> = ReasonPhrases.BAD_REQUEST,
|
|
8
|
+
) {
|
|
9
|
+
super(response, StatusCodes.BAD_REQUEST);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { HttpException } from './HttpException';
|
|
2
|
+
import { ReasonPhrases, StatusCodes } from 'http-status-codes';
|
|
3
|
+
|
|
4
|
+
export class ConflictException extends HttpException {
|
|
5
|
+
readonly name = 'ConflictException';
|
|
6
|
+
constructor(response: string | Record<string, any> = ReasonPhrases.CONFLICT) {
|
|
7
|
+
super(response, StatusCodes.CONFLICT);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { HttpException } from './HttpException';
|
|
2
|
+
import { ReasonPhrases, StatusCodes } from 'http-status-codes';
|
|
3
|
+
|
|
4
|
+
export class ForbiddenException extends HttpException {
|
|
5
|
+
readonly name = 'ForbiddenException';
|
|
6
|
+
constructor(
|
|
7
|
+
response: string | Record<string, any> = ReasonPhrases.FORBIDDEN,
|
|
8
|
+
) {
|
|
9
|
+
super(response, StatusCodes.FORBIDDEN);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines the base HTTP exception
|
|
3
|
+
*/
|
|
4
|
+
export class HttpException extends Error {
|
|
5
|
+
/**
|
|
6
|
+
* Instantiate a plain HTTP Exception.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* \`throw new HttpException()\`
|
|
10
|
+
* The \`status\` argument is required, and should be a valid HTTP status code.
|
|
11
|
+
* Best practice is to use the \`HttpStatus\` enum imported from \`nestjs/common\`.
|
|
12
|
+
*
|
|
13
|
+
* @param response string or object describing the error condition.
|
|
14
|
+
* @param status HTTP response status code.
|
|
15
|
+
*/
|
|
16
|
+
constructor(
|
|
17
|
+
private readonly response: string | Record<string, any>,
|
|
18
|
+
readonly status: number,
|
|
19
|
+
) {
|
|
20
|
+
super(typeof response === 'string' ? response : JSON.stringify(response));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { HttpException } from './HttpException';
|
|
2
|
+
import { ReasonPhrases, StatusCodes } from 'http-status-codes';
|
|
3
|
+
|
|
4
|
+
export class InternalServerException extends HttpException {
|
|
5
|
+
readonly name = 'NotFoundException';
|
|
6
|
+
constructor(
|
|
7
|
+
response:
|
|
8
|
+
| string
|
|
9
|
+
| Record<string, any> = ReasonPhrases.INTERNAL_SERVER_ERROR,
|
|
10
|
+
) {
|
|
11
|
+
super(response, StatusCodes.INTERNAL_SERVER_ERROR);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { HttpException } from './HttpException';
|
|
2
|
+
import { ReasonPhrases, StatusCodes } from 'http-status-codes';
|
|
3
|
+
|
|
4
|
+
export class NotFoundException extends HttpException {
|
|
5
|
+
readonly name = 'NotFoundException';
|
|
6
|
+
constructor(
|
|
7
|
+
response: string | Record<string, any> = ReasonPhrases.NOT_FOUND,
|
|
8
|
+
) {
|
|
9
|
+
super(response, StatusCodes.NOT_FOUND);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { HttpException } from './HttpException';
|
|
2
|
+
import { ReasonPhrases, StatusCodes } from 'http-status-codes';
|
|
3
|
+
|
|
4
|
+
export class UnauthorizedException extends HttpException {
|
|
5
|
+
readonly name = 'UnauthorizedException';
|
|
6
|
+
constructor(
|
|
7
|
+
response: string | Record<string, any> = ReasonPhrases.UNAUTHORIZED,
|
|
8
|
+
) {
|
|
9
|
+
super(response, StatusCodes.UNAUTHORIZED);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { EnvFileNames } from '../types/EnvFileNames';
|
|
2
|
+
import { Envs } from '../types/Envs';
|
|
3
|
+
|
|
4
|
+
export const getEnvFile = () => {
|
|
5
|
+
switch (process.env.BUN_ENV) {
|
|
6
|
+
case Envs.DEVELOPMENT:
|
|
7
|
+
return EnvFileNames.DEVELOPMENT;
|
|
8
|
+
case Envs.TESTING:
|
|
9
|
+
return EnvFileNames.TESTING;
|
|
10
|
+
case Envs.PRODUCTION:
|
|
11
|
+
return EnvFileNames.PRODUCTION;
|
|
12
|
+
case Envs.DEBUGGING:
|
|
13
|
+
return EnvFileNames.DEBUGGING;
|
|
14
|
+
case Envs.STAGING:
|
|
15
|
+
return EnvFileNames.STAGING;
|
|
16
|
+
default:
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Envs } from '@root/lib';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns the hostname based on the current environment.
|
|
5
|
+
* @returns The hostname based on the current environment.
|
|
6
|
+
*/
|
|
7
|
+
export function getHostname(): string {
|
|
8
|
+
const env = process.env.BUN_ENV || Envs.DEVELOPMENT;
|
|
9
|
+
console.log(`getHostname: Current environment is ${env}`);
|
|
10
|
+
if (env === Envs.PRODUCTION || env === Envs.STAGING) {
|
|
11
|
+
console.warn(
|
|
12
|
+
`getHostname: Running in ${env} environment, returning '0.0.0.0')`,
|
|
13
|
+
);
|
|
14
|
+
return '0.0.0.0';
|
|
15
|
+
} else {
|
|
16
|
+
return 'localhost';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { pgTable } from 'drizzle-orm/pg-core';
|
|
2
|
+
import type { TableNameToken } from '../types/TableNameToken';
|
|
3
|
+
import { baseSchema } from '@root/core/baseSchema';
|
|
4
|
+
|
|
5
|
+
export const withBaseSchema = <T>(
|
|
6
|
+
tableName: TableNameToken | string,
|
|
7
|
+
schemaObj: T,
|
|
8
|
+
extraConfig?: any,
|
|
9
|
+
baseSchemaOverrides: any = {},
|
|
10
|
+
) => {
|
|
11
|
+
let _baseSchema = { ...baseSchema };
|
|
12
|
+
const overridesKeys: string[] = Object.keys(baseSchemaOverrides);
|
|
13
|
+
const baseSchemaKeys: string[] = Object.keys(baseSchema);
|
|
14
|
+
overridesKeys.forEach((key) => {
|
|
15
|
+
if (baseSchemaKeys.includes(key)) {
|
|
16
|
+
_baseSchema = {
|
|
17
|
+
..._baseSchema,
|
|
18
|
+
[key]: baseSchemaOverrides[key],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
return pgTable(
|
|
23
|
+
tableName,
|
|
24
|
+
{
|
|
25
|
+
...schemaObj,
|
|
26
|
+
..._baseSchema,
|
|
27
|
+
},
|
|
28
|
+
extraConfig,
|
|
29
|
+
);
|
|
30
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Stubs for other missing types imported by copied files
|
|
2
|
+
export type PhoneNumberInterface = any;
|
|
3
|
+
export type Currency = string;
|
|
4
|
+
export type PlaceType = string;
|
|
5
|
+
export type LocationInterface = any;
|
|
6
|
+
export type PlanInterface = any;
|
|
7
|
+
export type ServiceCategoryInterface = any;
|
|
8
|
+
export type ServiceRequestInterface = any;
|
|
9
|
+
export type ServiceRequestPayload = any;
|
|
10
|
+
export type PropertyInterface = any;
|
|
11
|
+
export type WaveError = any;
|
|
12
|
+
export const WaveErrorCodes = {};
|
|
13
|
+
export type TelnyxError = any;
|
|
14
|
+
export const TelnyxErrorCodes = {};
|
|
15
|
+
export const retryWithIncreasingTime = async (fn: any) => fn();
|
|
16
|
+
export const createKeyEnum = (keys: string[]) => keys;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type TableNameToken = string & { readonly tableName: unique symbol };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { inject } from '@kanian77/simple-di';
|
|
2
|
+
import { Config, CONFIG } from 'config/Config';
|
|
3
|
+
import { Envs } from '@root/lib';
|
|
4
|
+
|
|
5
|
+
export const getCorsOrigin = () => {
|
|
6
|
+
if (
|
|
7
|
+
process.env.BUN_ENV !== Envs.PRODUCTION &&
|
|
8
|
+
process.env.BUN_ENV !== Envs.STAGING
|
|
9
|
+
) {
|
|
10
|
+
// Filter out undefined/null/empty string if 'result' isn't a valid URL
|
|
11
|
+
// get frontendUrl from config
|
|
12
|
+
const frontendUrl = inject<Config>(CONFIG).frontendUrl;
|
|
13
|
+
console.log('frontendUrl in getCorsOrigin', frontendUrl);
|
|
14
|
+
return ['http://localhost:5173', frontendUrl];
|
|
15
|
+
} else {
|
|
16
|
+
// For production/staging, ensure a valid string or array of strings
|
|
17
|
+
const prodOrigin = process.env.CORS_ORIGIN;
|
|
18
|
+
return prodOrigin ? prodOrigin.split(',').map((s) => s.trim()) : ['*'];
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import {
|
|
3
|
+
healthCheckRoutes,
|
|
4
|
+
healthCheckRoutesPath,
|
|
5
|
+
} from './use-case/health-check/healthCheckRoutes';
|
|
6
|
+
|
|
7
|
+
const mainRoutes = new Hono();
|
|
8
|
+
|
|
9
|
+
mainRoutes.route(healthCheckRoutesPath, healthCheckRoutes);
|
|
10
|
+
|
|
11
|
+
export { mainRoutes };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Module, Service } from '@kanian77/simple-di';
|
|
2
|
+
import type { IUseCase } from '../IUseCase';
|
|
3
|
+
import {
|
|
4
|
+
HealthCheckSuccess,
|
|
5
|
+
type HealthCheckPayload,
|
|
6
|
+
} from './outputs/HealthCheckSuccess';
|
|
7
|
+
|
|
8
|
+
export const HEALTH_CHECK_USE_CASE_TOKEN = 'HEALTH_CHECK_USE_CASE';
|
|
9
|
+
|
|
10
|
+
@Service({
|
|
11
|
+
token: HEALTH_CHECK_USE_CASE_TOKEN,
|
|
12
|
+
lifecycle: 'transient',
|
|
13
|
+
})
|
|
14
|
+
export class HealthCheck implements IUseCase {
|
|
15
|
+
async execute(): Promise<HealthCheckSuccess> {
|
|
16
|
+
const result: HealthCheckPayload = { status: 'healthy' };
|
|
17
|
+
return new HealthCheckSuccess(result);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const HealthCheckModule = new Module({
|
|
22
|
+
imports: [],
|
|
23
|
+
providers: [{ provide: HEALTH_CHECK_USE_CASE_TOKEN, useClass: HealthCheck }],
|
|
24
|
+
});
|