@almadar/server 2.0.3 → 2.0.5
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/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +531 -497
- package/dist/index.js.map +1 -1
- package/dist/lib/debugRouter.d.ts +1 -0
- package/dist/lib/debugRouter.d.ts.map +1 -1
- package/package.json +6 -1
- package/dist/routes/observability.d.ts +0 -10
- package/dist/routes/observability.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -3,8 +3,8 @@ import dotenv from 'dotenv';
|
|
|
3
3
|
import { Router } from 'express';
|
|
4
4
|
import admin from 'firebase-admin';
|
|
5
5
|
export { default as admin } from 'firebase-admin';
|
|
6
|
-
import { WebSocketServer, WebSocket } from 'ws';
|
|
7
6
|
import { faker } from '@faker-js/faker';
|
|
7
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
8
8
|
import { diffSchemas, categorizeRemovals, detectPageContentReduction, isDestructiveChange, hasSignificantPageReduction, requiresConfirmation } from '@almadar/core';
|
|
9
9
|
import { getObservabilityCollector, MemoryManager, SessionManager, getMultiUserManager, createWorkflowToolWrapper, createSkillAgent, createUserContext, getStateSyncManager } from '@almadar/agent';
|
|
10
10
|
|
|
@@ -452,27 +452,6 @@ var EventPersistence = class {
|
|
|
452
452
|
return this.store;
|
|
453
453
|
}
|
|
454
454
|
};
|
|
455
|
-
function debugEventsRouter() {
|
|
456
|
-
const router2 = Router();
|
|
457
|
-
if (process.env.NODE_ENV !== "development") {
|
|
458
|
-
return router2;
|
|
459
|
-
}
|
|
460
|
-
router2.get("/event-log", (_req, res) => {
|
|
461
|
-
const limit = parseInt(String(_req.query.limit) || "50", 10);
|
|
462
|
-
const events = getServerEventBus().getRecentEvents(limit);
|
|
463
|
-
res.json({ count: events.length, events });
|
|
464
|
-
});
|
|
465
|
-
router2.delete("/event-log", (_req, res) => {
|
|
466
|
-
getServerEventBus().clearEventLog();
|
|
467
|
-
res.json({ cleared: true });
|
|
468
|
-
});
|
|
469
|
-
router2.get("/listeners", (_req, res) => {
|
|
470
|
-
const counts = getServerEventBus().getListenerCounts();
|
|
471
|
-
const total = Object.values(counts).reduce((sum, n) => sum + n, 0);
|
|
472
|
-
res.json({ total, events: counts });
|
|
473
|
-
});
|
|
474
|
-
return router2;
|
|
475
|
-
}
|
|
476
455
|
function initializeFirebase() {
|
|
477
456
|
if (admin.apps.length > 0) {
|
|
478
457
|
return admin.app();
|
|
@@ -537,380 +516,117 @@ var db = new Proxy({}, {
|
|
|
537
516
|
return typeof value === "function" ? value.bind(firestore) : value;
|
|
538
517
|
}
|
|
539
518
|
});
|
|
540
|
-
var
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
wss.on("connection", (ws, req) => {
|
|
549
|
-
const clientId = req.headers["sec-websocket-key"] || "unknown";
|
|
550
|
-
logger.debug(`[WebSocket] Client connected: ${clientId}`);
|
|
551
|
-
ws.send(
|
|
552
|
-
JSON.stringify({
|
|
553
|
-
type: "CONNECTED",
|
|
554
|
-
timestamp: Date.now(),
|
|
555
|
-
message: "Connected to event stream"
|
|
556
|
-
})
|
|
557
|
-
);
|
|
558
|
-
ws.on("message", (data) => {
|
|
559
|
-
try {
|
|
560
|
-
const message = JSON.parse(data.toString());
|
|
561
|
-
logger.debug(`[WebSocket] Received from ${clientId}:`, message);
|
|
562
|
-
if (message.type && message.payload) {
|
|
563
|
-
getServerEventBus().emit(message.type, message.payload, {
|
|
564
|
-
orbital: "client",
|
|
565
|
-
entity: clientId
|
|
566
|
-
});
|
|
567
|
-
}
|
|
568
|
-
} catch (error) {
|
|
569
|
-
logger.error(`[WebSocket] Failed to parse message:`, error);
|
|
570
|
-
}
|
|
571
|
-
});
|
|
572
|
-
ws.on("close", () => {
|
|
573
|
-
logger.debug(`[WebSocket] Client disconnected: ${clientId}`);
|
|
574
|
-
});
|
|
575
|
-
ws.on("error", (error) => {
|
|
576
|
-
logger.error(`[WebSocket] Client error:`, error);
|
|
577
|
-
});
|
|
578
|
-
});
|
|
579
|
-
getServerEventBus().on("*", (event) => {
|
|
580
|
-
if (!wss) return;
|
|
581
|
-
const typedEvent = event;
|
|
582
|
-
const message = JSON.stringify({
|
|
583
|
-
type: typedEvent.type,
|
|
584
|
-
payload: typedEvent.payload,
|
|
585
|
-
timestamp: typedEvent.timestamp,
|
|
586
|
-
source: typedEvent.source
|
|
587
|
-
});
|
|
588
|
-
let broadcastCount = 0;
|
|
589
|
-
wss.clients.forEach((client) => {
|
|
590
|
-
if (client.readyState === WebSocket.OPEN) {
|
|
591
|
-
client.send(message);
|
|
592
|
-
broadcastCount++;
|
|
593
|
-
}
|
|
594
|
-
});
|
|
595
|
-
if (broadcastCount > 0) {
|
|
596
|
-
logger.debug(`[WebSocket] Broadcast ${typedEvent.type} to ${broadcastCount} client(s)`);
|
|
597
|
-
}
|
|
598
|
-
});
|
|
599
|
-
return wss;
|
|
600
|
-
}
|
|
601
|
-
function getWebSocketServer() {
|
|
602
|
-
return wss;
|
|
603
|
-
}
|
|
604
|
-
function closeWebSocketServer() {
|
|
605
|
-
return new Promise((resolve, reject) => {
|
|
606
|
-
if (!wss) {
|
|
607
|
-
resolve();
|
|
608
|
-
return;
|
|
519
|
+
var MockDataService = class {
|
|
520
|
+
stores = /* @__PURE__ */ new Map();
|
|
521
|
+
schemas = /* @__PURE__ */ new Map();
|
|
522
|
+
idCounters = /* @__PURE__ */ new Map();
|
|
523
|
+
constructor() {
|
|
524
|
+
if (env.MOCK_SEED !== void 0) {
|
|
525
|
+
faker.seed(env.MOCK_SEED);
|
|
526
|
+
logger.info(`[Mock] Using seed: ${env.MOCK_SEED}`);
|
|
609
527
|
}
|
|
610
|
-
wss.close((err) => {
|
|
611
|
-
if (err) {
|
|
612
|
-
reject(err);
|
|
613
|
-
} else {
|
|
614
|
-
wss = null;
|
|
615
|
-
resolve();
|
|
616
|
-
}
|
|
617
|
-
});
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
function getConnectedClientCount() {
|
|
621
|
-
if (!wss) return 0;
|
|
622
|
-
return wss.clients.size;
|
|
623
|
-
}
|
|
624
|
-
var AppError = class extends Error {
|
|
625
|
-
constructor(statusCode, message, code) {
|
|
626
|
-
super(message);
|
|
627
|
-
this.statusCode = statusCode;
|
|
628
|
-
this.message = message;
|
|
629
|
-
this.code = code;
|
|
630
|
-
this.name = "AppError";
|
|
631
528
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
529
|
+
// ============================================================================
|
|
530
|
+
// Store Management
|
|
531
|
+
// ============================================================================
|
|
532
|
+
/**
|
|
533
|
+
* Initialize store for an entity.
|
|
534
|
+
*/
|
|
535
|
+
getStore(entityName) {
|
|
536
|
+
const normalized = entityName.toLowerCase();
|
|
537
|
+
if (!this.stores.has(normalized)) {
|
|
538
|
+
this.stores.set(normalized, /* @__PURE__ */ new Map());
|
|
539
|
+
this.idCounters.set(normalized, 0);
|
|
540
|
+
}
|
|
541
|
+
return this.stores.get(normalized);
|
|
636
542
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
543
|
+
/**
|
|
544
|
+
* Generate next ID for an entity.
|
|
545
|
+
*/
|
|
546
|
+
nextId(entityName) {
|
|
547
|
+
const normalized = entityName.toLowerCase();
|
|
548
|
+
const counter = (this.idCounters.get(normalized) ?? 0) + 1;
|
|
549
|
+
this.idCounters.set(normalized, counter);
|
|
550
|
+
return `mock-${normalized}-${counter}`;
|
|
641
551
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
552
|
+
// ============================================================================
|
|
553
|
+
// Schema & Seeding
|
|
554
|
+
// ============================================================================
|
|
555
|
+
/**
|
|
556
|
+
* Register an entity schema.
|
|
557
|
+
*/
|
|
558
|
+
registerSchema(entityName, schema) {
|
|
559
|
+
this.schemas.set(entityName.toLowerCase(), schema);
|
|
646
560
|
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
561
|
+
/**
|
|
562
|
+
* Seed an entity with mock data.
|
|
563
|
+
*/
|
|
564
|
+
seed(entityName, fields, count = 10) {
|
|
565
|
+
const store = this.getStore(entityName);
|
|
566
|
+
const normalized = entityName.toLowerCase();
|
|
567
|
+
logger.info(`[Mock] Seeding ${count} ${entityName}...`);
|
|
568
|
+
for (let i = 0; i < count; i++) {
|
|
569
|
+
const item = this.generateMockItem(normalized, fields, i + 1);
|
|
570
|
+
store.set(item.id, item);
|
|
571
|
+
}
|
|
651
572
|
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
573
|
+
/**
|
|
574
|
+
* Generate a single mock item based on field schemas.
|
|
575
|
+
*/
|
|
576
|
+
generateMockItem(entityName, fields, index) {
|
|
577
|
+
const id = this.nextId(entityName);
|
|
578
|
+
const now = /* @__PURE__ */ new Date();
|
|
579
|
+
const item = {
|
|
580
|
+
id,
|
|
581
|
+
createdAt: faker.date.past({ years: 1 }),
|
|
582
|
+
updatedAt: now
|
|
583
|
+
};
|
|
584
|
+
for (const field of fields) {
|
|
585
|
+
if (field.name === "id" || field.name === "createdAt" || field.name === "updatedAt") {
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
item[field.name] = this.generateFieldValue(entityName, field, index);
|
|
589
|
+
}
|
|
590
|
+
return item;
|
|
656
591
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
Promise.resolve(fn(req, res, next)).catch(next);
|
|
696
|
-
};
|
|
697
|
-
var notFoundHandler = (req, res) => {
|
|
698
|
-
res.status(404).json({
|
|
699
|
-
success: false,
|
|
700
|
-
error: `Route ${req.method} ${req.path} not found`,
|
|
701
|
-
code: "ROUTE_NOT_FOUND"
|
|
702
|
-
});
|
|
703
|
-
};
|
|
704
|
-
var validateBody = (schema) => async (req, res, next) => {
|
|
705
|
-
try {
|
|
706
|
-
req.body = await schema.parseAsync(req.body);
|
|
707
|
-
next();
|
|
708
|
-
} catch (error) {
|
|
709
|
-
if (error instanceof ZodError) {
|
|
710
|
-
res.status(400).json({
|
|
711
|
-
success: false,
|
|
712
|
-
error: "Validation failed",
|
|
713
|
-
code: "VALIDATION_ERROR",
|
|
714
|
-
details: error.errors.map((e) => ({
|
|
715
|
-
path: e.path.join("."),
|
|
716
|
-
message: e.message
|
|
717
|
-
}))
|
|
718
|
-
});
|
|
719
|
-
return;
|
|
720
|
-
}
|
|
721
|
-
next(error);
|
|
722
|
-
}
|
|
723
|
-
};
|
|
724
|
-
var validateQuery = (schema) => async (req, res, next) => {
|
|
725
|
-
try {
|
|
726
|
-
req.query = await schema.parseAsync(req.query);
|
|
727
|
-
next();
|
|
728
|
-
} catch (error) {
|
|
729
|
-
if (error instanceof ZodError) {
|
|
730
|
-
res.status(400).json({
|
|
731
|
-
success: false,
|
|
732
|
-
error: "Invalid query parameters",
|
|
733
|
-
code: "VALIDATION_ERROR",
|
|
734
|
-
details: error.errors.map((e) => ({
|
|
735
|
-
path: e.path.join("."),
|
|
736
|
-
message: e.message
|
|
737
|
-
}))
|
|
738
|
-
});
|
|
739
|
-
return;
|
|
740
|
-
}
|
|
741
|
-
next(error);
|
|
742
|
-
}
|
|
743
|
-
};
|
|
744
|
-
var validateParams = (schema) => async (req, res, next) => {
|
|
745
|
-
try {
|
|
746
|
-
req.params = await schema.parseAsync(req.params);
|
|
747
|
-
next();
|
|
748
|
-
} catch (error) {
|
|
749
|
-
if (error instanceof ZodError) {
|
|
750
|
-
res.status(400).json({
|
|
751
|
-
success: false,
|
|
752
|
-
error: "Invalid path parameters",
|
|
753
|
-
code: "VALIDATION_ERROR",
|
|
754
|
-
details: error.errors.map((e) => ({
|
|
755
|
-
path: e.path.join("."),
|
|
756
|
-
message: e.message
|
|
757
|
-
}))
|
|
758
|
-
});
|
|
759
|
-
return;
|
|
760
|
-
}
|
|
761
|
-
next(error);
|
|
762
|
-
}
|
|
763
|
-
};
|
|
764
|
-
|
|
765
|
-
// src/middleware/authenticateFirebase.ts
|
|
766
|
-
var BEARER_PREFIX = "Bearer ";
|
|
767
|
-
var DEV_USER = {
|
|
768
|
-
uid: "dev-user-001",
|
|
769
|
-
email: "dev@localhost",
|
|
770
|
-
email_verified: true,
|
|
771
|
-
aud: "dev-project",
|
|
772
|
-
auth_time: Math.floor(Date.now() / 1e3),
|
|
773
|
-
exp: Math.floor(Date.now() / 1e3) + 3600,
|
|
774
|
-
iat: Math.floor(Date.now() / 1e3),
|
|
775
|
-
iss: "https://securetoken.google.com/dev-project",
|
|
776
|
-
sub: "dev-user-001",
|
|
777
|
-
firebase: {
|
|
778
|
-
identities: {},
|
|
779
|
-
sign_in_provider: "custom"
|
|
780
|
-
}
|
|
781
|
-
};
|
|
782
|
-
async function authenticateFirebase(req, res, next) {
|
|
783
|
-
const authorization = req.headers.authorization;
|
|
784
|
-
if (env.NODE_ENV === "development" && (!authorization || !authorization.startsWith(BEARER_PREFIX))) {
|
|
785
|
-
req.firebaseUser = DEV_USER;
|
|
786
|
-
res.locals.firebaseUser = DEV_USER;
|
|
787
|
-
return next();
|
|
788
|
-
}
|
|
789
|
-
try {
|
|
790
|
-
if (!authorization || !authorization.startsWith(BEARER_PREFIX)) {
|
|
791
|
-
return res.status(401).json({ error: "Authorization header missing or malformed" });
|
|
792
|
-
}
|
|
793
|
-
const token = authorization.slice(BEARER_PREFIX.length);
|
|
794
|
-
const decodedToken = await getAuth().verifyIdToken(token);
|
|
795
|
-
req.firebaseUser = decodedToken;
|
|
796
|
-
res.locals.firebaseUser = decodedToken;
|
|
797
|
-
return next();
|
|
798
|
-
} catch (error) {
|
|
799
|
-
console.error("Firebase authentication failed:", error);
|
|
800
|
-
return res.status(401).json({ error: "Unauthorized" });
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
var MockDataService = class {
|
|
804
|
-
stores = /* @__PURE__ */ new Map();
|
|
805
|
-
schemas = /* @__PURE__ */ new Map();
|
|
806
|
-
idCounters = /* @__PURE__ */ new Map();
|
|
807
|
-
constructor() {
|
|
808
|
-
if (env.MOCK_SEED !== void 0) {
|
|
809
|
-
faker.seed(env.MOCK_SEED);
|
|
810
|
-
logger.info(`[Mock] Using seed: ${env.MOCK_SEED}`);
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
// ============================================================================
|
|
814
|
-
// Store Management
|
|
815
|
-
// ============================================================================
|
|
816
|
-
/**
|
|
817
|
-
* Initialize store for an entity.
|
|
818
|
-
*/
|
|
819
|
-
getStore(entityName) {
|
|
820
|
-
const normalized = entityName.toLowerCase();
|
|
821
|
-
if (!this.stores.has(normalized)) {
|
|
822
|
-
this.stores.set(normalized, /* @__PURE__ */ new Map());
|
|
823
|
-
this.idCounters.set(normalized, 0);
|
|
824
|
-
}
|
|
825
|
-
return this.stores.get(normalized);
|
|
826
|
-
}
|
|
827
|
-
/**
|
|
828
|
-
* Generate next ID for an entity.
|
|
829
|
-
*/
|
|
830
|
-
nextId(entityName) {
|
|
831
|
-
const normalized = entityName.toLowerCase();
|
|
832
|
-
const counter = (this.idCounters.get(normalized) ?? 0) + 1;
|
|
833
|
-
this.idCounters.set(normalized, counter);
|
|
834
|
-
return `mock-${normalized}-${counter}`;
|
|
835
|
-
}
|
|
836
|
-
// ============================================================================
|
|
837
|
-
// Schema & Seeding
|
|
838
|
-
// ============================================================================
|
|
839
|
-
/**
|
|
840
|
-
* Register an entity schema.
|
|
841
|
-
*/
|
|
842
|
-
registerSchema(entityName, schema) {
|
|
843
|
-
this.schemas.set(entityName.toLowerCase(), schema);
|
|
844
|
-
}
|
|
845
|
-
/**
|
|
846
|
-
* Seed an entity with mock data.
|
|
847
|
-
*/
|
|
848
|
-
seed(entityName, fields, count = 10) {
|
|
849
|
-
const store = this.getStore(entityName);
|
|
850
|
-
const normalized = entityName.toLowerCase();
|
|
851
|
-
logger.info(`[Mock] Seeding ${count} ${entityName}...`);
|
|
852
|
-
for (let i = 0; i < count; i++) {
|
|
853
|
-
const item = this.generateMockItem(normalized, fields, i + 1);
|
|
854
|
-
store.set(item.id, item);
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
/**
|
|
858
|
-
* Generate a single mock item based on field schemas.
|
|
859
|
-
*/
|
|
860
|
-
generateMockItem(entityName, fields, index) {
|
|
861
|
-
const id = this.nextId(entityName);
|
|
862
|
-
const now = /* @__PURE__ */ new Date();
|
|
863
|
-
const item = {
|
|
864
|
-
id,
|
|
865
|
-
createdAt: faker.date.past({ years: 1 }),
|
|
866
|
-
updatedAt: now
|
|
867
|
-
};
|
|
868
|
-
for (const field of fields) {
|
|
869
|
-
if (field.name === "id" || field.name === "createdAt" || field.name === "updatedAt") {
|
|
870
|
-
continue;
|
|
871
|
-
}
|
|
872
|
-
item[field.name] = this.generateFieldValue(entityName, field, index);
|
|
873
|
-
}
|
|
874
|
-
return item;
|
|
875
|
-
}
|
|
876
|
-
/**
|
|
877
|
-
* Generate a mock value for a field based on its schema.
|
|
878
|
-
*/
|
|
879
|
-
generateFieldValue(entityName, field, index) {
|
|
880
|
-
if (!field.required && Math.random() > 0.8) {
|
|
881
|
-
return void 0;
|
|
882
|
-
}
|
|
883
|
-
switch (field.type) {
|
|
884
|
-
case "string":
|
|
885
|
-
return this.generateStringValue(entityName, field, index);
|
|
886
|
-
case "number":
|
|
887
|
-
return faker.number.int({
|
|
888
|
-
min: field.min ?? 0,
|
|
889
|
-
max: field.max ?? 1e3
|
|
890
|
-
});
|
|
891
|
-
case "boolean":
|
|
892
|
-
return faker.datatype.boolean();
|
|
893
|
-
case "date":
|
|
894
|
-
return this.generateDateValue(field);
|
|
895
|
-
case "enum":
|
|
896
|
-
if (field.enumValues && field.enumValues.length > 0) {
|
|
897
|
-
return faker.helpers.arrayElement(field.enumValues);
|
|
898
|
-
}
|
|
899
|
-
return null;
|
|
900
|
-
case "relation":
|
|
901
|
-
if (field.relatedEntity) {
|
|
902
|
-
const relatedStore = this.stores.get(field.relatedEntity.toLowerCase());
|
|
903
|
-
if (relatedStore && relatedStore.size > 0) {
|
|
904
|
-
const ids = Array.from(relatedStore.keys());
|
|
905
|
-
return faker.helpers.arrayElement(ids);
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
return null;
|
|
909
|
-
case "array":
|
|
910
|
-
return [];
|
|
911
|
-
default:
|
|
912
|
-
return null;
|
|
913
|
-
}
|
|
592
|
+
/**
|
|
593
|
+
* Generate a mock value for a field based on its schema.
|
|
594
|
+
*/
|
|
595
|
+
generateFieldValue(entityName, field, index) {
|
|
596
|
+
if (!field.required && Math.random() > 0.8) {
|
|
597
|
+
return void 0;
|
|
598
|
+
}
|
|
599
|
+
switch (field.type) {
|
|
600
|
+
case "string":
|
|
601
|
+
return this.generateStringValue(entityName, field, index);
|
|
602
|
+
case "number":
|
|
603
|
+
return faker.number.int({
|
|
604
|
+
min: field.min ?? 0,
|
|
605
|
+
max: field.max ?? 1e3
|
|
606
|
+
});
|
|
607
|
+
case "boolean":
|
|
608
|
+
return faker.datatype.boolean();
|
|
609
|
+
case "date":
|
|
610
|
+
return this.generateDateValue(field);
|
|
611
|
+
case "enum":
|
|
612
|
+
if (field.enumValues && field.enumValues.length > 0) {
|
|
613
|
+
return faker.helpers.arrayElement(field.enumValues);
|
|
614
|
+
}
|
|
615
|
+
return null;
|
|
616
|
+
case "relation":
|
|
617
|
+
if (field.relatedEntity) {
|
|
618
|
+
const relatedStore = this.stores.get(field.relatedEntity.toLowerCase());
|
|
619
|
+
if (relatedStore && relatedStore.size > 0) {
|
|
620
|
+
const ids = Array.from(relatedStore.keys());
|
|
621
|
+
return faker.helpers.arrayElement(ids);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
return null;
|
|
625
|
+
case "array":
|
|
626
|
+
return [];
|
|
627
|
+
default:
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
914
630
|
}
|
|
915
631
|
/**
|
|
916
632
|
* Generate a string value based on field name heuristics.
|
|
@@ -1349,128 +1065,429 @@ var FirebaseDataService = class {
|
|
|
1349
1065
|
const data = items.slice(startIndex, startIndex + pageSize);
|
|
1350
1066
|
return { data, total, page, pageSize, totalPages };
|
|
1351
1067
|
}
|
|
1352
|
-
async getById(collection, id) {
|
|
1353
|
-
const doc = await db.collection(collection).doc(id).get();
|
|
1354
|
-
if (!doc.exists) {
|
|
1355
|
-
return null;
|
|
1356
|
-
}
|
|
1357
|
-
return { id: doc.id, ...doc.data() };
|
|
1068
|
+
async getById(collection, id) {
|
|
1069
|
+
const doc = await db.collection(collection).doc(id).get();
|
|
1070
|
+
if (!doc.exists) {
|
|
1071
|
+
return null;
|
|
1072
|
+
}
|
|
1073
|
+
return { id: doc.id, ...doc.data() };
|
|
1074
|
+
}
|
|
1075
|
+
async create(collection, data) {
|
|
1076
|
+
const now = /* @__PURE__ */ new Date();
|
|
1077
|
+
const docRef = await db.collection(collection).add({
|
|
1078
|
+
...data,
|
|
1079
|
+
createdAt: now,
|
|
1080
|
+
updatedAt: now
|
|
1081
|
+
});
|
|
1082
|
+
return {
|
|
1083
|
+
...data,
|
|
1084
|
+
id: docRef.id,
|
|
1085
|
+
createdAt: now,
|
|
1086
|
+
updatedAt: now
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
async update(collection, id, data) {
|
|
1090
|
+
const docRef = db.collection(collection).doc(id);
|
|
1091
|
+
const doc = await docRef.get();
|
|
1092
|
+
if (!doc.exists) {
|
|
1093
|
+
return null;
|
|
1094
|
+
}
|
|
1095
|
+
const now = /* @__PURE__ */ new Date();
|
|
1096
|
+
await docRef.update({
|
|
1097
|
+
...data,
|
|
1098
|
+
updatedAt: now
|
|
1099
|
+
});
|
|
1100
|
+
return {
|
|
1101
|
+
...doc.data(),
|
|
1102
|
+
...data,
|
|
1103
|
+
id,
|
|
1104
|
+
updatedAt: now
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
async delete(collection, id) {
|
|
1108
|
+
const docRef = db.collection(collection).doc(id);
|
|
1109
|
+
const doc = await docRef.get();
|
|
1110
|
+
if (!doc.exists) {
|
|
1111
|
+
return false;
|
|
1112
|
+
}
|
|
1113
|
+
await docRef.delete();
|
|
1114
|
+
return true;
|
|
1115
|
+
}
|
|
1116
|
+
async query(collection, filters) {
|
|
1117
|
+
let query = db.collection(collection);
|
|
1118
|
+
const memoryFilters = [];
|
|
1119
|
+
for (const filter of filters) {
|
|
1120
|
+
if (["==", "!=", "<", "<=", ">", ">=", "in", "not-in"].includes(filter.op)) {
|
|
1121
|
+
query = query.where(filter.field, filter.op, filter.value);
|
|
1122
|
+
} else {
|
|
1123
|
+
memoryFilters.push(filter);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
const snapshot = await query.get();
|
|
1127
|
+
let items = snapshot.docs.map((doc) => ({
|
|
1128
|
+
id: doc.id,
|
|
1129
|
+
...doc.data()
|
|
1130
|
+
}));
|
|
1131
|
+
for (const filter of memoryFilters) {
|
|
1132
|
+
items = items.filter((item) => {
|
|
1133
|
+
const value = item[filter.field];
|
|
1134
|
+
return applyFilterCondition(value, filter.op, filter.value);
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
return items;
|
|
1138
|
+
}
|
|
1139
|
+
getStore(collection) {
|
|
1140
|
+
const svc = this;
|
|
1141
|
+
return {
|
|
1142
|
+
async getById(id) {
|
|
1143
|
+
return svc.getById(collection, id);
|
|
1144
|
+
},
|
|
1145
|
+
async create(data) {
|
|
1146
|
+
return svc.create(collection, data);
|
|
1147
|
+
},
|
|
1148
|
+
async update(id, data) {
|
|
1149
|
+
const result = await svc.update(collection, id, data);
|
|
1150
|
+
if (!result) throw new Error(`Entity ${id} not found in ${collection}`);
|
|
1151
|
+
return result;
|
|
1152
|
+
},
|
|
1153
|
+
async delete(id) {
|
|
1154
|
+
await svc.delete(collection, id);
|
|
1155
|
+
},
|
|
1156
|
+
async query(filters) {
|
|
1157
|
+
return svc.query(collection, filters);
|
|
1158
|
+
}
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
};
|
|
1162
|
+
function createDataService() {
|
|
1163
|
+
if (env.USE_MOCK_DATA) {
|
|
1164
|
+
logger.info("[DataService] Using MockDataService");
|
|
1165
|
+
return new MockDataServiceAdapter();
|
|
1166
|
+
}
|
|
1167
|
+
logger.info("[DataService] Using FirebaseDataService");
|
|
1168
|
+
return new FirebaseDataService();
|
|
1169
|
+
}
|
|
1170
|
+
var _dataService = null;
|
|
1171
|
+
function getDataService() {
|
|
1172
|
+
if (!_dataService) {
|
|
1173
|
+
_dataService = createDataService();
|
|
1174
|
+
}
|
|
1175
|
+
return _dataService;
|
|
1176
|
+
}
|
|
1177
|
+
function resetDataService() {
|
|
1178
|
+
_dataService = null;
|
|
1179
|
+
}
|
|
1180
|
+
function seedMockData(entities) {
|
|
1181
|
+
if (!env.USE_MOCK_DATA) {
|
|
1182
|
+
logger.info("[DataService] Mock mode disabled, skipping seed");
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
logger.info("[DataService] Seeding mock data...");
|
|
1186
|
+
for (const entity of entities) {
|
|
1187
|
+
getMockDataService().seed(entity.name, entity.fields, entity.seedCount);
|
|
1188
|
+
}
|
|
1189
|
+
logger.info("[DataService] Mock data seeding complete");
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
// src/lib/debugRouter.ts
|
|
1193
|
+
function debugEventsRouter() {
|
|
1194
|
+
const router2 = Router();
|
|
1195
|
+
if (process.env.NODE_ENV !== "development") {
|
|
1196
|
+
return router2;
|
|
1197
|
+
}
|
|
1198
|
+
router2.get("/event-log", (_req, res) => {
|
|
1199
|
+
const limit = parseInt(String(_req.query.limit) || "50", 10);
|
|
1200
|
+
const events = getServerEventBus().getRecentEvents(limit);
|
|
1201
|
+
res.json({ count: events.length, events });
|
|
1202
|
+
});
|
|
1203
|
+
router2.delete("/event-log", (_req, res) => {
|
|
1204
|
+
getServerEventBus().clearEventLog();
|
|
1205
|
+
res.json({ cleared: true });
|
|
1206
|
+
});
|
|
1207
|
+
router2.get("/listeners", (_req, res) => {
|
|
1208
|
+
const counts = getServerEventBus().getListenerCounts();
|
|
1209
|
+
const total = Object.values(counts).reduce((sum, n) => sum + n, 0);
|
|
1210
|
+
res.json({ total, events: counts });
|
|
1211
|
+
});
|
|
1212
|
+
router2.post("/seed", (req, res) => {
|
|
1213
|
+
const { entities } = req.body;
|
|
1214
|
+
if (!entities || !Array.isArray(entities)) {
|
|
1215
|
+
res.status(400).json({ error: 'Body must have "entities" array' });
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
const configs = entities.map((e) => ({
|
|
1219
|
+
name: e.name,
|
|
1220
|
+
fields: e.fields,
|
|
1221
|
+
seedCount: e.seedCount ?? 5
|
|
1222
|
+
}));
|
|
1223
|
+
seedMockData(configs);
|
|
1224
|
+
const summary = configs.map((c) => `${c.name}(${c.seedCount})`).join(", ");
|
|
1225
|
+
res.json({ seeded: true, summary });
|
|
1226
|
+
});
|
|
1227
|
+
return router2;
|
|
1228
|
+
}
|
|
1229
|
+
var wss = null;
|
|
1230
|
+
function setupEventBroadcast(server, path = "/ws/events") {
|
|
1231
|
+
if (wss) {
|
|
1232
|
+
logger.warn("[WebSocket] Server already initialized");
|
|
1233
|
+
return wss;
|
|
1234
|
+
}
|
|
1235
|
+
wss = new WebSocketServer({ server, path });
|
|
1236
|
+
logger.info(`[WebSocket] Server listening at ${path}`);
|
|
1237
|
+
wss.on("connection", (ws, req) => {
|
|
1238
|
+
const clientId = req.headers["sec-websocket-key"] || "unknown";
|
|
1239
|
+
logger.debug(`[WebSocket] Client connected: ${clientId}`);
|
|
1240
|
+
ws.send(
|
|
1241
|
+
JSON.stringify({
|
|
1242
|
+
type: "CONNECTED",
|
|
1243
|
+
timestamp: Date.now(),
|
|
1244
|
+
message: "Connected to event stream"
|
|
1245
|
+
})
|
|
1246
|
+
);
|
|
1247
|
+
ws.on("message", (data) => {
|
|
1248
|
+
try {
|
|
1249
|
+
const message = JSON.parse(data.toString());
|
|
1250
|
+
logger.debug(`[WebSocket] Received from ${clientId}:`, message);
|
|
1251
|
+
if (message.type && message.payload) {
|
|
1252
|
+
getServerEventBus().emit(message.type, message.payload, {
|
|
1253
|
+
orbital: "client",
|
|
1254
|
+
entity: clientId
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
} catch (error) {
|
|
1258
|
+
logger.error(`[WebSocket] Failed to parse message:`, error);
|
|
1259
|
+
}
|
|
1260
|
+
});
|
|
1261
|
+
ws.on("close", () => {
|
|
1262
|
+
logger.debug(`[WebSocket] Client disconnected: ${clientId}`);
|
|
1263
|
+
});
|
|
1264
|
+
ws.on("error", (error) => {
|
|
1265
|
+
logger.error(`[WebSocket] Client error:`, error);
|
|
1266
|
+
});
|
|
1267
|
+
});
|
|
1268
|
+
getServerEventBus().on("*", (event) => {
|
|
1269
|
+
if (!wss) return;
|
|
1270
|
+
const typedEvent = event;
|
|
1271
|
+
const message = JSON.stringify({
|
|
1272
|
+
type: typedEvent.type,
|
|
1273
|
+
payload: typedEvent.payload,
|
|
1274
|
+
timestamp: typedEvent.timestamp,
|
|
1275
|
+
source: typedEvent.source
|
|
1276
|
+
});
|
|
1277
|
+
let broadcastCount = 0;
|
|
1278
|
+
wss.clients.forEach((client) => {
|
|
1279
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
1280
|
+
client.send(message);
|
|
1281
|
+
broadcastCount++;
|
|
1282
|
+
}
|
|
1283
|
+
});
|
|
1284
|
+
if (broadcastCount > 0) {
|
|
1285
|
+
logger.debug(`[WebSocket] Broadcast ${typedEvent.type} to ${broadcastCount} client(s)`);
|
|
1286
|
+
}
|
|
1287
|
+
});
|
|
1288
|
+
return wss;
|
|
1289
|
+
}
|
|
1290
|
+
function getWebSocketServer() {
|
|
1291
|
+
return wss;
|
|
1292
|
+
}
|
|
1293
|
+
function closeWebSocketServer() {
|
|
1294
|
+
return new Promise((resolve, reject) => {
|
|
1295
|
+
if (!wss) {
|
|
1296
|
+
resolve();
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
wss.close((err) => {
|
|
1300
|
+
if (err) {
|
|
1301
|
+
reject(err);
|
|
1302
|
+
} else {
|
|
1303
|
+
wss = null;
|
|
1304
|
+
resolve();
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
function getConnectedClientCount() {
|
|
1310
|
+
if (!wss) return 0;
|
|
1311
|
+
return wss.clients.size;
|
|
1312
|
+
}
|
|
1313
|
+
var AppError = class extends Error {
|
|
1314
|
+
constructor(statusCode, message, code) {
|
|
1315
|
+
super(message);
|
|
1316
|
+
this.statusCode = statusCode;
|
|
1317
|
+
this.message = message;
|
|
1318
|
+
this.code = code;
|
|
1319
|
+
this.name = "AppError";
|
|
1320
|
+
}
|
|
1321
|
+
};
|
|
1322
|
+
var NotFoundError = class extends AppError {
|
|
1323
|
+
constructor(message = "Resource not found") {
|
|
1324
|
+
super(404, message, "NOT_FOUND");
|
|
1325
|
+
}
|
|
1326
|
+
};
|
|
1327
|
+
var ValidationError = class extends AppError {
|
|
1328
|
+
constructor(message = "Validation failed") {
|
|
1329
|
+
super(400, message, "VALIDATION_ERROR");
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
var UnauthorizedError = class extends AppError {
|
|
1333
|
+
constructor(message = "Unauthorized") {
|
|
1334
|
+
super(401, message, "UNAUTHORIZED");
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
var ForbiddenError = class extends AppError {
|
|
1338
|
+
constructor(message = "Forbidden") {
|
|
1339
|
+
super(403, message, "FORBIDDEN");
|
|
1340
|
+
}
|
|
1341
|
+
};
|
|
1342
|
+
var ConflictError = class extends AppError {
|
|
1343
|
+
constructor(message = "Resource conflict") {
|
|
1344
|
+
super(409, message, "CONFLICT");
|
|
1358
1345
|
}
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1346
|
+
};
|
|
1347
|
+
var errorHandler = (err, _req, res, _next) => {
|
|
1348
|
+
logger.error("Error:", { name: err.name, message: err.message, stack: err.stack });
|
|
1349
|
+
if (err instanceof ZodError) {
|
|
1350
|
+
res.status(400).json({
|
|
1351
|
+
success: false,
|
|
1352
|
+
error: "Validation failed",
|
|
1353
|
+
code: "VALIDATION_ERROR",
|
|
1354
|
+
details: err.errors.map((e) => ({
|
|
1355
|
+
path: e.path.join("."),
|
|
1356
|
+
message: e.message
|
|
1357
|
+
}))
|
|
1365
1358
|
});
|
|
1366
|
-
return
|
|
1367
|
-
...data,
|
|
1368
|
-
id: docRef.id,
|
|
1369
|
-
createdAt: now,
|
|
1370
|
-
updatedAt: now
|
|
1371
|
-
};
|
|
1359
|
+
return;
|
|
1372
1360
|
}
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
}
|
|
1379
|
-
const now = /* @__PURE__ */ new Date();
|
|
1380
|
-
await docRef.update({
|
|
1381
|
-
...data,
|
|
1382
|
-
updatedAt: now
|
|
1361
|
+
if (err instanceof AppError) {
|
|
1362
|
+
res.status(err.statusCode).json({
|
|
1363
|
+
success: false,
|
|
1364
|
+
error: err.message,
|
|
1365
|
+
code: err.code
|
|
1383
1366
|
});
|
|
1384
|
-
return
|
|
1385
|
-
...doc.data(),
|
|
1386
|
-
...data,
|
|
1387
|
-
id,
|
|
1388
|
-
updatedAt: now
|
|
1389
|
-
};
|
|
1367
|
+
return;
|
|
1390
1368
|
}
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
|
-
return true;
|
|
1369
|
+
if (err.name === "FirebaseError" || err.name === "FirestoreError") {
|
|
1370
|
+
res.status(500).json({
|
|
1371
|
+
success: false,
|
|
1372
|
+
error: "Database error",
|
|
1373
|
+
code: "DATABASE_ERROR"
|
|
1374
|
+
});
|
|
1375
|
+
return;
|
|
1399
1376
|
}
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1377
|
+
res.status(500).json({
|
|
1378
|
+
success: false,
|
|
1379
|
+
error: "Internal server error",
|
|
1380
|
+
code: "INTERNAL_ERROR"
|
|
1381
|
+
});
|
|
1382
|
+
};
|
|
1383
|
+
var asyncHandler = (fn) => (req, res, next) => {
|
|
1384
|
+
Promise.resolve(fn(req, res, next)).catch(next);
|
|
1385
|
+
};
|
|
1386
|
+
var notFoundHandler = (req, res) => {
|
|
1387
|
+
res.status(404).json({
|
|
1388
|
+
success: false,
|
|
1389
|
+
error: `Route ${req.method} ${req.path} not found`,
|
|
1390
|
+
code: "ROUTE_NOT_FOUND"
|
|
1391
|
+
});
|
|
1392
|
+
};
|
|
1393
|
+
var validateBody = (schema) => async (req, res, next) => {
|
|
1394
|
+
try {
|
|
1395
|
+
req.body = await schema.parseAsync(req.body);
|
|
1396
|
+
next();
|
|
1397
|
+
} catch (error) {
|
|
1398
|
+
if (error instanceof ZodError) {
|
|
1399
|
+
res.status(400).json({
|
|
1400
|
+
success: false,
|
|
1401
|
+
error: "Validation failed",
|
|
1402
|
+
code: "VALIDATION_ERROR",
|
|
1403
|
+
details: error.errors.map((e) => ({
|
|
1404
|
+
path: e.path.join("."),
|
|
1405
|
+
message: e.message
|
|
1406
|
+
}))
|
|
1419
1407
|
});
|
|
1408
|
+
return;
|
|
1420
1409
|
}
|
|
1421
|
-
|
|
1410
|
+
next(error);
|
|
1422
1411
|
}
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
}
|
|
1443
|
-
};
|
|
1412
|
+
};
|
|
1413
|
+
var validateQuery = (schema) => async (req, res, next) => {
|
|
1414
|
+
try {
|
|
1415
|
+
req.query = await schema.parseAsync(req.query);
|
|
1416
|
+
next();
|
|
1417
|
+
} catch (error) {
|
|
1418
|
+
if (error instanceof ZodError) {
|
|
1419
|
+
res.status(400).json({
|
|
1420
|
+
success: false,
|
|
1421
|
+
error: "Invalid query parameters",
|
|
1422
|
+
code: "VALIDATION_ERROR",
|
|
1423
|
+
details: error.errors.map((e) => ({
|
|
1424
|
+
path: e.path.join("."),
|
|
1425
|
+
message: e.message
|
|
1426
|
+
}))
|
|
1427
|
+
});
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
next(error);
|
|
1444
1431
|
}
|
|
1445
1432
|
};
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1433
|
+
var validateParams = (schema) => async (req, res, next) => {
|
|
1434
|
+
try {
|
|
1435
|
+
req.params = await schema.parseAsync(req.params);
|
|
1436
|
+
next();
|
|
1437
|
+
} catch (error) {
|
|
1438
|
+
if (error instanceof ZodError) {
|
|
1439
|
+
res.status(400).json({
|
|
1440
|
+
success: false,
|
|
1441
|
+
error: "Invalid path parameters",
|
|
1442
|
+
code: "VALIDATION_ERROR",
|
|
1443
|
+
details: error.errors.map((e) => ({
|
|
1444
|
+
path: e.path.join("."),
|
|
1445
|
+
message: e.message
|
|
1446
|
+
}))
|
|
1447
|
+
});
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
next(error);
|
|
1450
1451
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
var
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1452
|
+
};
|
|
1453
|
+
|
|
1454
|
+
// src/middleware/authenticateFirebase.ts
|
|
1455
|
+
var BEARER_PREFIX = "Bearer ";
|
|
1456
|
+
var DEV_USER = {
|
|
1457
|
+
uid: "dev-user-001",
|
|
1458
|
+
email: "dev@localhost",
|
|
1459
|
+
email_verified: true,
|
|
1460
|
+
aud: "dev-project",
|
|
1461
|
+
auth_time: Math.floor(Date.now() / 1e3),
|
|
1462
|
+
exp: Math.floor(Date.now() / 1e3) + 3600,
|
|
1463
|
+
iat: Math.floor(Date.now() / 1e3),
|
|
1464
|
+
iss: "https://securetoken.google.com/dev-project",
|
|
1465
|
+
sub: "dev-user-001",
|
|
1466
|
+
firebase: {
|
|
1467
|
+
identities: {},
|
|
1468
|
+
sign_in_provider: "custom"
|
|
1458
1469
|
}
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
logger.info("[DataService] Mock mode disabled, skipping seed");
|
|
1467
|
-
return;
|
|
1470
|
+
};
|
|
1471
|
+
async function authenticateFirebase(req, res, next) {
|
|
1472
|
+
const authorization = req.headers.authorization;
|
|
1473
|
+
if (env.NODE_ENV === "development" && (!authorization || !authorization.startsWith(BEARER_PREFIX))) {
|
|
1474
|
+
req.firebaseUser = DEV_USER;
|
|
1475
|
+
res.locals.firebaseUser = DEV_USER;
|
|
1476
|
+
return next();
|
|
1468
1477
|
}
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1478
|
+
try {
|
|
1479
|
+
if (!authorization || !authorization.startsWith(BEARER_PREFIX)) {
|
|
1480
|
+
return res.status(401).json({ error: "Authorization header missing or malformed" });
|
|
1481
|
+
}
|
|
1482
|
+
const token = authorization.slice(BEARER_PREFIX.length);
|
|
1483
|
+
const decodedToken = await getAuth().verifyIdToken(token);
|
|
1484
|
+
req.firebaseUser = decodedToken;
|
|
1485
|
+
res.locals.firebaseUser = decodedToken;
|
|
1486
|
+
return next();
|
|
1487
|
+
} catch (error) {
|
|
1488
|
+
console.error("Firebase authentication failed:", error);
|
|
1489
|
+
return res.status(401).json({ error: "Unauthorized" });
|
|
1472
1490
|
}
|
|
1473
|
-
logger.info("[DataService] Mock data seeding complete");
|
|
1474
1491
|
}
|
|
1475
1492
|
|
|
1476
1493
|
// src/stores/firestoreFormat.ts
|
|
@@ -2371,6 +2388,23 @@ router.get("/active-sessions", async (req, res) => {
|
|
|
2371
2388
|
});
|
|
2372
2389
|
var observability_default = router;
|
|
2373
2390
|
|
|
2374
|
-
|
|
2391
|
+
// src/index.ts
|
|
2392
|
+
var dataService = new Proxy({}, {
|
|
2393
|
+
get(_target, prop, receiver) {
|
|
2394
|
+
return Reflect.get(getDataService(), prop, receiver);
|
|
2395
|
+
}
|
|
2396
|
+
});
|
|
2397
|
+
var mockDataService = new Proxy({}, {
|
|
2398
|
+
get(_target, prop, receiver) {
|
|
2399
|
+
return Reflect.get(getMockDataService(), prop, receiver);
|
|
2400
|
+
}
|
|
2401
|
+
});
|
|
2402
|
+
var serverEventBus = new Proxy({}, {
|
|
2403
|
+
get(_target, prop, receiver) {
|
|
2404
|
+
return Reflect.get(getServerEventBus(), prop, receiver);
|
|
2405
|
+
}
|
|
2406
|
+
});
|
|
2407
|
+
|
|
2408
|
+
export { AppError, ChangeSetStore, ConflictError, DistributedEventBus, EventBus, EventPersistence, ForbiddenError, InMemoryEventStore, InMemoryServiceRegistry, InMemoryTransport, MockDataService, NotFoundError, RedisTransport, SchemaProtectionService, SchemaStore, ServiceDiscovery, SnapshotStore, UnauthorizedError, ValidationError, ValidationStore, applyFiltersToQuery, asyncHandler, authenticateFirebase, closeWebSocketServer, createServerSkillAgent, dataService, db, debugEventsRouter, emitEntityEvent, env, errorHandler, extractPaginationParams, fromFirestoreFormat, getMemoryManager as getAgentMemoryManager, getSessionManager as getAgentSessionManager, getAuth, getConnectedClientCount, getDataService, getFirestore, getMemoryManager, getMockDataService, getServerEventBus, getSessionManager, getWebSocketServer, initializeFirebase, logger, mockDataService, multiUserMiddleware, notFoundHandler, observability_default as observabilityRouter, parseQueryFilters, resetDataService, resetMemoryManager, resetMockDataService, resetServerEventBus, resetSessionManager, seedMockData, serverEventBus, setupEventBroadcast, setupStateSyncWebSocket, toFirestoreFormat, validateBody, validateParams, validateQuery, verifyFirebaseAuth };
|
|
2375
2409
|
//# sourceMappingURL=index.js.map
|
|
2376
2410
|
//# sourceMappingURL=index.js.map
|