Skip to content

Commit a8fd4f9

Browse files
committed
merge: main
2 parents 81ef468 + a096cf6 commit a8fd4f9

81 files changed

Lines changed: 1373 additions & 415 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

infrastructure/evault-core/src/core/db/db.service.ts

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,36 @@ export class DbService {
565565
{ id, ontology: meta.ontology, acl, eName },
566566
);
567567

568+
// Deduplicate envelopes — if multiple Envelope nodes share the
569+
// same ontology (field name), keep the first and delete the rest.
570+
// This prevents non-deterministic reads where collect(e) returns
571+
// duplicates in undefined order and reduce picks the wrong one.
572+
const seen = new Map<string, string>(); // ontology → kept envelope id
573+
const dupsToDelete: string[] = [];
574+
for (const env of existing.envelopes) {
575+
if (seen.has(env.ontology)) {
576+
dupsToDelete.push(env.id);
577+
} else {
578+
seen.set(env.ontology, env.id);
579+
}
580+
}
581+
if (dupsToDelete.length > 0) {
582+
console.warn(
583+
`[eVault] Cleaning ${dupsToDelete.length} duplicate envelope(s) for MetaEnvelope ${id}`,
584+
);
585+
for (const dupId of dupsToDelete) {
586+
await this.runQueryInternal(
587+
`MATCH (e:Envelope { id: $envelopeId }) DETACH DELETE e`,
588+
{ envelopeId: dupId },
589+
);
590+
}
591+
// Remove deleted dupes from the existing list so the update
592+
// loop below doesn't try to reference them.
593+
existing.envelopes = existing.envelopes.filter(
594+
(e) => !dupsToDelete.includes(e.id),
595+
);
596+
}
597+
568598
const createdEnvelopes: Envelope<T[keyof T]>[] = [];
569599
let counter = 0;
570600

@@ -601,21 +631,18 @@ export class DbService {
601631
valueType,
602632
});
603633
} else {
604-
// Create new envelope
634+
// Create new envelope — use MERGE on the relationship
635+
// + ontology to prevent duplicate Envelopes if two
636+
// concurrent updates race.
605637
const envW3id = await new W3IDBuilder().build();
606638
const envelopeId = envW3id.id;
607639

608640
await this.runQueryInternal(
609641
`
610642
MATCH (m:MetaEnvelope { id: $metaId, eName: $eName })
611-
CREATE (${alias}:Envelope {
612-
id: $${alias}_id,
613-
ontology: $${alias}_ontology,
614-
value: $${alias}_value,
615-
valueType: $${alias}_type
616-
})
617-
WITH m, ${alias}
618-
MERGE (m)-[:LINKS_TO]->(${alias})
643+
MERGE (m)-[:LINKS_TO]->(${alias}:Envelope { ontology: $${alias}_ontology })
644+
ON CREATE SET ${alias}.id = $${alias}_id, ${alias}.value = $${alias}_value, ${alias}.valueType = $${alias}_type
645+
ON MATCH SET ${alias}.value = $${alias}_value, ${alias}.valueType = $${alias}_type
619646
`,
620647
{
621648
metaId: id,

infrastructure/evault-core/src/core/protocol/graphql-server.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -374,12 +374,19 @@ export class GraphQLServer {
374374
context.eName,
375375
);
376376

377-
// Build the full metaEnvelope response
377+
// Build parsed from actual written envelopes, not input
378+
const parsedFromEnvelopes = result.envelopes.reduce(
379+
(acc: Record<string, any>, env: any) => {
380+
acc[env.ontology] = env.value;
381+
return acc;
382+
},
383+
{},
384+
);
378385
const metaEnvelope = {
379386
id: result.metaEnvelope.id,
380387
ontology: result.metaEnvelope.ontology,
381388
envelopes: result.envelopes,
382-
parsed: input.payload,
389+
parsed: parsedFromEnvelopes,
383390
};
384391

385392
// Deliver webhooks for create operation
@@ -508,12 +515,19 @@ export class GraphQLServer {
508515
context.eName,
509516
);
510517

511-
// Build the full metaEnvelope response
518+
// Build parsed from actual written envelopes, not input
519+
const parsedFromEnvelopes = result.envelopes.reduce(
520+
(acc: Record<string, any>, env: any) => {
521+
acc[env.ontology] = env.value;
522+
return acc;
523+
},
524+
{},
525+
);
512526
const metaEnvelope = {
513527
id: result.metaEnvelope.id,
514528
ontology: result.metaEnvelope.ontology,
515529
envelopes: result.envelopes,
516-
parsed: input.payload,
530+
parsed: parsedFromEnvelopes,
517531
};
518532

519533
// Deliver webhooks for update operation

platforms/blabsy/api/src/controllers/WebhookController.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ type Message = {
6868
isSystemMessage?: boolean; // Flag for system messages
6969
};
7070

71-
dotenv.config({ path: path.resolve(__dirname, "../../../../.env") });
71+
dotenv.config({ path: path.resolve(__dirname, "../../../../../.env") });
7272

7373
export const adapter = new Web3Adapter({
7474
schemasPath: path.resolve(__dirname, "../web3adapter/mappings/"),

platforms/blabsy/api/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as fs from "fs";
99
import { Web3Adapter } from "./web3adapter";
1010
import { WebhookController, adapter } from "./controllers/WebhookController";
1111

12-
config({ path: path.resolve(__dirname, "../../../.env") });
12+
config({ path: path.resolve(__dirname, "../../../../.env") });
1313

1414
const app = express();
1515
const port = process.env.PORT || 3000;

platforms/blabsy/api/src/web3adapter/watchers/firestoreWatcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { getFirestore } from "firebase-admin/firestore";
99
import path from "path";
1010
import dotenv from "dotenv";
1111
import { adapter } from "../../controllers/WebhookController";
12-
dotenv.config({ path: path.resolve(__dirname, "../../../../../.env") });
12+
dotenv.config({ path: path.resolve(__dirname, "../../../../../../.env") });
1313

1414
export class FirestoreWatcher {
1515
private unsubscribe: (() => void) | null = null;

platforms/calendar/client/next.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { readFileSync, existsSync } from "fs";
44

55
// Read NEXT_PUBLIC_CALENDAR_API_URL from monorepo root .env so client bundle gets it (Next loads project .env first, so process.env can be wrong)
66
function getRootEnv(name: string): string | undefined {
7-
const rootEnv = path.resolve(__dirname, "../../.env");
7+
const rootEnv = path.resolve(__dirname, "../../../.env");
88
if (!existsSync(rootEnv)) return undefined;
99
const content = readFileSync(rootEnv, "utf-8");
1010
const match = new RegExp(`^${name}\\s*=\\s*(.+)$`, "m").exec(content);

platforms/cerberus/client/src/database/data-source.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import path from "path";
99
import { UserEVaultMapping } from "./entities/UserEVaultMapping";
1010
import { VotingObservation } from "./entities/VotingObservation";
1111
import { CharterSignature } from "./entities/CharterSignature";
12+
import { Reference } from "./entities/Reference";
1213

13-
config({ path: path.resolve(__dirname, "../../../../.env") });
14+
config({ path: path.resolve(__dirname, "../../../../../.env") });
1415

1516
export const AppDataSource = new DataSource({
1617
type: "postgres",
@@ -25,6 +26,7 @@ export const AppDataSource = new DataSource({
2526
UserEVaultMapping,
2627
VotingObservation,
2728
CharterSignature,
29+
Reference,
2830
],
2931
migrations: [path.join(__dirname, "migrations", "*.ts")],
3032
subscribers: [PostgresSubscriber],
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from "typeorm";
2+
import { User } from "./User";
3+
4+
@Entity("references")
5+
export class Reference {
6+
@PrimaryGeneratedColumn("uuid")
7+
id!: string;
8+
9+
@Column()
10+
targetType!: string; // "user", "group", "platform"
11+
12+
@Column()
13+
targetId!: string;
14+
15+
@Column()
16+
targetName!: string;
17+
18+
@Column("text")
19+
content!: string;
20+
21+
@Column()
22+
referenceType!: string; // "general", "violation", etc.
23+
24+
@Column("int", { nullable: true })
25+
numericScore?: number; // 1-5 score
26+
27+
@Column()
28+
authorId!: string;
29+
30+
@ManyToOne(() => User)
31+
@JoinColumn({ name: "authorId" })
32+
author!: User;
33+
34+
@Column({ default: "signed" })
35+
status!: string; // "signed", "revoked"
36+
37+
@Column({ default: false })
38+
anonymous!: boolean;
39+
40+
@CreateDateColumn()
41+
createdAt!: Date;
42+
43+
@UpdateDateColumn()
44+
updatedAt!: Date;
45+
}

platforms/cerberus/client/src/index.ts

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -138,47 +138,19 @@ app.listen(port, () => {
138138
console.log(`Cerberus API running on port ${port}`);
139139
});
140140

141-
// Initialize Cerberus intervals and periodic check-ins for groups with charters
141+
// Initialize Cerberus intervals for groups with charters
142142
setTimeout(async () => {
143143
try {
144144
console.log("🐕 Starting Cerberus services...");
145-
145+
146146
// Import services after server is running
147-
const { CharterMonitoringService } = await import("./services/CharterMonitoringService");
148-
const { GroupService } = await import("./services/GroupService");
149147
const { CerberusIntervalService } = await import("./services/CerberusIntervalService");
150-
151-
const charterMonitoringService = new CharterMonitoringService();
152-
const groupService = new GroupService();
148+
153149
const intervalService = new CerberusIntervalService();
154-
150+
155151
// Initialize Cerberus intervals for all groups with charters
156152
await intervalService.initializeIntervals();
157-
158-
// Send periodic check-ins every 24 hours (separate from charter-based intervals)
159-
setInterval(async () => {
160-
try {
161-
const groups = await groupService.getAllGroups();
162-
const groupsWithCharters = groups.filter(group => group.charter && group.charter.trim() !== '');
163-
164-
console.log(`🐕 Sending periodic check-ins to ${groupsWithCharters.length} groups with charters...`);
165-
166-
for (const group of groupsWithCharters) {
167-
try {
168-
await charterMonitoringService.sendPeriodicCheckIn(group.id, group.name);
169-
// Add a small delay between messages to avoid overwhelming the system
170-
await new Promise(resolve => setTimeout(resolve, 1000));
171-
} catch (error) {
172-
console.error(`Error sending check-in to group ${group.name}:`, error);
173-
}
174-
}
175-
176-
console.log("✅ Periodic check-ins completed");
177-
} catch (error) {
178-
console.error("Error during periodic check-ins:", error);
179-
}
180-
}, 24 * 60 * 60 * 1000); // 24 hours
181-
153+
182154
console.log("✅ Cerberus services initialized");
183155

184156
// Graceful shutdown cleanup

0 commit comments

Comments
 (0)