Migrate KYC backend from Django to Express + Apollo Server + Prisma
All checks were successful
Build Docker Image / build (push) Successful in 2m12s
All checks were successful
Build Docker Image / build (push) Successful in 2m12s
Replace Python/Django/Graphene with TypeScript/Express/Apollo Server. Same 3 endpoints (public/user/m2m), same JWT auth via Logto. Prisma replaces Django ORM. MongoDB, Temporal and SurrealDB integrations preserved.
This commit is contained in:
65
src/services/mongodb.ts
Normal file
65
src/services/mongodb.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { MongoClient } from 'mongodb'
|
||||
|
||||
const MONGODB_URI = process.env.MONGODB_URI || ''
|
||||
const MONGODB_DB = process.env.MONGODB_DB || 'kyc'
|
||||
|
||||
export async function getCompanyDocuments(inn: string) {
|
||||
if (!MONGODB_URI) return []
|
||||
const client = new MongoClient(MONGODB_URI)
|
||||
try {
|
||||
await client.connect()
|
||||
const db = client.db(MONGODB_DB)
|
||||
return await db.collection('company_documents').find({ inn }).toArray()
|
||||
} finally {
|
||||
await client.close()
|
||||
}
|
||||
}
|
||||
|
||||
interface CompanySummary {
|
||||
inn?: string
|
||||
ogrn?: string
|
||||
name?: string
|
||||
companyType?: string
|
||||
registrationYear?: number
|
||||
isActive: boolean
|
||||
address?: string
|
||||
director?: string
|
||||
capital?: string
|
||||
activities: string[]
|
||||
sources: string[]
|
||||
lastUpdated?: string
|
||||
}
|
||||
|
||||
export function aggregateCompanyData(documents: Record<string, unknown>[]): CompanySummary {
|
||||
const summary: CompanySummary = {
|
||||
isActive: true,
|
||||
activities: [],
|
||||
sources: [],
|
||||
}
|
||||
|
||||
for (const doc of documents) {
|
||||
const source = (doc.source as string) || 'unknown'
|
||||
summary.sources.push(source)
|
||||
|
||||
const data = (doc.data as Record<string, unknown>) || {}
|
||||
|
||||
if (!summary.inn) summary.inn = doc.inn as string
|
||||
if (!summary.ogrn && data.ogrn) summary.ogrn = data.ogrn as string
|
||||
if (!summary.name && data.name) summary.name = data.name as string
|
||||
|
||||
if (!summary.companyType && summary.name) {
|
||||
const name = summary.name.toUpperCase()
|
||||
if (name.includes('ООО') || name.includes('ОБЩЕСТВО С ОГРАНИЧЕННОЙ')) summary.companyType = 'ООО'
|
||||
else if (name.includes('ПАО')) summary.companyType = 'ПАО'
|
||||
else if (name.includes('АО') || name.includes('АКЦИОНЕРНОЕ ОБЩЕСТВО')) summary.companyType = 'АО'
|
||||
else if (name.includes('ИП') || name.includes('ИНДИВИДУАЛЬНЫЙ ПРЕДПРИНИМАТЕЛЬ')) summary.companyType = 'ИП'
|
||||
}
|
||||
|
||||
const collectedAt = doc.collected_at as string | undefined
|
||||
if (collectedAt && (!summary.lastUpdated || collectedAt > summary.lastUpdated)) {
|
||||
summary.lastUpdated = collectedAt
|
||||
}
|
||||
}
|
||||
|
||||
return summary
|
||||
}
|
||||
47
src/services/surrealdb.ts
Normal file
47
src/services/surrealdb.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
const SURREALDB_URL = process.env.SURREALDB_URL || ''
|
||||
const SURREALDB_NS = process.env.SURREALDB_NS || 'optovia'
|
||||
const SURREALDB_DB = process.env.SURREALDB_DB || 'events'
|
||||
const SURREALDB_USER = process.env.SURREALDB_USER || ''
|
||||
const SURREALDB_PASS = process.env.SURREALDB_PASS || ''
|
||||
|
||||
export async function logKycEvent(
|
||||
kycId: string,
|
||||
userId: string,
|
||||
event: string,
|
||||
description: string,
|
||||
): Promise<boolean> {
|
||||
if (!SURREALDB_URL) return false
|
||||
|
||||
const payload = {
|
||||
kyc_id: kycId,
|
||||
user_id: userId,
|
||||
event,
|
||||
description,
|
||||
created_at: new Date().toISOString(),
|
||||
}
|
||||
|
||||
const query = `CREATE kyc_event CONTENT ${JSON.stringify(payload)};`
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'text/plain',
|
||||
Accept: 'application/json',
|
||||
NS: SURREALDB_NS,
|
||||
DB: SURREALDB_DB,
|
||||
}
|
||||
|
||||
if (SURREALDB_USER && SURREALDB_PASS) {
|
||||
headers.Authorization = `Basic ${Buffer.from(`${SURREALDB_USER}:${SURREALDB_PASS}`).toString('base64')}`
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`${SURREALDB_URL.replace(/\/$/, '')}/sql`, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: query,
|
||||
signal: AbortSignal.timeout(10000),
|
||||
})
|
||||
return res.ok
|
||||
} catch (e) {
|
||||
console.error('Failed to log KYC event:', e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
28
src/services/temporal.ts
Normal file
28
src/services/temporal.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Client, Connection } from '@temporalio/client'
|
||||
|
||||
const TEMPORAL_HOST = process.env.TEMPORAL_HOST || 'temporal:7233'
|
||||
const TEMPORAL_NAMESPACE = process.env.TEMPORAL_NAMESPACE || 'default'
|
||||
const TEMPORAL_TASK_QUEUE = process.env.TEMPORAL_TASK_QUEUE || 'kyc-task-queue'
|
||||
|
||||
interface KycWorkflowData {
|
||||
kyc_request_id: string
|
||||
team_name: string
|
||||
owner_id: string
|
||||
owner_email: string
|
||||
country_code: string
|
||||
country_data: Record<string, string>
|
||||
}
|
||||
|
||||
export async function startKycWorkflow(data: KycWorkflowData): Promise<string> {
|
||||
const connection = await Connection.connect({ address: TEMPORAL_HOST })
|
||||
const client = new Client({ connection, namespace: TEMPORAL_NAMESPACE })
|
||||
|
||||
const handle = await client.workflow.start('kyc_application', {
|
||||
args: [data],
|
||||
taskQueue: TEMPORAL_TASK_QUEUE,
|
||||
workflowId: data.kyc_request_id,
|
||||
})
|
||||
|
||||
console.log(`KYC workflow started: ${handle.workflowId}`)
|
||||
return handle.workflowId
|
||||
}
|
||||
Reference in New Issue
Block a user