@skalfa/skalfa-api 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +75 -75
- package/.github/workflows/publish.yml +7 -0
- package/CONTRIBUTING.md +45 -0
- package/LICENSE +21 -0
- package/README.md +79 -85
- package/app/app.ts +74 -74
- package/app/controllers/base.controller.ts +16 -16
- package/app/controllers/iam/auth.controller.ts +143 -143
- package/app/controllers/iam/user.controller.ts +92 -92
- package/app/jobs/crons/index.ts +8 -8
- package/app/jobs/crons/worker.cron.ts +8 -8
- package/app/jobs/queues/access-log.queue.worker.ts +32 -32
- package/app/jobs/queues/activity-log.queue.worker.ts +32 -32
- package/app/jobs/queues/auth.queue.worker.ts +9 -9
- package/app/jobs/queues/error-log.queue.worker.ts +32 -32
- package/app/jobs/queues/notification.queue.worker.ts +11 -11
- package/app/jobs/queues/worker.queue.ts +37 -37
- package/app/jobs/sockets/worker.socket.ts +2 -2
- package/app/models/iam/user.model.ts +17 -17
- package/app/outputs/mails/templates/layout.mail.stub +102 -102
- package/app/outputs/mails/templates/user-mail-token.mail.stub +28 -28
- package/app/outputs/mails/user-mail-token.mail.ts +17 -17
- package/app/outputs/notifications/example.notification.ts +10 -0
- package/app/outputs/notifications/index.ts +1 -0
- package/app/routes/base.routes.ts +25 -25
- package/barrels.json +9 -9
- package/database/da.migrations/0000_00/activity_logs.ts +27 -27
- package/database/da.migrations/0000_00/logs.ts +41 -41
- package/database/migrations/0000_00/notifications.ts +28 -0
- package/database/migrations/0000_00/storages.ts +22 -22
- package/database/migrations/0000_00/users.ts +55 -55
- package/database/seeders/user.seeder.ts +23 -23
- package/eslint.config.mjs +36 -36
- package/package.json +40 -39
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { queue, da, ErrorLog } from '@utils'
|
|
2
|
-
|
|
3
|
-
const ERROR_LOG_QUEUE = process.env.ERROR_LOG_QUEUE || "error-log"
|
|
4
|
-
const ACCESS_LOG_TABLE = process.env.ACCESS_LOG_TABLE || 'error_logs'
|
|
5
|
-
const ERROR_LOG_CONCURRENCY = process.env.ERROR_LOG_CONCURRENCY || 10
|
|
6
|
-
const ERROR_LOG_FLUSH = process.env.ERROR_LOG_FLUSH || 200
|
|
7
|
-
|
|
8
|
-
let errorBuffer: ErrorLog[] = []
|
|
9
|
-
let lastErrorFlush = Date.now()
|
|
10
|
-
|
|
11
|
-
export const errorLogQueueWorker = () => {
|
|
12
|
-
queue.worker(ERROR_LOG_QUEUE, async (payload) => {
|
|
13
|
-
errorBuffer.push(payload as ErrorLog)
|
|
14
|
-
|
|
15
|
-
const now = Date.now()
|
|
16
|
-
|
|
17
|
-
if (errorBuffer.length >= Number(ERROR_LOG_CONCURRENCY) || now - lastErrorFlush >= Number(ERROR_LOG_FLUSH)) {
|
|
18
|
-
if (!errorBuffer.length) return
|
|
19
|
-
|
|
20
|
-
const batch = errorBuffer
|
|
21
|
-
errorBuffer = []
|
|
22
|
-
lastErrorFlush = Date.now()
|
|
23
|
-
|
|
24
|
-
await da.insert(ACCESS_LOG_TABLE, {
|
|
25
|
-
values : batch,
|
|
26
|
-
format : 'JSONEachRow'
|
|
27
|
-
})
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
{ concurrency: 1, interval: 50 }
|
|
31
|
-
)
|
|
32
|
-
}
|
|
1
|
+
import { queue, da, ErrorLog } from '@utils'
|
|
2
|
+
|
|
3
|
+
const ERROR_LOG_QUEUE = process.env.ERROR_LOG_QUEUE || "error-log"
|
|
4
|
+
const ACCESS_LOG_TABLE = process.env.ACCESS_LOG_TABLE || 'error_logs'
|
|
5
|
+
const ERROR_LOG_CONCURRENCY = process.env.ERROR_LOG_CONCURRENCY || 10
|
|
6
|
+
const ERROR_LOG_FLUSH = process.env.ERROR_LOG_FLUSH || 200
|
|
7
|
+
|
|
8
|
+
let errorBuffer: ErrorLog[] = []
|
|
9
|
+
let lastErrorFlush = Date.now()
|
|
10
|
+
|
|
11
|
+
export const errorLogQueueWorker = () => {
|
|
12
|
+
queue.worker(ERROR_LOG_QUEUE, async (payload) => {
|
|
13
|
+
errorBuffer.push(payload as ErrorLog)
|
|
14
|
+
|
|
15
|
+
const now = Date.now()
|
|
16
|
+
|
|
17
|
+
if (errorBuffer.length >= Number(ERROR_LOG_CONCURRENCY) || now - lastErrorFlush >= Number(ERROR_LOG_FLUSH)) {
|
|
18
|
+
if (!errorBuffer.length) return
|
|
19
|
+
|
|
20
|
+
const batch = errorBuffer
|
|
21
|
+
errorBuffer = []
|
|
22
|
+
lastErrorFlush = Date.now()
|
|
23
|
+
|
|
24
|
+
await da.insert(ACCESS_LOG_TABLE, {
|
|
25
|
+
values : batch,
|
|
26
|
+
format : 'JSONEachRow'
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{ concurrency: 1, interval: 50 }
|
|
31
|
+
)
|
|
32
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { queue, notification, NotificationPayload, NotificationCancelPayload } from '@utils'
|
|
2
|
-
|
|
3
|
-
export const notificationQueueWorker = () => {
|
|
4
|
-
queue.worker("notifications", async (payload) => {
|
|
5
|
-
if (payload?.type != "cancel") {
|
|
6
|
-
notification.send(payload as NotificationPayload)
|
|
7
|
-
} else {
|
|
8
|
-
notification.cancel(payload as NotificationCancelPayload)
|
|
9
|
-
}
|
|
10
|
-
}, { concurrency: 1, interval: 50 })
|
|
11
|
-
}
|
|
1
|
+
import { queue, notification, NotificationPayload, NotificationCancelPayload } from '@utils'
|
|
2
|
+
|
|
3
|
+
export const notificationQueueWorker = () => {
|
|
4
|
+
queue.worker("notifications", async (payload) => {
|
|
5
|
+
if (payload?.type != "cancel") {
|
|
6
|
+
notification.send(payload as NotificationPayload)
|
|
7
|
+
} else {
|
|
8
|
+
notification.cancel(payload as NotificationCancelPayload)
|
|
9
|
+
}
|
|
10
|
+
}, { concurrency: 1, interval: 50 })
|
|
11
|
+
}
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
import { logger, queue } from "@utils";
|
|
2
|
-
import { activityLogQueueWorker } from "./activity-log.queue.worker";
|
|
3
|
-
import { accessLogQueueWorker } from "./access-log.queue.worker";
|
|
4
|
-
import { errorLogQueueWorker } from "./error-log.queue.worker";
|
|
5
|
-
import { notificationQueueWorker } from "./notification.queue.worker";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// ============================================>
|
|
10
|
-
// ## Run of queue workers.
|
|
11
|
-
// ============================================>
|
|
12
|
-
queue.worker("example", async (payload, id) => {
|
|
13
|
-
logger.queue(`Start queue ${id}`)
|
|
14
|
-
|
|
15
|
-
if (Math.random() < 0.5) {
|
|
16
|
-
logger.queueError(`Queue ${id} intentionally failed`)
|
|
17
|
-
throw new Error(`Random failure for job ${id}`)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const wait = () => new Promise((resolve) =>
|
|
21
|
-
setTimeout(() => {
|
|
22
|
-
logger.queue("Queue payload date:" + payload?.date)
|
|
23
|
-
resolve("")
|
|
24
|
-
}, 5000)
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
await wait()
|
|
28
|
-
|
|
29
|
-
logger.queue(`Finish queue ${id}`)
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
activityLogQueueWorker()
|
|
33
|
-
accessLogQueueWorker()
|
|
34
|
-
errorLogQueueWorker()
|
|
35
|
-
notificationQueueWorker()
|
|
36
|
-
|
|
37
|
-
|
|
1
|
+
import { logger, queue } from "@utils";
|
|
2
|
+
import { activityLogQueueWorker } from "./activity-log.queue.worker";
|
|
3
|
+
import { accessLogQueueWorker } from "./access-log.queue.worker";
|
|
4
|
+
import { errorLogQueueWorker } from "./error-log.queue.worker";
|
|
5
|
+
import { notificationQueueWorker } from "./notification.queue.worker";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
// ============================================>
|
|
10
|
+
// ## Run of queue workers.
|
|
11
|
+
// ============================================>
|
|
12
|
+
queue.worker("example", async (payload, id) => {
|
|
13
|
+
logger.queue(`Start queue ${id}`)
|
|
14
|
+
|
|
15
|
+
if (Math.random() < 0.5) {
|
|
16
|
+
logger.queueError(`Queue ${id} intentionally failed`)
|
|
17
|
+
throw new Error(`Random failure for job ${id}`)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const wait = () => new Promise((resolve) =>
|
|
21
|
+
setTimeout(() => {
|
|
22
|
+
logger.queue("Queue payload date:" + payload?.date)
|
|
23
|
+
resolve("")
|
|
24
|
+
}, 5000)
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
await wait()
|
|
28
|
+
|
|
29
|
+
logger.queue(`Finish queue ${id}`)
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
activityLogQueueWorker()
|
|
33
|
+
accessLogQueueWorker()
|
|
34
|
+
errorLogQueueWorker()
|
|
35
|
+
notificationQueueWorker()
|
|
36
|
+
|
|
37
|
+
|
|
38
38
|
logger.start(`Queue job workers is running!`)
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { socket } from "@utils";
|
|
2
|
-
|
|
1
|
+
import { socket } from "@utils";
|
|
2
|
+
|
|
3
3
|
socket.start(Number(process.env.SOCKET_PORT ?? 4500));
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { Field, Model } from '@utils'
|
|
2
|
-
|
|
3
|
-
export class User extends Model {
|
|
4
|
-
@Field(["fillable", "selectable", "searchable"])
|
|
5
|
-
name!: string
|
|
6
|
-
|
|
7
|
-
@Field(["fillable", "selectable", "searchable"])
|
|
8
|
-
email!: string
|
|
9
|
-
|
|
10
|
-
@Field(["fillable"])
|
|
11
|
-
password!: string
|
|
12
|
-
|
|
13
|
-
@Field(["fillable", "selectable"])
|
|
14
|
-
image!: string
|
|
15
|
-
|
|
16
|
-
@Field(["fillable", "selectable", "searchable"])
|
|
17
|
-
email_verification_at!: Date
|
|
1
|
+
import { Field, Model } from '@utils'
|
|
2
|
+
|
|
3
|
+
export class User extends Model {
|
|
4
|
+
@Field(["fillable", "selectable", "searchable"])
|
|
5
|
+
name!: string
|
|
6
|
+
|
|
7
|
+
@Field(["fillable", "selectable", "searchable"])
|
|
8
|
+
email!: string
|
|
9
|
+
|
|
10
|
+
@Field(["fillable"])
|
|
11
|
+
password!: string
|
|
12
|
+
|
|
13
|
+
@Field(["fillable", "selectable"])
|
|
14
|
+
image!: string
|
|
15
|
+
|
|
16
|
+
@Field(["fillable", "selectable", "searchable"])
|
|
17
|
+
email_verification_at!: Date
|
|
18
18
|
}
|
|
@@ -1,102 +1,102 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
|
7
|
-
<title>{{title}}</title>
|
|
8
|
-
|
|
9
|
-
<link
|
|
10
|
-
href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap"
|
|
11
|
-
rel="stylesheet"
|
|
12
|
-
/>
|
|
13
|
-
</head>
|
|
14
|
-
<body
|
|
15
|
-
style="
|
|
16
|
-
margin: 0;
|
|
17
|
-
font-family: 'Poppins', sans-serif;
|
|
18
|
-
background: #ebf4fc;
|
|
19
|
-
font-size: 14px;
|
|
20
|
-
"
|
|
21
|
-
>
|
|
22
|
-
<div
|
|
23
|
-
style="
|
|
24
|
-
max-width: 680px;
|
|
25
|
-
margin: 0 auto;
|
|
26
|
-
padding: 35px 10px 40px;
|
|
27
|
-
background: #ebf4fc;
|
|
28
|
-
font-size: 14px;
|
|
29
|
-
color: #575757;
|
|
30
|
-
"
|
|
31
|
-
>
|
|
32
|
-
<header>
|
|
33
|
-
<table style="width: 100%;">
|
|
34
|
-
<tbody>
|
|
35
|
-
<tr style="height: 0;">
|
|
36
|
-
<td style="font-weight: bold;font-size: 20;">
|
|
37
|
-
{{app_name}}
|
|
38
|
-
</td>
|
|
39
|
-
<td style="text-align: right;">
|
|
40
|
-
<span style="font-size: 16px; line-height: 30px;">{{date}}</span>
|
|
41
|
-
</td>
|
|
42
|
-
</tr>
|
|
43
|
-
</tbody>
|
|
44
|
-
</table>
|
|
45
|
-
</header>
|
|
46
|
-
|
|
47
|
-
<main>
|
|
48
|
-
<div
|
|
49
|
-
style="
|
|
50
|
-
margin: 0;
|
|
51
|
-
margin-top: 30px;
|
|
52
|
-
padding: 50px 20px 55px;
|
|
53
|
-
background: #ffffff;
|
|
54
|
-
border-radius: 30px;
|
|
55
|
-
text-align: center;
|
|
56
|
-
"
|
|
57
|
-
>
|
|
58
|
-
<div style="width: 100%; max-width: 489px; margin: 0 auto;">
|
|
59
|
-
<h1
|
|
60
|
-
style="
|
|
61
|
-
margin: 0;
|
|
62
|
-
font-size: 24px;
|
|
63
|
-
font-weight: 500;
|
|
64
|
-
"
|
|
65
|
-
>{{title}}</h1>
|
|
66
|
-
|
|
67
|
-
{{content}}
|
|
68
|
-
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
71
|
-
</main>
|
|
72
|
-
|
|
73
|
-
<footer
|
|
74
|
-
style="
|
|
75
|
-
width: 100%;
|
|
76
|
-
max-width: 490px;
|
|
77
|
-
margin: 20px auto 0;
|
|
78
|
-
text-align: center;
|
|
79
|
-
border-top: 1px solid #575757;
|
|
80
|
-
"
|
|
81
|
-
>
|
|
82
|
-
<p
|
|
83
|
-
style="
|
|
84
|
-
margin: 0;
|
|
85
|
-
margin-top: 20px;
|
|
86
|
-
font-size: 16px;
|
|
87
|
-
font-weight: 600;
|
|
88
|
-
color: #5aafff;
|
|
89
|
-
"
|
|
90
|
-
>
|
|
91
|
-
{{app_name}}
|
|
92
|
-
</p>
|
|
93
|
-
<p style="margin: 0; margin-top: 8px; color: #575757;">
|
|
94
|
-
sejedigital.com
|
|
95
|
-
</p>
|
|
96
|
-
<p style="margin: 0; margin-top: 10px; color: #575757;">
|
|
97
|
-
Copyright © 2022 {{app_name}}. All rights reserved.
|
|
98
|
-
</p>
|
|
99
|
-
</footer>
|
|
100
|
-
</div>
|
|
101
|
-
</body>
|
|
102
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
|
7
|
+
<title>{{title}}</title>
|
|
8
|
+
|
|
9
|
+
<link
|
|
10
|
+
href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap"
|
|
11
|
+
rel="stylesheet"
|
|
12
|
+
/>
|
|
13
|
+
</head>
|
|
14
|
+
<body
|
|
15
|
+
style="
|
|
16
|
+
margin: 0;
|
|
17
|
+
font-family: 'Poppins', sans-serif;
|
|
18
|
+
background: #ebf4fc;
|
|
19
|
+
font-size: 14px;
|
|
20
|
+
"
|
|
21
|
+
>
|
|
22
|
+
<div
|
|
23
|
+
style="
|
|
24
|
+
max-width: 680px;
|
|
25
|
+
margin: 0 auto;
|
|
26
|
+
padding: 35px 10px 40px;
|
|
27
|
+
background: #ebf4fc;
|
|
28
|
+
font-size: 14px;
|
|
29
|
+
color: #575757;
|
|
30
|
+
"
|
|
31
|
+
>
|
|
32
|
+
<header>
|
|
33
|
+
<table style="width: 100%;">
|
|
34
|
+
<tbody>
|
|
35
|
+
<tr style="height: 0;">
|
|
36
|
+
<td style="font-weight: bold;font-size: 20;">
|
|
37
|
+
{{app_name}}
|
|
38
|
+
</td>
|
|
39
|
+
<td style="text-align: right;">
|
|
40
|
+
<span style="font-size: 16px; line-height: 30px;">{{date}}</span>
|
|
41
|
+
</td>
|
|
42
|
+
</tr>
|
|
43
|
+
</tbody>
|
|
44
|
+
</table>
|
|
45
|
+
</header>
|
|
46
|
+
|
|
47
|
+
<main>
|
|
48
|
+
<div
|
|
49
|
+
style="
|
|
50
|
+
margin: 0;
|
|
51
|
+
margin-top: 30px;
|
|
52
|
+
padding: 50px 20px 55px;
|
|
53
|
+
background: #ffffff;
|
|
54
|
+
border-radius: 30px;
|
|
55
|
+
text-align: center;
|
|
56
|
+
"
|
|
57
|
+
>
|
|
58
|
+
<div style="width: 100%; max-width: 489px; margin: 0 auto;">
|
|
59
|
+
<h1
|
|
60
|
+
style="
|
|
61
|
+
margin: 0;
|
|
62
|
+
font-size: 24px;
|
|
63
|
+
font-weight: 500;
|
|
64
|
+
"
|
|
65
|
+
>{{title}}</h1>
|
|
66
|
+
|
|
67
|
+
{{content}}
|
|
68
|
+
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</main>
|
|
72
|
+
|
|
73
|
+
<footer
|
|
74
|
+
style="
|
|
75
|
+
width: 100%;
|
|
76
|
+
max-width: 490px;
|
|
77
|
+
margin: 20px auto 0;
|
|
78
|
+
text-align: center;
|
|
79
|
+
border-top: 1px solid #575757;
|
|
80
|
+
"
|
|
81
|
+
>
|
|
82
|
+
<p
|
|
83
|
+
style="
|
|
84
|
+
margin: 0;
|
|
85
|
+
margin-top: 20px;
|
|
86
|
+
font-size: 16px;
|
|
87
|
+
font-weight: 600;
|
|
88
|
+
color: #5aafff;
|
|
89
|
+
"
|
|
90
|
+
>
|
|
91
|
+
{{app_name}}
|
|
92
|
+
</p>
|
|
93
|
+
<p style="margin: 0; margin-top: 8px; color: #575757;">
|
|
94
|
+
sejedigital.com
|
|
95
|
+
</p>
|
|
96
|
+
<p style="margin: 0; margin-top: 10px; color: #575757;">
|
|
97
|
+
Copyright © 2022 {{app_name}}. All rights reserved.
|
|
98
|
+
</p>
|
|
99
|
+
</footer>
|
|
100
|
+
</div>
|
|
101
|
+
</body>
|
|
102
|
+
</html>
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
<p style="
|
|
2
|
-
margin: 0;
|
|
3
|
-
margin-top: 17px;
|
|
4
|
-
font-size: 16px;
|
|
5
|
-
font-weight: 500;
|
|
6
|
-
">Hai {{name}},</p>
|
|
7
|
-
|
|
8
|
-
<p style="
|
|
9
|
-
margin: 0;
|
|
10
|
-
margin-top: 17px;
|
|
11
|
-
font-weight: 500;
|
|
12
|
-
letter-spacing: 0.56px;
|
|
13
|
-
">Masukkan kode dibawah ini untuk verifikasi akun kamu, jika kode tidak berhasil coba meminta untuk kirim email kembali. kode hanya bisa digunakan selama 1 jam setelah email terkirim.</p>
|
|
14
|
-
|
|
15
|
-
<p style="
|
|
16
|
-
margin: 0;
|
|
17
|
-
margin-top: 40px;
|
|
18
|
-
font-size: 40px;
|
|
19
|
-
font-weight: 600;
|
|
20
|
-
letter-spacing: 15px;
|
|
21
|
-
color: #5aafff;
|
|
22
|
-
">{{token}}</p>
|
|
23
|
-
|
|
24
|
-
<p style="
|
|
25
|
-
margin: 0;
|
|
26
|
-
margin-top: 70px;
|
|
27
|
-
font-weight: 500;
|
|
28
|
-
letter-spacing: 0.56px;
|
|
1
|
+
<p style="
|
|
2
|
+
margin: 0;
|
|
3
|
+
margin-top: 17px;
|
|
4
|
+
font-size: 16px;
|
|
5
|
+
font-weight: 500;
|
|
6
|
+
">Hai {{name}},</p>
|
|
7
|
+
|
|
8
|
+
<p style="
|
|
9
|
+
margin: 0;
|
|
10
|
+
margin-top: 17px;
|
|
11
|
+
font-weight: 500;
|
|
12
|
+
letter-spacing: 0.56px;
|
|
13
|
+
">Masukkan kode dibawah ini untuk verifikasi akun kamu, jika kode tidak berhasil coba meminta untuk kirim email kembali. kode hanya bisa digunakan selama 1 jam setelah email terkirim.</p>
|
|
14
|
+
|
|
15
|
+
<p style="
|
|
16
|
+
margin: 0;
|
|
17
|
+
margin-top: 40px;
|
|
18
|
+
font-size: 40px;
|
|
19
|
+
font-weight: 600;
|
|
20
|
+
letter-spacing: 15px;
|
|
21
|
+
color: #5aafff;
|
|
22
|
+
">{{token}}</p>
|
|
23
|
+
|
|
24
|
+
<p style="
|
|
25
|
+
margin: 0;
|
|
26
|
+
margin-top: 70px;
|
|
27
|
+
font-weight: 500;
|
|
28
|
+
letter-spacing: 0.56px;
|
|
29
29
|
">Demi keamanan, jangan beritahukan kode verifikasi ini kepada siapapun! Abaikan jika kamu tidak merasa melakukan pendaftaran akun</p>
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { renderMailTemplate, sendMail } from "@utils"
|
|
2
|
-
|
|
3
|
-
export async function UserMailToken(user: Record<string, any>, token: string) {
|
|
4
|
-
const content = renderMailTemplate("user-mail-token", {
|
|
5
|
-
title: "Verifikasi E-mail",
|
|
6
|
-
...user,
|
|
7
|
-
token,
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
const send = await sendMail({
|
|
11
|
-
subject: "Email Verification",
|
|
12
|
-
to: user?.email,
|
|
13
|
-
content: content
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
return send;
|
|
17
|
-
}
|
|
1
|
+
import { renderMailTemplate, sendMail } from "@utils"
|
|
2
|
+
|
|
3
|
+
export async function UserMailToken(user: Record<string, any>, token: string) {
|
|
4
|
+
const content = renderMailTemplate("user-mail-token", {
|
|
5
|
+
title: "Verifikasi E-mail",
|
|
6
|
+
...user,
|
|
7
|
+
token,
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
const send = await sendMail({
|
|
11
|
+
subject: "Email Verification",
|
|
12
|
+
to: user?.email,
|
|
13
|
+
content: content
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
return send;
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { notification } from "@utils";
|
|
2
|
+
|
|
3
|
+
export async function ExampleNotification(userIds: number[], payload: Record<string, any>) {
|
|
4
|
+
await notification.send({
|
|
5
|
+
title: payload.title || "New Notification",
|
|
6
|
+
body: payload.body || "You have received a new notification.",
|
|
7
|
+
type: payload.type || "info",
|
|
8
|
+
userIds,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./example.notification";
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Elysia } from 'elysia'
|
|
3
|
-
import { api, middleware } from '@utils'
|
|
4
|
-
import {
|
|
5
|
-
AuthController,
|
|
6
|
-
BaseController,
|
|
7
|
-
UserController,
|
|
8
|
-
} from '@controllers'
|
|
9
|
-
|
|
10
|
-
export const routes = (app: any) => app.group('/api', (route: any) => {
|
|
11
|
-
route.get('/', BaseController.index)
|
|
12
|
-
route.get('/features', BaseController.feature)
|
|
13
|
-
route.get('/accesses', BaseController.access)
|
|
14
|
-
|
|
15
|
-
route.post('/login', AuthController.login)
|
|
16
|
-
route.post('/register', AuthController.register)
|
|
17
|
-
|
|
18
|
-
route.use(middleware.Private)
|
|
19
|
-
|
|
20
|
-
route.post('/verify', AuthController.verify)
|
|
21
|
-
route.get('/me', AuthController.me)
|
|
22
|
-
route.post('/me/update', AuthController.update)
|
|
23
|
-
|
|
24
|
-
api(route, "/users", UserController);
|
|
25
|
-
return route;
|
|
1
|
+
import {
|
|
2
|
+
Elysia } from 'elysia'
|
|
3
|
+
import { api, middleware } from '@utils'
|
|
4
|
+
import {
|
|
5
|
+
AuthController,
|
|
6
|
+
BaseController,
|
|
7
|
+
UserController,
|
|
8
|
+
} from '@controllers'
|
|
9
|
+
|
|
10
|
+
export const routes = (app: any) => app.group('/api', (route: any) => {
|
|
11
|
+
route.get('/', BaseController.index)
|
|
12
|
+
route.get('/features', BaseController.feature)
|
|
13
|
+
route.get('/accesses', BaseController.access)
|
|
14
|
+
|
|
15
|
+
route.post('/login', AuthController.login)
|
|
16
|
+
route.post('/register', AuthController.register)
|
|
17
|
+
|
|
18
|
+
route.use(middleware.Private)
|
|
19
|
+
|
|
20
|
+
route.post('/verify', AuthController.verify)
|
|
21
|
+
route.get('/me', AuthController.me)
|
|
22
|
+
route.post('/me/update', AuthController.update)
|
|
23
|
+
|
|
24
|
+
api(route, "/users", UserController);
|
|
25
|
+
return route;
|
|
26
26
|
})
|
package/barrels.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
{
|
|
2
|
-
"directory" : [
|
|
3
|
-
"app/models",
|
|
4
|
-
"app/controllers",
|
|
5
|
-
"app/routes"
|
|
6
|
-
],
|
|
7
|
-
"delete" : true,
|
|
8
|
-
"exclude" : ["index.ts"],
|
|
9
|
-
"structure" : "flat"
|
|
1
|
+
{
|
|
2
|
+
"directory" : [
|
|
3
|
+
"app/models",
|
|
4
|
+
"app/controllers",
|
|
5
|
+
"app/routes"
|
|
6
|
+
],
|
|
7
|
+
"delete" : true,
|
|
8
|
+
"exclude" : ["index.ts"],
|
|
9
|
+
"structure" : "flat"
|
|
10
10
|
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { DAMigration } from "@utils"
|
|
2
|
-
|
|
3
|
-
export default class CreateActivityLogsTable extends DAMigration {
|
|
4
|
-
async up() {
|
|
5
|
-
await this.createTable(
|
|
6
|
-
"activity_logs",
|
|
7
|
-
(table) => {
|
|
8
|
-
table.uuid()
|
|
9
|
-
table.string("feature")
|
|
10
|
-
table.string("action")
|
|
11
|
-
table.uint64("user_id")
|
|
12
|
-
table.json("changes")
|
|
13
|
-
table.dateTime("at")
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
engine: "MergeTree",
|
|
17
|
-
orderBy: ["feature", "action", "at"],
|
|
18
|
-
partitionBy: "toYYYYMM(at)",
|
|
19
|
-
ttl: "at + INTERVAL 90 DAY DELETE"
|
|
20
|
-
}
|
|
21
|
-
)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async down() {
|
|
25
|
-
await this.dropTable("activity_logs")
|
|
26
|
-
}
|
|
27
|
-
}
|
|
1
|
+
import { DAMigration } from "@utils"
|
|
2
|
+
|
|
3
|
+
export default class CreateActivityLogsTable extends DAMigration {
|
|
4
|
+
async up() {
|
|
5
|
+
await this.createTable(
|
|
6
|
+
"activity_logs",
|
|
7
|
+
(table) => {
|
|
8
|
+
table.uuid()
|
|
9
|
+
table.string("feature")
|
|
10
|
+
table.string("action")
|
|
11
|
+
table.uint64("user_id")
|
|
12
|
+
table.json("changes")
|
|
13
|
+
table.dateTime("at")
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
engine: "MergeTree",
|
|
17
|
+
orderBy: ["feature", "action", "at"],
|
|
18
|
+
partitionBy: "toYYYYMM(at)",
|
|
19
|
+
ttl: "at + INTERVAL 90 DAY DELETE"
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async down() {
|
|
25
|
+
await this.dropTable("activity_logs")
|
|
26
|
+
}
|
|
27
|
+
}
|