s3db.js 13.5.1 → 13.6.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 (105) hide show
  1. package/README.md +25 -10
  2. package/dist/{s3db.cjs.js → s3db.cjs} +30323 -24958
  3. package/dist/s3db.cjs.map +1 -0
  4. package/dist/s3db.es.js +24026 -18654
  5. package/dist/s3db.es.js.map +1 -1
  6. package/package.json +216 -20
  7. package/src/concerns/id.js +90 -6
  8. package/src/concerns/index.js +2 -1
  9. package/src/concerns/password-hashing.js +150 -0
  10. package/src/database.class.js +4 -0
  11. package/src/plugins/api/auth/basic-auth.js +23 -1
  12. package/src/plugins/api/auth/index.js +49 -3
  13. package/src/plugins/api/auth/oauth2-auth.js +171 -0
  14. package/src/plugins/api/auth/oidc-auth.js +789 -0
  15. package/src/plugins/api/auth/oidc-client.js +462 -0
  16. package/src/plugins/api/auth/path-auth-matcher.js +284 -0
  17. package/src/plugins/api/concerns/event-emitter.js +134 -0
  18. package/src/plugins/api/concerns/failban-manager.js +651 -0
  19. package/src/plugins/api/concerns/guards-helpers.js +402 -0
  20. package/src/plugins/api/concerns/metrics-collector.js +346 -0
  21. package/src/plugins/api/index.js +503 -54
  22. package/src/plugins/api/middlewares/failban.js +305 -0
  23. package/src/plugins/api/middlewares/rate-limit.js +301 -0
  24. package/src/plugins/api/middlewares/request-id.js +74 -0
  25. package/src/plugins/api/middlewares/security-headers.js +120 -0
  26. package/src/plugins/api/middlewares/session-tracking.js +194 -0
  27. package/src/plugins/api/routes/auth-routes.js +23 -3
  28. package/src/plugins/api/routes/resource-routes.js +71 -29
  29. package/src/plugins/api/server.js +1017 -94
  30. package/src/plugins/api/utils/guards.js +213 -0
  31. package/src/plugins/api/utils/mime-types.js +154 -0
  32. package/src/plugins/api/utils/openapi-generator.js +44 -11
  33. package/src/plugins/api/utils/path-matcher.js +173 -0
  34. package/src/plugins/api/utils/static-filesystem.js +262 -0
  35. package/src/plugins/api/utils/static-s3.js +231 -0
  36. package/src/plugins/api/utils/template-engine.js +188 -0
  37. package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +853 -0
  38. package/src/plugins/cloud-inventory/drivers/aws-driver.js +2554 -0
  39. package/src/plugins/cloud-inventory/drivers/azure-driver.js +637 -0
  40. package/src/plugins/cloud-inventory/drivers/base-driver.js +99 -0
  41. package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +620 -0
  42. package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +698 -0
  43. package/src/plugins/cloud-inventory/drivers/gcp-driver.js +645 -0
  44. package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +559 -0
  45. package/src/plugins/cloud-inventory/drivers/linode-driver.js +614 -0
  46. package/src/plugins/cloud-inventory/drivers/mock-drivers.js +449 -0
  47. package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +771 -0
  48. package/src/plugins/cloud-inventory/drivers/oracle-driver.js +768 -0
  49. package/src/plugins/cloud-inventory/drivers/vultr-driver.js +636 -0
  50. package/src/plugins/cloud-inventory/index.js +20 -0
  51. package/src/plugins/cloud-inventory/registry.js +146 -0
  52. package/src/plugins/cloud-inventory/terraform-exporter.js +362 -0
  53. package/src/plugins/cloud-inventory.plugin.js +1333 -0
  54. package/src/plugins/concerns/plugin-dependencies.js +61 -1
  55. package/src/plugins/eventual-consistency/analytics.js +1 -0
  56. package/src/plugins/identity/README.md +335 -0
  57. package/src/plugins/identity/concerns/mfa-manager.js +204 -0
  58. package/src/plugins/identity/concerns/password.js +138 -0
  59. package/src/plugins/identity/concerns/resource-schemas.js +273 -0
  60. package/src/plugins/identity/concerns/token-generator.js +172 -0
  61. package/src/plugins/identity/email-service.js +422 -0
  62. package/src/plugins/identity/index.js +1052 -0
  63. package/src/plugins/identity/oauth2-server.js +1033 -0
  64. package/src/plugins/identity/oidc-discovery.js +285 -0
  65. package/src/plugins/identity/rsa-keys.js +323 -0
  66. package/src/plugins/identity/server.js +500 -0
  67. package/src/plugins/identity/session-manager.js +453 -0
  68. package/src/plugins/identity/ui/layouts/base.js +251 -0
  69. package/src/plugins/identity/ui/middleware.js +135 -0
  70. package/src/plugins/identity/ui/pages/admin/client-form.js +247 -0
  71. package/src/plugins/identity/ui/pages/admin/clients.js +179 -0
  72. package/src/plugins/identity/ui/pages/admin/dashboard.js +181 -0
  73. package/src/plugins/identity/ui/pages/admin/user-form.js +283 -0
  74. package/src/plugins/identity/ui/pages/admin/users.js +263 -0
  75. package/src/plugins/identity/ui/pages/consent.js +262 -0
  76. package/src/plugins/identity/ui/pages/forgot-password.js +104 -0
  77. package/src/plugins/identity/ui/pages/login.js +144 -0
  78. package/src/plugins/identity/ui/pages/mfa-backup-codes.js +180 -0
  79. package/src/plugins/identity/ui/pages/mfa-enrollment.js +187 -0
  80. package/src/plugins/identity/ui/pages/mfa-verification.js +178 -0
  81. package/src/plugins/identity/ui/pages/oauth-error.js +225 -0
  82. package/src/plugins/identity/ui/pages/profile.js +361 -0
  83. package/src/plugins/identity/ui/pages/register.js +226 -0
  84. package/src/plugins/identity/ui/pages/reset-password.js +128 -0
  85. package/src/plugins/identity/ui/pages/verify-email.js +172 -0
  86. package/src/plugins/identity/ui/routes.js +2541 -0
  87. package/src/plugins/identity/ui/styles/main.css +465 -0
  88. package/src/plugins/index.js +4 -1
  89. package/src/plugins/ml/base-model.class.js +32 -7
  90. package/src/plugins/ml/classification-model.class.js +1 -1
  91. package/src/plugins/ml/timeseries-model.class.js +3 -1
  92. package/src/plugins/ml.plugin.js +124 -32
  93. package/src/plugins/shared/error-handler.js +147 -0
  94. package/src/plugins/shared/index.js +9 -0
  95. package/src/plugins/shared/middlewares/compression.js +117 -0
  96. package/src/plugins/shared/middlewares/cors.js +49 -0
  97. package/src/plugins/shared/middlewares/index.js +11 -0
  98. package/src/plugins/shared/middlewares/logging.js +54 -0
  99. package/src/plugins/shared/middlewares/rate-limit.js +73 -0
  100. package/src/plugins/shared/middlewares/security.js +158 -0
  101. package/src/plugins/shared/response-formatter.js +264 -0
  102. package/src/resource.class.js +140 -12
  103. package/src/schema.class.js +30 -1
  104. package/src/validator.class.js +57 -6
  105. package/dist/s3db.cjs.js.map +0 -1
@@ -0,0 +1,500 @@
1
+ /**
2
+ * Identity Server - Hono-based HTTP server for Identity Provider Plugin
3
+ *
4
+ * Manages OAuth2/OIDC endpoints only (no CRUD routes)
5
+ */
6
+
7
+ import { errorHandler } from '../shared/error-handler.js';
8
+ import * as formatter from '../shared/response-formatter.js';
9
+ import {
10
+ createCorsMiddleware,
11
+ createSecurityMiddleware,
12
+ createLoggingMiddleware
13
+ } from '../shared/middlewares/index.js';
14
+ import { idGenerator } from '../../concerns/id.js';
15
+
16
+ /**
17
+ * Create Express-style response adapter for Hono context
18
+ * Allows OAuth2Server handlers to use res.status().json() API
19
+ * @param {Object} c - Hono context
20
+ * @returns {Object} Express-style response object
21
+ */
22
+ function createExpressStyleResponse(c) {
23
+ let statusCode = 200;
24
+
25
+ return {
26
+ status(code) {
27
+ statusCode = code;
28
+ return this;
29
+ },
30
+ json(data) {
31
+ return c.json(data, statusCode);
32
+ }
33
+ };
34
+ }
35
+
36
+ /**
37
+ * Identity Server class
38
+ * @class
39
+ */
40
+ export class IdentityServer {
41
+ /**
42
+ * Create Identity server
43
+ * @param {Object} options - Server options
44
+ */
45
+ constructor(options = {}) {
46
+ this.options = {
47
+ port: options.port || 4000,
48
+ host: options.host || '0.0.0.0',
49
+ verbose: options.verbose || false,
50
+ issuer: options.issuer,
51
+ oauth2Server: options.oauth2Server,
52
+ sessionManager: options.sessionManager || null,
53
+ usersResource: options.usersResource || null,
54
+ identityPlugin: options.identityPlugin || null,
55
+ failbanManager: options.failbanManager || null,
56
+ failbanConfig: options.failbanConfig || {},
57
+ cors: options.cors || {},
58
+ security: options.security || {},
59
+ logging: options.logging || {}
60
+ };
61
+
62
+ this.app = null;
63
+ this.server = null;
64
+ this.isRunning = false;
65
+ this.initialized = false;
66
+ }
67
+
68
+ /**
69
+ * Setup failban middleware for brute force protection
70
+ * @private
71
+ */
72
+ _setupFailbanMiddleware() {
73
+ const { failbanManager } = this.options;
74
+
75
+ // Global ban check middleware
76
+ this.app.use('*', async (c, next) => {
77
+ // Extract IP address
78
+ const ip = c.req.header('x-forwarded-for')?.split(',')[0]?.trim() ||
79
+ c.req.header('x-real-ip') ||
80
+ c.env?.ip ||
81
+ 'unknown';
82
+
83
+ // Store IP in context for later use
84
+ c.set('clientIp', ip);
85
+
86
+ // Check if blacklisted
87
+ if (failbanManager.isBlacklisted(ip)) {
88
+ c.header('X-Ban-Status', 'blacklisted');
89
+ c.header('X-Ban-Reason', 'IP is permanently blacklisted');
90
+
91
+ if (this.options.verbose) {
92
+ console.log(`[Failban] Blocked blacklisted IP: ${ip}`);
93
+ }
94
+
95
+ return c.json({
96
+ error: 'Forbidden',
97
+ message: 'Your IP address has been permanently blocked',
98
+ ip
99
+ }, 403);
100
+ }
101
+
102
+ // Check country restrictions (GeoIP)
103
+ if (this.options.failbanConfig.geo?.enabled) {
104
+ const countryBlock = failbanManager.checkCountryBlock(ip);
105
+ if (countryBlock) {
106
+ c.header('X-Ban-Status', 'country_blocked');
107
+ c.header('X-Ban-Reason', countryBlock.reason);
108
+ c.header('X-Country-Code', countryBlock.country);
109
+
110
+ if (this.options.verbose) {
111
+ console.log(`[Failban] Blocked country ${countryBlock.country} for IP: ${ip}`);
112
+ }
113
+
114
+ return c.json({
115
+ error: 'Forbidden',
116
+ message: 'Access from your country is not allowed',
117
+ country: countryBlock.country,
118
+ ip
119
+ }, 403);
120
+ }
121
+ }
122
+
123
+ // Check if banned
124
+ if (failbanManager.isBanned(ip)) {
125
+ const ban = await failbanManager.getBan(ip);
126
+
127
+ if (ban) {
128
+ const expiresAt = new Date(ban.expiresAt);
129
+ const retryAfter = Math.ceil((expiresAt.getTime() - Date.now()) / 1000);
130
+
131
+ c.header('Retry-After', String(retryAfter));
132
+ c.header('X-Ban-Status', 'banned');
133
+ c.header('X-Ban-Reason', ban.reason);
134
+ c.header('X-Ban-Expires', ban.expiresAt);
135
+
136
+ if (this.options.verbose) {
137
+ console.log(`[Failban] Blocked banned IP: ${ip} (expires in ${retryAfter}s)`);
138
+ }
139
+
140
+ return c.json({
141
+ error: 'Forbidden',
142
+ message: 'Your IP address has been temporarily banned due to security violations',
143
+ reason: ban.reason,
144
+ expiresAt: ban.expiresAt,
145
+ retryAfter
146
+ }, 403);
147
+ }
148
+ }
149
+
150
+ // Not banned - continue
151
+ await next();
152
+ });
153
+
154
+ if (this.options.verbose) {
155
+ console.log('[Identity Server] Failban middleware enabled (global ban check)');
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Setup all routes
161
+ * @private
162
+ */
163
+ _setupRoutes() {
164
+ // Request ID middleware
165
+ this.app.use('*', async (c, next) => {
166
+ c.set('requestId', idGenerator());
167
+ c.set('verbose', this.options.verbose);
168
+ await next();
169
+ });
170
+
171
+ // Apply CORS middleware if enabled
172
+ if (this.options.cors.enabled) {
173
+ const corsMiddleware = createCorsMiddleware(this.options.cors);
174
+ this.app.use('*', corsMiddleware);
175
+ }
176
+
177
+ // Apply security headers if enabled
178
+ if (this.options.security.enabled) {
179
+ const securityMiddleware = createSecurityMiddleware(this.options.security);
180
+ this.app.use('*', securityMiddleware);
181
+ }
182
+
183
+ // Apply failban middleware if enabled (global IP ban check)
184
+ if (this.options.failbanManager && this.options.failbanConfig.enabled) {
185
+ this._setupFailbanMiddleware();
186
+ }
187
+
188
+ // Apply logging middleware if enabled
189
+ if (this.options.logging.enabled) {
190
+ const loggingMiddleware = createLoggingMiddleware(this.options.logging);
191
+ this.app.use('*', loggingMiddleware);
192
+ }
193
+
194
+ // Health check endpoints
195
+ this.app.get('/health', (c) => {
196
+ const response = formatter.success({
197
+ status: 'ok',
198
+ service: 'identity-provider',
199
+ uptime: process.uptime(),
200
+ timestamp: new Date().toISOString()
201
+ });
202
+ return c.json(response);
203
+ });
204
+
205
+ this.app.get('/health/live', (c) => {
206
+ const response = formatter.success({
207
+ status: 'alive',
208
+ timestamp: new Date().toISOString()
209
+ });
210
+ return c.json(response);
211
+ });
212
+
213
+ this.app.get('/health/ready', (c) => {
214
+ const isReady = this.options.oauth2Server !== null;
215
+
216
+ if (!isReady) {
217
+ const response = formatter.error('Service not ready', {
218
+ status: 503,
219
+ code: 'NOT_READY'
220
+ });
221
+ return c.json(response, 503);
222
+ }
223
+
224
+ const response = formatter.success({
225
+ status: 'ready',
226
+ timestamp: new Date().toISOString()
227
+ });
228
+ return c.json(response);
229
+ });
230
+
231
+ // Root endpoint - discovery redirect
232
+ this.app.get('/', (c) => {
233
+ return c.redirect('/.well-known/openid-configuration', 302);
234
+ });
235
+
236
+ // Setup OAuth2/OIDC routes
237
+ this._setupOAuth2Routes();
238
+
239
+ // Setup UI routes (login, register, profile, etc.)
240
+ this._setupUIRoutes();
241
+
242
+ // Global error handler
243
+ this.app.onError((err, c) => {
244
+ return errorHandler(err, c);
245
+ });
246
+
247
+ // 404 handler
248
+ this.app.notFound((c) => {
249
+ const response = formatter.error('Route not found', {
250
+ status: 404,
251
+ code: 'NOT_FOUND',
252
+ details: {
253
+ path: c.req.path,
254
+ method: c.req.method
255
+ }
256
+ });
257
+ return c.json(response, 404);
258
+ });
259
+ }
260
+
261
+ /**
262
+ * Setup OAuth2/OIDC routes
263
+ * @private
264
+ */
265
+ _setupOAuth2Routes() {
266
+ const { oauth2Server } = this.options;
267
+
268
+ if (!oauth2Server) {
269
+ console.error('[Identity Server] OAuth2 Server not provided');
270
+ return;
271
+ }
272
+
273
+ // OIDC Discovery endpoint
274
+ this.app.get('/.well-known/openid-configuration', async (c) => {
275
+ const res = createExpressStyleResponse(c);
276
+ return await oauth2Server.discoveryHandler(c.req, res);
277
+ });
278
+
279
+ // JWKS (JSON Web Key Set) endpoint
280
+ this.app.get('/.well-known/jwks.json', async (c) => {
281
+ const res = createExpressStyleResponse(c);
282
+ return await oauth2Server.jwksHandler(c.req, res);
283
+ });
284
+
285
+ // OAuth2 Token endpoint
286
+ this.app.post('/oauth/token', async (c) => {
287
+ const res = createExpressStyleResponse(c);
288
+ return await oauth2Server.tokenHandler(c.req, res);
289
+ });
290
+
291
+ // OIDC UserInfo endpoint
292
+ this.app.get('/oauth/userinfo', async (c) => {
293
+ const res = createExpressStyleResponse(c);
294
+ return await oauth2Server.userinfoHandler(c.req, res);
295
+ });
296
+
297
+ // Token introspection endpoint
298
+ this.app.post('/oauth/introspect', async (c) => {
299
+ const res = createExpressStyleResponse(c);
300
+ return await oauth2Server.introspectHandler(c.req, res);
301
+ });
302
+
303
+ // Authorization endpoint (GET for user consent UI)
304
+ this.app.get('/oauth/authorize', async (c) => {
305
+ const res = createExpressStyleResponse(c);
306
+ return await oauth2Server.authorizeHandler(c.req, res);
307
+ });
308
+
309
+ // Authorization endpoint (POST for processing login)
310
+ this.app.post('/oauth/authorize', async (c) => {
311
+ const res = createExpressStyleResponse(c);
312
+ return await oauth2Server.authorizePostHandler(c.req, res);
313
+ });
314
+
315
+ // Client registration endpoint
316
+ this.app.post('/oauth/register', async (c) => {
317
+ const res = createExpressStyleResponse(c);
318
+ return await oauth2Server.registerClientHandler(c.req, res);
319
+ });
320
+
321
+ // Token revocation endpoint
322
+ this.app.post('/oauth/revoke', async (c) => {
323
+ const res = createExpressStyleResponse(c);
324
+ return await oauth2Server.revokeHandler(c.req, res);
325
+ });
326
+
327
+ if (this.options.verbose) {
328
+ console.log('[Identity Server] Mounted OAuth2/OIDC routes:');
329
+ console.log('[Identity Server] GET /.well-known/openid-configuration (OIDC Discovery)');
330
+ console.log('[Identity Server] GET /.well-known/jwks.json (JWKS)');
331
+ console.log('[Identity Server] GET /oauth/authorize (Authorization UI)');
332
+ console.log('[Identity Server] POST /oauth/authorize (Process Login)');
333
+ console.log('[Identity Server] POST /oauth/token (Token)');
334
+ console.log('[Identity Server] GET /oauth/userinfo (UserInfo)');
335
+ console.log('[Identity Server] POST /oauth/introspect (Introspection)');
336
+ console.log('[Identity Server] POST /oauth/register (Client Registration)');
337
+ console.log('[Identity Server] POST /oauth/revoke (Token Revocation)');
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Setup UI routes (login, register, profile, etc.)
343
+ * @private
344
+ */
345
+ async _setupUIRoutes() {
346
+ const { sessionManager, identityPlugin } = this.options;
347
+
348
+ if (!sessionManager || !identityPlugin) {
349
+ if (this.options.verbose) {
350
+ console.log('[Identity Server] SessionManager or IdentityPlugin not provided, skipping UI routes');
351
+ }
352
+ return;
353
+ }
354
+
355
+ try {
356
+ // Dynamic import of UI routes
357
+ const { registerUIRoutes } = await import('./ui/routes.js');
358
+
359
+ // Register all UI routes (login, register, logout)
360
+ registerUIRoutes(this.app, identityPlugin);
361
+
362
+ if (this.options.verbose) {
363
+ console.log('[Identity Server] Mounted UI routes:');
364
+ console.log('[Identity Server] GET /login (Login Form)');
365
+ console.log('[Identity Server] POST /login (Process Login)');
366
+ console.log('[Identity Server] GET /register (Registration Form)');
367
+ console.log('[Identity Server] POST /register (Process Registration)');
368
+ console.log('[Identity Server] GET /logout (Logout)');
369
+ console.log('[Identity Server] POST /logout (Logout)');
370
+ console.log('[Identity Server] GET /forgot-password (Forgot Password Form)');
371
+ console.log('[Identity Server] POST /forgot-password (Process Forgot Password)');
372
+ console.log('[Identity Server] GET /reset-password (Reset Password Form)');
373
+ console.log('[Identity Server] POST /reset-password (Process Password Reset)');
374
+ console.log('[Identity Server] GET /profile (User Profile - Protected)');
375
+ console.log('[Identity Server] POST /profile/update (Update Profile)');
376
+ console.log('[Identity Server] POST /profile/change-password (Change Password)');
377
+ console.log('[Identity Server] POST /profile/logout-session (Logout Specific Session)');
378
+ console.log('[Identity Server] POST /profile/logout-all-sessions (Logout All Other Sessions)');
379
+ console.log('[Identity Server] GET /admin (Admin Dashboard - Protected)');
380
+ console.log('[Identity Server] GET /admin/clients (List OAuth2 Clients)');
381
+ console.log('[Identity Server] GET /admin/clients/new (New Client Form)');
382
+ console.log('[Identity Server] POST /admin/clients/create (Create Client)');
383
+ console.log('[Identity Server] GET /admin/clients/:id/edit (Edit Client Form)');
384
+ console.log('[Identity Server] POST /admin/clients/:id/update (Update Client)');
385
+ console.log('[Identity Server] POST /admin/clients/:id/delete (Delete Client)');
386
+ console.log('[Identity Server] POST /admin/clients/:id/rotate-secret (Rotate Client Secret)');
387
+ console.log('[Identity Server] POST /admin/clients/:id/toggle-active (Toggle Client Active)');
388
+ console.log('[Identity Server] GET /admin/users (List Users - Protected)');
389
+ console.log('[Identity Server] GET /admin/users/:id/edit (Edit User Form)');
390
+ console.log('[Identity Server] POST /admin/users/:id/update (Update User)');
391
+ console.log('[Identity Server] POST /admin/users/:id/delete (Delete User)');
392
+ console.log('[Identity Server] POST /admin/users/:id/change-status (Change User Status)');
393
+ console.log('[Identity Server] POST /admin/users/:id/verify-email (Mark Email Verified)');
394
+ console.log('[Identity Server] POST /admin/users/:id/reset-password (Send Password Reset)');
395
+ console.log('[Identity Server] POST /admin/users/:id/toggle-admin (Toggle Admin Role)');
396
+ console.log('[Identity Server] GET /oauth/authorize (OAuth2 Consent Screen - Overrides OAuth2Server)');
397
+ console.log('[Identity Server] POST /oauth/consent (Process OAuth2 Consent Decision)');
398
+ console.log('[Identity Server] GET /verify-email (Verify Email with Token)');
399
+ console.log('[Identity Server] POST /verify-email/resend (Resend Verification Email)');
400
+ }
401
+ } catch (error) {
402
+ console.error('[Identity Server] Failed to setup UI routes:', error);
403
+ }
404
+ }
405
+
406
+ /**
407
+ * Start the server
408
+ * @returns {Promise<void>}
409
+ */
410
+ async start() {
411
+ if (this.isRunning) {
412
+ console.warn('[Identity Server] Server is already running');
413
+ return;
414
+ }
415
+
416
+ // Dynamic import of Hono dependencies
417
+ if (!this.initialized) {
418
+ const { Hono } = await import('hono');
419
+ const { serve } = await import('@hono/node-server');
420
+
421
+ this.Hono = Hono;
422
+ this.serve = serve;
423
+
424
+ // Initialize app
425
+ this.app = new Hono();
426
+
427
+ // Setup routes
428
+ this._setupRoutes();
429
+
430
+ this.initialized = true;
431
+ }
432
+
433
+ const { port, host } = this.options;
434
+
435
+ return new Promise((resolve, reject) => {
436
+ try {
437
+ this.server = this.serve({
438
+ fetch: this.app.fetch,
439
+ port,
440
+ hostname: host
441
+ }, (info) => {
442
+ this.isRunning = true;
443
+ console.log(`[Identity Server] Server listening on http://${info.address}:${info.port}`);
444
+ console.log(`[Identity Server] Issuer: ${this.options.issuer}`);
445
+ console.log(`[Identity Server] Discovery: ${this.options.issuer}/.well-known/openid-configuration`);
446
+ resolve();
447
+ });
448
+ } catch (err) {
449
+ reject(err);
450
+ }
451
+ });
452
+ }
453
+
454
+ /**
455
+ * Stop the server
456
+ * @returns {Promise<void>}
457
+ */
458
+ async stop() {
459
+ if (!this.isRunning) {
460
+ console.warn('[Identity Server] Server is not running');
461
+ return;
462
+ }
463
+
464
+ if (this.server && typeof this.server.close === 'function') {
465
+ await new Promise((resolve) => {
466
+ this.server.close(() => {
467
+ this.isRunning = false;
468
+ console.log('[Identity Server] Server stopped');
469
+ resolve();
470
+ });
471
+ });
472
+ } else {
473
+ this.isRunning = false;
474
+ console.log('[Identity Server] Server stopped');
475
+ }
476
+ }
477
+
478
+ /**
479
+ * Get server info
480
+ * @returns {Object} Server information
481
+ */
482
+ getInfo() {
483
+ return {
484
+ isRunning: this.isRunning,
485
+ port: this.options.port,
486
+ host: this.options.host,
487
+ issuer: this.options.issuer
488
+ };
489
+ }
490
+
491
+ /**
492
+ * Get Hono app instance
493
+ * @returns {Hono} Hono app
494
+ */
495
+ getApp() {
496
+ return this.app;
497
+ }
498
+ }
499
+
500
+ export default IdentityServer;