vps-deployer 1.0.0
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/LICENSE +21 -0
- package/README.md +151 -0
- package/dist/app.d.ts +4 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +39 -0
- package/dist/app.js.map +1 -0
- package/dist/db/db.d.ts +5 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +100 -0
- package/dist/db/db.js.map +1 -0
- package/dist/helpers/arg.helper.d.ts +7 -0
- package/dist/helpers/arg.helper.d.ts.map +1 -0
- package/dist/helpers/arg.helper.js +28 -0
- package/dist/helpers/arg.helper.js.map +1 -0
- package/dist/helpers/cmd_executer.helper.d.ts +7 -0
- package/dist/helpers/cmd_executer.helper.d.ts.map +1 -0
- package/dist/helpers/cmd_executer.helper.js +88 -0
- package/dist/helpers/cmd_executer.helper.js.map +1 -0
- package/dist/helpers/common.helper.d.ts +2 -0
- package/dist/helpers/common.helper.d.ts.map +1 -0
- package/dist/helpers/common.helper.js +15 -0
- package/dist/helpers/common.helper.js.map +1 -0
- package/dist/helpers/config_files.helper.d.ts +2 -0
- package/dist/helpers/config_files.helper.d.ts.map +1 -0
- package/dist/helpers/config_files.helper.js +44 -0
- package/dist/helpers/config_files.helper.js.map +1 -0
- package/dist/helpers/create_systemd_service.helper.d.ts +9 -0
- package/dist/helpers/create_systemd_service.helper.d.ts.map +1 -0
- package/dist/helpers/create_systemd_service.helper.js +40 -0
- package/dist/helpers/create_systemd_service.helper.js.map +1 -0
- package/dist/helpers/date.helper.d.ts +2 -0
- package/dist/helpers/date.helper.d.ts.map +1 -0
- package/dist/helpers/date.helper.js +18 -0
- package/dist/helpers/date.helper.js.map +1 -0
- package/dist/helpers/email.helper.d.ts +11 -0
- package/dist/helpers/email.helper.d.ts.map +1 -0
- package/dist/helpers/email.helper.js +144 -0
- package/dist/helpers/email.helper.js.map +1 -0
- package/dist/helpers/logging.helper.d.ts +9 -0
- package/dist/helpers/logging.helper.d.ts.map +1 -0
- package/dist/helpers/logging.helper.js +32 -0
- package/dist/helpers/logging.helper.js.map +1 -0
- package/dist/helpers/remove_systemd_service.helper.d.ts +2 -0
- package/dist/helpers/remove_systemd_service.helper.d.ts.map +1 -0
- package/dist/helpers/remove_systemd_service.helper.js +21 -0
- package/dist/helpers/remove_systemd_service.helper.js.map +1 -0
- package/dist/helpers/start_systemd_service.helper.d.ts +2 -0
- package/dist/helpers/start_systemd_service.helper.d.ts.map +1 -0
- package/dist/helpers/start_systemd_service.helper.js +26 -0
- package/dist/helpers/start_systemd_service.helper.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +132 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/auth.middleware.d.ts +3 -0
- package/dist/middleware/auth.middleware.d.ts.map +1 -0
- package/dist/middleware/auth.middleware.js +7 -0
- package/dist/middleware/auth.middleware.js.map +1 -0
- package/dist/modules/auth/auth.route.d.ts +3 -0
- package/dist/modules/auth/auth.route.d.ts.map +1 -0
- package/dist/modules/auth/auth.route.js +16 -0
- package/dist/modules/auth/auth.route.js.map +1 -0
- package/dist/modules/auth/controllers/auth.controller.d.ts +6 -0
- package/dist/modules/auth/controllers/auth.controller.d.ts.map +1 -0
- package/dist/modules/auth/controllers/auth.controller.js +123 -0
- package/dist/modules/auth/controllers/auth.controller.js.map +1 -0
- package/dist/modules/auth/views/login.pug +254 -0
- package/dist/modules/auth/views/register.pug +298 -0
- package/dist/modules/common/views/404.pug +21 -0
- package/dist/modules/dashboard/dashboard.route.d.ts +3 -0
- package/dist/modules/dashboard/dashboard.route.d.ts.map +1 -0
- package/dist/modules/dashboard/dashboard.route.js +29 -0
- package/dist/modules/dashboard/dashboard.route.js.map +1 -0
- package/dist/modules/dashboard/views/dash.pug +149 -0
- package/dist/modules/dashboard/views/layout.pug +94 -0
- package/dist/modules/deployer/controllers/deployer.controller.d.ts +6 -0
- package/dist/modules/deployer/controllers/deployer.controller.d.ts.map +1 -0
- package/dist/modules/deployer/controllers/deployer.controller.js +119 -0
- package/dist/modules/deployer/controllers/deployer.controller.js.map +1 -0
- package/dist/modules/deployer/deployer.route.d.ts +3 -0
- package/dist/modules/deployer/deployer.route.d.ts.map +1 -0
- package/dist/modules/deployer/deployer.route.js +13 -0
- package/dist/modules/deployer/deployer.route.js.map +1 -0
- package/dist/modules/deployer/helper/deployer.d.ts +9 -0
- package/dist/modules/deployer/helper/deployer.d.ts.map +1 -0
- package/dist/modules/deployer/helper/deployer.js +89 -0
- package/dist/modules/deployer/helper/deployer.js.map +1 -0
- package/dist/modules/deployer/views/details.pug +157 -0
- package/dist/modules/deployer/views/index.pug +108 -0
- package/dist/modules/project/controllers/projects.controller.d.ts +12 -0
- package/dist/modules/project/controllers/projects.controller.d.ts.map +1 -0
- package/dist/modules/project/controllers/projects.controller.js +267 -0
- package/dist/modules/project/controllers/projects.controller.js.map +1 -0
- package/dist/modules/project/helper/project_file_sys.helper.d.ts +7 -0
- package/dist/modules/project/helper/project_file_sys.helper.d.ts.map +1 -0
- package/dist/modules/project/helper/project_file_sys.helper.js +93 -0
- package/dist/modules/project/helper/project_file_sys.helper.js.map +1 -0
- package/dist/modules/project/project.route.d.ts +3 -0
- package/dist/modules/project/project.route.d.ts.map +1 -0
- package/dist/modules/project/project.route.js +17 -0
- package/dist/modules/project/project.route.js.map +1 -0
- package/dist/modules/project/views/create.pug +189 -0
- package/dist/modules/project/views/details.pug +238 -0
- package/dist/modules/project/views/index.pug +114 -0
- package/dist/modules/settings/controllers/setting.controller.d.ts +5 -0
- package/dist/modules/settings/controllers/setting.controller.d.ts.map +1 -0
- package/dist/modules/settings/controllers/setting.controller.js +193 -0
- package/dist/modules/settings/controllers/setting.controller.js.map +1 -0
- package/dist/modules/settings/settings.route.d.ts +3 -0
- package/dist/modules/settings/settings.route.d.ts.map +1 -0
- package/dist/modules/settings/settings.route.js +10 -0
- package/dist/modules/settings/settings.route.js.map +1 -0
- package/dist/modules/settings/views/index.pug +173 -0
- package/dist/modules/webhook/controllers/webhook.controller.d.ts +3 -0
- package/dist/modules/webhook/controllers/webhook.controller.d.ts.map +1 -0
- package/dist/modules/webhook/controllers/webhook.controller.js +72 -0
- package/dist/modules/webhook/controllers/webhook.controller.js.map +1 -0
- package/dist/modules/webhook/webhook.route.d.ts +3 -0
- package/dist/modules/webhook/webhook.route.d.ts.map +1 -0
- package/dist/modules/webhook/webhook.route.js +6 -0
- package/dist/modules/webhook/webhook.route.js.map +1 -0
- package/dist/utils/app_config.d.ts +3 -0
- package/dist/utils/app_config.d.ts.map +1 -0
- package/dist/utils/app_config.js +5 -0
- package/dist/utils/app_config.js.map +1 -0
- package/dist/utils/cli.d.ts +2 -0
- package/dist/utils/cli.d.ts.map +1 -0
- package/dist/utils/cli.js +68 -0
- package/dist/utils/cli.js.map +1 -0
- package/package.json +80 -0
- package/public/favicon.svg +18 -0
- package/public/js/deploy.js +36 -0
- package/public/js/deployment-details.js +56 -0
- package/public/js/login.js +21 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
extends ../../dashboard/views/layout.pug
|
|
2
|
+
|
|
3
|
+
block content
|
|
4
|
+
// hidden deploy id for JS
|
|
5
|
+
div#deploy-id(data-id=deployment.id)
|
|
6
|
+
|
|
7
|
+
// Page Header
|
|
8
|
+
.d-flex.justify-content-between.align-items-center.mb-4
|
|
9
|
+
div
|
|
10
|
+
h2.mb-1 Deployment Details
|
|
11
|
+
p.text-muted.mb-0 Monitor deployment progress and logs
|
|
12
|
+
div
|
|
13
|
+
a.btn.btn-outline-secondary(href="/deployments")
|
|
14
|
+
i.bi.bi-arrow-left.me-2
|
|
15
|
+
| Back to Deployments
|
|
16
|
+
|
|
17
|
+
.row
|
|
18
|
+
// LEFT PANEL
|
|
19
|
+
.col-md-4.mb-4
|
|
20
|
+
.card.border-0.shadow-sm.mb-4
|
|
21
|
+
.card-header.bg-white.border-bottom-0.pt-3
|
|
22
|
+
.d-flex.align-items-center
|
|
23
|
+
i.bi.bi-info-circle.text-primary.me-2(style="font-size: 1.25rem;")
|
|
24
|
+
h5.mb-0 Deployment Info
|
|
25
|
+
.card-body
|
|
26
|
+
.mb-3.pb-2.border-bottom
|
|
27
|
+
.d-flex.justify-content-between
|
|
28
|
+
small.text-muted Deployment ID
|
|
29
|
+
strong.text-dark #{deployment.id}
|
|
30
|
+
|
|
31
|
+
.mb-3.pb-2.border-bottom
|
|
32
|
+
.d-flex.justify-content-between.align-items-center
|
|
33
|
+
small.text-muted Status
|
|
34
|
+
span#deploy-status
|
|
35
|
+
if deployment.status === "running"
|
|
36
|
+
span.badge.bg-warning.text-dark.px-3.py-2
|
|
37
|
+
i.bi.bi-play-fill.me-1
|
|
38
|
+
| running
|
|
39
|
+
else if deployment.status === "success"
|
|
40
|
+
span.badge.bg-success.px-3.py-2
|
|
41
|
+
i.bi.bi-check-circle-fill.me-1
|
|
42
|
+
| success
|
|
43
|
+
else
|
|
44
|
+
span.badge.bg-danger.px-3.py-2
|
|
45
|
+
i.bi.bi-x-circle-fill.me-1
|
|
46
|
+
| failed
|
|
47
|
+
|
|
48
|
+
.mb-3.pb-2.border-bottom
|
|
49
|
+
.d-flex.justify-content-between
|
|
50
|
+
small.text-muted Started
|
|
51
|
+
strong.text-dark #{deployment.started_at}
|
|
52
|
+
|
|
53
|
+
.mb-3
|
|
54
|
+
.d-flex.justify-content-between
|
|
55
|
+
small.text-muted Finished
|
|
56
|
+
strong.text-dark#finished-at #{deployment.finished_at || "Running..."}
|
|
57
|
+
|
|
58
|
+
.card.border-0.shadow-sm
|
|
59
|
+
.card-header.bg-white.border-bottom-0.pt-3
|
|
60
|
+
.d-flex.align-items-center
|
|
61
|
+
i.bi.bi-folder.text-primary.me-2(style="font-size: 1.25rem;")
|
|
62
|
+
h5.mb-0 Project Info
|
|
63
|
+
.card-body
|
|
64
|
+
.mb-3.pb-2.border-bottom
|
|
65
|
+
.d-flex.justify-content-between
|
|
66
|
+
small.text-muted Project Name
|
|
67
|
+
strong.text-dark #{deployment.name}
|
|
68
|
+
|
|
69
|
+
.mb-3.pb-2.border-bottom
|
|
70
|
+
.d-flex.flex-column
|
|
71
|
+
small.text-muted.mb-1 Repository
|
|
72
|
+
a.text-decoration-none(href=deployment.github_url, target="_blank")
|
|
73
|
+
i.bi.bi-github.me-1
|
|
74
|
+
| #{deployment.github_url}
|
|
75
|
+
|
|
76
|
+
.mb-3
|
|
77
|
+
.d-flex.justify-content-between
|
|
78
|
+
small.text-muted Branch
|
|
79
|
+
strong.text-dark #{deployment.branch_name}
|
|
80
|
+
|
|
81
|
+
// RIGHT PANEL
|
|
82
|
+
.col-md-8
|
|
83
|
+
.card.border-0.shadow-sm
|
|
84
|
+
.card-header.bg-white.border-bottom-0.pt-3
|
|
85
|
+
.d-flex.align-items-center
|
|
86
|
+
i.bi.bi-terminal.text-primary.me-2(style="font-size: 1.25rem;")
|
|
87
|
+
h5.mb-0 Deployment Logs
|
|
88
|
+
.card-body
|
|
89
|
+
#logs
|
|
90
|
+
if logs.length === 0
|
|
91
|
+
.text-center.py-5
|
|
92
|
+
i.bi.bi-console.display-1.text-muted
|
|
93
|
+
p.text-muted.mt-3.mb-0 No logs yet
|
|
94
|
+
small.text-muted Waiting for deployment to start...
|
|
95
|
+
else
|
|
96
|
+
each log in logs
|
|
97
|
+
.card.mb-3.border-0.shadow-sm
|
|
98
|
+
.card-header.bg-light.border-bottom-0
|
|
99
|
+
.d-flex.justify-content-between.align-items-center
|
|
100
|
+
strong
|
|
101
|
+
i.bi.bi-terminal.me-2
|
|
102
|
+
| #{log.cmd}
|
|
103
|
+
if log.status === "running"
|
|
104
|
+
span.badge.bg-warning.text-dark.px-3.py-2
|
|
105
|
+
i.bi.bi-play-fill.me-1
|
|
106
|
+
| running
|
|
107
|
+
else if log.status === "success"
|
|
108
|
+
span.badge.bg-success.px-3.py-2
|
|
109
|
+
i.bi.bi-check-circle-fill.me-1
|
|
110
|
+
| success
|
|
111
|
+
else
|
|
112
|
+
span.badge.bg-danger.px-3.py-2
|
|
113
|
+
i.bi.bi-x-circle-fill.me-1
|
|
114
|
+
| failed
|
|
115
|
+
.card-body
|
|
116
|
+
.mb-2
|
|
117
|
+
small.text-muted
|
|
118
|
+
i.bi.bi-clock.me-1
|
|
119
|
+
| Started: #{log.started_at}
|
|
120
|
+
br
|
|
121
|
+
i.bi.bi-stopwatch.me-1.mt-1
|
|
122
|
+
| Finished: #{log.finished_at || "Running..."}
|
|
123
|
+
pre.bg-dark.text-success.p-3.mt-2(style="max-height:300px; overflow:auto; border-radius: 6px;")
|
|
124
|
+
= log.log || ""
|
|
125
|
+
|
|
126
|
+
// Additional styles
|
|
127
|
+
style.
|
|
128
|
+
.card {
|
|
129
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.card:hover {
|
|
133
|
+
transform: translateY(-2px);
|
|
134
|
+
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.08) !important;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.badge {
|
|
138
|
+
font-weight: 500;
|
|
139
|
+
font-size: 0.75rem;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
pre {
|
|
143
|
+
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', monospace;
|
|
144
|
+
font-size: 0.8rem;
|
|
145
|
+
line-height: 1.5;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.bg-dark {
|
|
149
|
+
background-color: #1a1f36 !important;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.text-success {
|
|
153
|
+
color: #10b981 !important;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// external script (CSP-safe)
|
|
157
|
+
script(src="/js/deployment-details.js")
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
extends ../../dashboard/views/layout.pug
|
|
2
|
+
|
|
3
|
+
block content
|
|
4
|
+
// Page Header
|
|
5
|
+
.d-flex.justify-content-between.align-items-center.mb-4
|
|
6
|
+
div
|
|
7
|
+
h2.mb-1 Deployments
|
|
8
|
+
p.text-muted.mb-0 Track and monitor all your deployment history
|
|
9
|
+
div
|
|
10
|
+
i.bi.bi-clock-history.text-muted(style="font-size: 1.5rem;")
|
|
11
|
+
|
|
12
|
+
// Empty State
|
|
13
|
+
if deployments.length === 0
|
|
14
|
+
.card.border-0.shadow-sm
|
|
15
|
+
.card-body.text-center.py-5
|
|
16
|
+
i.bi.bi-inbox.display-1.text-muted
|
|
17
|
+
h4.mt-3.text-muted No deployments found
|
|
18
|
+
p.text-muted.mb-3 Deployments will appear here once you deploy your projects
|
|
19
|
+
a.btn.btn-primary(href="/projects")
|
|
20
|
+
i.bi.bi-folder.me-2
|
|
21
|
+
| Go to Projects
|
|
22
|
+
|
|
23
|
+
else
|
|
24
|
+
// Deployments Table
|
|
25
|
+
.card.border-0.shadow-sm
|
|
26
|
+
.card-header.bg-white.border-bottom-0.pt-3.pb-0
|
|
27
|
+
.d-flex.align-items-center
|
|
28
|
+
i.bi.bi-rocket-takeoff.text-primary.me-2(style="font-size: 1.25rem;")
|
|
29
|
+
h5.mb-0 All Deployments
|
|
30
|
+
.card-body.p-0
|
|
31
|
+
.table-responsive
|
|
32
|
+
table.table.table-hover.align-middle.mb-0
|
|
33
|
+
thead.table-light
|
|
34
|
+
tr
|
|
35
|
+
th(style="font-weight: 600; width: 80px;") ID
|
|
36
|
+
th(style="font-weight: 600;") Project
|
|
37
|
+
th(style="font-weight: 600; width: 120px;") Status
|
|
38
|
+
th(style="font-weight: 600;") Started
|
|
39
|
+
th(style="font-weight: 600; width: 100px;") Action
|
|
40
|
+
tbody
|
|
41
|
+
each dep in deployments
|
|
42
|
+
tr
|
|
43
|
+
td
|
|
44
|
+
code.text-small #{dep.id}
|
|
45
|
+
td
|
|
46
|
+
.d-flex.align-items-center
|
|
47
|
+
i.bi.bi-folder-fill.text-primary.me-2
|
|
48
|
+
span.fw-medium #{dep.name}
|
|
49
|
+
td
|
|
50
|
+
if dep.status === "running"
|
|
51
|
+
span.badge.bg-warning.text-dark.px-3.py-2
|
|
52
|
+
i.bi.bi-play-fill.me-1
|
|
53
|
+
| running
|
|
54
|
+
else if dep.status === "success"
|
|
55
|
+
span.badge.bg-success.px-3.py-2
|
|
56
|
+
i.bi.bi-check-circle-fill.me-1
|
|
57
|
+
| success
|
|
58
|
+
else
|
|
59
|
+
span.badge.bg-danger.px-3.py-2
|
|
60
|
+
i.bi.bi-x-circle-fill.me-1
|
|
61
|
+
| failed
|
|
62
|
+
td
|
|
63
|
+
.d-flex.align-items-center
|
|
64
|
+
i.bi.bi-clock.text-muted.me-2
|
|
65
|
+
small #{dep.started_at}
|
|
66
|
+
td
|
|
67
|
+
a.btn.btn-sm.btn-outline-primary(href=`/deployment/${dep.id}`)
|
|
68
|
+
i.bi.bi-eye.me-1
|
|
69
|
+
| View
|
|
70
|
+
|
|
71
|
+
// Additional styles
|
|
72
|
+
style.
|
|
73
|
+
.card {
|
|
74
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.card:hover {
|
|
78
|
+
transform: translateY(-2px);
|
|
79
|
+
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.08) !important;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.table-hover tbody tr:hover {
|
|
83
|
+
background-color: #f8f9fa;
|
|
84
|
+
cursor: pointer;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.badge {
|
|
88
|
+
font-weight: 500;
|
|
89
|
+
font-size: 0.75rem;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
code {
|
|
93
|
+
background: #f8f9fa;
|
|
94
|
+
padding: 0.2rem 0.4rem;
|
|
95
|
+
border-radius: 4px;
|
|
96
|
+
font-size: 0.8rem;
|
|
97
|
+
color: #d63384;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.btn-sm {
|
|
101
|
+
padding: 0.375rem 0.75rem;
|
|
102
|
+
font-size: 0.813rem;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.table-responsive {
|
|
106
|
+
border-radius: 8px;
|
|
107
|
+
overflow: hidden;
|
|
108
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Request, Response } from "express";
|
|
2
|
+
export declare const listProjects: (_req: Request, res: Response) => void;
|
|
3
|
+
export declare const viewCreateProject: (_req: Request, res: Response) => void;
|
|
4
|
+
export declare const createProject: (req: Request, res: Response) => Promise<void>;
|
|
5
|
+
export declare const deleteProject: (req: Request, res: Response) => Promise<void>;
|
|
6
|
+
export declare const viewProjectDetails: (req: Request, res: Response) => void;
|
|
7
|
+
export declare const addEnv: (req: Request, res: Response) => Promise<void>;
|
|
8
|
+
export declare const deleteEnv: (req: Request, res: Response) => Promise<void>;
|
|
9
|
+
export declare const addCommand: (req: Request, res: Response) => void;
|
|
10
|
+
export declare const deleteCommand: (req: Request, res: Response) => void;
|
|
11
|
+
export declare const updateProject: (req: Request, res: Response) => void;
|
|
12
|
+
//# sourceMappingURL=projects.controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.controller.d.ts","sourceRoot":"","sources":["../../../../src/modules/project/controllers/projects.controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAejD,eAAO,MAAM,YAAY,GAAI,MAAM,OAAO,EAAE,KAAK,QAAQ,SAMxD,CAAC;AAGF,eAAO,MAAM,iBAAiB,GAAI,MAAM,OAAO,EAAE,KAAK,QAAQ,SAE7D,CAAC;AAGF,eAAO,MAAM,aAAa,GAAU,KAAK,OAAO,EAAE,KAAK,QAAQ,kBAyE9D,CAAC;AAGF,eAAO,MAAM,aAAa,GAAU,KAAK,OAAO,EAAE,KAAK,QAAQ,kBAkC9D,CAAC;AAGF,eAAO,MAAM,kBAAkB,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,SA0B7D,CAAC;AAEF,eAAO,MAAM,MAAM,GAAU,KAAK,OAAO,EAAE,KAAK,QAAQ,kBAkDvD,CAAC;AAEF,eAAO,MAAM,SAAS,GAAU,KAAK,OAAO,EAAE,KAAK,QAAQ,kBA2C1D,CAAC;AAGF,eAAO,MAAM,UAAU,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,SAsBrD,CAAC;AAGF,eAAO,MAAM,aAAa,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,SAOxD,CAAC;AAGF,eAAO,MAAM,aAAa,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,SA4CxD,CAAC"}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
2
|
+
import { getDB } from "../../../db/db.js";
|
|
3
|
+
import { cloneRepo, createEnvFile, createProjDir, deleteProjDir, removeEnvKeyFromFile, upsertEnvFile, } from "../helper/project_file_sys.helper.js";
|
|
4
|
+
import { writeToLogFile } from "../../../helpers/logging.helper.js";
|
|
5
|
+
import { normalizeRepoUrl } from "../../../helpers/common.helper.js";
|
|
6
|
+
// LIST PROJECTS
|
|
7
|
+
export const listProjects = (_req, res) => {
|
|
8
|
+
const db = getDB();
|
|
9
|
+
const projects = db.prepare("SELECT * FROM project").all();
|
|
10
|
+
return res.render("project/views/index", { active: "project", projects });
|
|
11
|
+
};
|
|
12
|
+
// VIEW CREATE PAGE
|
|
13
|
+
export const viewCreateProject = (_req, res) => {
|
|
14
|
+
return res.render("project/views/create", { active: "project" });
|
|
15
|
+
};
|
|
16
|
+
// CREATE PROJECT
|
|
17
|
+
export const createProject = async (req, res) => {
|
|
18
|
+
const { name, github_url, branch_name, receive_email_notf, auto_deploy } = req.body;
|
|
19
|
+
const receiveEmail = receive_email_notf ? 1 : 0;
|
|
20
|
+
const autoDeploy = auto_deploy ? 1 : 0;
|
|
21
|
+
if (!name || !github_url || !branch_name) {
|
|
22
|
+
return res.render("project/views/create", {
|
|
23
|
+
error: "All fields are required",
|
|
24
|
+
active: "project",
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const db = getDB();
|
|
28
|
+
try {
|
|
29
|
+
// Validate email config
|
|
30
|
+
if (receiveEmail === 1) {
|
|
31
|
+
const emailConfig = db.prepare("SELECT * FROM email LIMIT 1").get();
|
|
32
|
+
if (!emailConfig) {
|
|
33
|
+
return res.render("project/views/create", {
|
|
34
|
+
error: "Email settings not configured",
|
|
35
|
+
active: "project",
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const secret = crypto.randomBytes(32).toString("hex");
|
|
40
|
+
const projectId = crypto.randomUUID();
|
|
41
|
+
// 1. Insert into DB
|
|
42
|
+
db.prepare(`
|
|
43
|
+
INSERT INTO project (
|
|
44
|
+
id, name, github_url, branch_name, receive_email_notf, auto_deploy, webhook_secret
|
|
45
|
+
)
|
|
46
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
47
|
+
`).run(projectId, name.trim(), github_url.trim(), branch_name.trim(), receiveEmail, autoDeploy, secret);
|
|
48
|
+
// 2. Create FS resources
|
|
49
|
+
try {
|
|
50
|
+
await createProjDir(projectId);
|
|
51
|
+
const repoUrl = normalizeRepoUrl(github_url.trim());
|
|
52
|
+
await cloneRepo(repoUrl, projectId, branch_name.trim());
|
|
53
|
+
await createEnvFile(projectId);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
// rollback DB
|
|
57
|
+
db.prepare("DELETE FROM project WHERE id = ?").run(projectId);
|
|
58
|
+
deleteProjDir(projectId).catch(() => { });
|
|
59
|
+
return res.render("project/views/create", {
|
|
60
|
+
error: err.message || "Failed to setup project",
|
|
61
|
+
active: "project",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return res.redirect("/projects");
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
return res.render("project/views/create", {
|
|
68
|
+
error: error.message || "Something went wrong",
|
|
69
|
+
active: "project",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
// DELETE PROJECT
|
|
74
|
+
export const deleteProject = async (req, res) => {
|
|
75
|
+
const { id } = req.params;
|
|
76
|
+
const db = getDB();
|
|
77
|
+
try {
|
|
78
|
+
db.prepare("DELETE FROM project WHERE id = ?").run(id);
|
|
79
|
+
if (typeof id !== "string") {
|
|
80
|
+
return res.render("project/views/index", {
|
|
81
|
+
error: "Id must be string",
|
|
82
|
+
active: "project",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
// best effort cleanup
|
|
86
|
+
deleteProjDir(id).catch((err) => {
|
|
87
|
+
writeToLogFile("FS cleanup failed", {
|
|
88
|
+
level: "ERROR",
|
|
89
|
+
source: "FS",
|
|
90
|
+
meta: { projectId: id, error: err.message },
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
return res.redirect("/projects");
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
writeToLogFile("Project deletion failed", {
|
|
97
|
+
level: "ERROR",
|
|
98
|
+
source: "DB",
|
|
99
|
+
meta: { projectId: id, error: error.message },
|
|
100
|
+
});
|
|
101
|
+
return res.render("project/views/index", {
|
|
102
|
+
error: error.message || "Failed to delete project",
|
|
103
|
+
active: "project",
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
// VIEW DETAILS
|
|
108
|
+
export const viewProjectDetails = (req, res) => {
|
|
109
|
+
const { id } = req.params;
|
|
110
|
+
const db = getDB();
|
|
111
|
+
const project = db.prepare("SELECT * FROM project WHERE id = ?").get(id);
|
|
112
|
+
if (!project) {
|
|
113
|
+
return res.status(404).render("common/views/404");
|
|
114
|
+
}
|
|
115
|
+
const commands = db
|
|
116
|
+
.prepare("SELECT * FROM project_commands WHERE proj_id = ? ORDER BY seq_no ASC")
|
|
117
|
+
.all(id);
|
|
118
|
+
const envs = db
|
|
119
|
+
.prepare("SELECT * FROM project_env WHERE proj_id = ?")
|
|
120
|
+
.all(id);
|
|
121
|
+
return res.render("project/views/details", {
|
|
122
|
+
project,
|
|
123
|
+
commands,
|
|
124
|
+
envs,
|
|
125
|
+
active: "project",
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
export const addEnv = async (req, res) => {
|
|
129
|
+
const { id } = req.params;
|
|
130
|
+
const { key, value } = req.body;
|
|
131
|
+
if (!key || !value) {
|
|
132
|
+
return res.redirect(`/projects/${id}`);
|
|
133
|
+
}
|
|
134
|
+
const db = getDB();
|
|
135
|
+
const cleanKey = key.trim();
|
|
136
|
+
const cleanValue = value.trim();
|
|
137
|
+
try {
|
|
138
|
+
// Try insert first
|
|
139
|
+
db.prepare(`
|
|
140
|
+
INSERT INTO project_env (id, proj_id, key, value)
|
|
141
|
+
VALUES (?, ?, ?, ?)
|
|
142
|
+
`).run(crypto.randomUUID(), id, cleanKey, cleanValue);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
// If duplicate → update instead
|
|
146
|
+
if (err.code === "SQLITE_CONSTRAINT_UNIQUE") {
|
|
147
|
+
db.prepare(`
|
|
148
|
+
UPDATE project_env
|
|
149
|
+
SET value = ?
|
|
150
|
+
WHERE proj_id = ? AND key = ?
|
|
151
|
+
`).run(cleanValue, id, cleanKey);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
writeToLogFile("Env DB operation failed", {
|
|
155
|
+
level: "ERROR",
|
|
156
|
+
source: "DB",
|
|
157
|
+
meta: { projectId: id, key: cleanKey, error: err.message },
|
|
158
|
+
});
|
|
159
|
+
return res.redirect(`/projects/${id}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Sync with .env file (important)
|
|
163
|
+
try {
|
|
164
|
+
await upsertEnvFile(id.toString(), cleanKey, cleanValue);
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
writeToLogFile("Env file sync failed", {
|
|
168
|
+
level: "ERROR",
|
|
169
|
+
source: "FS",
|
|
170
|
+
meta: { projectId: id, key: cleanKey, error: err.message },
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
return res.redirect(`/projects/${id}`);
|
|
174
|
+
};
|
|
175
|
+
export const deleteEnv = async (req, res) => {
|
|
176
|
+
const { id, envId } = req.params;
|
|
177
|
+
const db = getDB();
|
|
178
|
+
let key = null;
|
|
179
|
+
try {
|
|
180
|
+
// 1. get key before deleting (important)
|
|
181
|
+
const row = db
|
|
182
|
+
.prepare("SELECT key FROM project_env WHERE id = ?")
|
|
183
|
+
.get(envId);
|
|
184
|
+
if (!row) {
|
|
185
|
+
return res.redirect(`/projects/${id}`);
|
|
186
|
+
}
|
|
187
|
+
key = row.key;
|
|
188
|
+
// 2. delete from DB
|
|
189
|
+
db.prepare("DELETE FROM project_env WHERE id = ?").run(envId);
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
writeToLogFile("Env delete DB failed", {
|
|
193
|
+
level: "ERROR",
|
|
194
|
+
source: "DB",
|
|
195
|
+
meta: { projectId: id, envId, error: err.message },
|
|
196
|
+
});
|
|
197
|
+
return res.redirect(`/projects/${id}`);
|
|
198
|
+
}
|
|
199
|
+
// 3. sync .env file
|
|
200
|
+
try {
|
|
201
|
+
await removeEnvKeyFromFile(id.toString(), key ?? 'key');
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
writeToLogFile("Env file delete sync failed", {
|
|
205
|
+
level: "ERROR",
|
|
206
|
+
source: "FS",
|
|
207
|
+
meta: { projectId: id, key, error: err.message },
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return res.redirect(`/projects/${id}`);
|
|
211
|
+
};
|
|
212
|
+
// ADD COMMAND
|
|
213
|
+
export const addCommand = (req, res) => {
|
|
214
|
+
const { id } = req.params;
|
|
215
|
+
const { cmd } = req.body;
|
|
216
|
+
if (!cmd) {
|
|
217
|
+
return res.redirect(`/projects/${id}`);
|
|
218
|
+
}
|
|
219
|
+
const db = getDB();
|
|
220
|
+
const seq = db
|
|
221
|
+
.prepare("SELECT COUNT(*) as count FROM project_commands WHERE proj_id = ?")
|
|
222
|
+
.get(id);
|
|
223
|
+
db.prepare(`
|
|
224
|
+
INSERT INTO project_commands (id, proj_id, seq_no, cmd)
|
|
225
|
+
VALUES (?, ?, ?, ?)
|
|
226
|
+
`).run(crypto.randomUUID(), id, seq.count + 1, cmd);
|
|
227
|
+
return res.redirect(`/projects/${id}`);
|
|
228
|
+
};
|
|
229
|
+
// DELETE COMMAND
|
|
230
|
+
export const deleteCommand = (req, res) => {
|
|
231
|
+
const { id, cmdId } = req.params;
|
|
232
|
+
const db = getDB();
|
|
233
|
+
db.prepare("DELETE FROM project_commands WHERE id = ?").run(cmdId);
|
|
234
|
+
return res.redirect(`/projects/${id}`);
|
|
235
|
+
};
|
|
236
|
+
// UPDATE PROJECT
|
|
237
|
+
export const updateProject = (req, res) => {
|
|
238
|
+
const { id } = req.params;
|
|
239
|
+
const { name, github_url, branch_name, receive_email_notf, auto_deploy } = req.body;
|
|
240
|
+
const receiveEmail = receive_email_notf ? 1 : 0;
|
|
241
|
+
const autoDeploy = auto_deploy ? 1 : 0;
|
|
242
|
+
const db = getDB();
|
|
243
|
+
try {
|
|
244
|
+
// Validate email config if enabled
|
|
245
|
+
if (receiveEmail === 1) {
|
|
246
|
+
const emailConfig = db.prepare("SELECT * FROM email LIMIT 1").get();
|
|
247
|
+
if (!emailConfig) {
|
|
248
|
+
return res.redirect(`/projects/${id}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
db.prepare(`
|
|
252
|
+
UPDATE project
|
|
253
|
+
SET
|
|
254
|
+
name = ?,
|
|
255
|
+
github_url = ?,
|
|
256
|
+
branch_name = ?,
|
|
257
|
+
receive_email_notf = ?,
|
|
258
|
+
auto_deploy = ?
|
|
259
|
+
WHERE id = ?
|
|
260
|
+
`).run(name.trim(), github_url.trim(), branch_name.trim(), receiveEmail, autoDeploy, id);
|
|
261
|
+
return res.redirect(`/projects/${id}`);
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
return res.redirect(`/projects/${id}`);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
//# sourceMappingURL=projects.controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.controller.js","sourceRoot":"","sources":["../../../../src/modules/project/controllers/projects.controller.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const createProjDir: (projectId: string) => Promise<void>;
|
|
2
|
+
export declare const createEnvFile: (projectId: string) => Promise<void>;
|
|
3
|
+
export declare const cloneRepo: (repoUrl: string, projId: string, branch: string) => Promise<void>;
|
|
4
|
+
export declare const deleteProjDir: (projectId: string) => Promise<void>;
|
|
5
|
+
export declare const upsertEnvFile: (projId: string, key: string, value: string) => Promise<void>;
|
|
6
|
+
export declare const removeEnvKeyFromFile: (projId: string, key: string) => Promise<void>;
|
|
7
|
+
//# sourceMappingURL=project_file_sys.helper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project_file_sys.helper.d.ts","sourceRoot":"","sources":["../../../../src/modules/project/helper/project_file_sys.helper.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,aAAa,GAAU,WAAW,MAAM,kBAMpD,CAAC;AAEF,eAAO,MAAM,aAAa,GAAU,WAAW,MAAM,kBAKpD,CAAC;AAGF,eAAO,MAAM,SAAS,GAAI,SAAS,MAAM,EAAE,QAAQ,MAAM,EAAC,QAAO,MAAM,kBAuCtE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAU,WAAW,MAAM,kBAKpD,CAAC;AAEF,eAAO,MAAM,aAAa,GACxB,QAAQ,MAAM,EACd,KAAK,MAAM,EACX,OAAO,MAAM,kBA8Bd,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,MAAM,EACd,KAAK,MAAM,kBAmBZ,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { spawn } from "child_process";
|
|
4
|
+
import { getWorkDirPath } from "../../../helpers/arg.helper.js";
|
|
5
|
+
import { writeToLogFile } from "../../../helpers/logging.helper.js";
|
|
6
|
+
export const createProjDir = async (projectId) => {
|
|
7
|
+
const workDir = getWorkDirPath();
|
|
8
|
+
const projPath = path.join(workDir, projectId);
|
|
9
|
+
await fs.mkdir(projPath, { recursive: true });
|
|
10
|
+
};
|
|
11
|
+
export const createEnvFile = async (projectId) => {
|
|
12
|
+
const workDir = getWorkDirPath();
|
|
13
|
+
const filePath = path.join(workDir, projectId, ".env");
|
|
14
|
+
await fs.writeFile(filePath, "");
|
|
15
|
+
};
|
|
16
|
+
export const cloneRepo = (repoUrl, projId, branch) => {
|
|
17
|
+
const cwd = path.join(getWorkDirPath(), projId);
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
let stderr = "";
|
|
20
|
+
let stdout = "";
|
|
21
|
+
const child = spawn("git", ["clone", "-b", branch, repoUrl, "."], { cwd });
|
|
22
|
+
child.stdout.on("data", (data) => {
|
|
23
|
+
const msg = data.toString();
|
|
24
|
+
stdout += msg;
|
|
25
|
+
writeToLogFile(msg, { source: "GIT" });
|
|
26
|
+
});
|
|
27
|
+
child.stderr.on("data", (data) => {
|
|
28
|
+
const msg = data.toString();
|
|
29
|
+
stderr += msg;
|
|
30
|
+
writeToLogFile(msg, { level: "ERROR", source: "GIT" });
|
|
31
|
+
});
|
|
32
|
+
child.on("error", (err) => {
|
|
33
|
+
// process spawn failed (git not found, etc.)
|
|
34
|
+
reject(new Error(`Failed to start git process: ${err.message}`));
|
|
35
|
+
});
|
|
36
|
+
child.on("close", (code) => {
|
|
37
|
+
if (code === 0) {
|
|
38
|
+
return resolve();
|
|
39
|
+
}
|
|
40
|
+
// extract meaningful error
|
|
41
|
+
const errorMsg = stderr.trim() ||
|
|
42
|
+
stdout.trim() ||
|
|
43
|
+
`git clone failed with exit code ${code}`;
|
|
44
|
+
reject(new Error(errorMsg));
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
export const deleteProjDir = async (projectId) => {
|
|
49
|
+
const workDir = getWorkDirPath();
|
|
50
|
+
const projPath = path.join(workDir, projectId);
|
|
51
|
+
await fs.rm(projPath, { recursive: true, force: true });
|
|
52
|
+
};
|
|
53
|
+
export const upsertEnvFile = async (projId, key, value) => {
|
|
54
|
+
const workDir = getWorkDirPath();
|
|
55
|
+
const filePath = path.join(workDir, projId, ".env");
|
|
56
|
+
let content = "";
|
|
57
|
+
try {
|
|
58
|
+
content = await fs.readFile(filePath, "utf-8");
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// file may not exist → fine
|
|
62
|
+
}
|
|
63
|
+
const lines = content.split("\n").filter(Boolean);
|
|
64
|
+
let found = false;
|
|
65
|
+
const updated = lines.map((line) => {
|
|
66
|
+
if (line.startsWith(`${key}=`)) {
|
|
67
|
+
found = true;
|
|
68
|
+
return `${key}=${value}`;
|
|
69
|
+
}
|
|
70
|
+
return line;
|
|
71
|
+
});
|
|
72
|
+
if (!found) {
|
|
73
|
+
updated.push(`${key}=${value}`);
|
|
74
|
+
}
|
|
75
|
+
await fs.writeFile(filePath, updated.join("\n") + "\n");
|
|
76
|
+
};
|
|
77
|
+
export const removeEnvKeyFromFile = async (projId, key) => {
|
|
78
|
+
const workDir = getWorkDirPath();
|
|
79
|
+
const filePath = path.join(workDir, projId, ".env");
|
|
80
|
+
let content = "";
|
|
81
|
+
try {
|
|
82
|
+
content = await fs.readFile(filePath, "utf-8");
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return; // file may not exist → fine
|
|
86
|
+
}
|
|
87
|
+
const updated = content
|
|
88
|
+
.split("\n")
|
|
89
|
+
.filter((line) => line && !line.startsWith(`${key}=`))
|
|
90
|
+
.join("\n");
|
|
91
|
+
await fs.writeFile(filePath, updated + (updated ? "\n" : ""));
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=project_file_sys.helper.js.map
|