@skalfa/skalfa-api 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/.env.example +75 -75
  2. package/.github/workflows/publish.yml +7 -0
  3. package/CONTRIBUTING.md +45 -0
  4. package/LICENSE +21 -0
  5. package/README.md +79 -85
  6. package/app/app.ts +74 -74
  7. package/app/controllers/base.controller.ts +16 -16
  8. package/app/controllers/iam/auth.controller.ts +143 -143
  9. package/app/controllers/iam/user.controller.ts +92 -92
  10. package/app/jobs/crons/index.ts +8 -8
  11. package/app/jobs/crons/worker.cron.ts +8 -8
  12. package/app/jobs/queues/access-log.queue.worker.ts +32 -32
  13. package/app/jobs/queues/activity-log.queue.worker.ts +32 -32
  14. package/app/jobs/queues/auth.queue.worker.ts +9 -9
  15. package/app/jobs/queues/error-log.queue.worker.ts +32 -32
  16. package/app/jobs/queues/notification.queue.worker.ts +11 -11
  17. package/app/jobs/queues/worker.queue.ts +37 -37
  18. package/app/jobs/sockets/worker.socket.ts +2 -2
  19. package/app/models/iam/user.model.ts +17 -17
  20. package/app/outputs/mails/templates/layout.mail.stub +102 -102
  21. package/app/outputs/mails/templates/user-mail-token.mail.stub +28 -28
  22. package/app/outputs/mails/user-mail-token.mail.ts +17 -17
  23. package/app/outputs/notifications/example.notification.ts +10 -0
  24. package/app/outputs/notifications/index.ts +1 -0
  25. package/app/routes/base.routes.ts +25 -25
  26. package/barrels.json +9 -9
  27. package/database/da.migrations/0000_00/activity_logs.ts +27 -27
  28. package/database/da.migrations/0000_00/logs.ts +41 -41
  29. package/database/migrations/0000_00/notifications.ts +28 -0
  30. package/database/migrations/0000_00/storages.ts +22 -22
  31. package/database/migrations/0000_00/users.ts +55 -55
  32. package/database/seeders/user.seeder.ts +23 -23
  33. package/eslint.config.mjs +36 -36
  34. package/package.json +40 -39
@@ -1,41 +1,41 @@
1
- import { DAMigration } from "@utils"
2
-
3
- export default class CreateAccessLogsTable extends DAMigration {
4
- async up() {
5
- await this.createTable("access_logs",(table) => {
6
- table.uuid()
7
- table.string("method")
8
- table.string("path")
9
- table.int32("status")
10
- table.int32("latency")
11
- table.string("ip")
12
- table.string("agent")
13
- table.dateTime("at")
14
- }, {
15
- engine: "MergeTree",
16
- orderBy: ["at"],
17
- partitionBy: "toYYYYMM(at)",
18
- ttl: "at + INTERVAL 30 DAY DELETE"
19
- })
20
-
21
- await this.createTable("error_logs",(table) => {
22
- table.uuid()
23
- table.string("service")
24
- table.string("key")
25
- table.string("feature")
26
- table.string("error")
27
- table.string("reference")
28
- table.dateTime("at")
29
- }, {
30
- engine: "MergeTree",
31
- orderBy: ["at"],
32
- partitionBy: "toYYYYMM(at)",
33
- ttl: "at + INTERVAL 30 DAY DELETE"
34
- })
35
- }
36
-
37
- async down() {
38
- await this.dropTable("access_logs")
39
- await this.dropTable("error_logs")
40
- }
41
- }
1
+ import { DAMigration } from "@utils"
2
+
3
+ export default class CreateAccessLogsTable extends DAMigration {
4
+ async up() {
5
+ await this.createTable("access_logs",(table) => {
6
+ table.uuid()
7
+ table.string("method")
8
+ table.string("path")
9
+ table.int32("status")
10
+ table.int32("latency")
11
+ table.string("ip")
12
+ table.string("agent")
13
+ table.dateTime("at")
14
+ }, {
15
+ engine: "MergeTree",
16
+ orderBy: ["at"],
17
+ partitionBy: "toYYYYMM(at)",
18
+ ttl: "at + INTERVAL 30 DAY DELETE"
19
+ })
20
+
21
+ await this.createTable("error_logs",(table) => {
22
+ table.uuid()
23
+ table.string("service")
24
+ table.string("key")
25
+ table.string("feature")
26
+ table.string("error")
27
+ table.string("reference")
28
+ table.dateTime("at")
29
+ }, {
30
+ engine: "MergeTree",
31
+ orderBy: ["at"],
32
+ partitionBy: "toYYYYMM(at)",
33
+ ttl: "at + INTERVAL 30 DAY DELETE"
34
+ })
35
+ }
36
+
37
+ async down() {
38
+ await this.dropTable("access_logs")
39
+ await this.dropTable("error_logs")
40
+ }
41
+ }
@@ -0,0 +1,28 @@
1
+ import type { Knex } from 'knex'
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex.schema.createTable('notifications', (table) => {
5
+ table.bigIncrements('id').primary()
6
+ table.string('title')
7
+ table.string('body').nullable()
8
+ table.string('type').nullable()
9
+ table.string('redirect').nullable()
10
+ table.json('data').nullable()
11
+ table.timestamps(true, true)
12
+ table.timestamp('deleted_at', { useTz: true }).nullable().index()
13
+ })
14
+
15
+ await knex.schema.createTable('notification_users', (table) => {
16
+ table.bigIncrements('id').primary()
17
+ table.bigInteger('user_id').nullable().index()
18
+ table.bigInteger('notification_id').nullable().index()
19
+ table.timestamps(true, true)
20
+ table.timestamp('canceled_at', { useTz: true }).nullable().index()
21
+ table.timestamp('deleted_at', { useTz: true }).nullable().index()
22
+ })
23
+ }
24
+
25
+ export async function down(knex: Knex): Promise<void> {
26
+ await knex.schema.dropTableIfExists('notification_users')
27
+ await knex.schema.dropTableIfExists('notifications')
28
+ }
@@ -1,22 +1,22 @@
1
- import type { Knex } from 'knex'
2
-
3
- export async function up(knex: Knex): Promise<void> {
4
- await knex.schema.createTable('storages', (table) => {
5
- table.bigIncrements('id').primary()
6
- table.bigInteger('user_id').nullable().index()
7
- table.string('disk').notNullable().index()
8
- table.string('path').notNullable().index()
9
- table.string('filename').nullable()
10
- table.string('filetype').nullable()
11
- table.string('filesize').nullable()
12
- table.timestamps(true, true)
13
- })
14
-
15
- await knex.schema.createTable('storage_permissions', (table) => {
16
- table.bigIncrements('id').primary()
17
- table.bigInteger('storage_id').unsigned().notNullable().index()
18
- table.bigInteger('user_id').nullable().index()
19
- table.bigInteger('role_id').nullable().index()
20
- table.timestamps(true, true)
21
- })
22
- }
1
+ import type { Knex } from 'knex'
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex.schema.createTable('storages', (table) => {
5
+ table.bigIncrements('id').primary()
6
+ table.bigInteger('user_id').nullable().index()
7
+ table.string('disk').notNullable().index()
8
+ table.string('path').notNullable().index()
9
+ table.string('filename').nullable()
10
+ table.string('filetype').nullable()
11
+ table.string('filesize').nullable()
12
+ table.timestamps(true, true)
13
+ })
14
+
15
+ await knex.schema.createTable('storage_permissions', (table) => {
16
+ table.bigIncrements('id').primary()
17
+ table.bigInteger('storage_id').unsigned().notNullable().index()
18
+ table.bigInteger('user_id').nullable().index()
19
+ table.bigInteger('role_id').nullable().index()
20
+ table.timestamps(true, true)
21
+ })
22
+ }
@@ -1,56 +1,56 @@
1
- import type { Knex } from "knex"
2
-
3
- export async function up(knex: Knex): Promise<void> {
4
- await knex.schema.createTable("users", (table) => {
5
- table.bigIncrements("id").primary()
6
- table.string("name").notNullable()
7
- table.string("email").unique().notNullable()
8
- table.string("password")
9
- table.string("image")
10
- table.timestamp("email_verification_at")
11
- table.timestamps(true, true)
12
- table.softDelete()
13
- })
14
-
15
- await knex.schema.createTable("user_roles", (table) => {
16
- table.bigIncrements("id").primary()
17
- table.string("name").notNullable()
18
- })
19
-
20
- await knex.schema.createTable("user_has_user_roles", (table) => {
21
- table.bigIncrements("id").primary()
22
- table.foreignIdFor("users").notNullable()
23
- table.foreignIdFor("user_roles").notNullable()
24
- table.softDelete()
25
- })
26
-
27
- await knex.schema.createTable("user_access_tokens", (table) => {
28
- table.bigIncrements("id").primary()
29
- table.foreignIdFor("users").notNullable()
30
- table.string("agent").index().notNullable()
31
- table.string("token").unique().index().notNullable()
32
- table.json("permissions").defaultTo(knex.raw(`'[]'::json`))
33
- table.string("last_used_ip").nullable()
34
- table.timestamp("last_used_at")
35
- table.timestamp("expired_at")
36
- table.timestamps(true, true)
37
- table.softDelete()
38
- })
39
-
40
- await knex.schema.createTable("user_mail_tokens", (table) => {
41
- table.bigIncrements("id").primary()
42
- table.foreignIdFor("users").notNullable()
43
- table.string("token").unique().notNullable()
44
- table.timestamp("used_at")
45
- table.timestamps(true, true)
46
- table.softDelete()
47
- })
48
-
49
- await knex.schema.createTable("user_permissions", (table) => {
50
- table.bigIncrements("id").primary()
51
- table.foreignIdFor("users").notNullable()
52
- table.foreignIdFor("user_roles").notNullable()
53
- table.json("permissions").defaultTo(knex.raw(`'[]'::json`))
54
- table.softDelete()
55
- })
1
+ import type { Knex } from "knex"
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex.schema.createTable("users", (table) => {
5
+ table.bigIncrements("id").primary()
6
+ table.string("name").notNullable()
7
+ table.string("email").unique().notNullable()
8
+ table.string("password")
9
+ table.string("image")
10
+ table.timestamp("email_verification_at")
11
+ table.timestamps(true, true)
12
+ table.softDelete()
13
+ })
14
+
15
+ await knex.schema.createTable("user_roles", (table) => {
16
+ table.bigIncrements("id").primary()
17
+ table.string("name").notNullable()
18
+ })
19
+
20
+ await knex.schema.createTable("user_has_user_roles", (table) => {
21
+ table.bigIncrements("id").primary()
22
+ table.foreignIdFor("users").notNullable()
23
+ table.foreignIdFor("user_roles").notNullable()
24
+ table.softDelete()
25
+ })
26
+
27
+ await knex.schema.createTable("user_access_tokens", (table) => {
28
+ table.bigIncrements("id").primary()
29
+ table.foreignIdFor("users").notNullable()
30
+ table.string("agent").index().notNullable()
31
+ table.string("token").unique().index().notNullable()
32
+ table.json("permissions").defaultTo(knex.raw(`'[]'::json`))
33
+ table.string("last_used_ip").nullable()
34
+ table.timestamp("last_used_at")
35
+ table.timestamp("expired_at")
36
+ table.timestamps(true, true)
37
+ table.softDelete()
38
+ })
39
+
40
+ await knex.schema.createTable("user_mail_tokens", (table) => {
41
+ table.bigIncrements("id").primary()
42
+ table.foreignIdFor("users").notNullable()
43
+ table.string("token").unique().notNullable()
44
+ table.timestamp("used_at")
45
+ table.timestamps(true, true)
46
+ table.softDelete()
47
+ })
48
+
49
+ await knex.schema.createTable("user_permissions", (table) => {
50
+ table.bigIncrements("id").primary()
51
+ table.foreignIdFor("users").notNullable()
52
+ table.foreignIdFor("user_roles").notNullable()
53
+ table.json("permissions").defaultTo(knex.raw(`'[]'::json`))
54
+ table.softDelete()
55
+ })
56
56
  }
@@ -1,24 +1,24 @@
1
- import { Role, User } from "@models";
2
- import { db } from "@utils";
3
-
4
- export default async function UserSeeder() {
5
- // =========================>
6
- // ## Seed the application's database
7
- // =========================>
8
- await (new Role).pump([
9
- {"name": "Admin"},
10
- {"name": "Petugas"}
11
- ]);
12
-
13
- await (new User).pump([
14
- {"name": "Admin", "email": "admin@skalfa.id", "password": "$2b$10$tPX5QhnM.vUEDmDpht6O4OarVyTh43NTxhkzFrNxfRijJ3uhSHcli"},
15
- {"name": "Petugas", "email": "petugas@skalfa.id", "password": "$2b$10$tPX5QhnM.vUEDmDpht6O4OarVyTh43NTxhkzFrNxfRijJ3uhSHcli"}
16
- ]);
17
-
18
- if (db) {
19
- await db("user_has_user_roles").insert([
20
- { "user_id": 1, "user_role_id": 1 },
21
- { "user_id": 2, "user_role_id": 2 }
22
- ]);
23
- }
1
+ import { Role, User } from "@models";
2
+ import { db } from "@utils";
3
+
4
+ export default async function UserSeeder() {
5
+ // =========================>
6
+ // ## Seed the application's database
7
+ // =========================>
8
+ await (new Role).pump([
9
+ {"name": "Admin"},
10
+ {"name": "Petugas"}
11
+ ]);
12
+
13
+ await (new User).pump([
14
+ {"name": "Admin", "email": "admin@skalfa.id", "password": "$2b$10$tPX5QhnM.vUEDmDpht6O4OarVyTh43NTxhkzFrNxfRijJ3uhSHcli"},
15
+ {"name": "Petugas", "email": "petugas@skalfa.id", "password": "$2b$10$tPX5QhnM.vUEDmDpht6O4OarVyTh43NTxhkzFrNxfRijJ3uhSHcli"}
16
+ ]);
17
+
18
+ if (db) {
19
+ await db("user_has_user_roles").insert([
20
+ { "user_id": 1, "user_role_id": 1 },
21
+ { "user_id": 2, "user_role_id": 2 }
22
+ ]);
23
+ }
24
24
  }
package/eslint.config.mjs CHANGED
@@ -1,36 +1,36 @@
1
- import { dirname } from "path";
2
- import { fileURLToPath } from "url";
3
- import { FlatCompat } from "@eslint/eslintrc";
4
- import tsParser from "@typescript-eslint/parser"
5
- import tsPlugin from "@typescript-eslint/eslint-plugin"
6
- import prettier from "eslint-config-prettier";
7
-
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = dirname(__filename);
10
-
11
- const compat = new FlatCompat({
12
- baseDirectory: __dirname,
13
- });
14
-
15
- const eslintConfig = [
16
- ...compat.extends("prettier"),
17
- {
18
- files: ['**/*.{js,ts}'],
19
- languageOptions: {
20
- parser: tsParser,
21
- },
22
- plugins: {
23
- '@typescript-eslint': tsPlugin,
24
- },
25
- rules: {
26
- "@typescript-eslint/no-explicit-any" : 0,
27
- "no-console" : 1,
28
- "no-nested-ternary" : 0,
29
- "@typescript-eslint/no-unused-expressions" : 0,
30
- "@typescript-eslint/no-array-constructor" : 0,
31
- },
32
- },
33
- prettier,
34
- ];
35
-
36
- export default eslintConfig;
1
+ import { dirname } from "path";
2
+ import { fileURLToPath } from "url";
3
+ import { FlatCompat } from "@eslint/eslintrc";
4
+ import tsParser from "@typescript-eslint/parser"
5
+ import tsPlugin from "@typescript-eslint/eslint-plugin"
6
+ import prettier from "eslint-config-prettier";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+
11
+ const compat = new FlatCompat({
12
+ baseDirectory: __dirname,
13
+ });
14
+
15
+ const eslintConfig = [
16
+ ...compat.extends("prettier"),
17
+ {
18
+ files: ['**/*.{js,ts}'],
19
+ languageOptions: {
20
+ parser: tsParser,
21
+ },
22
+ plugins: {
23
+ '@typescript-eslint': tsPlugin,
24
+ },
25
+ rules: {
26
+ "@typescript-eslint/no-explicit-any" : 0,
27
+ "no-console" : 1,
28
+ "no-nested-ternary" : 0,
29
+ "@typescript-eslint/no-unused-expressions" : 0,
30
+ "@typescript-eslint/no-array-constructor" : 0,
31
+ },
32
+ },
33
+ prettier,
34
+ ];
35
+
36
+ export default eslintConfig;
package/package.json CHANGED
@@ -1,39 +1,40 @@
1
- {
2
- "name": "@skalfa/skalfa-api",
3
- "version": "1.0.1",
4
- "scripts": {
5
- "dev" : "concurrently --raw \"bun run --watch app/app.ts\" \"bun skalfa watch:barrels\"",
6
- "watch" : "bun run --watch app/app.ts",
7
- "start" : "bun run app/app.ts",
8
- "test" : "bun tsc --noEmit --ignoreDeprecations 6.0",
9
- "lint" : "bunx eslint app/* utils/* database/*",
10
- "barrels" : "bun run utils/commands/skalfa.ts barrels",
11
- "skalfa" : "bun run utils/commands/skalfa.ts"
12
- },
13
- "dependencies": {
14
- "@skalfa/skalfa-api-core": "^1.0.2",
15
- "@skalfa/skalfa-orm" : "^1.0.0",
16
- "bcrypt" : "^6.0.0",
17
- "dotenv" : "^17.2.2",
18
- "elysia" : "latest",
19
- "knex" : "^3.1.0",
20
- "pg" : "^8.16.3",
21
- "nodemailer" : "^7.0.9",
22
- "tsconfig-paths" : "^4.2.0",
23
- "validator" : "^13.15.15",
24
- "commander" : "^12.1.0"
25
- },
26
- "devDependencies": {
27
- "eslint" : "^10.0.0",
28
- "eslint-config-prettier" : "^10.1.8",
29
- "@eslint/eslintrc" : "eslint/eslintrc",
30
- "@typescript-eslint/eslint-plugin" : "^8.55.0",
31
- "@typescript-eslint/parser" : "^8.55.0",
32
- "@types/bcrypt" : "^6.0.0",
33
- "@types/nodemailer" : "^7.0.2",
34
- "@types/validator" : "^13.15.3",
35
- "bun-types" : "latest",
36
- "barrelsby" : "^2.8.1",
37
- "concurrently" : "^9.2.1"
38
- }
39
- }
1
+ {
2
+ "name": "@skalfa/skalfa-api",
3
+ "version": "1.0.3",
4
+ "scripts": {
5
+ "dev": "concurrently --raw \"bun run --watch app/app.ts\" \"bun skalfa watch:barrels\"",
6
+ "watch": "bun run --watch app/app.ts",
7
+ "start": "bun run app/app.ts",
8
+ "test": "bun tsc --noEmit --ignoreDeprecations 6.0",
9
+ "lint": "bunx eslint app/* utils/* database/*",
10
+ "barrels": "bun run utils/commands/skalfa.ts barrels",
11
+ "skalfa": "bun run utils/commands/skalfa.ts"
12
+ },
13
+ "dependencies": {
14
+ "@skalfa/skalfa-api-core": "^1.0.2",
15
+ "@skalfa/skalfa-orm": "^1.0.0",
16
+ "bcrypt": "^6.0.0",
17
+ "dotenv": "^17.2.2",
18
+ "elysia": "^1.2.0",
19
+ "knex": "^3.1.0",
20
+ "pg": "^8.16.3",
21
+ "nodemailer": "^7.0.9",
22
+ "tsconfig-paths": "^4.2.0",
23
+ "validator": "^13.15.15",
24
+ "commander": "^12.1.0"
25
+ },
26
+ "devDependencies": {
27
+ "eslint": "^10.0.0",
28
+ "eslint-config-prettier": "^10.1.8",
29
+ "@eslint/eslintrc": "eslint/eslintrc",
30
+ "@typescript-eslint/eslint-plugin": "^8.55.0",
31
+ "@typescript-eslint/parser": "^8.55.0",
32
+ "@types/bcrypt": "^6.0.0",
33
+ "@types/nodemailer": "^7.0.2",
34
+ "@types/validator": "^13.15.3",
35
+ "bun-types": "latest",
36
+ "barrelsby": "^2.8.1",
37
+ "concurrently": "^9.2.1"
38
+ },
39
+ "description": "Premium backend starter template powered by Elysia and Bun, pre-configured with modular utilities."
40
+ }