@lovelybunch/api 1.0.55 → 1.0.57
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/dist/lib/auth/auth-manager.d.ts +122 -0
- package/dist/lib/auth/auth-manager.js +411 -0
- package/dist/middleware/auth.d.ts +31 -0
- package/dist/middleware/auth.js +142 -0
- package/dist/routes/api/v1/api-keys/index.d.ts +1 -0
- package/dist/routes/api/v1/api-keys/index.js +1 -0
- package/dist/routes/api/v1/api-keys/route.d.ts +3 -0
- package/dist/routes/api/v1/api-keys/route.js +120 -0
- package/dist/routes/api/v1/auth/index.d.ts +1 -0
- package/dist/routes/api/v1/auth/index.js +1 -0
- package/dist/routes/api/v1/auth/route.d.ts +3 -0
- package/dist/routes/api/v1/auth/route.js +230 -0
- package/dist/routes/api/v1/auth-settings/index.d.ts +1 -0
- package/dist/routes/api/v1/auth-settings/index.js +1 -0
- package/dist/routes/api/v1/auth-settings/route.d.ts +3 -0
- package/dist/routes/api/v1/auth-settings/route.js +249 -0
- package/dist/routes/api/v1/context/knowledge/[filename]/route.js +1 -1
- package/dist/routes/api/v1/context/knowledge/route.js +2 -2
- package/dist/server-with-static.js +9 -0
- package/dist/server.js +9 -0
- package/package.json +11 -4
- package/static/assets/index-CRg4lVi6.js +779 -0
- package/static/assets/index-VqhUTak4.css +33 -0
- package/static/index.html +2 -2
- package/static/assets/index-CXdqQvuB.js +0 -755
- package/static/assets/index-DthyPP76.css +0 -33
- package/static/assets/index-f9JbHO5w.js +0 -755
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { getAuthManager } from '../../../../lib/auth/auth-manager.js';
|
|
3
|
+
import { requireAuth } from '../../../../middleware/auth.js';
|
|
4
|
+
const apiKeys = new Hono();
|
|
5
|
+
/**
|
|
6
|
+
* GET /api/v1/api-keys
|
|
7
|
+
* List all API keys
|
|
8
|
+
*/
|
|
9
|
+
apiKeys.get('/', async (c) => {
|
|
10
|
+
try {
|
|
11
|
+
const session = requireAuth(c);
|
|
12
|
+
// If auth is disabled (session is null), return empty list
|
|
13
|
+
if (!session) {
|
|
14
|
+
return c.json({
|
|
15
|
+
success: true,
|
|
16
|
+
data: {
|
|
17
|
+
apiKeys: [],
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
const authManager = getAuthManager();
|
|
22
|
+
const keys = await authManager.listApiKeys();
|
|
23
|
+
// Return keys without actual key values
|
|
24
|
+
return c.json({
|
|
25
|
+
success: true,
|
|
26
|
+
data: {
|
|
27
|
+
apiKeys: keys.map((k) => ({
|
|
28
|
+
id: k.id,
|
|
29
|
+
name: k.name,
|
|
30
|
+
keyPreview: k.keyPreview,
|
|
31
|
+
createdBy: k.createdBy,
|
|
32
|
+
createdAt: k.createdAt,
|
|
33
|
+
expiresAt: k.expiresAt,
|
|
34
|
+
lastUsedAt: k.lastUsedAt,
|
|
35
|
+
scopes: k.scopes,
|
|
36
|
+
})),
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error('List API keys error:', error);
|
|
42
|
+
return c.json({ success: false, error: error.message || 'Failed to list API keys' }, error.message === 'Authentication required' ? 401 : 500);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
/**
|
|
46
|
+
* POST /api/v1/api-keys
|
|
47
|
+
* Create a new API key
|
|
48
|
+
*/
|
|
49
|
+
apiKeys.post('/', async (c) => {
|
|
50
|
+
try {
|
|
51
|
+
const session = requireAuth(c);
|
|
52
|
+
if (!session) {
|
|
53
|
+
return c.json({ success: false, error: 'Authentication is required to create API keys' }, 401);
|
|
54
|
+
}
|
|
55
|
+
const authManager = getAuthManager();
|
|
56
|
+
const body = await c.req.json();
|
|
57
|
+
const { name, expiresIn, scopes } = body;
|
|
58
|
+
if (!name) {
|
|
59
|
+
return c.json({ success: false, error: 'Name is required' }, 400);
|
|
60
|
+
}
|
|
61
|
+
if (!scopes || scopes.length === 0) {
|
|
62
|
+
return c.json({ success: false, error: 'At least one scope is required' }, 400);
|
|
63
|
+
}
|
|
64
|
+
// Create API key
|
|
65
|
+
const { apiKey, rawKey } = await authManager.createApiKey(name, session.userId, scopes, expiresIn);
|
|
66
|
+
return c.json({
|
|
67
|
+
success: true,
|
|
68
|
+
data: {
|
|
69
|
+
apiKey: {
|
|
70
|
+
id: apiKey.id,
|
|
71
|
+
name: apiKey.name,
|
|
72
|
+
keyPreview: apiKey.keyPreview,
|
|
73
|
+
createdBy: apiKey.createdBy,
|
|
74
|
+
createdAt: apiKey.createdAt,
|
|
75
|
+
expiresAt: apiKey.expiresAt,
|
|
76
|
+
scopes: apiKey.scopes,
|
|
77
|
+
},
|
|
78
|
+
key: rawKey, // Only returned once!
|
|
79
|
+
},
|
|
80
|
+
message: 'API key created successfully. Save this key securely - it will not be shown again.',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
console.error('Create API key error:', error);
|
|
85
|
+
return c.json({ success: false, error: error.message || 'Failed to create API key' }, error.message === 'Authentication required' ? 401 : 500);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
/**
|
|
89
|
+
* DELETE /api/v1/api-keys/:id
|
|
90
|
+
* Delete an API key
|
|
91
|
+
*/
|
|
92
|
+
apiKeys.delete('/:id', async (c) => {
|
|
93
|
+
try {
|
|
94
|
+
const session = requireAuth(c);
|
|
95
|
+
if (!session) {
|
|
96
|
+
return c.json({ success: false, error: 'Authentication is required to delete API keys' }, 401);
|
|
97
|
+
}
|
|
98
|
+
const apiKeyId = c.req.param('id');
|
|
99
|
+
const authManager = getAuthManager();
|
|
100
|
+
const keys = await authManager.listApiKeys();
|
|
101
|
+
const key = keys.find((k) => k.id === apiKeyId);
|
|
102
|
+
if (!key) {
|
|
103
|
+
return c.json({ success: false, error: 'API key not found' }, 404);
|
|
104
|
+
}
|
|
105
|
+
// Check if user is admin or the creator of the key
|
|
106
|
+
if (session.role !== 'admin' && key.createdBy !== session.userId) {
|
|
107
|
+
return c.json({ success: false, error: 'Not authorized to delete this API key' }, 403);
|
|
108
|
+
}
|
|
109
|
+
await authManager.deleteApiKey(apiKeyId);
|
|
110
|
+
return c.json({
|
|
111
|
+
success: true,
|
|
112
|
+
message: 'API key deleted successfully',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
console.error('Delete API key error:', error);
|
|
117
|
+
return c.json({ success: false, error: error.message || 'Failed to delete API key' }, error.message === 'Authentication required' ? 401 : 500);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
export default apiKeys;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './route.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './route.js';
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { setCookie, deleteCookie } from 'hono/cookie';
|
|
3
|
+
import { getAuthManager } from '../../../../lib/auth/auth-manager.js';
|
|
4
|
+
import { getSession } from '../../../../middleware/auth.js';
|
|
5
|
+
const auth = new Hono();
|
|
6
|
+
/**
|
|
7
|
+
* POST /api/v1/auth/login
|
|
8
|
+
* Login with email and password
|
|
9
|
+
*/
|
|
10
|
+
auth.post('/login', async (c) => {
|
|
11
|
+
try {
|
|
12
|
+
const authManager = getAuthManager();
|
|
13
|
+
const body = await c.req.json();
|
|
14
|
+
const { email, password } = body;
|
|
15
|
+
if (!email || !password) {
|
|
16
|
+
return c.json({ success: false, error: 'Email and password are required' }, 400);
|
|
17
|
+
}
|
|
18
|
+
// Verify credentials
|
|
19
|
+
const user = await authManager.verifyCredentials(email, password);
|
|
20
|
+
if (!user) {
|
|
21
|
+
return c.json({ success: false, error: 'Invalid email or password' }, 401);
|
|
22
|
+
}
|
|
23
|
+
// Generate token
|
|
24
|
+
const token = await authManager.generateToken(user);
|
|
25
|
+
// Get session config
|
|
26
|
+
const config = await authManager.loadAuthConfig();
|
|
27
|
+
// Set cookie
|
|
28
|
+
setCookie(c, config.session.cookieName, token, {
|
|
29
|
+
httpOnly: true,
|
|
30
|
+
secure: config.session.secure || false,
|
|
31
|
+
sameSite: 'Lax',
|
|
32
|
+
maxAge: authManager['parseExpiry'](config.session.expiresIn),
|
|
33
|
+
path: '/',
|
|
34
|
+
});
|
|
35
|
+
return c.json({
|
|
36
|
+
success: true,
|
|
37
|
+
data: {
|
|
38
|
+
user: {
|
|
39
|
+
id: user.id,
|
|
40
|
+
email: user.email,
|
|
41
|
+
name: user.name,
|
|
42
|
+
role: user.role,
|
|
43
|
+
},
|
|
44
|
+
token, // Also return token for programmatic use
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.error('Login error:', error);
|
|
50
|
+
return c.json({ success: false, error: 'Login failed' }, 500);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* POST /api/v1/auth/register
|
|
55
|
+
* Register a new user (must be whitelisted)
|
|
56
|
+
*/
|
|
57
|
+
auth.post('/register', async (c) => {
|
|
58
|
+
try {
|
|
59
|
+
const authManager = getAuthManager();
|
|
60
|
+
const body = await c.req.json();
|
|
61
|
+
const { email, password, name } = body;
|
|
62
|
+
if (!email || !password || !name) {
|
|
63
|
+
return c.json({ success: false, error: 'Email, password, and name are required' }, 400);
|
|
64
|
+
}
|
|
65
|
+
// Validate password strength
|
|
66
|
+
if (password.length < 8) {
|
|
67
|
+
return c.json({ success: false, error: 'Password must be at least 8 characters' }, 400);
|
|
68
|
+
}
|
|
69
|
+
// Check if email is whitelisted
|
|
70
|
+
const isWhitelisted = await authManager.isEmailWhitelisted(email);
|
|
71
|
+
if (!isWhitelisted) {
|
|
72
|
+
return c.json({ success: false, error: 'Email not authorized for registration' }, 403);
|
|
73
|
+
}
|
|
74
|
+
// Register user
|
|
75
|
+
const user = await authManager.registerUser(email, password, name);
|
|
76
|
+
// Generate token
|
|
77
|
+
const token = await authManager.generateToken(user);
|
|
78
|
+
// Get session config
|
|
79
|
+
const config = await authManager.loadAuthConfig();
|
|
80
|
+
// Set cookie
|
|
81
|
+
setCookie(c, config.session.cookieName, token, {
|
|
82
|
+
httpOnly: true,
|
|
83
|
+
secure: config.session.secure || false,
|
|
84
|
+
sameSite: 'Lax',
|
|
85
|
+
maxAge: authManager['parseExpiry'](config.session.expiresIn),
|
|
86
|
+
path: '/',
|
|
87
|
+
});
|
|
88
|
+
return c.json({
|
|
89
|
+
success: true,
|
|
90
|
+
data: {
|
|
91
|
+
user: {
|
|
92
|
+
id: user.id,
|
|
93
|
+
email: user.email,
|
|
94
|
+
name: user.name,
|
|
95
|
+
role: user.role,
|
|
96
|
+
},
|
|
97
|
+
token,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
console.error('Registration error:', error);
|
|
103
|
+
return c.json({ success: false, error: error.message || 'Registration failed' }, 400);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
/**
|
|
107
|
+
* POST /api/v1/auth/logout
|
|
108
|
+
* Logout and clear session
|
|
109
|
+
*/
|
|
110
|
+
auth.post('/logout', async (c) => {
|
|
111
|
+
try {
|
|
112
|
+
const authManager = getAuthManager();
|
|
113
|
+
const config = await authManager.loadAuthConfig();
|
|
114
|
+
// Clear cookie
|
|
115
|
+
deleteCookie(c, config.session.cookieName, {
|
|
116
|
+
path: '/',
|
|
117
|
+
});
|
|
118
|
+
return c.json({
|
|
119
|
+
success: true,
|
|
120
|
+
message: 'Logged out successfully',
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
console.error('Logout error:', error);
|
|
125
|
+
return c.json({ success: false, error: 'Logout failed' }, 500);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
/**
|
|
129
|
+
* GET /api/v1/auth/me
|
|
130
|
+
* Get current user info
|
|
131
|
+
*/
|
|
132
|
+
auth.get('/me', async (c) => {
|
|
133
|
+
try {
|
|
134
|
+
const session = getSession(c);
|
|
135
|
+
if (!session) {
|
|
136
|
+
return c.json({ success: false, error: 'Not authenticated' }, 401);
|
|
137
|
+
}
|
|
138
|
+
const authManager = getAuthManager();
|
|
139
|
+
const user = await authManager.findUserById(session.userId);
|
|
140
|
+
if (!user) {
|
|
141
|
+
return c.json({ success: false, error: 'User not found' }, 404);
|
|
142
|
+
}
|
|
143
|
+
return c.json({
|
|
144
|
+
success: true,
|
|
145
|
+
data: {
|
|
146
|
+
user: {
|
|
147
|
+
id: user.id,
|
|
148
|
+
email: user.email,
|
|
149
|
+
name: user.name,
|
|
150
|
+
role: user.role,
|
|
151
|
+
lastLoginAt: user.lastLoginAt,
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
console.error('Get user error:', error);
|
|
158
|
+
return c.json({ success: false, error: 'Failed to get user info' }, 500);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
/**
|
|
162
|
+
* POST /api/v1/auth/change-password
|
|
163
|
+
* Change current user's password
|
|
164
|
+
*/
|
|
165
|
+
auth.post('/change-password', async (c) => {
|
|
166
|
+
try {
|
|
167
|
+
const session = getSession(c);
|
|
168
|
+
if (!session) {
|
|
169
|
+
return c.json({ success: false, error: 'Not authenticated' }, 401);
|
|
170
|
+
}
|
|
171
|
+
const authManager = getAuthManager();
|
|
172
|
+
const body = await c.req.json();
|
|
173
|
+
const { currentPassword, newPassword } = body;
|
|
174
|
+
if (!currentPassword || !newPassword) {
|
|
175
|
+
return c.json({ success: false, error: 'Current and new password are required' }, 400);
|
|
176
|
+
}
|
|
177
|
+
// Validate new password strength
|
|
178
|
+
if (newPassword.length < 8) {
|
|
179
|
+
return c.json({ success: false, error: 'New password must be at least 8 characters' }, 400);
|
|
180
|
+
}
|
|
181
|
+
await authManager.changePassword(session.userId, currentPassword, newPassword);
|
|
182
|
+
return c.json({
|
|
183
|
+
success: true,
|
|
184
|
+
message: 'Password changed successfully',
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.error('Change password error:', error);
|
|
189
|
+
return c.json({ success: false, error: error.message || 'Failed to change password' }, 400);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
/**
|
|
193
|
+
* GET /api/v1/auth/status
|
|
194
|
+
* Check if auth is enabled
|
|
195
|
+
*/
|
|
196
|
+
auth.get('/status', async (c) => {
|
|
197
|
+
try {
|
|
198
|
+
const authManager = getAuthManager();
|
|
199
|
+
const authEnabled = await authManager.isAuthEnabled();
|
|
200
|
+
let config = null;
|
|
201
|
+
if (authEnabled) {
|
|
202
|
+
const authConfig = await authManager.loadAuthConfig();
|
|
203
|
+
config = {
|
|
204
|
+
allowRegistration: authConfig.allowRegistration,
|
|
205
|
+
providers: {
|
|
206
|
+
local: authConfig.providers.local.enabled,
|
|
207
|
+
google: authConfig.providers.oauth.google?.enabled || false,
|
|
208
|
+
github: authConfig.providers.oauth.github?.enabled || false,
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return c.json({
|
|
213
|
+
success: true,
|
|
214
|
+
data: {
|
|
215
|
+
enabled: authEnabled,
|
|
216
|
+
config,
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
console.error('Auth status error:', error);
|
|
222
|
+
return c.json({
|
|
223
|
+
success: true,
|
|
224
|
+
data: {
|
|
225
|
+
enabled: false,
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
export default auth;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './route.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './route.js';
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { getAuthManager } from '../../../../lib/auth/auth-manager.js';
|
|
3
|
+
import { requireAdmin } from '../../../../middleware/auth.js';
|
|
4
|
+
const authSettings = new Hono();
|
|
5
|
+
/**
|
|
6
|
+
* GET /api/v1/auth-settings
|
|
7
|
+
* Get auth configuration (admin only)
|
|
8
|
+
*/
|
|
9
|
+
authSettings.get('/', async (c) => {
|
|
10
|
+
try {
|
|
11
|
+
requireAdmin(c);
|
|
12
|
+
const authManager = getAuthManager();
|
|
13
|
+
const config = await authManager.loadAuthConfig();
|
|
14
|
+
// Return config without sensitive data (passwords, secrets, API keys)
|
|
15
|
+
return c.json({
|
|
16
|
+
success: true,
|
|
17
|
+
data: {
|
|
18
|
+
enabled: config.enabled,
|
|
19
|
+
allowRegistration: config.allowRegistration,
|
|
20
|
+
providers: {
|
|
21
|
+
local: {
|
|
22
|
+
enabled: config.providers.local.enabled,
|
|
23
|
+
users: config.providers.local.users.map((u) => ({
|
|
24
|
+
id: u.id,
|
|
25
|
+
email: u.email,
|
|
26
|
+
name: u.name,
|
|
27
|
+
role: u.role,
|
|
28
|
+
registered: u.registered,
|
|
29
|
+
createdAt: u.createdAt,
|
|
30
|
+
updatedAt: u.updatedAt,
|
|
31
|
+
lastLoginAt: u.lastLoginAt,
|
|
32
|
+
})),
|
|
33
|
+
},
|
|
34
|
+
oauth: {
|
|
35
|
+
google: config.providers.oauth.google
|
|
36
|
+
? {
|
|
37
|
+
enabled: config.providers.oauth.google.enabled,
|
|
38
|
+
clientId: config.providers.oauth.google.clientId
|
|
39
|
+
? '***'
|
|
40
|
+
: undefined,
|
|
41
|
+
hasClientSecret: !!config.providers.oauth.google.clientSecret,
|
|
42
|
+
callbackUrl: config.providers.oauth.google.callbackUrl,
|
|
43
|
+
}
|
|
44
|
+
: undefined,
|
|
45
|
+
github: config.providers.oauth.github
|
|
46
|
+
? {
|
|
47
|
+
enabled: config.providers.oauth.github.enabled,
|
|
48
|
+
clientId: config.providers.oauth.github.clientId
|
|
49
|
+
? '***'
|
|
50
|
+
: undefined,
|
|
51
|
+
hasClientSecret: !!config.providers.oauth.github.clientSecret,
|
|
52
|
+
callbackUrl: config.providers.oauth.github.callbackUrl,
|
|
53
|
+
}
|
|
54
|
+
: undefined,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
session: {
|
|
58
|
+
expiresIn: config.session.expiresIn,
|
|
59
|
+
cookieName: config.session.cookieName,
|
|
60
|
+
secure: config.session.secure,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error('Get auth settings error:', error);
|
|
67
|
+
return c.json({ success: false, error: error.message || 'Failed to get auth settings' }, error.message === 'Admin access required' ? 403 : 500);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
/**
|
|
71
|
+
* PUT /api/v1/auth-settings
|
|
72
|
+
* Update auth configuration (admin only)
|
|
73
|
+
*/
|
|
74
|
+
authSettings.put('/', async (c) => {
|
|
75
|
+
try {
|
|
76
|
+
requireAdmin(c);
|
|
77
|
+
const authManager = getAuthManager();
|
|
78
|
+
const config = await authManager.loadAuthConfig();
|
|
79
|
+
const body = await c.req.json();
|
|
80
|
+
// Update allowed fields
|
|
81
|
+
if (typeof body.enabled === 'boolean') {
|
|
82
|
+
config.enabled = body.enabled;
|
|
83
|
+
}
|
|
84
|
+
if (typeof body.allowRegistration === 'boolean') {
|
|
85
|
+
config.allowRegistration = body.allowRegistration;
|
|
86
|
+
}
|
|
87
|
+
if (body.session) {
|
|
88
|
+
if (body.session.expiresIn) {
|
|
89
|
+
config.session.expiresIn = body.session.expiresIn;
|
|
90
|
+
}
|
|
91
|
+
if (typeof body.session.secure === 'boolean') {
|
|
92
|
+
config.session.secure = body.session.secure;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
await authManager.saveAuthConfig(config);
|
|
96
|
+
return c.json({
|
|
97
|
+
success: true,
|
|
98
|
+
message: 'Auth settings updated successfully',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
console.error('Update auth settings error:', error);
|
|
103
|
+
return c.json({ success: false, error: error.message || 'Failed to update auth settings' }, error.message === 'Admin access required' ? 403 : 500);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
/**
|
|
107
|
+
* POST /api/v1/auth-settings/users
|
|
108
|
+
* Add a new whitelisted user (admin only)
|
|
109
|
+
*/
|
|
110
|
+
authSettings.post('/users', async (c) => {
|
|
111
|
+
try {
|
|
112
|
+
requireAdmin(c);
|
|
113
|
+
const authManager = getAuthManager();
|
|
114
|
+
const body = await c.req.json();
|
|
115
|
+
const { email, name, role = 'viewer' } = body;
|
|
116
|
+
if (!email || !name) {
|
|
117
|
+
return c.json({ success: false, error: 'Email and name are required' }, 400);
|
|
118
|
+
}
|
|
119
|
+
const user = await authManager.addWhitelistedUser(email, name, role);
|
|
120
|
+
return c.json({
|
|
121
|
+
success: true,
|
|
122
|
+
data: {
|
|
123
|
+
user: {
|
|
124
|
+
id: user.id,
|
|
125
|
+
email: user.email,
|
|
126
|
+
name: user.name,
|
|
127
|
+
role: user.role,
|
|
128
|
+
registered: user.registered,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
console.error('Add user error:', error);
|
|
135
|
+
return c.json({ success: false, error: error.message || 'Failed to add user' }, error.message === 'Admin access required' ? 403 : 400);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
/**
|
|
139
|
+
* PUT /api/v1/auth-settings/users/:id
|
|
140
|
+
* Update a user (admin only)
|
|
141
|
+
*/
|
|
142
|
+
authSettings.put('/users/:id', async (c) => {
|
|
143
|
+
try {
|
|
144
|
+
requireAdmin(c);
|
|
145
|
+
const authManager = getAuthManager();
|
|
146
|
+
const userId = c.req.param('id');
|
|
147
|
+
const body = await c.req.json();
|
|
148
|
+
const user = await authManager.findUserById(userId);
|
|
149
|
+
if (!user) {
|
|
150
|
+
return c.json({ success: false, error: 'User not found' }, 404);
|
|
151
|
+
}
|
|
152
|
+
// Update role if provided
|
|
153
|
+
if (body.role) {
|
|
154
|
+
await authManager.updateUserRole(userId, body.role);
|
|
155
|
+
}
|
|
156
|
+
// Update name if provided
|
|
157
|
+
if (body.name) {
|
|
158
|
+
const config = await authManager.loadAuthConfig();
|
|
159
|
+
const userIndex = config.providers.local.users.findIndex((u) => u.id === userId);
|
|
160
|
+
if (userIndex !== -1) {
|
|
161
|
+
config.providers.local.users[userIndex].name = body.name;
|
|
162
|
+
config.providers.local.users[userIndex].updatedAt = new Date();
|
|
163
|
+
await authManager.saveAuthConfig(config);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return c.json({
|
|
167
|
+
success: true,
|
|
168
|
+
message: 'User updated successfully',
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
console.error('Update user error:', error);
|
|
173
|
+
return c.json({ success: false, error: error.message || 'Failed to update user' }, error.message === 'Admin access required' ? 403 : 500);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
/**
|
|
177
|
+
* POST /api/v1/auth-settings/users/:id/reset-password
|
|
178
|
+
* Admin reset user password (admin only)
|
|
179
|
+
*/
|
|
180
|
+
authSettings.post('/users/:id/reset-password', async (c) => {
|
|
181
|
+
try {
|
|
182
|
+
requireAdmin(c);
|
|
183
|
+
const authManager = getAuthManager();
|
|
184
|
+
const userId = c.req.param('id');
|
|
185
|
+
const body = await c.req.json();
|
|
186
|
+
if (!body.newPassword) {
|
|
187
|
+
return c.json({ success: false, error: 'New password is required' }, 400);
|
|
188
|
+
}
|
|
189
|
+
if (body.newPassword.length < 8) {
|
|
190
|
+
return c.json({ success: false, error: 'Password must be at least 8 characters' }, 400);
|
|
191
|
+
}
|
|
192
|
+
await authManager.adminResetPassword(userId, body.newPassword);
|
|
193
|
+
return c.json({
|
|
194
|
+
success: true,
|
|
195
|
+
message: 'Password reset successfully',
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
console.error('Reset password error:', error);
|
|
200
|
+
return c.json({ success: false, error: error.message || 'Failed to reset password' }, error.message === 'Admin access required' ? 403 : 400);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
/**
|
|
204
|
+
* DELETE /api/v1/auth-settings/users/:id
|
|
205
|
+
* Remove a user (admin only)
|
|
206
|
+
*/
|
|
207
|
+
authSettings.delete('/users/:id', async (c) => {
|
|
208
|
+
try {
|
|
209
|
+
requireAdmin(c);
|
|
210
|
+
const authManager = getAuthManager();
|
|
211
|
+
const userId = c.req.param('id');
|
|
212
|
+
await authManager.removeUser(userId);
|
|
213
|
+
return c.json({
|
|
214
|
+
success: true,
|
|
215
|
+
message: 'User removed successfully',
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
console.error('Remove user error:', error);
|
|
220
|
+
return c.json({ success: false, error: error.message || 'Failed to remove user' }, error.message === 'Admin access required' ? 403 : 400);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
/**
|
|
224
|
+
* PUT /api/v1/auth-settings/oauth/:provider
|
|
225
|
+
* Configure OAuth provider (admin only)
|
|
226
|
+
*/
|
|
227
|
+
authSettings.put('/oauth/:provider', async (c) => {
|
|
228
|
+
try {
|
|
229
|
+
requireAdmin(c);
|
|
230
|
+
const authManager = getAuthManager();
|
|
231
|
+
const provider = c.req.param('provider');
|
|
232
|
+
const body = await c.req.json();
|
|
233
|
+
const config = await authManager.loadAuthConfig();
|
|
234
|
+
if (!config.providers.oauth) {
|
|
235
|
+
config.providers.oauth = {};
|
|
236
|
+
}
|
|
237
|
+
config.providers.oauth[provider] = body;
|
|
238
|
+
await authManager.saveAuthConfig(config);
|
|
239
|
+
return c.json({
|
|
240
|
+
success: true,
|
|
241
|
+
message: `${provider} OAuth configuration updated successfully`,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
console.error('Update OAuth config error:', error);
|
|
246
|
+
return c.json({ success: false, error: error.message || 'Failed to update OAuth configuration' }, error.message === 'Admin access required' ? 403 : 500);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
export default authSettings;
|
|
@@ -94,7 +94,7 @@ app.put('/:filename', async (c) => {
|
|
|
94
94
|
const updatedMetadata = {
|
|
95
95
|
...currentData,
|
|
96
96
|
...body.metadata,
|
|
97
|
-
updated: new Date().toISOString()
|
|
97
|
+
updated: new Date().toISOString(),
|
|
98
98
|
// Ensure these are arrays
|
|
99
99
|
tags: body.metadata?.tags !== undefined ? body.metadata.tags : (currentData.tags || []),
|
|
100
100
|
sources: body.metadata?.sources !== undefined ? body.metadata.sources : (currentData.sources || [])
|
|
@@ -53,7 +53,7 @@ app.get('/', async (c) => {
|
|
|
53
53
|
filename: file,
|
|
54
54
|
metadata: {
|
|
55
55
|
...data,
|
|
56
|
-
updated: data.updated || new Date().toISOString()
|
|
56
|
+
updated: data.updated || new Date().toISOString(),
|
|
57
57
|
tags: data.tags || [],
|
|
58
58
|
sources: data.sources || []
|
|
59
59
|
},
|
|
@@ -101,7 +101,7 @@ app.post('/', async (c) => {
|
|
|
101
101
|
const now = new Date();
|
|
102
102
|
const frontmatter = {
|
|
103
103
|
version: '1.0',
|
|
104
|
-
updated: now.toISOString()
|
|
104
|
+
updated: now.toISOString(),
|
|
105
105
|
type: 'knowledge',
|
|
106
106
|
category: body.metadata?.category || 'general',
|
|
107
107
|
tags: body.metadata?.tags || [],
|
|
@@ -21,6 +21,9 @@ app.use('/api/*', cors({
|
|
|
21
21
|
}));
|
|
22
22
|
// Handle trailing slashes consistently for API routes
|
|
23
23
|
app.use('/api/*', trimTrailingSlash());
|
|
24
|
+
// Import and apply authentication middleware
|
|
25
|
+
import { authMiddleware } from './middleware/auth.js';
|
|
26
|
+
app.use('/api/*', authMiddleware);
|
|
24
27
|
// Create WebSocket support
|
|
25
28
|
const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app });
|
|
26
29
|
// WebSocket route for terminal sessions
|
|
@@ -75,6 +78,9 @@ app.get('/ws/terminal-preview/:sessionId', upgradeWebSocket((c) => ({
|
|
|
75
78
|
},
|
|
76
79
|
})));
|
|
77
80
|
// Import and register API routes
|
|
81
|
+
import auth from './routes/api/v1/auth/index.js';
|
|
82
|
+
import authSettings from './routes/api/v1/auth-settings/index.js';
|
|
83
|
+
import apiKeys from './routes/api/v1/api-keys/index.js';
|
|
78
84
|
import proposals from './routes/api/v1/proposals/index.js';
|
|
79
85
|
import terminalSessions from './routes/api/v1/terminal/sessions/index.js';
|
|
80
86
|
import terminalCreate from './routes/api/v1/terminal/[proposalId]/create/index.js';
|
|
@@ -95,6 +101,9 @@ import git from './routes/api/v1/git/index.js';
|
|
|
95
101
|
import mcp from './routes/api/v1/mcp/index.js';
|
|
96
102
|
// Register API routes FIRST
|
|
97
103
|
console.log('🔗 Registering API routes...');
|
|
104
|
+
app.route('/api/v1/auth', auth);
|
|
105
|
+
app.route('/api/v1/auth-settings', authSettings);
|
|
106
|
+
app.route('/api/v1/api-keys', apiKeys);
|
|
98
107
|
app.route('/api/v1/proposals', proposals);
|
|
99
108
|
app.route('/api/v1/terminal/sessions', terminalSessions);
|
|
100
109
|
app.route('/api/v1/terminal/:proposalId/create', terminalCreate);
|