vg-coder-cli 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.
@@ -0,0 +1,142 @@
1
+ const jwt = require('jsonwebtoken');
2
+ const User = require('../models/User');
3
+
4
+ const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
5
+
6
+ /**
7
+ * Authentication middleware
8
+ * Verifies JWT token and attaches user to request
9
+ */
10
+ const authMiddleware = async (req, res, next) => {
11
+ try {
12
+ const authHeader = req.headers.authorization;
13
+
14
+ if (!authHeader) {
15
+ return res.status(401).json({
16
+ error: 'Access denied. No token provided.'
17
+ });
18
+ }
19
+
20
+ const token = authHeader.startsWith('Bearer ')
21
+ ? authHeader.slice(7)
22
+ : authHeader;
23
+
24
+ if (!token) {
25
+ return res.status(401).json({
26
+ error: 'Access denied. Invalid token format.'
27
+ });
28
+ }
29
+
30
+ // Verify token
31
+ const decoded = jwt.verify(token, JWT_SECRET);
32
+
33
+ // Get user from database
34
+ const user = await User.findByPk(decoded.userId);
35
+ if (!user) {
36
+ return res.status(401).json({
37
+ error: 'Access denied. User not found.'
38
+ });
39
+ }
40
+
41
+ // Check if user is active
42
+ if (!user.isActive) {
43
+ return res.status(401).json({
44
+ error: 'Access denied. User account is disabled.'
45
+ });
46
+ }
47
+
48
+ // Attach user to request
49
+ req.user = user;
50
+ next();
51
+
52
+ } catch (error) {
53
+ if (error.name === 'JsonWebTokenError') {
54
+ return res.status(401).json({
55
+ error: 'Access denied. Invalid token.'
56
+ });
57
+ }
58
+
59
+ if (error.name === 'TokenExpiredError') {
60
+ return res.status(401).json({
61
+ error: 'Access denied. Token expired.'
62
+ });
63
+ }
64
+
65
+ console.error('Auth middleware error:', error);
66
+ res.status(500).json({
67
+ error: 'Internal server error during authentication.'
68
+ });
69
+ }
70
+ };
71
+
72
+ /**
73
+ * Role-based authorization middleware
74
+ */
75
+ const requireRole = (roles) => {
76
+ return (req, res, next) => {
77
+ if (!req.user) {
78
+ return res.status(401).json({
79
+ error: 'Access denied. User not authenticated.'
80
+ });
81
+ }
82
+
83
+ const userRoles = Array.isArray(req.user.roles)
84
+ ? req.user.roles
85
+ : [req.user.role];
86
+
87
+ const hasRequiredRole = roles.some(role =>
88
+ userRoles.includes(role)
89
+ );
90
+
91
+ if (!hasRequiredRole) {
92
+ return res.status(403).json({
93
+ error: 'Access denied. Insufficient permissions.',
94
+ required: roles,
95
+ current: userRoles
96
+ });
97
+ }
98
+
99
+ next();
100
+ };
101
+ };
102
+
103
+ /**
104
+ * Optional authentication middleware
105
+ * Attaches user if token is valid, but doesn't require it
106
+ */
107
+ const optionalAuth = async (req, res, next) => {
108
+ try {
109
+ const authHeader = req.headers.authorization;
110
+
111
+ if (!authHeader) {
112
+ return next();
113
+ }
114
+
115
+ const token = authHeader.startsWith('Bearer ')
116
+ ? authHeader.slice(7)
117
+ : authHeader;
118
+
119
+ if (!token) {
120
+ return next();
121
+ }
122
+
123
+ const decoded = jwt.verify(token, JWT_SECRET);
124
+ const user = await User.findByPk(decoded.userId);
125
+
126
+ if (user && user.isActive) {
127
+ req.user = user;
128
+ }
129
+
130
+ next();
131
+
132
+ } catch (error) {
133
+ // Ignore auth errors in optional auth
134
+ next();
135
+ }
136
+ };
137
+
138
+ module.exports = {
139
+ authMiddleware,
140
+ requireRole,
141
+ optionalAuth
142
+ };
@@ -0,0 +1,287 @@
1
+ /* Main stylesheet for test project */
2
+
3
+ :root {
4
+ --primary-color: #007bff;
5
+ --secondary-color: #6c757d;
6
+ --success-color: #28a745;
7
+ --danger-color: #dc3545;
8
+ --warning-color: #ffc107;
9
+ --info-color: #17a2b8;
10
+ --light-color: #f8f9fa;
11
+ --dark-color: #343a40;
12
+
13
+ --font-family-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
14
+ --font-family-mono: SFMono-Regular, Menlo, Monaco, Consolas, monospace;
15
+
16
+ --border-radius: 0.375rem;
17
+ --box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
18
+ --transition: all 0.15s ease-in-out;
19
+ }
20
+
21
+ * {
22
+ box-sizing: border-box;
23
+ }
24
+
25
+ body {
26
+ font-family: var(--font-family-sans);
27
+ line-height: 1.6;
28
+ color: var(--dark-color);
29
+ background-color: #fff;
30
+ margin: 0;
31
+ padding: 0;
32
+ }
33
+
34
+ .container {
35
+ max-width: 1200px;
36
+ margin: 0 auto;
37
+ padding: 0 1rem;
38
+ }
39
+
40
+ .header {
41
+ background-color: var(--primary-color);
42
+ color: white;
43
+ padding: 1rem 0;
44
+ box-shadow: var(--box-shadow);
45
+ }
46
+
47
+ .header h1 {
48
+ margin: 0;
49
+ font-size: 1.75rem;
50
+ font-weight: 600;
51
+ }
52
+
53
+ .nav {
54
+ background-color: var(--light-color);
55
+ border-bottom: 1px solid #dee2e6;
56
+ padding: 0.5rem 0;
57
+ }
58
+
59
+ .nav ul {
60
+ list-style: none;
61
+ margin: 0;
62
+ padding: 0;
63
+ display: flex;
64
+ gap: 2rem;
65
+ }
66
+
67
+ .nav a {
68
+ color: var(--dark-color);
69
+ text-decoration: none;
70
+ padding: 0.5rem 1rem;
71
+ border-radius: var(--border-radius);
72
+ transition: var(--transition);
73
+ }
74
+
75
+ .nav a:hover,
76
+ .nav a.active {
77
+ background-color: var(--primary-color);
78
+ color: white;
79
+ }
80
+
81
+ .main {
82
+ padding: 2rem 0;
83
+ min-height: calc(100vh - 200px);
84
+ }
85
+
86
+ .card {
87
+ background: white;
88
+ border: 1px solid #dee2e6;
89
+ border-radius: var(--border-radius);
90
+ box-shadow: var(--box-shadow);
91
+ margin-bottom: 1.5rem;
92
+ }
93
+
94
+ .card-header {
95
+ padding: 1rem 1.25rem;
96
+ background-color: var(--light-color);
97
+ border-bottom: 1px solid #dee2e6;
98
+ font-weight: 600;
99
+ }
100
+
101
+ .card-body {
102
+ padding: 1.25rem;
103
+ }
104
+
105
+ .btn {
106
+ display: inline-block;
107
+ padding: 0.5rem 1rem;
108
+ font-size: 1rem;
109
+ font-weight: 400;
110
+ line-height: 1.5;
111
+ text-align: center;
112
+ text-decoration: none;
113
+ vertical-align: middle;
114
+ cursor: pointer;
115
+ border: 1px solid transparent;
116
+ border-radius: var(--border-radius);
117
+ transition: var(--transition);
118
+ user-select: none;
119
+ }
120
+
121
+ .btn-primary {
122
+ color: white;
123
+ background-color: var(--primary-color);
124
+ border-color: var(--primary-color);
125
+ }
126
+
127
+ .btn-primary:hover {
128
+ background-color: #0056b3;
129
+ border-color: #004085;
130
+ }
131
+
132
+ .btn-secondary {
133
+ color: white;
134
+ background-color: var(--secondary-color);
135
+ border-color: var(--secondary-color);
136
+ }
137
+
138
+ .btn-success {
139
+ color: white;
140
+ background-color: var(--success-color);
141
+ border-color: var(--success-color);
142
+ }
143
+
144
+ .btn-danger {
145
+ color: white;
146
+ background-color: var(--danger-color);
147
+ border-color: var(--danger-color);
148
+ }
149
+
150
+ .form-group {
151
+ margin-bottom: 1rem;
152
+ }
153
+
154
+ .form-label {
155
+ display: block;
156
+ margin-bottom: 0.5rem;
157
+ font-weight: 500;
158
+ }
159
+
160
+ .form-control {
161
+ display: block;
162
+ width: 100%;
163
+ padding: 0.5rem 0.75rem;
164
+ font-size: 1rem;
165
+ line-height: 1.5;
166
+ color: var(--dark-color);
167
+ background-color: white;
168
+ border: 1px solid #ced4da;
169
+ border-radius: var(--border-radius);
170
+ transition: var(--transition);
171
+ }
172
+
173
+ .form-control:focus {
174
+ outline: 0;
175
+ border-color: #80bdff;
176
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
177
+ }
178
+
179
+ .table {
180
+ width: 100%;
181
+ margin-bottom: 1rem;
182
+ border-collapse: collapse;
183
+ }
184
+
185
+ .table th,
186
+ .table td {
187
+ padding: 0.75rem;
188
+ vertical-align: top;
189
+ border-top: 1px solid #dee2e6;
190
+ }
191
+
192
+ .table thead th {
193
+ vertical-align: bottom;
194
+ border-bottom: 2px solid #dee2e6;
195
+ background-color: var(--light-color);
196
+ font-weight: 600;
197
+ }
198
+
199
+ .table tbody tr:hover {
200
+ background-color: rgba(0, 0, 0, 0.075);
201
+ }
202
+
203
+ .alert {
204
+ position: relative;
205
+ padding: 0.75rem 1.25rem;
206
+ margin-bottom: 1rem;
207
+ border: 1px solid transparent;
208
+ border-radius: var(--border-radius);
209
+ }
210
+
211
+ .alert-success {
212
+ color: #155724;
213
+ background-color: #d4edda;
214
+ border-color: #c3e6cb;
215
+ }
216
+
217
+ .alert-danger {
218
+ color: #721c24;
219
+ background-color: #f8d7da;
220
+ border-color: #f5c6cb;
221
+ }
222
+
223
+ .alert-warning {
224
+ color: #856404;
225
+ background-color: #fff3cd;
226
+ border-color: #ffeaa7;
227
+ }
228
+
229
+ .footer {
230
+ background-color: var(--dark-color);
231
+ color: white;
232
+ text-align: center;
233
+ padding: 1rem 0;
234
+ margin-top: auto;
235
+ }
236
+
237
+ /* Responsive design */
238
+ @media (max-width: 768px) {
239
+ .container {
240
+ padding: 0 0.5rem;
241
+ }
242
+
243
+ .nav ul {
244
+ flex-direction: column;
245
+ gap: 0;
246
+ }
247
+
248
+ .nav a {
249
+ display: block;
250
+ border-radius: 0;
251
+ }
252
+
253
+ .main {
254
+ padding: 1rem 0;
255
+ }
256
+
257
+ .card-body {
258
+ padding: 1rem;
259
+ }
260
+ }
261
+
262
+ /* Utility classes */
263
+ .text-center { text-align: center; }
264
+ .text-left { text-align: left; }
265
+ .text-right { text-align: right; }
266
+
267
+ .mt-1 { margin-top: 0.25rem; }
268
+ .mt-2 { margin-top: 0.5rem; }
269
+ .mt-3 { margin-top: 1rem; }
270
+ .mt-4 { margin-top: 1.5rem; }
271
+ .mt-5 { margin-top: 3rem; }
272
+
273
+ .mb-1 { margin-bottom: 0.25rem; }
274
+ .mb-2 { margin-bottom: 0.5rem; }
275
+ .mb-3 { margin-bottom: 1rem; }
276
+ .mb-4 { margin-bottom: 1.5rem; }
277
+ .mb-5 { margin-bottom: 3rem; }
278
+
279
+ .d-none { display: none; }
280
+ .d-block { display: block; }
281
+ .d-inline { display: inline; }
282
+ .d-inline-block { display: inline-block; }
283
+ .d-flex { display: flex; }
284
+
285
+ .justify-content-center { justify-content: center; }
286
+ .justify-content-between { justify-content: space-between; }
287
+ .align-items-center { align-items: center; }
Binary file