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.
Files changed (134) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +151 -0
  3. package/dist/app.d.ts +4 -0
  4. package/dist/app.d.ts.map +1 -0
  5. package/dist/app.js +39 -0
  6. package/dist/app.js.map +1 -0
  7. package/dist/db/db.d.ts +5 -0
  8. package/dist/db/db.d.ts.map +1 -0
  9. package/dist/db/db.js +100 -0
  10. package/dist/db/db.js.map +1 -0
  11. package/dist/helpers/arg.helper.d.ts +7 -0
  12. package/dist/helpers/arg.helper.d.ts.map +1 -0
  13. package/dist/helpers/arg.helper.js +28 -0
  14. package/dist/helpers/arg.helper.js.map +1 -0
  15. package/dist/helpers/cmd_executer.helper.d.ts +7 -0
  16. package/dist/helpers/cmd_executer.helper.d.ts.map +1 -0
  17. package/dist/helpers/cmd_executer.helper.js +88 -0
  18. package/dist/helpers/cmd_executer.helper.js.map +1 -0
  19. package/dist/helpers/common.helper.d.ts +2 -0
  20. package/dist/helpers/common.helper.d.ts.map +1 -0
  21. package/dist/helpers/common.helper.js +15 -0
  22. package/dist/helpers/common.helper.js.map +1 -0
  23. package/dist/helpers/config_files.helper.d.ts +2 -0
  24. package/dist/helpers/config_files.helper.d.ts.map +1 -0
  25. package/dist/helpers/config_files.helper.js +44 -0
  26. package/dist/helpers/config_files.helper.js.map +1 -0
  27. package/dist/helpers/create_systemd_service.helper.d.ts +9 -0
  28. package/dist/helpers/create_systemd_service.helper.d.ts.map +1 -0
  29. package/dist/helpers/create_systemd_service.helper.js +40 -0
  30. package/dist/helpers/create_systemd_service.helper.js.map +1 -0
  31. package/dist/helpers/date.helper.d.ts +2 -0
  32. package/dist/helpers/date.helper.d.ts.map +1 -0
  33. package/dist/helpers/date.helper.js +18 -0
  34. package/dist/helpers/date.helper.js.map +1 -0
  35. package/dist/helpers/email.helper.d.ts +11 -0
  36. package/dist/helpers/email.helper.d.ts.map +1 -0
  37. package/dist/helpers/email.helper.js +144 -0
  38. package/dist/helpers/email.helper.js.map +1 -0
  39. package/dist/helpers/logging.helper.d.ts +9 -0
  40. package/dist/helpers/logging.helper.d.ts.map +1 -0
  41. package/dist/helpers/logging.helper.js +32 -0
  42. package/dist/helpers/logging.helper.js.map +1 -0
  43. package/dist/helpers/remove_systemd_service.helper.d.ts +2 -0
  44. package/dist/helpers/remove_systemd_service.helper.d.ts.map +1 -0
  45. package/dist/helpers/remove_systemd_service.helper.js +21 -0
  46. package/dist/helpers/remove_systemd_service.helper.js.map +1 -0
  47. package/dist/helpers/start_systemd_service.helper.d.ts +2 -0
  48. package/dist/helpers/start_systemd_service.helper.d.ts.map +1 -0
  49. package/dist/helpers/start_systemd_service.helper.js +26 -0
  50. package/dist/helpers/start_systemd_service.helper.js.map +1 -0
  51. package/dist/index.d.ts +2 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +132 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/middleware/auth.middleware.d.ts +3 -0
  56. package/dist/middleware/auth.middleware.d.ts.map +1 -0
  57. package/dist/middleware/auth.middleware.js +7 -0
  58. package/dist/middleware/auth.middleware.js.map +1 -0
  59. package/dist/modules/auth/auth.route.d.ts +3 -0
  60. package/dist/modules/auth/auth.route.d.ts.map +1 -0
  61. package/dist/modules/auth/auth.route.js +16 -0
  62. package/dist/modules/auth/auth.route.js.map +1 -0
  63. package/dist/modules/auth/controllers/auth.controller.d.ts +6 -0
  64. package/dist/modules/auth/controllers/auth.controller.d.ts.map +1 -0
  65. package/dist/modules/auth/controllers/auth.controller.js +123 -0
  66. package/dist/modules/auth/controllers/auth.controller.js.map +1 -0
  67. package/dist/modules/auth/views/login.pug +254 -0
  68. package/dist/modules/auth/views/register.pug +298 -0
  69. package/dist/modules/common/views/404.pug +21 -0
  70. package/dist/modules/dashboard/dashboard.route.d.ts +3 -0
  71. package/dist/modules/dashboard/dashboard.route.d.ts.map +1 -0
  72. package/dist/modules/dashboard/dashboard.route.js +29 -0
  73. package/dist/modules/dashboard/dashboard.route.js.map +1 -0
  74. package/dist/modules/dashboard/views/dash.pug +149 -0
  75. package/dist/modules/dashboard/views/layout.pug +94 -0
  76. package/dist/modules/deployer/controllers/deployer.controller.d.ts +6 -0
  77. package/dist/modules/deployer/controllers/deployer.controller.d.ts.map +1 -0
  78. package/dist/modules/deployer/controllers/deployer.controller.js +119 -0
  79. package/dist/modules/deployer/controllers/deployer.controller.js.map +1 -0
  80. package/dist/modules/deployer/deployer.route.d.ts +3 -0
  81. package/dist/modules/deployer/deployer.route.d.ts.map +1 -0
  82. package/dist/modules/deployer/deployer.route.js +13 -0
  83. package/dist/modules/deployer/deployer.route.js.map +1 -0
  84. package/dist/modules/deployer/helper/deployer.d.ts +9 -0
  85. package/dist/modules/deployer/helper/deployer.d.ts.map +1 -0
  86. package/dist/modules/deployer/helper/deployer.js +89 -0
  87. package/dist/modules/deployer/helper/deployer.js.map +1 -0
  88. package/dist/modules/deployer/views/details.pug +157 -0
  89. package/dist/modules/deployer/views/index.pug +108 -0
  90. package/dist/modules/project/controllers/projects.controller.d.ts +12 -0
  91. package/dist/modules/project/controllers/projects.controller.d.ts.map +1 -0
  92. package/dist/modules/project/controllers/projects.controller.js +267 -0
  93. package/dist/modules/project/controllers/projects.controller.js.map +1 -0
  94. package/dist/modules/project/helper/project_file_sys.helper.d.ts +7 -0
  95. package/dist/modules/project/helper/project_file_sys.helper.d.ts.map +1 -0
  96. package/dist/modules/project/helper/project_file_sys.helper.js +93 -0
  97. package/dist/modules/project/helper/project_file_sys.helper.js.map +1 -0
  98. package/dist/modules/project/project.route.d.ts +3 -0
  99. package/dist/modules/project/project.route.d.ts.map +1 -0
  100. package/dist/modules/project/project.route.js +17 -0
  101. package/dist/modules/project/project.route.js.map +1 -0
  102. package/dist/modules/project/views/create.pug +189 -0
  103. package/dist/modules/project/views/details.pug +238 -0
  104. package/dist/modules/project/views/index.pug +114 -0
  105. package/dist/modules/settings/controllers/setting.controller.d.ts +5 -0
  106. package/dist/modules/settings/controllers/setting.controller.d.ts.map +1 -0
  107. package/dist/modules/settings/controllers/setting.controller.js +193 -0
  108. package/dist/modules/settings/controllers/setting.controller.js.map +1 -0
  109. package/dist/modules/settings/settings.route.d.ts +3 -0
  110. package/dist/modules/settings/settings.route.d.ts.map +1 -0
  111. package/dist/modules/settings/settings.route.js +10 -0
  112. package/dist/modules/settings/settings.route.js.map +1 -0
  113. package/dist/modules/settings/views/index.pug +173 -0
  114. package/dist/modules/webhook/controllers/webhook.controller.d.ts +3 -0
  115. package/dist/modules/webhook/controllers/webhook.controller.d.ts.map +1 -0
  116. package/dist/modules/webhook/controllers/webhook.controller.js +72 -0
  117. package/dist/modules/webhook/controllers/webhook.controller.js.map +1 -0
  118. package/dist/modules/webhook/webhook.route.d.ts +3 -0
  119. package/dist/modules/webhook/webhook.route.d.ts.map +1 -0
  120. package/dist/modules/webhook/webhook.route.js +6 -0
  121. package/dist/modules/webhook/webhook.route.js.map +1 -0
  122. package/dist/utils/app_config.d.ts +3 -0
  123. package/dist/utils/app_config.d.ts.map +1 -0
  124. package/dist/utils/app_config.js +5 -0
  125. package/dist/utils/app_config.js.map +1 -0
  126. package/dist/utils/cli.d.ts +2 -0
  127. package/dist/utils/cli.d.ts.map +1 -0
  128. package/dist/utils/cli.js +68 -0
  129. package/dist/utils/cli.js.map +1 -0
  130. package/package.json +80 -0
  131. package/public/favicon.svg +18 -0
  132. package/public/js/deploy.js +36 -0
  133. package/public/js/deployment-details.js +56 -0
  134. 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