Skip to content

Commit aec8be0

Browse files
Feat/ereputation adapter (#439)
* chore: move API * feat: erputation for platforms * feat: core eReputation logic done * feat: add filters * feat: better context handling on failovers * fix: synchronization issue * feat: charter-evoting integration * feat: add eReputation weight * feat: add messages * feat: marketplace * chore: fix build * chore: fix check * chore: format * Update platforms/eReputation-api/src/controllers/GroupController.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * chore: fix code rabbit suggestions * fix: drawer swipe and simplify app url * fix: check --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent 208acdc commit aec8be0

File tree

123 files changed

+10911
-23452
lines changed

Some content is hidden

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

123 files changed

+10911
-23452
lines changed

infrastructure/blindvote/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"author": "",
2222
"license": "ISC",
2323
"devDependencies": {
24+
"@types/eventsource": "^1.1.15",
2425
"@types/jest": "^29.0.0",
2526
"@types/node": "^20.0.0",
2627
"jest": "^29.0.0",

infrastructure/blindvote/pnpm-lock.yaml

Lines changed: 0 additions & 3563 deletions
This file was deleted.

infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.svelte

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
import { cn } from "$lib/utils";
33
import { CupertinoPane } from "cupertino-pane";
44
import type { Snippet } from "svelte";
5-
import { swipe } from "svelte-gestures";
5+
import { useSwipe } from "svelte-gestures";
6+
import type { SwipeCustomEvent } from "svelte-gestures";
67
import type { HTMLAttributes } from "svelte/elements";
78
89
interface IDrawerProps extends HTMLAttributes<HTMLDivElement> {
@@ -23,6 +24,24 @@ let {
2324
...restProps
2425
}: IDrawerProps = $props();
2526
27+
const handleDrawerSwipe = (event: SwipeCustomEvent) => {
28+
if (event.detail.direction === ("down" as string)) {
29+
handleSwipe?.(isPaneOpen);
30+
}
31+
};
32+
33+
const swipeResult = useSwipe(
34+
handleDrawerSwipe,
35+
() => ({
36+
timeframe: 300,
37+
minSwipeDistance: 60,
38+
}),
39+
undefined,
40+
true,
41+
);
42+
// biome-ignore lint/suspicious/noExplicitAny: svelte-gestures type definitions are incomplete
43+
const swipe = swipeResult.swipe as any;
44+
2645
// Disabled click outside behavior to prevent white screen issues
2746
// const handleClickOutside = () => {
2847
// pane?.destroy({ animate: true });
@@ -70,11 +89,7 @@ $effect(() => {
7089

7190
<div
7291
{...restProps}
73-
use:swipe={() => ({
74-
timeframe: 300,
75-
minSwipeDistance: 60,
76-
})}
77-
onswipe={() => handleSwipe?.(isPaneOpen)}
92+
use:swipe
7893
bind:this={drawerElem}
7994
class={cn(restProps.class)}
8095
>

infrastructure/w3id/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"devDependencies": {
2828
"@biomejs/biome": "^1.9.4",
2929
"@ngneat/falso": "^7.3.0",
30+
"@types/eventsource": "^1.1.15",
3031
"@types/node": "^22.13.10",
3132
"typescript": "^5.8.2",
3233
"vitest": "^3.0.9"

platforms/dreamSync/package.json

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,5 @@
9898
},
9999
"optionalDependencies": {
100100
"bufferutil": "^4.0.8"
101-
},
102-
"pnpm": {
103-
"overrides": {
104-
"react": "18.3.1",
105-
"react-dom": "18.3.1",
106-
"@types/react": "18.3.11",
107-
"@types/react-dom": "18.3.1"
108-
}
109101
}
110102
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "ereputation-api",
3+
"version": "1.0.0",
4+
"description": "eReputation Platform API",
5+
"main": "src/index.ts",
6+
"scripts": {
7+
"start": "ts-node --project tsconfig.json src/index.ts",
8+
"dev": "nodemon --exec \"npx ts-node\" src/index.ts",
9+
"build": "tsc",
10+
"typeorm": "typeorm-ts-node-commonjs",
11+
"migration:generate": "typeorm-ts-node-commonjs migration:generate -d src/database/data-source.ts",
12+
"migration:run": "typeorm-ts-node-commonjs migration:run -d src/database/data-source.ts",
13+
"migration:revert": "typeorm-ts-node-commonjs migration:revert -d src/database/data-source.ts"
14+
},
15+
"dependencies": {
16+
"axios": "^1.6.7",
17+
"bullmq": "^5.3.0",
18+
"cors": "^2.8.5",
19+
"dotenv": "^16.4.5",
20+
"express": "^4.18.2",
21+
"ioredis": "^5.3.2",
22+
"jsonwebtoken": "^9.0.2",
23+
"openai": "^4.20.1",
24+
"pg": "^8.11.3",
25+
"reflect-metadata": "^0.2.1",
26+
"typeorm": "^0.3.24",
27+
"uuid": "^9.0.1",
28+
"web3-adapter": "link:../../infrastructure/web3-adapter"
29+
},
30+
"devDependencies": {
31+
"@types/cors": "^2.8.17",
32+
"@types/express": "^4.17.21",
33+
"@types/jsonwebtoken": "^9.0.5",
34+
"@types/node": "^20.11.24",
35+
"@types/pg": "^8.11.2",
36+
"@types/uuid": "^9.0.8",
37+
"@typescript-eslint/eslint-plugin": "^7.0.1",
38+
"@typescript-eslint/parser": "^7.0.1",
39+
"eslint": "^8.56.0",
40+
"nodemon": "^3.0.3",
41+
"ts-node": "^10.9.2",
42+
"typescript": "^5.3.3"
43+
}
44+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { Request, Response } from "express";
2+
import { v4 as uuidv4 } from "uuid";
3+
import { UserService } from "../services/UserService";
4+
import { EventEmitter } from "events";
5+
import { signToken } from "../utils/jwt";
6+
7+
export class AuthController {
8+
private userService: UserService;
9+
private eventEmitter: EventEmitter;
10+
11+
constructor() {
12+
this.userService = new UserService();
13+
this.eventEmitter = new EventEmitter();
14+
}
15+
16+
sseStream = async (req: Request, res: Response) => {
17+
const { id } = req.params;
18+
19+
res.writeHead(200, {
20+
"Content-Type": "text/event-stream",
21+
"Cache-Control": "no-cache",
22+
Connection: "keep-alive",
23+
"Access-Control-Allow-Origin": "*",
24+
});
25+
26+
const handler = (data: any) => {
27+
res.write(`data: ${JSON.stringify(data)}\n\n`);
28+
};
29+
30+
this.eventEmitter.on(id, handler);
31+
32+
req.on("close", () => {
33+
this.eventEmitter.off(id, handler);
34+
res.end();
35+
});
36+
37+
req.on("error", (error) => {
38+
console.error("SSE Error:", error);
39+
this.eventEmitter.off(id, handler);
40+
res.end();
41+
});
42+
};
43+
44+
getOffer = async (req: Request, res: Response) => {
45+
const baseUrl = process.env.VITE_EREPUTATION_BASE_URL || "http://localhost:8765";
46+
const url = new URL(
47+
"/api/auth",
48+
baseUrl,
49+
).toString();
50+
const sessionId = uuidv4();
51+
const offer = `w3ds://auth?redirect=${url}&session=${sessionId}&platform=ereputation`;
52+
res.json({ offer, sessionId });
53+
};
54+
55+
login = async (req: Request, res: Response) => {
56+
try {
57+
const { ename, session, w3id, signature } = req.body;
58+
59+
if (!ename) {
60+
return res.status(400).json({ error: "ename is required" });
61+
}
62+
63+
if (!session) {
64+
return res.status(400).json({ error: "session is required" });
65+
}
66+
67+
// Only find existing users - don't create new ones during auth
68+
const user = await this.userService.findUser(ename);
69+
70+
if (!user) {
71+
// User doesn't exist - they need to be created via webhook first
72+
return res.status(404).json({
73+
error: "User not found",
74+
message: "User must be created via eVault webhook before authentication"
75+
});
76+
}
77+
78+
const token = signToken({ userId: user.id });
79+
80+
const data = {
81+
user: {
82+
id: user.id,
83+
ename: user.ename,
84+
name: user.name,
85+
handle: user.handle,
86+
description: user.description,
87+
avatarUrl: user.avatarUrl,
88+
bannerUrl: user.bannerUrl,
89+
isVerified: user.isVerified,
90+
isPrivate: user.isPrivate,
91+
email: user.email,
92+
emailVerified: user.emailVerified,
93+
createdAt: user.createdAt,
94+
updatedAt: user.updatedAt,
95+
},
96+
token,
97+
};
98+
this.eventEmitter.emit(session, data);
99+
res.status(200).send();
100+
} catch (error) {
101+
console.error("Error during login:", error);
102+
res.status(500).json({ error: "Internal server error" });
103+
}
104+
};
105+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { Request, Response } from "express";
2+
import { CalculationService } from "../services/CalculationService";
3+
import { authGuard } from "../middleware/auth";
4+
5+
export class CalculationController {
6+
private calculationService: CalculationService;
7+
8+
constructor() {
9+
this.calculationService = new CalculationService();
10+
}
11+
12+
calculateReputation = async (req: Request, res: Response) => {
13+
try {
14+
const { targetType, targetId, targetName, userValues } = req.body;
15+
const calculatorId = req.user!.id;
16+
17+
// Handle self-evaluation: use calculator's ID and name
18+
let finalTargetId = targetId;
19+
let finalTargetName = targetName;
20+
let finalTargetType = targetType;
21+
22+
if (targetType === "self") {
23+
finalTargetId = calculatorId;
24+
finalTargetName = req.user!.ename || req.user!.name || "Personal Profile";
25+
finalTargetType = "self";
26+
}
27+
28+
if (!finalTargetType || !finalTargetId || !finalTargetName || !userValues) {
29+
return res.status(400).json({ error: "Missing required fields" });
30+
}
31+
32+
// Create calculation record
33+
const calculation = await this.calculationService.createCalculation({
34+
targetType: finalTargetType,
35+
targetId: finalTargetId,
36+
targetName: finalTargetName,
37+
userValues,
38+
calculatorId
39+
});
40+
41+
try {
42+
// Calculate reputation synchronously
43+
const result = await this.calculationService.calculateReputation(calculation.id);
44+
45+
const details = result.calculationDetails ? JSON.parse(result.calculationDetails) : {};
46+
47+
res.json({
48+
score: result.calculatedScore?.toString() || "0",
49+
analysis: details.explanation || "No analysis available",
50+
targetName: result.targetName,
51+
calculationId: result.id
52+
});
53+
} catch (calcError) {
54+
// If calculation fails, the service already deleted the record
55+
// Just return an error response
56+
console.error("Error calculating reputation:", calcError);
57+
const errorMessage = calcError instanceof Error ? calcError.message : "Failed to calculate reputation";
58+
res.status(500).json({ error: errorMessage });
59+
}
60+
} catch (error) {
61+
console.error("Error calculating reputation:", error);
62+
res.status(500).json({ error: "Internal server error" });
63+
}
64+
};
65+
66+
getCalculationResult = async (req: Request, res: Response) => {
67+
try {
68+
const { calculationId } = req.params;
69+
const userId = req.user!.id;
70+
71+
const calculation = await this.calculationService.getCalculationById(calculationId);
72+
73+
if (!calculation) {
74+
return res.status(404).json({ error: "Calculation not found" });
75+
}
76+
77+
// Check if user is authorized to view this calculation
78+
if (calculation.calculatorId !== userId) {
79+
return res.status(403).json({ error: "Not authorized to view this calculation" });
80+
}
81+
82+
const details = calculation.calculationDetails ? JSON.parse(calculation.calculationDetails) : {};
83+
84+
res.json({
85+
id: calculation.id,
86+
targetType: calculation.targetType,
87+
targetName: calculation.targetName,
88+
userValues: calculation.userValues,
89+
calculatedScore: calculation.calculatedScore,
90+
status: calculation.status,
91+
details: details,
92+
createdAt: calculation.createdAt,
93+
updatedAt: calculation.updatedAt
94+
});
95+
} catch (error) {
96+
console.error("Error getting calculation result:", error);
97+
res.status(500).json({ error: "Internal server error" });
98+
}
99+
};
100+
101+
getUserCalculations = async (req: Request, res: Response) => {
102+
try {
103+
const userId = req.user!.id;
104+
const calculations = await this.calculationService.getUserCalculations(userId);
105+
106+
res.json({
107+
calculations: calculations.map(calc => {
108+
const details = calc.calculationDetails ? JSON.parse(calc.calculationDetails) : {};
109+
return {
110+
id: calc.id,
111+
targetType: calc.targetType,
112+
targetName: calc.targetName,
113+
calculatedScore: calc.calculatedScore,
114+
status: calc.status,
115+
details: details,
116+
createdAt: calc.createdAt,
117+
updatedAt: calc.updatedAt
118+
};
119+
})
120+
});
121+
} catch (error) {
122+
console.error("Error getting user calculations:", error);
123+
res.status(500).json({ error: "Internal server error" });
124+
}
125+
};
126+
}

0 commit comments

Comments
 (0)