From 70e782c3737f37501896c0de6a8f9c33b46b37c0 Mon Sep 17 00:00:00 2001 From: zulfikar-ditya Date: Sat, 7 Feb 2026 12:17:11 +0700 Subject: [PATCH 1/3] refactor: update import paths and enhance organization of modules --- .github/copilot-instructions.md | 69 +- .gitignore | 4 +- .husky/pre-commit | 2 +- src/base.ts | 2 +- src/bull/queue/send-mail-queue.ts | 2 +- src/bull/worker/send-mail-worker.ts | 5 +- src/index.ts | 3 +- src/libs/cache/cache.ts | 3 +- src/libs/database/postgres/index.ts | 2 +- .../migrations/meta/0000_snapshot.json | 1005 ++++++++--------- .../postgres/migrations/meta/_journal.json | 24 +- src/libs/database/postgres/seed/rbac.seed.ts | 2 +- src/libs/database/postgres/seed/user.seed.ts | 3 +- src/libs/database/redis/index.ts | 2 +- src/libs/guards/permission.guard.ts | 3 +- src/libs/guards/role.guard.ts | 3 +- src/libs/mailer/services/auth-mail.service.ts | 16 +- src/libs/mailer/services/mail.service.ts | 3 +- src/libs/plugins/auth.plugin.ts | 13 +- src/libs/plugins/docs.plugin.ts | 2 +- src/libs/plugins/security.plugin.ts | 2 +- .../repositories/permission.repository.ts | 19 +- src/libs/repositories/role.repository.ts | 20 +- src/libs/repositories/user.repository.ts | 15 +- src/libs/types/elysia/datatable.ts | 3 +- src/libs/types/repositories/user.ts | 2 +- src/libs/utils/elysia/datatable.ts | 8 +- src/libs/utils/elysia/logger.ts | 2 +- src/libs/utils/security/encrypt.ts | 2 +- src/libs/utils/toolkit/date.ts | 2 +- src/modules/auth/index.ts | 6 +- src/modules/auth/schema.ts | 3 +- src/modules/auth/service.ts | 18 +- src/modules/home/index.ts | 11 +- src/modules/profile/index.ts | 6 +- src/modules/profile/service.ts | 13 +- src/modules/settings/permission/index.ts | 8 +- src/modules/settings/permission/service.ts | 10 +- src/modules/settings/role/index.ts | 8 +- src/modules/settings/role/service.ts | 8 +- src/modules/settings/select-option/index.ts | 3 +- src/modules/settings/select-option/service.ts | 2 +- src/modules/settings/user/index.ts | 9 +- src/modules/settings/user/schema.ts | 3 +- src/modules/settings/user/service.ts | 11 +- tsconfig.json | 11 +- 46 files changed, 677 insertions(+), 696 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 661c517..93bddca 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -70,19 +70,76 @@ return await UserRepository().UserInformation(user.id); ### Import Organization -Use absolute imports with path aliases configured in tsconfig.json: +Use absolute imports with granular path aliases configured in tsconfig.json. Each directory in `src/libs` has its own dedicated alias: ```typescript -import { UserRepository, Hash, BadRequestError } from "@libs"; +import { BadRequestError, UnauthorizedError } from "@errors"; +import { db, users, userRoles } from "@database"; +import { UserRepository } from "@repositories"; +import { Hash, log } from "@utils"; +import { UserInformation, DatatableType } from "@types"; +import { StrongPassword } from "@default"; +import { AuthPlugin } from "@plugins"; import { eq, and, or } from "drizzle-orm"; ``` -Group imports in this order: +**Available Path Aliases:** + +- `@base` - Base Elysia app configuration +- `@bull` - Queue and worker files +- `@cache` - Cache utilities and constants +- `@config` - Configuration files (AppConfig, DatabaseConfig, etc.) +- `@database` - Database related (db instance, schemas, tables, RedisClient) +- `@default` - Default constants (StrongPassword, paginationLength, etc.) +- `@errors` - Custom error classes +- `@guards` - Authorization guards (RoleGuard, PermissionGuard) +- `@mailer` - Email services and templates +- `@plugins` - Elysia plugins (AuthPlugin, SecurityPlugin, etc.) +- `@repositories` - Repository pattern implementations +- `@types` - TypeScript type definitions and interfaces +- `@utils` - Utility functions (Hash, log, ResponseToolkit, etc.) +- `@modules` - Application modules + +**Import Grouping Order:** 1. External libraries (elysia, drizzle-orm, bullmq, etc.) -2. Internal modules from @libs -3. Internal modules from @modules -4. Type imports +2. Granular aliases by category: + - Configuration: `@config` + - Database: `@database` + - Errors: `@errors` + - Types: `@types` + - Repositories: `@repositories` + - Utils: `@utils` + - Others as needed +3. Relative imports (if absolutely necessary) +4. Type-only imports + +**Examples:** + +```typescript +// Module service example +import { BadRequestError } from "@errors"; +import { db, emailVerifications, users } from "@database"; +import { ForgotPasswordRepository, UserRepository } from "@repositories"; +import { Hash, log } from "@utils"; +import { UserInformation } from "@types"; +import { AuthMailService } from "@mailer"; +import { eq } from "drizzle-orm"; + +// Module index example +import { AuthPlugin } from "@plugins"; +import { JWT_CONFIG } from "@config"; +import { CommonResponseSchemas, ResponseToolkit } from "@utils"; +import { UserInformation } from "@types"; +import Elysia from "elysia"; + +// Repository example +import { BadRequestError, UnauthorizedError } from "@errors"; +import { db, DbTransaction, userRoles, users } from "@database"; +import { Hash } from "@utils"; +import { defaultSort } from "@default"; +import { DatatableType, PaginationResponse, UserInformation } from "@types"; +``` ### File Naming diff --git a/.gitignore b/.gitignore index 996fc0e..0a93789 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,6 @@ yarn-error.log* **/*.tgz **/*.log package-lock.json -**/*.bun \ No newline at end of file +**/*.bun + +/dist \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit index ed25bac..df26641 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -2,4 +2,4 @@ bun install bun lint-staged bunx drizzle-kit generate bunx drizzle-kit migrate -bun run build \ No newline at end of file +bun run build:all \ No newline at end of file diff --git a/src/base.ts b/src/base.ts index c37a88d..50fda8a 100644 --- a/src/base.ts +++ b/src/base.ts @@ -3,7 +3,7 @@ import { LoggerPlugin, RequestPlugin, SecurityPlugin, -} from "@libs"; +} from "@plugins"; import { Elysia } from "elysia"; export const baseApp = new Elysia({ name: "base-app" }) diff --git a/src/bull/queue/send-mail-queue.ts b/src/bull/queue/send-mail-queue.ts index 0dae315..2efe7a8 100644 --- a/src/bull/queue/send-mail-queue.ts +++ b/src/bull/queue/send-mail-queue.ts @@ -1,4 +1,4 @@ -import { RedisClient } from "@libs"; +import { RedisClient } from "@database"; import { Queue } from "bullmq"; const queueRedis = RedisClient.getQueueRedisClient(); diff --git a/src/bull/worker/send-mail-worker.ts b/src/bull/worker/send-mail-worker.ts index 3bdb3d5..14cb661 100644 --- a/src/bull/worker/send-mail-worker.ts +++ b/src/bull/worker/send-mail-worker.ts @@ -1,4 +1,7 @@ -import { EmailOptions, EmailService, log, RedisClient } from "@libs"; +import { RedisClient } from "@database"; +import { EmailService } from "@mailer"; +import { EmailOptions } from "@types"; +import { log } from "@utils"; import { Worker } from "bullmq"; const queueRedis = RedisClient.getQueueRedisClient(); diff --git a/src/index.ts b/src/index.ts index 6153b70..9eab79d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ -import { AppConfig, DocsPlugin } from "@libs"; +import { AppConfig } from "@config"; import { bootstraps } from "@modules"; +import { DocsPlugin } from "@plugins"; import { Elysia } from "elysia"; const app = new Elysia() diff --git a/src/libs/cache/cache.ts b/src/libs/cache/cache.ts index a3ca601..a6cfcb1 100644 --- a/src/libs/cache/cache.ts +++ b/src/libs/cache/cache.ts @@ -1,4 +1,5 @@ -import { log, RedisClient } from "@libs"; +import { RedisClient } from "@database"; +import { log } from "@utils"; import Redis from "ioredis"; class Cache { diff --git a/src/libs/database/postgres/index.ts b/src/libs/database/postgres/index.ts index 7f9b4b9..5ce7b28 100644 --- a/src/libs/database/postgres/index.ts +++ b/src/libs/database/postgres/index.ts @@ -1,4 +1,4 @@ -import { DatabaseConfig } from "@libs"; +import { DatabaseConfig } from "@config"; import { ExtractTablesWithRelations } from "drizzle-orm"; import { drizzle } from "drizzle-orm/node-postgres"; import { PgTransaction } from "drizzle-orm/pg-core"; diff --git a/src/libs/database/postgres/migrations/meta/0000_snapshot.json b/src/libs/database/postgres/migrations/meta/0000_snapshot.json index e09f23e..1c90418 100644 --- a/src/libs/database/postgres/migrations/meta/0000_snapshot.json +++ b/src/libs/database/postgres/migrations/meta/0000_snapshot.json @@ -1,523 +1,484 @@ { - "id": "56d919db-24bf-4263-b628-d5653fc4aadc", - "prevId": "00000000-0000-0000-0000-000000000000", - "version": "7", - "dialect": "postgresql", - "tables": { - "public.email_verifications": { - "name": "email_verifications", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": true, - "notNull": true, - "default": "gen_random_uuid()" - }, - "user_id": { - "name": "user_id", - "type": "uuid", - "primaryKey": false, - "notNull": true - }, - "token": { - "name": "token", - "type": "varchar(255)", - "primaryKey": false, - "notNull": true - }, - "expired_at": { - "name": "expired_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false, - "default": "now()" - } - }, - "indexes": { - "email_verification_token_index": { - "name": "email_verification_token_index", - "columns": [ - { - "expression": "token", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": false, - "method": "btree", - "with": {} - } - }, - "foreignKeys": { - "email_verifications_user_id_users_id_fk": { - "name": "email_verifications_user_id_users_id_fk", - "tableFrom": "email_verifications", - "tableTo": "users", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.password_reset_tokens": { - "name": "password_reset_tokens", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": true, - "notNull": true, - "default": "gen_random_uuid()" - }, - "user_id": { - "name": "user_id", - "type": "uuid", - "primaryKey": false, - "notNull": true - }, - "token": { - "name": "token", - "type": "varchar(255)", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false, - "default": "now()" - } - }, - "indexes": { - "password_reset_token_token_index": { - "name": "password_reset_token_token_index", - "columns": [ - { - "expression": "token", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": false, - "method": "btree", - "with": {} - } - }, - "foreignKeys": { - "password_reset_tokens_user_id_users_id_fk": { - "name": "password_reset_tokens_user_id_users_id_fk", - "tableFrom": "password_reset_tokens", - "tableTo": "users", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.permissions": { - "name": "permissions", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": true, - "notNull": true, - "default": "gen_random_uuid()" - }, - "name": { - "name": "name", - "type": "varchar(255)", - "primaryKey": false, - "notNull": true - }, - "group": { - "name": "group", - "type": "varchar(100)", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "permissions_name_unique": { - "name": "permissions_name_unique", - "nullsNotDistinct": false, - "columns": [ - "name" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.role_permissions": { - "name": "role_permissions", - "schema": "", - "columns": { - "role_id": { - "name": "role_id", - "type": "uuid", - "primaryKey": false, - "notNull": true - }, - "permission_id": { - "name": "permission_id", - "type": "uuid", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "role_permissions_role_id_roles_id_fk": { - "name": "role_permissions_role_id_roles_id_fk", - "tableFrom": "role_permissions", - "tableTo": "roles", - "columnsFrom": [ - "role_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "role_permissions_permission_id_permissions_id_fk": { - "name": "role_permissions_permission_id_permissions_id_fk", - "tableFrom": "role_permissions", - "tableTo": "permissions", - "columnsFrom": [ - "permission_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "role_permissions_role_id_permission_id_pk": { - "name": "role_permissions_role_id_permission_id_pk", - "columns": [ - "role_id", - "permission_id" - ] - } - }, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.roles": { - "name": "roles", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": true, - "notNull": true, - "default": "gen_random_uuid()" - }, - "name": { - "name": "name", - "type": "varchar(100)", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "roles_name_unique": { - "name": "roles_name_unique", - "nullsNotDistinct": false, - "columns": [ - "name" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.user_roles": { - "name": "user_roles", - "schema": "", - "columns": { - "user_id": { - "name": "user_id", - "type": "uuid", - "primaryKey": false, - "notNull": true - }, - "role_id": { - "name": "role_id", - "type": "uuid", - "primaryKey": false, - "notNull": true - }, - "assigned_at": { - "name": "assigned_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "user_roles_user_id_users_id_fk": { - "name": "user_roles_user_id_users_id_fk", - "tableFrom": "user_roles", - "tableTo": "users", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "user_roles_role_id_roles_id_fk": { - "name": "user_roles_role_id_roles_id_fk", - "tableFrom": "user_roles", - "tableTo": "roles", - "columnsFrom": [ - "role_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "user_roles_user_id_role_id_pk": { - "name": "user_roles_user_id_role_id_pk", - "columns": [ - "user_id", - "role_id" - ] - } - }, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.users": { - "name": "users", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": true, - "notNull": true, - "default": "gen_random_uuid()" - }, - "name": { - "name": "name", - "type": "varchar(255)", - "primaryKey": false, - "notNull": true - }, - "email": { - "name": "email", - "type": "varchar(255)", - "primaryKey": false, - "notNull": true - }, - "status": { - "name": "status", - "type": "user_status", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'active'" - }, - "remark": { - "name": "remark", - "type": "varchar(255)", - "primaryKey": false, - "notNull": false - }, - "password": { - "name": "password", - "type": "varchar(255)", - "primaryKey": false, - "notNull": true - }, - "email_verified_at": { - "name": "email_verified_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "deleted_at": { - "name": "deleted_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": { - "users_email_deleted_at_status_index": { - "name": "users_email_deleted_at_status_index", - "columns": [ - { - "expression": "email", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "deleted_at", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "status", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": false, - "method": "btree", - "with": {} - } - }, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - } - }, - "enums": { - "public.user_status": { - "name": "user_status", - "schema": "public", - "values": [ - "active", - "inactive", - "suspended", - "blocked" - ] - } - }, - "schemas": {}, - "sequences": {}, - "roles": {}, - "policies": {}, - "views": {}, - "_meta": { - "columns": {}, - "schemas": {}, - "tables": {} - } -} \ No newline at end of file + "id": "56d919db-24bf-4263-b628-d5653fc4aadc", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.email_verifications": { + "name": "email_verifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "expired_at": { + "name": "expired_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "email_verification_token_index": { + "name": "email_verification_token_index", + "columns": [ + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_verifications_user_id_users_id_fk": { + "name": "email_verifications_user_id_users_id_fk", + "tableFrom": "email_verifications", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.password_reset_tokens": { + "name": "password_reset_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "password_reset_token_token_index": { + "name": "password_reset_token_token_index", + "columns": [ + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "password_reset_tokens_user_id_users_id_fk": { + "name": "password_reset_tokens_user_id_users_id_fk", + "tableFrom": "password_reset_tokens", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.permissions": { + "name": "permissions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "group": { + "name": "group", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "permissions_name_unique": { + "name": "permissions_name_unique", + "nullsNotDistinct": false, + "columns": ["name"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.role_permissions": { + "name": "role_permissions", + "schema": "", + "columns": { + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "permission_id": { + "name": "permission_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "role_permissions_role_id_roles_id_fk": { + "name": "role_permissions_role_id_roles_id_fk", + "tableFrom": "role_permissions", + "tableTo": "roles", + "columnsFrom": ["role_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "role_permissions_permission_id_permissions_id_fk": { + "name": "role_permissions_permission_id_permissions_id_fk", + "tableFrom": "role_permissions", + "tableTo": "permissions", + "columnsFrom": ["permission_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "role_permissions_role_id_permission_id_pk": { + "name": "role_permissions_role_id_permission_id_pk", + "columns": ["role_id", "permission_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.roles": { + "name": "roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "roles_name_unique": { + "name": "roles_name_unique", + "nullsNotDistinct": false, + "columns": ["name"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_roles": { + "name": "user_roles", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "assigned_at": { + "name": "assigned_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_roles_user_id_users_id_fk": { + "name": "user_roles_user_id_users_id_fk", + "tableFrom": "user_roles", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_roles_role_id_roles_id_fk": { + "name": "user_roles_role_id_roles_id_fk", + "tableFrom": "user_roles", + "tableTo": "roles", + "columnsFrom": ["role_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_roles_user_id_role_id_pk": { + "name": "user_roles_user_id_role_id_pk", + "columns": ["user_id", "role_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "user_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "remark": { + "name": "remark", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email_verified_at": { + "name": "email_verified_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "users_email_deleted_at_status_index": { + "name": "users_email_deleted_at_status_index", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.user_status": { + "name": "user_status", + "schema": "public", + "values": ["active", "inactive", "suspended", "blocked"] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/src/libs/database/postgres/migrations/meta/_journal.json b/src/libs/database/postgres/migrations/meta/_journal.json index 0434142..e862142 100644 --- a/src/libs/database/postgres/migrations/meta/_journal.json +++ b/src/libs/database/postgres/migrations/meta/_journal.json @@ -1,13 +1,13 @@ { - "version": "7", - "dialect": "postgresql", - "entries": [ - { - "idx": 0, - "version": "7", - "when": 1770020814009, - "tag": "0000_amused_eddie_brock", - "breakpoints": true - } - ] -} \ No newline at end of file + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1770020814009, + "tag": "0000_amused_eddie_brock", + "breakpoints": true + } + ] +} diff --git a/src/libs/database/postgres/seed/rbac.seed.ts b/src/libs/database/postgres/seed/rbac.seed.ts index 1c2a11b..25e3606 100644 --- a/src/libs/database/postgres/seed/rbac.seed.ts +++ b/src/libs/database/postgres/seed/rbac.seed.ts @@ -1,4 +1,4 @@ -import { db, permissions, roles } from "@libs"; +import { db, permissions, roles } from "@database"; export const RBACSeeder = () => { return db.transaction(async (tx) => { diff --git a/src/libs/database/postgres/seed/user.seed.ts b/src/libs/database/postgres/seed/user.seed.ts index 80221d5..b041168 100644 --- a/src/libs/database/postgres/seed/user.seed.ts +++ b/src/libs/database/postgres/seed/user.seed.ts @@ -1,4 +1,5 @@ -import { db, Hash, roles, userRoles, users } from "@libs"; +import { db, roles, userRoles, users } from "@database"; +import { Hash } from "@utils"; import { eq } from "drizzle-orm"; export const UserSeeder = async () => { diff --git a/src/libs/database/redis/index.ts b/src/libs/database/redis/index.ts index 8bdf656..ec31160 100644 --- a/src/libs/database/redis/index.ts +++ b/src/libs/database/redis/index.ts @@ -1,4 +1,4 @@ -import { RedisConfig } from "@libs"; +import { RedisConfig } from "@config"; import Redis from "ioredis"; export class RedisClient { diff --git a/src/libs/guards/permission.guard.ts b/src/libs/guards/permission.guard.ts index b281eba..443f0ab 100644 --- a/src/libs/guards/permission.guard.ts +++ b/src/libs/guards/permission.guard.ts @@ -1,4 +1,5 @@ -import { ForbiddenError, UserInformation } from "@libs"; +import { ForbiddenError } from "@errors"; +import { UserInformation } from "@types"; export class PermissionGuard { static canActivate( diff --git a/src/libs/guards/role.guard.ts b/src/libs/guards/role.guard.ts index 38dc869..e9b42aa 100644 --- a/src/libs/guards/role.guard.ts +++ b/src/libs/guards/role.guard.ts @@ -1,4 +1,5 @@ -import { ForbiddenError, UserInformation } from "@libs"; +import { ForbiddenError } from "@errors"; +import { UserInformation } from "@types"; export class RoleGuard { static canActivate(user: UserInformation, requiredRoles: string[]): boolean { diff --git a/src/libs/mailer/services/auth-mail.service.ts b/src/libs/mailer/services/auth-mail.service.ts index 470474d..2000184 100644 --- a/src/libs/mailer/services/auth-mail.service.ts +++ b/src/libs/mailer/services/auth-mail.service.ts @@ -12,17 +12,11 @@ // import { StrToolkit } from "@toolkit/string"; import { sendEmailQueue } from "@bull"; -import { - AppConfig, - db, - DbTransaction, - emailVerifications, - ForgotPasswordRepository, - log, - StrToolkit, - UserRepository, - verificationTokenLifetime, -} from "@libs"; +import { AppConfig } from "@config"; +import { db, DbTransaction, emailVerifications } from "@database"; +import { verificationTokenLifetime } from "@default"; +import { ForgotPasswordRepository, UserRepository } from "@repositories"; +import { log, StrToolkit } from "@utils"; export class AuthMailService { async sendVerificationEmail(userId: string, tx?: DbTransaction) { diff --git a/src/libs/mailer/services/mail.service.ts b/src/libs/mailer/services/mail.service.ts index c7457ea..f20fa24 100644 --- a/src/libs/mailer/services/mail.service.ts +++ b/src/libs/mailer/services/mail.service.ts @@ -1,5 +1,6 @@ import { AppConfig, MailConfig } from "@config"; -import { EmailOptions, log } from "@libs"; +import { EmailOptions } from "@types"; +import { log } from "@utils"; import fs from "fs"; import path from "path"; diff --git a/src/libs/plugins/auth.plugin.ts b/src/libs/plugins/auth.plugin.ts index 7aa1cfb..b231ae1 100644 --- a/src/libs/plugins/auth.plugin.ts +++ b/src/libs/plugins/auth.plugin.ts @@ -1,13 +1,10 @@ +import { Cache, UserInformationCacheKey } from "@cache"; +import { JWT_CONFIG } from "@config"; import bearer from "@elysiajs/bearer"; import jwt from "@elysiajs/jwt"; -import { - Cache, - JWT_CONFIG, - UnauthorizedError, - UserInformation, - UserInformationCacheKey, - UserRepository, -} from "@libs"; +import { UnauthorizedError } from "@errors"; +import { UserRepository } from "@repositories"; +import { UserInformation } from "@types"; import Elysia from "elysia"; export const AuthPlugin = new Elysia({ name: "auth" }) diff --git a/src/libs/plugins/docs.plugin.ts b/src/libs/plugins/docs.plugin.ts index 0fc2ecc..39153dd 100644 --- a/src/libs/plugins/docs.plugin.ts +++ b/src/libs/plugins/docs.plugin.ts @@ -1,5 +1,5 @@ +import { AppConfig } from "@config"; import openapi from "@elysiajs/openapi"; -import { AppConfig } from "@libs"; import { Elysia } from "elysia"; export const DocsPlugin = new Elysia({ name: "docs" }).use( diff --git a/src/libs/plugins/security.plugin.ts b/src/libs/plugins/security.plugin.ts index f0b8c5c..8dd1d44 100644 --- a/src/libs/plugins/security.plugin.ts +++ b/src/libs/plugins/security.plugin.ts @@ -1,5 +1,5 @@ +import { CORSConfig } from "@config"; import cors from "@elysiajs/cors"; -import { CORSConfig } from "@libs"; import { Elysia } from "elysia"; import { helmet } from "elysia-helmet"; import { rateLimit } from "elysia-rate-limit"; diff --git a/src/libs/repositories/permission.repository.ts b/src/libs/repositories/permission.repository.ts index 67964b1..0ce8abf 100644 --- a/src/libs/repositories/permission.repository.ts +++ b/src/libs/repositories/permission.repository.ts @@ -1,22 +1,15 @@ -// import { DatatableType, SortDirection } from "@app/apis/types/datatable"; -// import { PaginationResponse } from "@app/apis/types/pagination"; -// import { defaultSort } from "@default/sort"; -// import { NotFoundError, UnprocessableEntityError } from "@packages"; -import { and, asc, desc, eq, ilike, not, or, SQL } from "drizzle-orm"; -import { NotFoundError } from "elysia"; - +import { db, DbTransaction, permissions } from "@database"; +import { defaultSort } from "@default"; +import { UnprocessableEntityError } from "@errors"; import { DatatableType, - db, - DbTransaction, - defaultSort, PaginationResponse, PermissionList, - permissions, PermissionSelectOptions, SortDirection, - UnprocessableEntityError, -} from ".."; +} from "@types"; +import { and, asc, desc, eq, ilike, not, or, SQL } from "drizzle-orm"; +import { NotFoundError } from "elysia"; export const PermissionRepository = () => { const dbInstance = db; diff --git a/src/libs/repositories/role.repository.ts b/src/libs/repositories/role.repository.ts index c0cf5d0..af3bd9d 100644 --- a/src/libs/repositories/role.repository.ts +++ b/src/libs/repositories/role.repository.ts @@ -1,19 +1,15 @@ -import { RoleList } from "@libs"; -import { and, asc, desc, eq, ilike, ne, not, or, SQL } from "drizzle-orm"; -import { NotFoundError } from "elysia"; - +import { db, DbTransaction, rolePermissions, roles } from "@database"; +import { defaultSort } from "@default"; +import { UnprocessableEntityError } from "@errors"; import { - DatatableToolkit, DatatableType, - db, - DbTransaction, - defaultSort, PaginationResponse, - rolePermissions, - roles, + RoleList, SortDirection, - UnprocessableEntityError, -} from ".."; +} from "@types"; +import { DatatableToolkit } from "@utils"; +import { and, asc, desc, eq, ilike, ne, not, or, SQL } from "drizzle-orm"; +import { NotFoundError } from "elysia"; export const RoleRepository = () => { const dbInstance = db; diff --git a/src/libs/repositories/user.repository.ts b/src/libs/repositories/user.repository.ts index 1987fbc..0ed89ee 100644 --- a/src/libs/repositories/user.repository.ts +++ b/src/libs/repositories/user.repository.ts @@ -1,22 +1,17 @@ +import { db, DbTransaction, userRoles, users, UserStatusEnum } from "@database"; +import { defaultSort } from "@default"; +import { BadRequestError, UnauthorizedError } from "@errors"; import { - BadRequestError, DatatableType, - db, - DbTransaction, - defaultSort, - Hash, PaginationResponse, SortDirection, - UnauthorizedError, UserCreate, UserDetail, UserForAuth, UserInformation, UserList, - userRoles, - users, - UserStatusEnum, -} from "@libs"; +} from "@types"; +import { Hash } from "@utils"; import { and, asc, diff --git a/src/libs/types/elysia/datatable.ts b/src/libs/types/elysia/datatable.ts index 7950ec0..24d0d31 100644 --- a/src/libs/types/elysia/datatable.ts +++ b/src/libs/types/elysia/datatable.ts @@ -1,4 +1,5 @@ -import { paginationLength, SortDirection } from "@libs"; +import { paginationLength } from "@default"; +import { SortDirection } from "@types"; import { t } from "elysia"; export type DatatableType = { diff --git a/src/libs/types/repositories/user.ts b/src/libs/types/repositories/user.ts index 226a29f..06865fd 100644 --- a/src/libs/types/repositories/user.ts +++ b/src/libs/types/repositories/user.ts @@ -1,4 +1,4 @@ -import { UserStatusEnum } from "@libs"; +import { UserStatusEnum } from "@database"; import { t } from "elysia"; export interface UserInformation { diff --git a/src/libs/utils/elysia/datatable.ts b/src/libs/utils/elysia/datatable.ts index 7d53de8..b5cc3c5 100644 --- a/src/libs/utils/elysia/datatable.ts +++ b/src/libs/utils/elysia/datatable.ts @@ -1,9 +1,5 @@ -import { - DatatableType, - defaultSort, - paginationLength, - SortDirection, -} from "@libs"; +import { defaultSort, paginationLength } from "@default"; +import { DatatableType, SortDirection } from "@types"; import { PgColumn } from "drizzle-orm/pg-core"; // Define the query type that includes filter parameters diff --git a/src/libs/utils/elysia/logger.ts b/src/libs/utils/elysia/logger.ts index fc5530d..ebfd610 100644 --- a/src/libs/utils/elysia/logger.ts +++ b/src/libs/utils/elysia/logger.ts @@ -1,5 +1,5 @@ import { LoggerOptions } from "@bogeychan/elysia-logger/dist/types"; -import { AppConfig } from "@libs"; +import { AppConfig } from "@config"; import { destination, Logger, pino } from "pino"; const isProd = AppConfig.APP_ENV !== "development"; diff --git a/src/libs/utils/security/encrypt.ts b/src/libs/utils/security/encrypt.ts index 6ee1332..96f893e 100644 --- a/src/libs/utils/security/encrypt.ts +++ b/src/libs/utils/security/encrypt.ts @@ -1,4 +1,4 @@ -import { AppConfig } from "@libs"; +import { AppConfig } from "@config"; import * as CryptoJS from "crypto-js"; export class EncryptionToolkit { diff --git a/src/libs/utils/toolkit/date.ts b/src/libs/utils/toolkit/date.ts index 778c267..5ba191e 100644 --- a/src/libs/utils/toolkit/date.ts +++ b/src/libs/utils/toolkit/date.ts @@ -1,4 +1,4 @@ -import { AppConfig } from "@libs"; +import { AppConfig } from "@config"; import dayjs from "dayjs"; import advancedFormat from "dayjs/plugin/advancedFormat"; import relativeTime from "dayjs/plugin/relativeTime"; diff --git a/src/modules/auth/index.ts b/src/modules/auth/index.ts index 53c94b5..6c99f69 100644 --- a/src/modules/auth/index.ts +++ b/src/modules/auth/index.ts @@ -1,11 +1,11 @@ +import { JWT_CONFIG } from "@config"; import { jwt } from "@elysiajs/jwt"; +import { UserInformation } from "@types"; import { CommonResponseSchemas, - JWT_CONFIG, ResponseToolkit, SuccessResponseSchema, - UserInformation, -} from "@libs"; +} from "@utils"; import Elysia, { t } from "elysia"; import { baseApp } from "../../base"; diff --git a/src/modules/auth/schema.ts b/src/modules/auth/schema.ts index f172ecf..888554c 100644 --- a/src/modules/auth/schema.ts +++ b/src/modules/auth/schema.ts @@ -1,4 +1,5 @@ -import { StrongPassword, UserInformationTypeBox } from "@libs"; +import { StrongPassword } from "@default"; +import { UserInformationTypeBox } from "@types"; import { t } from "elysia"; export const LoginSchema = t.Object({ diff --git a/src/modules/auth/service.ts b/src/modules/auth/service.ts index 06337f2..cba3c69 100644 --- a/src/modules/auth/service.ts +++ b/src/modules/auth/service.ts @@ -1,15 +1,9 @@ -import { - AuthMailService, - BadRequestError, - db, - emailVerifications, - ForgotPasswordRepository, - Hash, - log, - UserInformation, - UserRepository, - users, -} from "@libs"; +import { db, emailVerifications, users } from "@database"; +import { BadRequestError } from "@errors"; +import { AuthMailService } from "@mailer"; +import { ForgotPasswordRepository, UserRepository } from "@repositories"; +import { UserInformation } from "@types"; +import { Hash, log } from "@utils"; import { eq } from "drizzle-orm"; export const AuthService = { diff --git a/src/modules/home/index.ts b/src/modules/home/index.ts index a3352a4..6bd9634 100644 --- a/src/modules/home/index.ts +++ b/src/modules/home/index.ts @@ -1,11 +1,6 @@ -import { - AppConfig, - DateToolkit, - db, - RedisClient, - ResponseToolkit, - SuccessResponseSchema, -} from "@libs"; +import { AppConfig } from "@config"; +import { db, RedisClient } from "@database"; +import { DateToolkit, ResponseToolkit, SuccessResponseSchema } from "@utils"; import { Elysia, t } from "elysia"; import { baseApp } from "../../base"; diff --git a/src/modules/profile/index.ts b/src/modules/profile/index.ts index 209087e..1af3e73 100644 --- a/src/modules/profile/index.ts +++ b/src/modules/profile/index.ts @@ -1,10 +1,10 @@ +import { AuthPlugin } from "@plugins"; +import { UserInformationTypeBox } from "@types"; import { - AuthPlugin, CommonResponseSchemas, ResponseToolkit, SuccessResponseSchema, - UserInformationTypeBox, -} from "@libs"; +} from "@utils"; import Elysia, { t } from "elysia"; import { ProfileService } from "./service"; diff --git a/src/modules/profile/service.ts b/src/modules/profile/service.ts index ba4b9a2..935d845 100644 --- a/src/modules/profile/service.ts +++ b/src/modules/profile/service.ts @@ -1,11 +1,8 @@ -import { - Cache, - db, - UnprocessableEntityError, - UserInformation, - UserInformationCacheKey, - UserRepository, -} from "@libs"; +import { Cache, UserInformationCacheKey } from "@cache"; +import { db } from "@database"; +import { UnprocessableEntityError } from "@errors"; +import { UserRepository } from "@repositories"; +import { UserInformation } from "@types"; import { NotFoundError } from "elysia"; export const ProfileService = { diff --git a/src/modules/settings/permission/index.ts b/src/modules/settings/permission/index.ts index e81888f..dc8852d 100644 --- a/src/modules/settings/permission/index.ts +++ b/src/modules/settings/permission/index.ts @@ -1,13 +1,13 @@ +import { PermissionGuard } from "@guards"; +import { AuthPlugin } from "@plugins"; +import { DatatableQueryParams } from "@types"; import { - AuthPlugin, CommonResponseSchemas, - DatatableQueryParams, DatatableToolkit, PaginatedResponseSchema, - PermissionGuard, ResponseToolkit, SuccessResponseSchema, -} from "@libs"; +} from "@utils"; import Elysia, { t } from "elysia"; import { diff --git a/src/modules/settings/permission/service.ts b/src/modules/settings/permission/service.ts index 21872b4..c2c6f8f 100644 --- a/src/modules/settings/permission/service.ts +++ b/src/modules/settings/permission/service.ts @@ -1,10 +1,6 @@ -import { - DatatableType, - db, - PaginationResponse, - PermissionList, - PermissionRepository, -} from "@libs"; +import { db } from "@database"; +import { PermissionRepository } from "@repositories"; +import { DatatableType, PaginationResponse, PermissionList } from "@types"; export const PermissionService = { findAll: async ( diff --git a/src/modules/settings/role/index.ts b/src/modules/settings/role/index.ts index e6878a3..1487765 100644 --- a/src/modules/settings/role/index.ts +++ b/src/modules/settings/role/index.ts @@ -1,13 +1,13 @@ +import { PermissionGuard } from "@guards"; +import { AuthPlugin } from "@plugins"; +import { DatatableQueryParams } from "@types"; import { - AuthPlugin, CommonResponseSchemas, - DatatableQueryParams, DatatableToolkit, PaginatedResponseSchema, - PermissionGuard, ResponseToolkit, SuccessResponseSchema, -} from "@libs"; +} from "@utils"; import Elysia, { t } from "elysia"; import { CreateRoleSchema, RoleListSchema, UpdateRoleSchema } from "./schema"; diff --git a/src/modules/settings/role/service.ts b/src/modules/settings/role/service.ts index e90cfb3..724ab3b 100644 --- a/src/modules/settings/role/service.ts +++ b/src/modules/settings/role/service.ts @@ -1,9 +1,5 @@ -import { - DatatableType, - PaginationResponse, - RoleList, - RoleRepository, -} from "@libs"; +import { RoleRepository } from "@repositories"; +import { DatatableType, PaginationResponse, RoleList } from "@types"; export const RoleService = { findAll: async ( diff --git a/src/modules/settings/select-option/index.ts b/src/modules/settings/select-option/index.ts index f1f9790..45fb803 100644 --- a/src/modules/settings/select-option/index.ts +++ b/src/modules/settings/select-option/index.ts @@ -1,4 +1,5 @@ -import { AuthPlugin, ResponseToolkit } from "@libs"; +import { AuthPlugin } from "@plugins"; +import { ResponseToolkit } from "@utils"; import Elysia from "elysia"; import { SelectOptionService } from "./service"; diff --git a/src/modules/settings/select-option/service.ts b/src/modules/settings/select-option/service.ts index f25aca7..9ada11f 100644 --- a/src/modules/settings/select-option/service.ts +++ b/src/modules/settings/select-option/service.ts @@ -1,4 +1,4 @@ -import { PermissionRepository, RoleRepository } from "@libs"; +import { PermissionRepository, RoleRepository } from "@repositories"; export const SelectOptionService = { permissionSelect: async () => { diff --git a/src/modules/settings/user/index.ts b/src/modules/settings/user/index.ts index 020afcf..63de5c9 100644 --- a/src/modules/settings/user/index.ts +++ b/src/modules/settings/user/index.ts @@ -1,14 +1,13 @@ +import { PermissionGuard, RoleGuard } from "@guards"; +import { AuthPlugin } from "@plugins"; +import { DatatableQueryParams } from "@types"; import { - AuthPlugin, CommonResponseSchemas, - DatatableQueryParams, DatatableToolkit, PaginatedResponseSchema, - PermissionGuard, ResponseToolkit, - RoleGuard, SuccessResponseSchema, -} from "@libs"; +} from "@utils"; import Elysia from "elysia"; import { diff --git a/src/modules/settings/user/schema.ts b/src/modules/settings/user/schema.ts index fd387d9..4d7b4d3 100644 --- a/src/modules/settings/user/schema.ts +++ b/src/modules/settings/user/schema.ts @@ -1,4 +1,5 @@ -import { StrongPassword, UserStatus } from "@libs"; +import { UserStatus } from "@database"; +import { StrongPassword } from "@default"; import { t } from "elysia"; export const UserStatusSchema = t.Enum(UserStatus); diff --git a/src/modules/settings/user/service.ts b/src/modules/settings/user/service.ts index 9ea1699..ec1c34f 100644 --- a/src/modules/settings/user/service.ts +++ b/src/modules/settings/user/service.ts @@ -1,14 +1,13 @@ +import { db, users } from "@database"; +import { AuthMailService } from "@mailer"; +import { UserRepository } from "@repositories"; import { - AuthMailService, DatatableType, - db, - Hash, PaginationResponse, UserList, - UserRepository, - users, UserStatusEnum, -} from "@libs"; +} from "@types"; +import { Hash } from "@utils"; import { eq } from "drizzle-orm"; export const UserService = { diff --git a/tsconfig.json b/tsconfig.json index 8c5c967..6af1f02 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -103,19 +103,20 @@ "skipLibCheck": true /* Skip type checking all .d.ts files. */, "paths": { - // "@": ["./src", "./src/*"], - "@libs": ["./src/libs", "./src/libs/*"], - "@modules": ["./src/modules", "./src/modules/*"], + "@base": ["./src/base.ts"], + "@bull": ["./src/bull", "./src/bull/*"], + "@cache": ["./src/libs/cache", "./src/libs/cache/*"], "@config": ["./src/libs/config", "./src/libs/config/*"], "@database": ["./src/libs/database", "./src/libs/database/*"], "@default": ["./src/libs/default", "./src/libs/default/*"], "@errors": ["./src/libs/errors", "./src/libs/errors/*"], "@guards": ["./src/libs/guards", "./src/libs/guards/*"], + "@mailer": ["./src/libs/mailer", "./src/libs/mailer/*"], "@plugins": ["./src/libs/plugins", "./src/libs/plugins/*"], "@repositories": ["./src/libs/repositories", "./src/libs/repositories/*"], + "@types": ["./src/libs/types", "./src/libs/types/*"], "@utils": ["./src/libs/utils", "./src/libs/utils/*"], - "@modules/*": ["./src/modules/*"], - "@bull": ["./src/bull", "./src/bull/*"] + "@modules": ["./src/modules", "./src/modules/*"] } } } From 1f630d1a6243d7b8832dda1e09d75c669dbffd25 Mon Sep 17 00:00:00 2001 From: zulfikar-ditya Date: Sat, 7 Feb 2026 21:08:51 +0700 Subject: [PATCH 2/3] refactor: streamline package.json scripts and improve error handling --- .env.example | 3 +- .github/workflows/build.yml | 40 ++---------- .husky/pre-commit | 2 +- Dockerfile | 4 +- Makefile | 79 +++++------------------- bun.lock | 57 ++--------------- package.json | 18 ++---- src/base.ts | 10 +-- src/index.ts | 3 +- src/libs/config/app.config.ts | 2 +- src/libs/plugins/error-handler.plugin.ts | 65 +++++++++---------- 11 files changed, 69 insertions(+), 214 deletions(-) diff --git a/.env.example b/.env.example index 0dc9389..012ca13 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,8 @@ +NODE_ENV="development" + APP_NAME="Elysia APP" APP_PORT=3000 APP_URL="http://localhost:3000" -APP_ENV="development" APP_TIMEZONE="UTC" APP_KEY="your-app-key" APP_JWT_SECRET="your-jwt-secret" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4fd7467..b4ea580 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -63,16 +63,11 @@ jobs: with: bun-version: latest - - name: Set up Node - uses: actions/setup-node@v4 - with: - node-version: "22" - - name: Install dependencies (Bun) run: bun install - name: Install drizzle-kit (Node + npx) - run: npm install drizzle-kit + run: bun add -g drizzle-kit - name: Lint run: bun run lint @@ -81,15 +76,12 @@ jobs: run: bun run format - name: Drizzle Generate - run: npx drizzle-kit generate --config=drizzle.config.ts + run: bunx drizzle-kit generate --config=drizzle.config.ts - name: Drizzle Migrate env: DATABASE_URL: postgres://postgres:postgres@localhost:5432/elysia_db - # REDIS_HOST: localhost - # REDIS_PORT: 6379 - # REDIS_PASSWORD: password123 - run: npx drizzle-kit migrate --config=drizzle.config.ts + run: bunx drizzle-kit migrate --config=drizzle.config.ts - name: Clickhouse Migrate env: @@ -102,28 +94,4 @@ jobs: run: bun run migrate:clickhouse:status - name: Build - run: bun run build:all - - # docker-compose-up: - # needs: build-and-lint - # runs-on: ubuntu-latest - # steps: - # - name: Checkout code - # uses: actions/checkout@v4 - - # - name: Set up Docker Buildx - # uses: docker/setup-buildx-action@v3 - - # - name: Install Docker Compose - # run: | - # sudo apt-get update - # sudo apt-get install -y docker-compose - - # - name: Docker Compose Up - # run: docker-compose up -d - - # - name: Wait for services to be healthy - # run: | - # docker-compose ps - # sleep 20 - # docker-compose ps + run: bun run build diff --git a/.husky/pre-commit b/.husky/pre-commit index df26641..ed25bac 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -2,4 +2,4 @@ bun install bun lint-staged bunx drizzle-kit generate bunx drizzle-kit migrate -bun run build:all \ No newline at end of file +bun run build \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index acf88e8..ddeafde 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,8 +14,8 @@ RUN bun install --production --ignore-scripts COPY . . # Build the app (if needed) -RUN bun run build:all +RUN bun run build EXPOSE 3000 -CMD ["bun", "run", "start:all"] +CMD ["bun", "run", "start"] diff --git a/Makefile b/Makefile index 69e4ef6..3457b4f 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,12 @@ -.PHONY: help dev build start lint lint-fix format seed db-generate db-migrate db-push db-pull db-studio db-drop dev-worker build-worker start-worker dev-server build-server start-server dev-all build-all start-all +.PHONY: help lint lint-fix format seed db-generate db-migrate db-push db-pull db-studio db-drop dev build start # Default target help: @echo "Available commands:" @echo " Development:" - @echo " dev-api - Start API development server with hot reload" - @echo " build-api - Build the API application" - @echo " start-api - Start the API production server" - @echo " dev-server - Start SERVER development server with hot reload" - @echo " build-server - Build the SERVER application" - @echo " start-server - Start the SERVER production server" - @echo " dev-worker - Start WORKER development with hot reload" - @echo " build-worker - Build the WORKER application" - @echo " start-worker - Start the WORKER production service" - @echo " dev-all - Run server and worker in dev mode concurrently" - @echo " build-all - Build server and worker concurrently" - @echo " start-all - Run server and worker in production concurrently" + @echo " dev - Start development server with hot reload" + @echo " build - Build the application" + @echo " start - Start the production server" @echo " lint - Run ESLint" @echo " lint-fix - Fix linting issues automatically" @echo " format - Format code with Prettier" @@ -34,32 +25,14 @@ help: @echo " migrate-clickhouse-status - Check status of ClickHouse migrations" # Development commands -dev-api: - bun run dev:api +dev: + bun run dev -build-api: - bun run build:api +build: + bun run build -start-api: - bun run start:api - -dev-server: - bun run dev:server - -build-server: - bun run build:server - -start-server: - bun run start:server - -dev-worker: - bun run dev:worker - -build-worker: - bun run build:worker - -start-worker: - bun run start:worker +start: + bun run start lint: bun run lint @@ -98,32 +71,12 @@ db-studio: db-drop: bunx drizzle-kit drop -# db-generate: -# npx drizzle-kit generate - -# db-migrate: -# npx drizzle-kit migrate - -# db-push: -# npx drizzle-kit push - -# db-pull: -# npx drizzle-kit introspect - -# db-studio: -# npx drizzle-kit studio - -# db-drop: -# npx drizzle-kit drop - -dev-all: - bun run dev:all - -build-all: - bun run build:all - -start-all: - bun run start:all +deploy-prepare: + @echo "Preparing deployment..." + bun install + bunx drizzle-kit migrate + bun run build + @echo "Deployment package is ready!" # Combined commands for common workflows fresh: db-drop db-push seed diff --git a/bun.lock b/bun.lock index fa26b4d..207d75a 100644 --- a/bun.lock +++ b/bun.lock @@ -37,7 +37,6 @@ "@typescript-eslint/eslint-plugin": "^8.54.0", "@typescript-eslint/parser": "^8.54.0", "bun-types": "latest", - "concurrently": "^9.2.1", "drizzle-kit": "^0.31.8", "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", @@ -229,7 +228,7 @@ "ansi-escapes": ["ansi-escapes@7.2.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw=="], - "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -281,8 +280,6 @@ "cli-truncate": ["cli-truncate@5.1.1", "", { "dependencies": { "slice-ansi": "^7.1.0", "string-width": "^8.0.0" } }, "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A=="], - "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], - "cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="], "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], @@ -295,8 +292,6 @@ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], - "concurrently": ["concurrently@9.2.1", "", { "dependencies": { "chalk": "4.1.2", "rxjs": "7.8.2", "shell-quote": "1.8.3", "supports-color": "8.1.1", "tree-kill": "1.2.2", "yargs": "17.7.2" }, "bin": { "conc": "dist/bin/concurrently.js", "concurrently": "dist/bin/concurrently.js" } }, "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng=="], - "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], "cron-parser": ["cron-parser@4.9.0", "", { "dependencies": { "luxon": "^3.2.1" } }, "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q=="], @@ -345,7 +340,7 @@ "elysia-rate-limit": ["elysia-rate-limit@4.5.0", "", { "dependencies": { "@alloc/quick-lru": "5.2.0", "debug": "4.3.4" }, "peerDependencies": { "elysia": ">= 1.0.0" } }, "sha512-nsbl3WLvrGiG/SdTgevAsjCUJhY34Bgf+7bDOYrjTPZyS7Hd4MLuLc4MUr9TFsSJWPvKpzyrX2HW8IWjRbex1Q=="], - "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], @@ -369,8 +364,6 @@ "esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="], - "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], "eslint": ["eslint@9.39.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw=="], @@ -445,8 +438,6 @@ "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], - "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], - "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], @@ -515,7 +506,7 @@ "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], - "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + "is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], @@ -713,8 +704,6 @@ "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="], - "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], - "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], @@ -725,8 +714,6 @@ "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], - "rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="], - "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="], @@ -749,8 +736,6 @@ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], - "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], @@ -777,7 +762,7 @@ "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], - "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "string-width": ["string-width@8.1.1", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw=="], "string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-object-atoms": "^1.0.0", "has-property-descriptors": "^1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="], @@ -785,7 +770,7 @@ "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="], - "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], @@ -793,7 +778,7 @@ "strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="], - "supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], @@ -807,8 +792,6 @@ "token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="], - "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], - "ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="], "tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="], @@ -859,14 +842,8 @@ "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], - "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], - "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], - "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], - - "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], - "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], @@ -883,12 +860,6 @@ "bullmq/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], - "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - - "cli-truncate/string-width": ["string-width@8.1.1", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw=="], - - "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], - "elysia-rate-limit/debug": ["debug@4.3.4", "", { "dependencies": { "ms": "2.1.2" } }, "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ=="], "eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], @@ -901,12 +872,8 @@ "fdir/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - "log-update/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], - "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], - "tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], "tsx/esbuild": ["esbuild@0.27.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="], @@ -915,8 +882,6 @@ "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], - "wrap-ansi/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], @@ -963,12 +928,8 @@ "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], - "cli-truncate/string-width/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - "elysia-rate-limit/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="], - "log-update/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="], "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.2", "", { "os": "android", "cpu": "arm" }, "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA=="], @@ -1020,11 +981,5 @@ "tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ=="], "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.2", "", { "os": "win32", "cpu": "x64" }, "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ=="], - - "wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], - - "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - - "cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], } } diff --git a/package.json b/package.json index a5c7416..163debd 100644 --- a/package.json +++ b/package.json @@ -7,22 +7,13 @@ "type": "module", "private": true, "scripts": { - "start:server": "bun run ./src/index.ts", - "dev:server": "bun run --hot --watch ./src/index.ts", - "build:server": "bun build ./src/index.ts --outdir=dist --target=node", - "start:worker": "bun run ./src/bull/index.ts", - "dev:worker": "bun run --hot --watch ./src/bull/index.ts", - "build:worker": "bun build ./src/bull/index.ts --outdir=dist --target=node", - "start:api": "bun run ./src/index.ts", - "dev:api": "bun run --hot --watch ./src/index.ts", - "build:api": "bun build ./src/index.ts --outdir=dist --target=node", + "start": "bun run ./dist/server/index.js", + "dev": "bun run --hot --watch ./src/index.ts", + "build": "bun build ./src/index.ts --outdir=dist/server --target=node", "lint": "bun run eslint . --ext .ts,.js", "lint:fix": "bun run eslint . --ext .ts,.js --fix", "format": "bun run prettier --write .", - "prepare": "husky", - "dev:all": "concurrently --names \"server,worker\" --prefix-colors \"blue,magenta\" \"bun run dev:server\" \"bun run dev:worker\"", - "build:all": "concurrently --names \"server,worker\" --prefix-colors \"blue,magenta\" \"bun run build:server\" \"bun run build:worker\"", - "start:all": "concurrently --names \"server,worker\" --prefix-colors \"blue,magenta\" \"bun run start:server\" \"bun run start:worker\"", + "prepare": "husky install", "db:postgres:seed": "bun run ./src/libs/database/postgres/seed/index.ts", "migrate:clickhouse": "bun run ./src/libs/database/clickhouse/scripts/migrate.ts migrate", "migrate:clickhouse:status": "bun run ./src/libs/database/clickhouse/scripts/migrate.ts status" @@ -60,7 +51,6 @@ "@typescript-eslint/eslint-plugin": "^8.54.0", "@typescript-eslint/parser": "^8.54.0", "bun-types": "latest", - "concurrently": "^9.2.1", "drizzle-kit": "^0.31.8", "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", diff --git a/src/base.ts b/src/base.ts index 50fda8a..4d43043 100644 --- a/src/base.ts +++ b/src/base.ts @@ -1,13 +1,7 @@ -import { - ErrorHandlerPlugin, - LoggerPlugin, - RequestPlugin, - SecurityPlugin, -} from "@plugins"; +import { LoggerPlugin, RequestPlugin, SecurityPlugin } from "@plugins"; import { Elysia } from "elysia"; export const baseApp = new Elysia({ name: "base-app" }) .use(RequestPlugin) .use(SecurityPlugin) - .use(LoggerPlugin) - .use(ErrorHandlerPlugin); + .use(LoggerPlugin); diff --git a/src/index.ts b/src/index.ts index 9eab79d..726b9a3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,11 @@ import { AppConfig } from "@config"; import { bootstraps } from "@modules"; -import { DocsPlugin } from "@plugins"; +import { DocsPlugin, ErrorHandlerPlugin } from "@plugins"; import { Elysia } from "elysia"; const app = new Elysia() .use(DocsPlugin) + .use(ErrorHandlerPlugin) .use(bootstraps) .listen(AppConfig.APP_PORT); diff --git a/src/libs/config/app.config.ts b/src/libs/config/app.config.ts index 6a49d50..0343515 100644 --- a/src/libs/config/app.config.ts +++ b/src/libs/config/app.config.ts @@ -18,7 +18,7 @@ export const AppConfig: IAppConfig = { APP_NAME: process.env.APP_NAME || "Elysia App", APP_PORT: Number(process.env.APP_PORT) || 3000, APP_URL: process.env.APP_URL || "http://localhost:3000", - APP_ENV: (process.env.APP_ENV || "development") as + APP_ENV: (process.env.NODE_ENV || "development") as | "development" | "staging" | "production", diff --git a/src/libs/plugins/error-handler.plugin.ts b/src/libs/plugins/error-handler.plugin.ts index d48736f..bb0963e 100644 --- a/src/libs/plugins/error-handler.plugin.ts +++ b/src/libs/plugins/error-handler.plugin.ts @@ -1,26 +1,15 @@ +import { Elysia } from "elysia"; + import { BadRequestError, ForbiddenError, + NotFoundError, RateLimitError, UnauthorizedError, UnprocessableEntityError, -} from "@errors"; -import { Elysia, NotFoundError } from "elysia"; - +} from "../errors"; import { LoggerPlugin } from "./logger.plugin"; -// Type definitions for Elysia validation errors -interface ValidationError { - path?: string; - message?: string; -} - -interface ValidationErrorDetails { - validator?: { - errors?: ValidationError[]; - }; -} - export const ErrorHandlerPlugin = new Elysia({ name: "error-handler", }) @@ -28,7 +17,6 @@ export const ErrorHandlerPlugin = new Elysia({ .onError(({ code, error, set, log }) => { if (error instanceof BadRequestError) { set.status = 400; - log?.warn({ error: error.message, errors: error.error }, "Bad request"); return { status: 400, success: false, @@ -39,10 +27,6 @@ export const ErrorHandlerPlugin = new Elysia({ if (error instanceof UnprocessableEntityError) { set.status = 422; - log?.warn( - { error: error.message, errors: error.error }, - "Validation error", - ); return { status: 422, success: false, @@ -53,7 +37,6 @@ export const ErrorHandlerPlugin = new Elysia({ if (error instanceof NotFoundError) { set.status = 404; - log?.warn({ error: error.message }, "Not found"); return { status: 404, success: false, @@ -64,7 +47,6 @@ export const ErrorHandlerPlugin = new Elysia({ if (error instanceof UnauthorizedError) { set.status = 401; - log?.warn({ error: error.message }, "Unauthorized"); return { status: 401, success: false, @@ -75,7 +57,6 @@ export const ErrorHandlerPlugin = new Elysia({ if (error instanceof ForbiddenError) { set.status = 403; - log?.warn({ error: error.message }, "Forbidden"); return { status: 403, success: false, @@ -86,7 +67,6 @@ export const ErrorHandlerPlugin = new Elysia({ if (error instanceof RateLimitError) { set.status = 429; - log?.warn({ error: error.message }, "Rate limited"); return { status: 429, success: false, @@ -97,23 +77,35 @@ export const ErrorHandlerPlugin = new Elysia({ if (code === "VALIDATION") { set.status = 422; - log?.warn({ error }, "Request validation failed"); - const validationError = error as ValidationErrorDetails; - const errors = - validationError?.validator?.errors?.map((err: ValidationError) => ({ - field: err.path?.replace("/", "") || "unknown", - message: err.message || "Validation failed", - })) ?? []; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const validationError = error as any; + const errors: { field: string; message: string }[] = []; + + if (validationError.all && typeof validationError.all === "object") { + for (const err of validationError.all) { + errors.push({ + field: err.path?.replace(/^\//, "") || "unknown", + message: err.message || "Validation failed", + }); + } + } + + if (!errors.length && validationError.valueError) { + errors.push({ + field: + validationError.valueError.path?.replace(/^\//, "") || "unknown", + message: validationError.valueError.message || "Validation failed", + }); + } return { status: 422, success: false, message: "Request validation failed", - errors: - errors.length > 0 - ? errors - : [{ field: "general", message: "Invalid request data" }], + errors: errors.length + ? errors + : [{ field: "general", message: "Invalid request data" }], }; } @@ -146,4 +138,5 @@ export const ErrorHandlerPlugin = new Elysia({ message: "Internal Server Error", data: null, }; - }); + }) + .as("global"); From cff010d42122b1471dc69774b2af3642be98fa3c Mon Sep 17 00:00:00 2001 From: zulfikar-ditya Date: Sun, 8 Feb 2026 15:36:19 +0700 Subject: [PATCH 3/3] refactor: update Makefile paths, enhance error handling, and improve response schemas --- Makefile | 2 +- eslint.config.mjs | 9 +- src/libs/mailer/services/auth-mail.service.ts | 17 +- src/libs/mailer/services/mail.service.ts | 4 +- src/libs/plugins/error-handler.plugin.ts | 23 +- src/libs/repositories/user.repository.ts | 9 +- src/libs/utils/elysia/response.ts | 99 +++++- src/modules/auth/service.ts | 299 +++++++----------- src/modules/settings/select-option/index.ts | 7 + 9 files changed, 239 insertions(+), 230 deletions(-) diff --git a/Makefile b/Makefile index 3457b4f..7e04b48 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ format: bun run format db-seed: - bun run infra/seed/index.ts + bun run ./src/libs/database/postgres/seed/index.ts migrate-clickhouse: bun run migrate:clickhouse diff --git a/eslint.config.mjs b/eslint.config.mjs index 07f24e1..56ba158 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -85,7 +85,14 @@ export default tseslint.config( // ---- General JS Rules ---- // "no-unused-expressions": "error", - "no-unused-vars": "error", + "no-unused-vars": [ + "error", + { + args: "after-used", + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + }, + ], "no-console": "warn", "no-undef": "off", "no-redeclare": "warn", diff --git a/src/libs/mailer/services/auth-mail.service.ts b/src/libs/mailer/services/auth-mail.service.ts index 2000184..5aebc91 100644 --- a/src/libs/mailer/services/auth-mail.service.ts +++ b/src/libs/mailer/services/auth-mail.service.ts @@ -1,16 +1,3 @@ -// import { sendEmailQueue } from "@app/worker/queue/send-email.queue"; -// import { AppConfig } from "@config"; -// import { verificationTokenLifetime } from "@default/token-lifetime"; -// import { log } from "@packages"; -// import { db } from "@postgres/index"; -// import { -// DbTransaction, -// ForgotPasswordRepository, -// UserRepository, -// } from "@postgres/repositories"; -// import { emailVerifications } from "@postgres/schema"; -// import { StrToolkit } from "@toolkit/string"; - import { sendEmailQueue } from "@bull"; import { AppConfig } from "@config"; import { db, DbTransaction, emailVerifications } from "@database"; @@ -34,7 +21,7 @@ export class AuthMailService { await sendEmailQueue.add("send-email", { subject: "Email verification", to: user.email, - template: "/auth/email-verification", + template: "auth/email-verification", variables: { user_id: user.id, user_name: user.name, @@ -61,7 +48,7 @@ export class AuthMailService { await sendEmailQueue.add("send-email", { subject: "Reset Password", to: user.email, - template: "/auth/forgot-password", + template: "auth/forgot-password", variables: { user_id: user.id, user_name: user.name, diff --git a/src/libs/mailer/services/mail.service.ts b/src/libs/mailer/services/mail.service.ts index f20fa24..6996b0b 100644 --- a/src/libs/mailer/services/mail.service.ts +++ b/src/libs/mailer/services/mail.service.ts @@ -14,8 +14,8 @@ export const EmailService = { if (options.template) { try { const templatePath = path.join( - __dirname, - "templates", + process.cwd(), + "src/libs/mailer/templates", `${options.template}.html`, ); htmlContent = fs.readFileSync(templatePath, "utf-8"); diff --git a/src/libs/plugins/error-handler.plugin.ts b/src/libs/plugins/error-handler.plugin.ts index bb0963e..73aa44e 100644 --- a/src/libs/plugins/error-handler.plugin.ts +++ b/src/libs/plugins/error-handler.plugin.ts @@ -78,11 +78,11 @@ export const ErrorHandlerPlugin = new Elysia({ if (code === "VALIDATION") { set.status = 422; - // eslint-disable-next-line @typescript-eslint/no-explicit-any + /* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call */ const validationError = error as any; const errors: { field: string; message: string }[] = []; - if (validationError.all && typeof validationError.all === "object") { + if (Array.isArray(validationError.all)) { for (const err of validationError.all) { errors.push({ field: err.path?.replace(/^\//, "") || "unknown", @@ -92,12 +92,25 @@ export const ErrorHandlerPlugin = new Elysia({ } if (!errors.length && validationError.valueError) { + const valueErrors = Array.isArray(validationError.valueError) + ? validationError.valueError + : [validationError.valueError]; + + for (const err of valueErrors) { + errors.push({ + field: err.path?.replace(/^\//, "") || "unknown", + message: err.message || "Validation failed", + }); + } + } + + if (!errors.length && validationError.message) { errors.push({ - field: - validationError.valueError.path?.replace(/^\//, "") || "unknown", - message: validationError.valueError.message || "Validation failed", + field: "general", + message: validationError.message, }); } + /* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call */ return { status: 422, diff --git a/src/libs/repositories/user.repository.ts b/src/libs/repositories/user.repository.ts index 0ed89ee..9c33395 100644 --- a/src/libs/repositories/user.repository.ts +++ b/src/libs/repositories/user.repository.ts @@ -480,7 +480,7 @@ export const UserRepository = () => { findByEmail: async ( email: string, tx?: DbTransaction, - ): Promise => { + ): Promise => { const database = tx || dbInstance; const user = await database.query.users.findFirst({ where: and(eq(users.email, email), isNull(users.deleted_at)), @@ -495,12 +495,7 @@ export const UserRepository = () => { }); if (!user) { - throw new BadRequestError("Validation error", [ - { - field: "email", - message: "Invalid email or password", - }, - ]); + return null; } return user; diff --git a/src/libs/utils/elysia/response.ts b/src/libs/utils/elysia/response.ts index d51d482..a4cb00f 100644 --- a/src/libs/utils/elysia/response.ts +++ b/src/libs/utils/elysia/response.ts @@ -6,14 +6,14 @@ import { t, TSchema } from "elysia"; export const SuccessResponseSchema = (dataSchema: T) => t.Object({ - status: t.Number(), + status: t.Literal(200), success: t.Literal(true), message: t.String(), data: dataSchema, }); export const ErrorResponseSchema = t.Object({ - status: t.Number(), + status: t.Literal(400), success: t.Literal(false), message: t.String(), data: t.Null(), @@ -32,7 +32,7 @@ export const BadRequestResponseSchema = t.Object({ }); export const ValidationErrorResponseSchema = t.Object({ - status: t.Number(), + status: t.Literal(422), success: t.Literal(false), message: t.String(), errors: t.Array( @@ -45,7 +45,7 @@ export const ValidationErrorResponseSchema = t.Object({ export const PaginatedResponseSchema = (itemSchema: T) => t.Object({ - status: t.Number(), + status: t.Literal(200), success: t.Literal(true), message: t.String(), data: t.Object({ @@ -62,22 +62,41 @@ export const PaginatedResponseSchema = (itemSchema: T) => // RESPONSE TYPES // ============================================ -export type SuccessResponse = { - status: number; +export type SuccessResponse200 = { + status: 200; + success: true; + message: string; + data: T; +}; + +export type SuccessResponse201 = { + status: 201; + success: true; + message: string; + data: T; +}; + +export type SuccessResponse202 = { + status: 202; success: true; message: string; data: T; }; +export type SuccessResponse = + | SuccessResponse200 + | SuccessResponse201 + | SuccessResponse202; + export type ErrorResponse = { - status: number; + status: 400 | 401 | 403 | 404 | 409 | 429 | 500 | 503; success: false; message: string; data: null; }; export type ValidationErrorResponse = { - status: number; + status: 422; success: false; message: string; errors: Array<{ @@ -87,7 +106,7 @@ export type ValidationErrorResponse = { }; export type PaginatedResponse = { - status: number; + status: 200; success: true; message: string; data: { @@ -105,6 +124,33 @@ export type PaginatedResponse = { // ============================================ export class ResponseToolkit { + /** + * Create a success response with status 200 + */ + static success( + _data: T, + _message?: string, + _status?: 200, + ): SuccessResponse200; + + /** + * Create a success response with status 201 + */ + static success( + _data: T, + _message: string, + _status: 201, + ): SuccessResponse201; + + /** + * Create a success response with status 202 + */ + static success( + _data: T, + _message: string, + _status: 202, + ): SuccessResponse202; + /** * Create a success response * @param data - The response data @@ -114,10 +160,26 @@ export class ResponseToolkit { static success( data: T, message: string = "Success", - status: number = 200, - ): SuccessResponse { + status: 200 | 201 | 202 = 200, + ): SuccessResponse200 | SuccessResponse201 | SuccessResponse202 { + if (status === 200) { + return { + status: 200, + success: true, + message, + data, + }; + } + if (status === 201) { + return { + status: 201, + success: true, + message, + data, + }; + } return { - status, + status: 202, success: true, message, data, @@ -139,7 +201,7 @@ export class ResponseToolkit { totalCount: number; }, message: string = "Data retrieved successfully", - status: number = 200, + status: 200 = 200, ): PaginatedResponse { return { status, @@ -157,7 +219,10 @@ export class ResponseToolkit { * @param message - Error message * @param status - HTTP status code (default: 400) */ - static error(message: string, status: number = 400): ErrorResponse { + static error( + message: string, + status: 400 | 401 | 403 | 404 | 409 | 429 | 500 | 503 = 400, + ): ErrorResponse { return { status, success: false, @@ -175,7 +240,7 @@ export class ResponseToolkit { static validationError( errors: Array<{ field: string; message: string }>, message: string = "Validation failed", - status: number = 422, + status: 422 = 422, ): ValidationErrorResponse { return { status, @@ -243,7 +308,7 @@ export class ResponseToolkit { static created( data: T, message: string = "Resource created successfully", - ): SuccessResponse { + ): SuccessResponse201 { return this.success(data, message, 201); } @@ -262,7 +327,7 @@ export class ResponseToolkit { static accepted( data: T, message: string = "Request accepted for processing", - ): SuccessResponse { + ): SuccessResponse202 { return this.success(data, message, 202); } } diff --git a/src/modules/auth/service.ts b/src/modules/auth/service.ts index cba3c69..d773866 100644 --- a/src/modules/auth/service.ts +++ b/src/modules/auth/service.ts @@ -8,66 +8,52 @@ import { eq } from "drizzle-orm"; export const AuthService = { singIn: async (email: string, password: string): Promise => { - try { - const user = await UserRepository().findByEmail(email); - - if (!user) { - throw new BadRequestError("Validation error", [ - { - field: "email", - message: "Invalid email or password", - }, - ]); - } - - if (user.email_verified_at === null) { - throw new BadRequestError("Validation error", [ - { - field: "email", - message: "Email not verified. Please check your inbox.", - }, - ]); - } - - if (user.status !== "active") { - throw new BadRequestError("Validation error", [ - { - field: "email", - message: "Your account is inactive. Please contact support.", - }, - ]); - } - - const isPasswordValid = await Hash.compareHash(password, user.password); - - if (!isPasswordValid) { - throw new BadRequestError("Validation error", [ - { - field: "email", - message: "Invalid email or password", - }, - ]); - } - - // Log successful login - log.info( - { userId: user.id, email: user.email }, - "User logged in successfully", - ); + const user = await UserRepository().findByEmail(email); + if (!user) { + throw new BadRequestError("Validation error", [ + { + field: "email", + message: "Invalid email or password", + }, + ]); + } + + if (user.email_verified_at === null) { + throw new BadRequestError("Validation error", [ + { + field: "email", + message: "Email not verified. Please check your inbox.", + }, + ]); + } + + if (user.status !== "active") { + throw new BadRequestError("Validation error", [ + { + field: "email", + message: "Your account is inactive. Please contact support.", + }, + ]); + } - return await UserRepository().UserInformation(user.id); - } catch (error) { - if (error instanceof BadRequestError) { - throw error; - } - log.error({ error, email }, "Login error"); + const isPasswordValid = await Hash.compareHash(password, user.password); + + if (!isPasswordValid) { throw new BadRequestError("Validation error", [ { field: "email", - message: "An error occurred during login", + message: "Invalid email or password", }, ]); } + + // Log successful login + log.info( + { userId: user.id, email: user.email }, + "User logged in successfully", + ); + + return await UserRepository().UserInformation(user.id); }, register: async (data: { @@ -75,117 +61,84 @@ export const AuthService = { email: string; password: string; }): Promise => { - try { - const existingUser = await UserRepository() - .findByEmail(data.email) - .catch(() => null); - - if (existingUser) { - throw new BadRequestError("Validation error", [ - { - field: "email", - message: "Email is already registered", - }, - ]); - } - - const hashedPassword = await Hash.generateHash(data.password); - - await db.transaction(async (tx) => { - const newUser = await UserRepository().create( - { - name: data.name, - email: data.email, - password: hashedPassword, - }, - tx, - ); - - const authMailService = new AuthMailService(); - await authMailService.sendVerificationEmail(newUser.id, tx); - }); - } catch (error) { - if (error instanceof BadRequestError) { - throw error; - } - log.error({ error, email: data.email }, "Registration error"); + const existingUser = await UserRepository().findByEmail(data.email); + if (existingUser) { throw new BadRequestError("Validation error", [ { - field: "general", - message: "An error occurred during registration", + field: "email", + message: "Email is already registered", }, ]); } + + const hashedPassword = await Hash.generateHash(data.password); + + const newUser = await db.transaction(async (tx) => { + return await UserRepository().create( + { + name: data.name, + email: data.email, + password: hashedPassword, + }, + tx, + ); + }); + + const authMailService = new AuthMailService(); + await authMailService.sendVerificationEmail(newUser.id); }, async resentVerificationEmail(email: string): Promise { - try { - const user = await UserRepository() - .findByEmail(email) - .catch(() => null); - - if (!user) { - return; - } - - if (user.email_verified_at) { - log.info( - { userId: user.id, email }, - "Verification email requested for already verified user", - ); - return; - } - - const authMailService = new AuthMailService(); - await authMailService.sendVerificationEmail(user.id); - } catch (error) { - log.error({ error, email }, "Error resending verification email"); + const user = await UserRepository() + .findByEmail(email) + .catch(() => null); + + if (!user) { + return; + } + + if (user.email_verified_at) { + log.info( + { userId: user.id, email }, + "Verification email requested for already verified user", + ); + return; } + + const authMailService = new AuthMailService(); + await authMailService.sendVerificationEmail(user.id); }, verifyEmail: async (token: string): Promise => { - try { - const record = - ( - await db - .select() - .from(emailVerifications) - .where(eq(emailVerifications.token, token)) - )[0] ?? null; - - if (!record || record.expired_at < new Date()) { - throw new BadRequestError("Validation error", [ - { - field: "token", - message: "Invalid or expired verification token", - }, - ]); - } - - await db.transaction(async (trx) => { - await trx - .update(users) - .set({ email_verified_at: new Date() }) - .where(eq(users.id, record.user_id)); - - await trx - .delete(emailVerifications) - .where(eq(emailVerifications.user_id, record.user_id)); - }); - - log.info({ userId: record.user_id }, "Email verified successfully"); - } catch (error) { - if (error instanceof BadRequestError) { - throw error; - } - log.error({ error, token }, "Email verification error"); + const record = + ( + await db + .select() + .from(emailVerifications) + .where(eq(emailVerifications.token, token)) + )[0] ?? null; + + if (!record || record.expired_at < new Date()) { throw new BadRequestError("Validation error", [ { field: "token", - message: "An error occurred during email verification", + message: "Invalid or expired verification token", }, ]); } + + await db.transaction(async (trx) => { + await trx + .update(users) + .set({ email_verified_at: new Date() }) + .where(eq(users.id, record.user_id)); + + await trx + .delete(emailVerifications) + .where(eq(emailVerifications.user_id, record.user_id)); + }); + + log.info({ userId: record.user_id }, "Email verified successfully"); }, forgotPassword: async (email: string): Promise => { @@ -202,48 +155,30 @@ export const AuthService = { }, resetPassword: async (token: string, password: string): Promise => { - try { - const passwordReset = await ForgotPasswordRepository().findByToken(token); - - if (!passwordReset) { - throw new BadRequestError("Validation error", [ - { - field: "token", - message: "Invalid or expired password reset token", - }, - ]); - } - - const hashedPassword = await Hash.generateHash(password); - - await db.transaction(async (trx) => { - await trx - .update(users) - .set({ password: hashedPassword }) - .where(eq(users.id, passwordReset.user_id)); - - await trx - .delete(ForgotPasswordRepository().getTable()) - .where( - eq(ForgotPasswordRepository().getTable().id, passwordReset.id), - ); - }); + const passwordReset = await ForgotPasswordRepository().findByToken(token); - log.info( - { userId: passwordReset.user_id }, - "Password reset successfully", - ); - } catch (error) { - if (error instanceof BadRequestError) { - throw error; - } - log.error({ error, token }, "Password reset error"); + if (!passwordReset) { throw new BadRequestError("Validation error", [ { field: "token", - message: "An error occurred during password reset", + message: "Invalid or expired password reset token", }, ]); } + + const hashedPassword = await Hash.generateHash(password); + + await db.transaction(async (trx) => { + await trx + .update(users) + .set({ password: hashedPassword }) + .where(eq(users.id, passwordReset.user_id)); + + await trx + .delete(ForgotPasswordRepository().getTable()) + .where(eq(ForgotPasswordRepository().getTable().id, passwordReset.id)); + }); + + log.info({ userId: passwordReset.user_id }, "Password reset successfully"); }, }; diff --git a/src/modules/settings/select-option/index.ts b/src/modules/settings/select-option/index.ts index 45fb803..9b71fd0 100644 --- a/src/modules/settings/select-option/index.ts +++ b/src/modules/settings/select-option/index.ts @@ -1,3 +1,4 @@ +import { RoleGuard } from "@guards"; import { AuthPlugin } from "@plugins"; import { ResponseToolkit } from "@utils"; import Elysia from "elysia"; @@ -27,6 +28,9 @@ export const SelectOptionModule = new Elysia({ summary: "Get permission select options", description: "Retrieve select options for permissions.", }, + beforeHandle: function ({ user }) { + RoleGuard.canActivate(user, ["superuser"]); + }, }, ) .get( @@ -44,5 +48,8 @@ export const SelectOptionModule = new Elysia({ summary: "Get role select options", description: "Retrieve select options for roles.", }, + beforeHandle: function ({ user }) { + RoleGuard.canActivate(user, ["superuser"]); + }, }, );