返回 AiToEarn
relay-client.service.ts
根目录 / project / aitoearn-backend / apps / aitoearn-server / src / core / channels / relay / relay-client.service.ts
1 import { basename } from 'node:path'
2 import { Injectable, Logger } from '@nestjs/common'
3 import { AppException, CommonResponse, ResponseCode } from '@yikart/common'
4 import { AssetType } from '@yikart/mongodb'
5 import axios, { AxiosRequestConfig } from 'axios'
6 import { config } from '../../../config'
7
8 interface UploadSignResult {
9 id: string
10 path: string
11 url: string
12 uploadUrl: string
13 }
14
15 @Injectable()
16 export class RelayClientService {
17 private readonly logger = new Logger(RelayClientService.name)
18
19 get enabled() {
20 return !!config.relay
21 }
22
23 async get<T>(path: string, params?: Record<string, any>): Promise<T> {
24 return this.request<T>({ method: 'GET', url: path, params })
25 }
26
27 async post<T>(path: string, data?: unknown): Promise<T> {
28 return this.request<T>({ method: 'POST', url: path, data })
29 }
30
31 async patch<T>(path: string, data?: unknown): Promise<T> {
32 return this.request<T>({ method: 'PATCH', url: path, data })
33 }
34
35 async delete<T>(path: string, data?: unknown): Promise<T> {
36 return this.request<T>({ method: 'DELETE', url: path, data })
37 }
38
39 async uploadFileFromLocalUrl(localUrl: string): Promise<string> {
40 const filename = basename(new URL(localUrl).pathname)
41
42 const fileResponse = await axios.get(localUrl, { responseType: 'arraybuffer' })
43 const contentType = fileResponse.headers['content-type'] || 'application/octet-stream'
44 const size = (fileResponse.data as ArrayBuffer).byteLength
45
46 const signResult = await this.post<UploadSignResult>('/assets/uploadSign', {
47 filename,
48 type: AssetType.PublishMedia,
49 size,
50 })
51
52 if (!signResult?.uploadUrl) {
53 throw new Error(`uploadSign returned no uploadUrl: ${JSON.stringify(signResult)}`)
54 }
55
56 await axios.put(signResult.uploadUrl, fileResponse.data, {
57 headers: { 'Content-Type': contentType },
58 })
59
60 await this.post(`/assets/${signResult.id}/confirm`, {})
61
62 return signResult.url
63 }
64
65 private async request<T>(options: AxiosRequestConfig): Promise<T> {
66 if (!config.relay) {
67 throw new AppException(ResponseCode.RelayServerUnavailable)
68 }
69
70 try {
71 const response = await axios<CommonResponse<T>>({
72 ...options,
73 url: `${config.relay.serverUrl}${options.url}`,
74 headers: {
75 ...options.headers,
76 'x-api-key': config.relay.apiKey,
77 },
78 })
79 if (response.data.code !== 0) {
80 this.logger.error({ message: 'Relay API returned error', url: options.url, code: response.data.code, relayMessage: response.data.message })
81 throw new Error(`Relay API error [${response.data.code}]: ${response.data.message}`)
82 }
83 return response.data.data as T
84 }
85 catch (error) {
86 this.logger.error(error, `Relay request failed: ${options.url}`)
87 throw new AppException(ResponseCode.RelayServerUnavailable)
88 }
89 }
90 }
91
91 lines TYPESCRIPT