forked from Prompt-Hash-Stellar/prompt-hash
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsubmit.ts
More file actions
108 lines (92 loc) · 3.4 KB
/
Copy pathsubmit.ts
File metadata and controls
108 lines (92 loc) · 3.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/**
* Review Submission Endpoint
*
* Allows verified buyers to submit ratings and reviews for purchased prompts.
* Verifies wallet ownership and purchase access before accepting reviews.
* Prevents duplicate reviews from the same wallet for the same prompt.
*/
import { hasAccess, type PromptHashConfig } from "../../src/lib/stellar/promptHashClient";
import { addReview, hasUserReviewed } from "../../src/lib/reviews/reviewStore";
interface ReviewSubmission {
promptId: string;
userAddress: string;
rating: number;
text: string;
signature?: string;
}
function getServerConfig(): PromptHashConfig {
const rpcUrl = process.env.PUBLIC_STELLAR_RPC_URL ?? "https://soroban-testnet.stellar.org";
const networkPassphrase = process.env.PUBLIC_STELLAR_NETWORK_PASSPHRASE ?? "Test SDF Network ; September 2015";
const promptHashContractId = process.env.PUBLIC_PROMPT_HASH_CONTRACT_ID ?? "";
const nativeAssetContractId = process.env.PUBLIC_STELLAR_NATIVE_ASSET_CONTRACT_ID ?? "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC";
const simulationAccount = process.env.PUBLIC_STELLAR_SIMULATION_ACCOUNT ?? process.env.UNLOCK_PUBLIC_KEY ?? "";
return {
rpcUrl,
networkPassphrase,
promptHashContractId,
nativeAssetContractId,
simulationAccount,
allowHttp: new URL(rpcUrl).hostname === "localhost",
};
}
export default async function handler(req: any, res: any) {
if (req.method !== "POST") {
res.status(405).json({ error: "Method not allowed" });
return;
}
const { promptId, userAddress, rating, text }: ReviewSubmission = req.body ?? {};
// Validation
if (!promptId || !userAddress || rating === undefined || !text) {
res.status(400).json({ error: "Missing required fields" });
return;
}
if (rating < 1 || rating > 5) {
res.status(400).json({ error: "Rating must be between 1 and 5" });
return;
}
if (text.trim().length < 10) {
res.status(400).json({ error: "Review text must be at least 10 characters" });
return;
}
if (text.length > 500) {
res.status(400).json({ error: "Review text must not exceed 500 characters" });
return;
}
try {
// Check if user already reviewed this prompt before performing network RPC
if (hasUserReviewed(String(promptId), userAddress)) {
res.status(409).json({ error: "You have already reviewed this prompt" });
return;
}
// Verify user has purchased the prompt on-chain
const config = getServerConfig();
const access = await hasAccess(config, userAddress, promptId);
if (!access) {
res.status(403).json({
error: "Only verified buyers can submit reviews",
verified: false
});
return;
}
// Save review to review store
const review = addReview(String(promptId), userAddress, Number(rating), text);
console.log(`✓ Review submitted for prompt ${promptId} by ${userAddress.slice(0, 8)}...`);
res.status(201).json({
success: true,
review: {
id: review.id,
rating: review.rating,
createdAt: review.createdAt,
status: review.status,
},
});
} catch (error) {
const message = error instanceof Error ? error.message : "Failed to submit review";
console.error("Review submission error:", message);
if (message.includes("already reviewed")) {
res.status(409).json({ error: message });
} else {
res.status(500).json({ error: message });
}
}
}