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,123 @@
1
+ import { getDB } from "../../../db/db.js";
2
+ import bcrypt from "bcrypt";
3
+ export const viewLogin = async (req, res) => {
4
+ // block if already logged in
5
+ if (req.session && req.session.user) {
6
+ return res.redirect("/");
7
+ }
8
+ return res.render("auth/views/login");
9
+ };
10
+ export const login = async (req, res) => {
11
+ // block if already logged in
12
+ if (req.session && req.session.user) {
13
+ return res.redirect("/");
14
+ }
15
+ const { username, password } = req.body || {};
16
+ const errorMessages = [];
17
+ if (!username || username.trim() === "") {
18
+ errorMessages.push("Username is required");
19
+ }
20
+ if (!password || password.trim() === "") {
21
+ errorMessages.push("Password is required");
22
+ }
23
+ if (errorMessages.length > 0) {
24
+ return res.render("auth/views/login", {
25
+ error: errorMessages.join(", "),
26
+ });
27
+ }
28
+ const db = getDB();
29
+ try {
30
+ const user = db
31
+ .prepare("SELECT id, username, email, password FROM user WHERE username = ?")
32
+ .get(username.trim());
33
+ if (!user) {
34
+ return res.render("auth/views/login", {
35
+ error: "Invalid username or password",
36
+ });
37
+ }
38
+ const isMatch = await bcrypt.compare(password, user.password);
39
+ if (!isMatch) {
40
+ return res.render("auth/views/login", {
41
+ error: "Invalid username or password",
42
+ });
43
+ }
44
+ db.prepare(`
45
+ UPDATE user
46
+ SET last_logged_in = datetime('now')
47
+ WHERE id = ?
48
+ `).run(user.id);
49
+ // create session
50
+ req.session.user = {
51
+ id: user.id,
52
+ email: user.email,
53
+ };
54
+ return res.redirect("/");
55
+ }
56
+ catch (error) {
57
+ return res.render("auth/views/login", {
58
+ error: error.message || "Something went wrong",
59
+ });
60
+ }
61
+ };
62
+ export const viewRegister = async (_req, res) => {
63
+ const db = getDB();
64
+ const result = db.prepare("SELECT COUNT(id) as count FROM user").get();
65
+ return res.render("auth/views/register", {
66
+ isForbidden: result.count === 0 ? false : true,
67
+ });
68
+ };
69
+ export const register = async (req, res) => {
70
+ const { username, email, password } = req.body || {};
71
+ // Collect errors as strings
72
+ const errorMessages = [];
73
+ if (!username || username.trim() === "") {
74
+ errorMessages.push("Username is required");
75
+ }
76
+ else if (username.length < 3) {
77
+ errorMessages.push("Username must be at least 3 characters");
78
+ }
79
+ if (!email || email.trim() === "") {
80
+ errorMessages.push("Email is required");
81
+ }
82
+ else {
83
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
84
+ if (!emailRegex.test(email)) {
85
+ errorMessages.push("Invalid email format");
86
+ }
87
+ }
88
+ if (!password || password.trim() === "") {
89
+ errorMessages.push("Password is required");
90
+ }
91
+ else if (password.length < 6) {
92
+ errorMessages.push("Password must be at least 6 characters");
93
+ }
94
+ // If any validation errors → join into single line
95
+ if (errorMessages.length > 0) {
96
+ return res.render("auth/views/register", {
97
+ error: errorMessages.join(", "),
98
+ });
99
+ }
100
+ const db = getDB();
101
+ try {
102
+ const result = db.prepare("SELECT COUNT(id) as count FROM user").get();
103
+ if (result.count >= 1) {
104
+ return res.render("auth/views/register", {
105
+ isForbidden: true,
106
+ });
107
+ }
108
+ const hashedPass = await bcrypt.hash(password, 10);
109
+ db.prepare(`
110
+ INSERT INTO user (id, username, email, password, created_at)
111
+ VALUES (?, ?, ?, ?, datetime('now'))
112
+ `).run(crypto.randomUUID(), username.trim(), email.trim(), hashedPass);
113
+ return res.render("auth/views/register", {
114
+ success: "User created successfully",
115
+ });
116
+ }
117
+ catch (error) {
118
+ return res.render("auth/views/register", {
119
+ error: error.message || "Something went wrong",
120
+ });
121
+ }
122
+ };
123
+ //# sourceMappingURL=auth.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.controller.js","sourceRoot":"","sources":["../../../../src/modules/auth/controllers/auth.controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,254 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title Login - VPS Deployer
5
+ meta(charset="UTF-8")
6
+ meta(name="viewport", content="width=device-width, initial-scale=1")
7
+
8
+ // Bootstrap 5 CSS + Icons
9
+ link(href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css", rel="stylesheet")
10
+ link(rel="stylesheet", href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css")
11
+ link(rel="icon", type="image/svg+xml", href="/favicon.svg")
12
+
13
+ style.
14
+ * {
15
+ margin: 0;
16
+ padding: 0;
17
+ box-sizing: border-box;
18
+ }
19
+
20
+ body {
21
+ background: #f5f7fa;
22
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
23
+ }
24
+
25
+ .login-container {
26
+ min-height: 100vh;
27
+ background: #f5f7fa;
28
+ }
29
+
30
+ .login-card {
31
+ background: #ffffff;
32
+ border-radius: 8px;
33
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04);
34
+ border: 1px solid #e9ecef;
35
+ }
36
+
37
+ .form-label {
38
+ font-size: 0.875rem;
39
+ font-weight: 500;
40
+ color: #1a1f36;
41
+ margin-bottom: 0.5rem;
42
+ }
43
+
44
+ .form-control {
45
+ border: 1px solid #dde1e6;
46
+ border-radius: 6px;
47
+ padding: 0.625rem 0.875rem;
48
+ font-size: 0.9375rem;
49
+ transition: all 0.15s ease;
50
+ }
51
+
52
+ .form-control:focus {
53
+ border-color: #0066ff;
54
+ box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.1);
55
+ outline: none;
56
+ }
57
+
58
+ .input-group-text {
59
+ background: #ffffff;
60
+ border: 1px solid #dde1e6;
61
+ border-right: none;
62
+ color: #6c757d;
63
+ font-size: 0.9375rem;
64
+ }
65
+
66
+ .input-group .form-control {
67
+ border-left: none;
68
+ }
69
+
70
+ .input-group .form-control:focus {
71
+ border-left: none;
72
+ }
73
+
74
+ .input-group:focus-within .input-group-text {
75
+ border-color: #0066ff;
76
+ border-right: none;
77
+ }
78
+
79
+ .btn-login {
80
+ background: #0066ff;
81
+ border: none;
82
+ border-radius: 6px;
83
+ padding: 0.625rem 1rem;
84
+ font-size: 0.9375rem;
85
+ font-weight: 500;
86
+ color: #ffffff;
87
+ transition: all 0.15s ease;
88
+ }
89
+
90
+ .btn-login:hover {
91
+ background: #0052cc;
92
+ }
93
+
94
+ .btn-login:active {
95
+ background: #0047b3;
96
+ }
97
+
98
+ .btn-outline-secondary {
99
+ border: 1px solid #dde1e6;
100
+ border-left: none;
101
+ color: #6c757d;
102
+ }
103
+
104
+ .btn-outline-secondary:hover {
105
+ background: #f8f9fa;
106
+ border-color: #dde1e6;
107
+ border-left: none;
108
+ color: #495057;
109
+ }
110
+
111
+ .alert {
112
+ border-radius: 6px;
113
+ border: 1px solid;
114
+ font-size: 0.875rem;
115
+ padding: 0.75rem 1rem;
116
+ }
117
+
118
+ .alert-danger {
119
+ background: #fff5f5;
120
+ border-color: #fcc5c5;
121
+ color: #c62828;
122
+ }
123
+
124
+ .alert-success {
125
+ background: #f0f9f0;
126
+ border-color: #c5e6c5;
127
+ color: #2e7d32;
128
+ }
129
+
130
+ .register-link {
131
+ color: #0066ff;
132
+ text-decoration: none;
133
+ font-weight: 500;
134
+ }
135
+
136
+ .register-link:hover {
137
+ color: #0052cc;
138
+ text-decoration: underline;
139
+ }
140
+
141
+ .divider {
142
+ border-top: 1px solid #e9ecef;
143
+ margin: 1.5rem 0 1rem;
144
+ }
145
+
146
+ h3 {
147
+ font-size: 1.5rem;
148
+ font-weight: 600;
149
+ color: #1a1f36;
150
+ margin-bottom: 0.5rem;
151
+ }
152
+
153
+ .subtitle {
154
+ color: #6c757d;
155
+ font-size: 0.875rem;
156
+ }
157
+
158
+ .icon-circle {
159
+ width: 48px;
160
+ height: 48px;
161
+ background: #f0f7ff;
162
+ border-radius: 12px;
163
+ display: flex;
164
+ align-items: center;
165
+ justify-content: center;
166
+ margin: 0 auto 1rem;
167
+ }
168
+
169
+ .icon-circle i {
170
+ font-size: 1.5rem;
171
+ color: #0066ff;
172
+ }
173
+
174
+ body
175
+ .login-container.d-flex.align-items-center.justify-content-center
176
+ .container
177
+ .row.justify-content-center
178
+ .col-md-6.col-lg-5
179
+ .login-card.p-4.p-md-5
180
+
181
+ // Header
182
+ .text-center.mb-4
183
+ .icon-circle
184
+ i.bi.bi-cloud
185
+ h3.mb-1 Sign in
186
+ p.subtitle Access your VPS Deployer dashboard
187
+
188
+ // Flash Messages
189
+ if error
190
+ .alert.alert-danger.alert-dismissible.fade-show.d-flex.align-items-center.mb-4(role="alert")
191
+ i.bi.bi-exclamation-triangle-fill.me-2(style="font-size: 0.875rem;")
192
+ span.flex-grow-1 #{error}
193
+
194
+
195
+ if success
196
+ .alert.alert-success.alert-dismissible.fade-show.d-flex.align-items-center.mb-4(role="alert")
197
+ i.bi.bi-check-circle-fill.me-2(style="font-size: 0.875rem;")
198
+ span.flex-grow-1 #{success}
199
+
200
+
201
+ // Login Form
202
+ form(method="POST", action="#")
203
+
204
+ // Username Field
205
+ .mb-3
206
+ label.form-label(for="username") Username
207
+ .input-group
208
+ span.input-group-text
209
+ i.bi.bi-person
210
+ input.form-control(
211
+ type="text",
212
+ id="username",
213
+ name="username",
214
+ placeholder="Enter your username",
215
+ required,
216
+ autocomplete="username",
217
+ autofocus
218
+ )
219
+
220
+ // Password Field
221
+ .mb-4
222
+ label.form-label(for="password") Password
223
+ .input-group
224
+ span.input-group-text
225
+ i.bi.bi-lock
226
+ input.form-control(
227
+ type="password",
228
+ id="password",
229
+ name="password",
230
+ placeholder="Enter your password",
231
+ required,
232
+ autocomplete="current-password"
233
+ )
234
+ button.btn.btn-outline-secondary(type="button", id="togglePassword", style="border-left: none;")
235
+ i.bi.bi-eye-slash
236
+
237
+ // Submit Button
238
+ button.btn.btn-login.w-100(type="submit")
239
+ i.bi.bi-box-arrow-in-right.me-2
240
+ | Sign in
241
+
242
+ // Divider
243
+ .divider
244
+
245
+ // Footer
246
+ .text-center
247
+ p.text-muted.mb-0(style="font-size: 0.875rem;")
248
+ | Don't have an account?
249
+ a.register-link(href="/register") Create account
250
+
251
+ // Scripts
252
+ script(src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js")
253
+ script(src="/js/login.js")
254
+
@@ -0,0 +1,298 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title Register - VPS Deployer
5
+ meta(charset="UTF-8")
6
+ meta(name="viewport", content="width=device-width, initial-scale=1")
7
+
8
+ // Bootstrap 5 CSS + Icons
9
+ link(href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css", rel="stylesheet")
10
+ link(rel="stylesheet", href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css")
11
+ link(rel="icon", type="image/svg+xml", href="/favicon.svg")
12
+
13
+ style.
14
+ * {
15
+ margin: 0;
16
+ padding: 0;
17
+ box-sizing: border-box;
18
+ }
19
+
20
+ body {
21
+ background: #f5f7fa;
22
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
23
+ }
24
+
25
+ .register-container {
26
+ min-height: 100vh;
27
+ background: #f5f7fa;
28
+ }
29
+
30
+ .register-card {
31
+ background: #ffffff;
32
+ border-radius: 8px;
33
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04);
34
+ border: 1px solid #e9ecef;
35
+ }
36
+
37
+ .form-label {
38
+ font-size: 0.875rem;
39
+ font-weight: 500;
40
+ color: #1a1f36;
41
+ margin-bottom: 0.5rem;
42
+ }
43
+
44
+ .form-control {
45
+ border: 1px solid #dde1e6;
46
+ border-radius: 6px;
47
+ padding: 0.625rem 0.875rem;
48
+ font-size: 0.9375rem;
49
+ transition: all 0.15s ease;
50
+ }
51
+
52
+ .form-control:focus {
53
+ border-color: #0066ff;
54
+ box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.1);
55
+ outline: none;
56
+ }
57
+
58
+ .form-text {
59
+ font-size: 0.75rem;
60
+ color: #6c757d;
61
+ margin-top: 0.375rem;
62
+ }
63
+
64
+ .input-group-text {
65
+ background: #ffffff;
66
+ border: 1px solid #dde1e6;
67
+ border-right: none;
68
+ color: #6c757d;
69
+ font-size: 0.9375rem;
70
+ }
71
+
72
+ .input-group .form-control {
73
+ border-left: none;
74
+ }
75
+
76
+ .input-group .form-control:focus {
77
+ border-left: none;
78
+ }
79
+
80
+ .input-group:focus-within .input-group-text {
81
+ border-color: #0066ff;
82
+ border-right: none;
83
+ }
84
+
85
+ .btn-register {
86
+ background: #0066ff;
87
+ border: none;
88
+ border-radius: 6px;
89
+ padding: 0.625rem 1rem;
90
+ font-size: 0.9375rem;
91
+ font-weight: 500;
92
+ color: #ffffff;
93
+ transition: all 0.15s ease;
94
+ }
95
+
96
+ .btn-register:hover {
97
+ background: #0052cc;
98
+ }
99
+
100
+ .btn-register:active {
101
+ background: #0047b3;
102
+ }
103
+
104
+ .alert {
105
+ border-radius: 6px;
106
+ border: 1px solid;
107
+ font-size: 0.875rem;
108
+ padding: 0.75rem 1rem;
109
+ }
110
+
111
+ .alert-danger {
112
+ background: #fff5f5;
113
+ border-color: #fcc5c5;
114
+ color: #c62828;
115
+ }
116
+
117
+ .alert-success {
118
+ background: #f0f9f0;
119
+ border-color: #c5e6c5;
120
+ color: #2e7d32;
121
+ }
122
+
123
+ .register-link {
124
+ color: #0066ff;
125
+ text-decoration: none;
126
+ font-weight: 500;
127
+ }
128
+
129
+ .register-link:hover {
130
+ color: #0052cc;
131
+ text-decoration: underline;
132
+ }
133
+
134
+ .divider {
135
+ border-top: 1px solid #e9ecef;
136
+ margin: 1.5rem 0 1rem;
137
+ }
138
+
139
+ h3 {
140
+ font-size: 1.5rem;
141
+ font-weight: 600;
142
+ color: #1a1f36;
143
+ margin-bottom: 0.5rem;
144
+ }
145
+
146
+ .subtitle {
147
+ color: #6c757d;
148
+ font-size: 0.875rem;
149
+ }
150
+
151
+ .icon-circle {
152
+ width: 48px;
153
+ height: 48px;
154
+ background: #f0f7ff;
155
+ border-radius: 12px;
156
+ display: flex;
157
+ align-items: center;
158
+ justify-content: center;
159
+ margin: 0 auto 1rem;
160
+ }
161
+
162
+ .icon-circle i {
163
+ font-size: 1.5rem;
164
+ color: #0066ff;
165
+ }
166
+
167
+ // Closed state styles
168
+ .closed-icon-circle {
169
+ width: 64px;
170
+ height: 64px;
171
+ background: #f8f9fa;
172
+ border-radius: 50%;
173
+ display: flex;
174
+ align-items: center;
175
+ justify-content: center;
176
+ margin: 0 auto 1rem;
177
+ }
178
+
179
+ .closed-icon-circle i {
180
+ font-size: 2rem;
181
+ color: #dc3545;
182
+ }
183
+
184
+ body
185
+ if isForbidden
186
+ // Registration Closed State
187
+ .register-container.d-flex.align-items-center.justify-content-center
188
+ .container
189
+ .row.justify-content-center
190
+ .col-md-6.col-lg-5
191
+ .register-card.p-4.p-md-5.text-center
192
+ .closed-icon-circle
193
+ i.bi.bi-lock-fill
194
+ h3.mb-2 Registration Closed
195
+ p.subtitle.mb-4 VPS Deployer is currently not accepting new registrations
196
+ hr.mb-4
197
+ p.text-muted.mb-3
198
+ | Please contact an administrator if you need access.
199
+ a.btn.btn-register(href="/login", style="display: inline-block; width: auto; padding: 0.625rem 1.5rem;")
200
+ i.bi.bi-box-arrow-in-right.me-2
201
+ | Go to Login
202
+ else
203
+ // Registration Form - Active State
204
+ .register-container.d-flex.align-items-center.justify-content-center
205
+ .container
206
+ .row.justify-content-center
207
+ .col-md-6.col-lg-5
208
+ .register-card.p-4.p-md-5
209
+
210
+ // Header
211
+ .text-center.mb-4
212
+ .icon-circle
213
+ i.bi.bi-person-plus
214
+ h3.mb-1 Create an account
215
+ p.subtitle Join VPS Deployer
216
+
217
+ // Flash Messages
218
+ if error
219
+ .alert.alert-danger.alert-dismissible.fade-show.d-flex.align-items-center.mb-4(role="alert")
220
+ i.bi.bi-exclamation-triangle-fill.me-2(style="font-size: 0.875rem;")
221
+ span.flex-grow-1 #{error}
222
+
223
+ if success
224
+ .alert.alert-success.alert-dismissible.fade-show.d-flex.align-items-center.mb-4(role="alert")
225
+ i.bi.bi-check-circle-fill.me-2(style="font-size: 0.875rem;")
226
+ span.flex-grow-1 #{success}
227
+
228
+ // Registration Form
229
+ form(method="POST", action="#")
230
+
231
+ // Username Field
232
+ .mb-3
233
+ label.form-label(for="username")
234
+ i.bi.bi-person.me-1
235
+ | Username
236
+ .input-group
237
+ span.input-group-text
238
+ i.bi.bi-person
239
+ input.form-control(
240
+ type="text",
241
+ id="username",
242
+ name="username",
243
+ placeholder="Choose a username",
244
+ required,
245
+ autocomplete="username",
246
+ autofocus
247
+ )
248
+ .form-text Must be unique and at least 3 characters
249
+
250
+ // Email Field
251
+ .mb-3
252
+ label.form-label(for="email")
253
+ i.bi.bi-envelope.me-1
254
+ | Email Address
255
+ .input-group
256
+ span.input-group-text
257
+ i.bi.bi-envelope
258
+ input.form-control(
259
+ type="email",
260
+ id="email",
261
+ name="email",
262
+ placeholder="your@email.com",
263
+ required,
264
+ autocomplete="email"
265
+ )
266
+ .form-text We'll never share your email
267
+
268
+ // Password Field
269
+ .mb-4
270
+ label.form-label(for="password")
271
+ i.bi.bi-lock.me-1
272
+ | Password
273
+ .input-group
274
+ span.input-group-text
275
+ i.bi.bi-lock
276
+ input.form-control(
277
+ type="password",
278
+ id="password",
279
+ name="password",
280
+ placeholder="••••••••",
281
+ required,
282
+ autocomplete="new-password"
283
+ )
284
+ .form-text Minimum 6 characters
285
+
286
+ // Submit Button
287
+ button.btn.btn-register.w-100(type="submit")
288
+ i.bi.bi-person-plus.me-2
289
+ | Create account
290
+
291
+ // Divider
292
+ .divider
293
+
294
+ // Footer Link
295
+ .text-center
296
+ p.text-muted.mb-0(style="font-size: 0.875rem;")
297
+ | Already have an account?
298
+ a.register-link(href="/login") Sign in
@@ -0,0 +1,21 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title 404 - Not Found
5
+ meta(charset="UTF-8")
6
+ meta(name="viewport", content="width=device-width, initial-scale=1")
7
+
8
+ link(
9
+ href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css",
10
+ rel="stylesheet"
11
+ )
12
+
13
+ body.bg-light
14
+ .container.d-flex.align-items-center.justify-content-center(style="min-height: 100vh;")
15
+ .text-center
16
+
17
+ h1.display-1.fw-bold.text-danger 404
18
+ h4.mb-3 Page Not Found
19
+ p.text-muted.mb-4 The page you are looking for does not exist.
20
+
21
+ a.btn.btn-primary(href="/") Go to Dashboard