-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathseed.ts
More file actions
201 lines (184 loc) · 8.24 KB
/
Copy pathseed.ts
File metadata and controls
201 lines (184 loc) · 8.24 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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/**
* Database seed script.
* Creates the default admin, one institution (FUTMINNA), one employer (FirstBank),
* and pre-seeded claim definitions (both manual and auto).
*
* Credentials (development only — change before any deployment):
* Admin: admin@veridaq.xyz / Admin2026!@#
* Institution: futminna@veridaq.xyz / Inst@2026!
* Employer: firstbank@veridaq.xyz / Emp@2026!
*
* Run with: pnpm db:seed
*/
import "dotenv/config"
import { PrismaClient } from "@prisma/client"
import bcryptjs from "bcryptjs"
import crypto from "crypto"
import { createCipheriv } from "crypto"
import { privateKeyToAccount } from "viem/accounts"
const prisma = new PrismaClient()
const COST = 12
const ALGORITHM = "aes-256-gcm"
const ENCRYPTION_KEY = Buffer.from(process.env.ENCRYPTION_KEY || "0".repeat(64), "hex")
function encryptValue(plaintext: string): { encryptedData: string; encryptedIv: string; encryptedTag: string } {
const iv = crypto.randomBytes(12)
const cipher = createCipheriv(ALGORITHM, ENCRYPTION_KEY, iv)
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()])
return {
encryptedData: encrypted.toString("hex"),
encryptedIv: iv.toString("hex"),
encryptedTag: cipher.getAuthTag().toString("hex"),
}
}
function bytes32FromName(name: string): string {
const hex = crypto.createHash("sha256").update(name).digest("hex")
return `0x${hex}` as `0x${string}`
}
async function main() {
console.info("Seeding database...")
// ── Admin ──────────────────────────────────────────────────────────────
const adminHash = await bcryptjs.hash("Admin2026!@#", COST)
await prisma.admin.upsert({
where: { email: "admin@veridaq.xyz" },
update: { passwordHash: adminHash },
create: {
email: "admin@veridaq.xyz",
passwordHash: adminHash,
name: "VERIDAQ ADMIN",
},
})
console.info("Seeded: admin@veridaq.xyz / Admin2026!@#")
// ── Institution: FUTMINNA ─────────────────────────────────────────────
const instPk = "0x" + crypto.randomBytes(32).toString("hex")
const instAccount = privateKeyToAccount(instPk as `0x${string}`)
const instId = "institution-futminna-001"
const instOnChainId = bytes32FromName("Federal University of Technology, Minna")
const adminPk = "0x" + crypto.randomBytes(32).toString("hex")
const adminKeyEnc = encryptValue(adminPk.slice(2))
const institutionKeyRaw = crypto.randomBytes(32).toString("hex")
const institutionKeyEnc = encryptValue(institutionKeyRaw)
const instHash = await bcryptjs.hash("Inst@2026!", COST)
const inst = await prisma.institution.upsert({
where: { email: "futminna@veridaq.xyz" },
update: {
passwordHash: instHash,
onChainId: instOnChainId,
adminWallet: instAccount.address,
adminKeyEncrypted: adminKeyEnc.encryptedData,
adminKeyIv: adminKeyEnc.encryptedIv,
adminKeyTag: adminKeyEnc.encryptedTag,
institutionKeyEncrypted: institutionKeyEnc.encryptedData,
institutionKeyIv: institutionKeyEnc.encryptedIv,
institutionKeyTag: institutionKeyEnc.encryptedTag,
},
create: {
id: instId,
onChainId: instOnChainId,
name: "Federal University of Technology, Minna",
email: "futminna@veridaq.xyz",
passwordHash: instHash,
publicKey: instAccount.address,
adminWallet: instAccount.address,
adminKeyEncrypted: adminKeyEnc.encryptedData,
adminKeyIv: adminKeyEnc.encryptedIv,
adminKeyTag: adminKeyEnc.encryptedTag,
institutionKeyEncrypted: institutionKeyEnc.encryptedData,
institutionKeyIv: institutionKeyEnc.encryptedIv,
institutionKeyTag: institutionKeyEnc.encryptedTag,
tier: "PAID",
kycApproved: true,
active: true,
blockchainStatus: "REGISTERED",
paymasterBalance: "50000000000000000", // 0.05 ETH
alsoEmployer: true,
},
})
console.info("Seeded: futminna@veridaq.xyz / Inst@2026! (PAID, KYC approved)")
// ── Employer profile for FUTMINNA (institution-as-employer) ────────────
const empPk = "0x" + crypto.randomBytes(32).toString("hex")
const empAccount = privateKeyToAccount(empPk as `0x${string}`)
const empId = "employer-futminna-001"
await prisma.employer.upsert({
where: { email: "futminna-employer@veridaq.xyz" },
update: {},
create: {
id: empId,
name: "FUTMINNA Admissions",
cacNumber: "RC-FUT-2024-001",
email: "futminna-employer@veridaq.xyz",
passwordHash: instHash, // same password as institution
walletAddress: empAccount.address,
kycApproved: true,
active: true,
freeVerificationsRemaining: 3,
verificationCredits: 10,
institutionId: instId,
},
})
console.info("Seeded: employer profile for FUTMINNA (institution-as-employer)")
// ── Employer: FirstBank ───────────────────────────────────────────────
const fbPk = "0x" + crypto.randomBytes(32).toString("hex")
const fbAccount = privateKeyToAccount(fbPk as `0x${string}`)
const fbHash = await bcryptjs.hash("Emp@2026!", COST)
await prisma.employer.upsert({
where: { email: "firstbank@veridaq.xyz" },
update: { passwordHash: fbHash },
create: {
name: "First Bank of Nigeria PLC",
cacNumber: "RC-FBN-1894-001",
email: "firstbank@veridaq.xyz",
passwordHash: fbHash,
walletAddress: fbAccount.address,
kycApproved: true,
active: true,
freeVerificationsRemaining: 3,
verificationCredits: 50,
},
})
console.info("Seeded: firstbank@veridaq.xyz / Emp@2026! (KYC approved, 50 credits)")
// ── Claim Definitions (pre-seeded for FUTMINNA) ───────────────────────
// These map 1:1 to the 6 circuit claim types in credential.circom:
// T=1: Graduated — valid year 1960-2030
// T=2: Minimum Second Class Lower — classification >= 2
// T=3: Minimum Second Class Upper — classification >= 3
// T=4: First Class — classification == 4
// T=5: CGPA Above Threshold — employer specifies threshold manually
// T=6: Programme Completion — course + year, manual institution review
const claimDefs = [
{ label: "Graduated", claimCode: 1, threshold: 0, reviewType: "AUTO" as const, description: "Verify the student graduated in a valid year (1960–2030)" },
{ label: "Minimum Second Class Lower", claimCode: 2, threshold: 0, reviewType: "AUTO" as const, description: "Verify the student achieved at least Second Class Lower (classification ≥ 2)" },
{ label: "Minimum Second Class Upper", claimCode: 3, threshold: 0, reviewType: "AUTO" as const, description: "Verify the student achieved at least Second Class Upper (classification ≥ 3)" },
{ label: "First Class", claimCode: 4, threshold: 0, reviewType: "AUTO" as const, description: "Verify the student achieved First Class (classification == 4)" },
{ label: "CGPA Above Threshold", claimCode: 5, threshold: 350, reviewType: "AUTO" as const, description: "Verify CGPA meets a minimum threshold (employer sets the value)" },
{ label: "Programme Completion", claimCode: 6, threshold: 0, reviewType: "MANUAL" as const, description: "Institution confirms the student completed a specific programme" },
]
for (const cd of claimDefs) {
await prisma.claimDefinition.upsert({
where: {
institutionId_claimCode_threshold: {
institutionId: instId,
claimCode: cd.claimCode,
threshold: cd.threshold,
},
},
update: { label: cd.label, reviewType: cd.reviewType, description: cd.description },
create: {
institutionId: instId,
label: cd.label,
claimCode: cd.claimCode,
threshold: cd.threshold,
reviewType: cd.reviewType,
description: cd.description,
active: true,
},
})
}
console.info(`Seeded: ${claimDefs.length} claim definitions`)
console.info("Seed complete.")
}
main()
.catch((err) => {
console.error("Seed failed:", err)
process.exit(1)
})
.finally(() => prisma.$disconnect())