squarefi-bff-api-module 1.32.1 → 1.32.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/auth.d.ts +29 -0
- package/dist/api/auth.js +59 -0
- package/dist/api/bank-data.d.ts +4 -0
- package/dist/api/bank-data.js +6 -0
- package/dist/api/counterparties.d.ts +14 -0
- package/dist/api/counterparties.js +16 -0
- package/dist/api/developer.d.ts +12 -0
- package/dist/api/developer.js +12 -0
- package/dist/api/exchange.d.ts +14 -0
- package/dist/api/exchange.js +20 -0
- package/dist/api/frontend.d.ts +11 -0
- package/dist/api/frontend.js +11 -0
- package/dist/api/index.d.ts +38 -0
- package/dist/api/index.js +36 -0
- package/dist/api/issuing.d.ts +64 -0
- package/dist/api/issuing.js +140 -0
- package/dist/api/kyc.d.ts +21 -0
- package/dist/api/kyc.js +21 -0
- package/dist/api/list.d.ts +16 -0
- package/dist/api/list.js +16 -0
- package/dist/api/orders.d.ts +49 -0
- package/dist/api/orders.js +84 -0
- package/dist/api/persona.d.ts +7 -0
- package/dist/api/persona.js +7 -0
- package/dist/api/storage.d.ts +8 -0
- package/dist/api/storage.js +16 -0
- package/dist/api/tenants.d.ts +6 -0
- package/dist/api/tenants.js +6 -0
- package/dist/api/totp.d.ts +17 -0
- package/dist/api/totp.js +45 -0
- package/{src/api/types/autogen/apiV2.types.ts → dist/api/types/autogen/apiV2.types.d.ts} +0 -1
- package/dist/api/types/autogen/apiV2.types.js +5 -0
- package/dist/api/types/types.d.ts +2258 -0
- package/dist/api/types/types.js +1 -0
- package/dist/api/user.d.ts +18 -0
- package/dist/api/user.js +18 -0
- package/dist/api/virtual-accounts.d.ts +9 -0
- package/dist/api/virtual-accounts.js +9 -0
- package/dist/api/wallets.d.ts +24 -0
- package/dist/api/wallets.js +30 -0
- package/dist/constants.d.ts +303 -0
- package/dist/constants.js +332 -0
- package/dist/hooks/index.js +3 -0
- package/dist/hooks/useCalc.d.ts +25 -0
- package/dist/hooks/useCalc.js +115 -0
- package/dist/hooks/useFileUpload.d.ts +49 -0
- package/dist/hooks/useFileUpload.js +100 -0
- package/dist/hooks/useSupabaseSubscription/config.d.ts +2 -0
- package/dist/hooks/useSupabaseSubscription/config.js +5 -0
- package/dist/hooks/useSupabaseSubscription/index.js +2 -0
- package/dist/hooks/useSupabaseSubscription/specialized.d.ts +5 -0
- package/{src/hooks/useSupabaseSubscription/specialized.ts → dist/hooks/useSupabaseSubscription/specialized.js} +2 -5
- package/dist/hooks/useSupabaseSubscription/types.d.ts +16 -0
- package/dist/hooks/useSupabaseSubscription/types.js +1 -0
- package/dist/hooks/useSupabaseSubscription/useSupabaseSubscription.d.ts +5 -0
- package/dist/hooks/useSupabaseSubscription/useSupabaseSubscription.js +37 -0
- package/dist/index.d.ts +7 -0
- package/dist/utils/apiClientFactory.d.ts +31 -0
- package/dist/utils/apiClientFactory.js +138 -0
- package/dist/utils/converters.d.ts +1 -0
- package/dist/utils/converters.js +1 -0
- package/dist/utils/encrypt.d.ts +10 -0
- package/dist/utils/encrypt.js +77 -0
- package/dist/utils/fileStorage.d.ts +120 -0
- package/dist/utils/fileStorage.js +292 -0
- package/dist/utils/storage.d.ts +3 -0
- package/dist/utils/storage.js +24 -0
- package/dist/utils/supabase.d.ts +1 -0
- package/dist/utils/supabase.js +12 -0
- package/dist/utils/tokensFactory.d.ts +12 -0
- package/dist/utils/tokensFactory.js +42 -0
- package/package.json +4 -1
- package/.env.example +0 -1
- package/.husky/pre-commit +0 -2
- package/.prettierignore +0 -6
- package/.prettierrc +0 -7
- package/CHANGELOG.md +0 -1415
- package/FIXED_RLS_ERROR.md +0 -146
- package/QUICK_TEST.md +0 -127
- package/STORAGE_MODULE_SUMMARY.md +0 -228
- package/TEST_INSTRUCTIONS.md +0 -122
- package/docs/AUTH_TOKEN_USAGE.md +0 -290
- package/docs/BACKEND_SERVICE_URL.md +0 -334
- package/docs/FRONTEND_STORAGE_GUIDE.md +0 -529
- package/docs/STORAGE_MODULE.md +0 -490
- package/docs/STORAGE_QUICK_START.md +0 -76
- package/scripts/generate-openapi-types.ts +0 -41
- package/scripts/supabase-storage-setup.sql +0 -223
- package/src/api/auth.ts +0 -78
- package/src/api/bank-data.ts +0 -11
- package/src/api/counterparties.ts +0 -73
- package/src/api/developer.ts +0 -20
- package/src/api/exchange.ts +0 -44
- package/src/api/frontend.ts +0 -20
- package/src/api/index.ts +0 -57
- package/src/api/issuing.ts +0 -214
- package/src/api/kyc.ts +0 -41
- package/src/api/list.ts +0 -26
- package/src/api/orders.ts +0 -255
- package/src/api/persona.ts +0 -16
- package/src/api/storage.ts +0 -24
- package/src/api/tenants.ts +0 -8
- package/src/api/totp.ts +0 -51
- package/src/api/types/types.ts +0 -2820
- package/src/api/user.ts +0 -27
- package/src/api/virtual-accounts.ts +0 -15
- package/src/api/wallets.ts +0 -65
- package/src/constants.ts +0 -343
- package/src/hooks/useCalc.ts +0 -181
- package/src/hooks/useFileUpload.ts +0 -129
- package/src/hooks/useSupabaseSubscription/config.ts +0 -7
- package/src/hooks/useSupabaseSubscription/types.ts +0 -18
- package/src/hooks/useSupabaseSubscription/useSupabaseSubscription.ts +0 -53
- package/src/utils/apiClientFactory.ts +0 -194
- package/src/utils/converters.ts +0 -1
- package/src/utils/encrypt.ts +0 -96
- package/src/utils/fileStorage.ts +0 -353
- package/src/utils/storage.ts +0 -29
- package/src/utils/supabase.ts +0 -16
- package/src/utils/tokensFactory.ts +0 -59
- package/tsconfig.json +0 -15
- package/types.d.ts +0 -11
- /package/{src/hooks/index.ts → dist/hooks/index.d.ts} +0 -0
- /package/{src/hooks/useSupabaseSubscription/index.ts → dist/hooks/useSupabaseSubscription/index.d.ts} +0 -0
- /package/{src/index.ts → dist/index.js} +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface SubscriptionConfig {
|
|
2
|
+
channelName: string;
|
|
3
|
+
table: string;
|
|
4
|
+
schema?: string;
|
|
5
|
+
event?: 'INSERT' | 'UPDATE' | 'DELETE' | '*';
|
|
6
|
+
filter?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface UseSupabaseSubscriptionProps {
|
|
9
|
+
config: SubscriptionConfig;
|
|
10
|
+
callback: (payload?: unknown) => void;
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
key?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface UseWalletTransactionsSubscriptionProps extends Omit<UseSupabaseSubscriptionProps, 'config'> {
|
|
15
|
+
walletId: string | undefined;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { supabaseClient } from '../../utils/supabase';
|
|
4
|
+
export const useSupabaseSubscription = ({ config, callback, enabled = true, key }) => {
|
|
5
|
+
const subscriptionRef = useRef(null);
|
|
6
|
+
const callbackRef = useRef(callback);
|
|
7
|
+
callbackRef.current = callback;
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
if (!enabled || !supabaseClient) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (subscriptionRef.current) {
|
|
13
|
+
supabaseClient.removeChannel(subscriptionRef.current);
|
|
14
|
+
subscriptionRef.current = null;
|
|
15
|
+
}
|
|
16
|
+
const subscription = supabaseClient
|
|
17
|
+
.channel(key || config.channelName)
|
|
18
|
+
.on('postgres_changes', {
|
|
19
|
+
event: config.event || '*',
|
|
20
|
+
schema: config.schema || 'public',
|
|
21
|
+
table: config.table,
|
|
22
|
+
...(config.filter && { filter: config.filter }),
|
|
23
|
+
}, (payload) => callbackRef.current(payload))
|
|
24
|
+
.subscribe();
|
|
25
|
+
subscriptionRef.current = subscription;
|
|
26
|
+
return () => {
|
|
27
|
+
if (subscriptionRef.current && supabaseClient) {
|
|
28
|
+
supabaseClient.removeChannel(subscriptionRef.current);
|
|
29
|
+
subscriptionRef.current = null;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}, [enabled, config.channelName, config.table, config.schema, config.event, config.filter, supabaseClient, key]);
|
|
33
|
+
return {
|
|
34
|
+
isConnected: !!subscriptionRef.current,
|
|
35
|
+
isClientAvailable: !!supabaseClient,
|
|
36
|
+
};
|
|
37
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { AxiosRequestConfig } from 'axios';
|
|
2
|
+
type CreateApiClientOptions = {
|
|
3
|
+
baseURL: string;
|
|
4
|
+
tenantId: string;
|
|
5
|
+
isBearerToken?: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare const createApiClient: ({ baseURL, isBearerToken, tenantId }: CreateApiClientOptions) => {
|
|
8
|
+
patchRequest: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>;
|
|
9
|
+
postRequest: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>;
|
|
10
|
+
deleteRequest: (url: string, config?: AxiosRequestConfig) => Promise<any>;
|
|
11
|
+
getRequest: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>;
|
|
12
|
+
};
|
|
13
|
+
export declare const apiClientV1: {
|
|
14
|
+
patchRequest: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>;
|
|
15
|
+
postRequest: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>;
|
|
16
|
+
deleteRequest: (url: string, config?: AxiosRequestConfig) => Promise<any>;
|
|
17
|
+
getRequest: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>;
|
|
18
|
+
};
|
|
19
|
+
export declare const apiClientV2: {
|
|
20
|
+
patchRequest: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>;
|
|
21
|
+
postRequest: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>;
|
|
22
|
+
deleteRequest: (url: string, config?: AxiosRequestConfig) => Promise<any>;
|
|
23
|
+
getRequest: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>;
|
|
24
|
+
};
|
|
25
|
+
export declare const apiClientTOTP: {
|
|
26
|
+
patchRequest: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>;
|
|
27
|
+
postRequest: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>;
|
|
28
|
+
deleteRequest: (url: string, config?: AxiosRequestConfig) => Promise<any>;
|
|
29
|
+
getRequest: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>;
|
|
30
|
+
};
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { isTMA } from '@telegram-apps/sdk-react';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
import { telegramSignUpPath, telegramSignInPath, refreshTokenPath } from '../api/auth';
|
|
5
|
+
import { AppEnviroment, ResponseStatus } from '../constants';
|
|
6
|
+
import { deleteTokens, getTokens, refreshTokens } from './tokensFactory';
|
|
7
|
+
// eslint-disable-next-line no-constant-condition
|
|
8
|
+
const apiV1BaseURL = process.env.API_URL ?? 'ENV variable API_URL is not defined';
|
|
9
|
+
const apiV2BaseURL = process.env.API_V2_URL ?? 'ENV variable API_V2_URL is not defined';
|
|
10
|
+
const apiTOTPBaseURL = process.env.API_TOTP_URL ?? 'ENV variable API_TOTP_URL is not defined';
|
|
11
|
+
const envTenantId = process.env.TENANT_ID ?? 'ENV variable TENANT_ID is not defined';
|
|
12
|
+
const envLogoutURL = process.env.LOGOUT_URL ?? '/auth/logout';
|
|
13
|
+
let isTokenRefreshing = false;
|
|
14
|
+
let requestQueue = [];
|
|
15
|
+
export const createApiClient = ({ baseURL, isBearerToken, tenantId }) => {
|
|
16
|
+
const instance = axios.create({
|
|
17
|
+
baseURL,
|
|
18
|
+
timeout: 60000,
|
|
19
|
+
});
|
|
20
|
+
instance.interceptors.request.use((config) => {
|
|
21
|
+
const { access_token } = getTokens();
|
|
22
|
+
const modifiedHeaders = {
|
|
23
|
+
...config.headers,
|
|
24
|
+
'x-tenant-id': tenantId,
|
|
25
|
+
};
|
|
26
|
+
if (access_token) {
|
|
27
|
+
const authHeader = isBearerToken ? `Bearer ${access_token}` : access_token;
|
|
28
|
+
modifiedHeaders.Authorization = authHeader;
|
|
29
|
+
}
|
|
30
|
+
config.context = {
|
|
31
|
+
...config.context,
|
|
32
|
+
appEnvironment: isTMA() ? AppEnviroment.TELEGRAM : AppEnviroment.WEB,
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
...config,
|
|
36
|
+
headers: modifiedHeaders,
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
instance.interceptors.response.use((response) => response, (error) => {
|
|
40
|
+
if (typeof window === 'undefined') {
|
|
41
|
+
return Promise.reject(error);
|
|
42
|
+
}
|
|
43
|
+
if (error?.response?.status === ResponseStatus.UNAUTHORIZED &&
|
|
44
|
+
!error?.response?.config.context?.bypassUnauthorizedHandler) {
|
|
45
|
+
const { response, config: failedRequestConfig } = error;
|
|
46
|
+
const { refresh_token } = getTokens();
|
|
47
|
+
const isRetryRequest = failedRequestConfig.context?.isRetryRequest;
|
|
48
|
+
const isRefreshTokenRequest = failedRequestConfig.url?.includes(refreshTokenPath);
|
|
49
|
+
const isTelegramSignInRequest = failedRequestConfig.url?.includes(telegramSignInPath);
|
|
50
|
+
const isTelegramSignUpRequest = failedRequestConfig.url?.includes(telegramSignUpPath);
|
|
51
|
+
const isRefreshNotRequired = !refresh_token && !isTMA();
|
|
52
|
+
const isLogoutNeccesary = isRefreshNotRequired ||
|
|
53
|
+
isTelegramSignInRequest ||
|
|
54
|
+
isTelegramSignUpRequest ||
|
|
55
|
+
isRefreshTokenRequest ||
|
|
56
|
+
isRetryRequest;
|
|
57
|
+
const isRefreshAvailable = !isTokenRefreshing && !isRefreshNotRequired;
|
|
58
|
+
if (isLogoutNeccesary) {
|
|
59
|
+
if (typeof window !== 'undefined') {
|
|
60
|
+
window.location.href = envLogoutURL;
|
|
61
|
+
deleteTokens();
|
|
62
|
+
}
|
|
63
|
+
requestQueue = [];
|
|
64
|
+
return Promise.reject(response);
|
|
65
|
+
}
|
|
66
|
+
if (isRefreshAvailable) {
|
|
67
|
+
isTokenRefreshing = true;
|
|
68
|
+
refreshTokens()
|
|
69
|
+
.then((data) => {
|
|
70
|
+
if (data?.access_token) {
|
|
71
|
+
requestQueue.forEach((request) => request.resolve());
|
|
72
|
+
requestQueue = [];
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
.catch((tokenRefreshError) => {
|
|
76
|
+
if (typeof window !== 'undefined') {
|
|
77
|
+
window.location.href = envLogoutURL;
|
|
78
|
+
deleteTokens();
|
|
79
|
+
}
|
|
80
|
+
requestQueue = [];
|
|
81
|
+
return Promise.reject(tokenRefreshError);
|
|
82
|
+
})
|
|
83
|
+
.finally(() => {
|
|
84
|
+
isTokenRefreshing = false;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return new Promise((res, rej) => {
|
|
88
|
+
requestQueue.push({
|
|
89
|
+
resolve: () => {
|
|
90
|
+
failedRequestConfig.context = {
|
|
91
|
+
...failedRequestConfig.context,
|
|
92
|
+
isRetryRequest: true,
|
|
93
|
+
};
|
|
94
|
+
return res(instance(failedRequestConfig));
|
|
95
|
+
},
|
|
96
|
+
reject: () => rej(instance(failedRequestConfig)),
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
console.error('Axios error', error);
|
|
101
|
+
return Promise.reject(error);
|
|
102
|
+
});
|
|
103
|
+
const patchRequest = async (url, config) => {
|
|
104
|
+
const { data = {}, ...restConfig } = config ?? {};
|
|
105
|
+
const res = await instance.patch(url, data, restConfig);
|
|
106
|
+
return res.data;
|
|
107
|
+
};
|
|
108
|
+
const postRequest = async (url, config) => {
|
|
109
|
+
const { data = {}, ...restConfig } = config ?? {};
|
|
110
|
+
const res = await instance.post(url, data, restConfig);
|
|
111
|
+
return res.data;
|
|
112
|
+
};
|
|
113
|
+
const deleteRequest = async (url, config) => {
|
|
114
|
+
const { data = {}, ...restConfig } = config ?? {};
|
|
115
|
+
const res = await instance.delete(url, { data, ...restConfig });
|
|
116
|
+
return res.data;
|
|
117
|
+
};
|
|
118
|
+
const getRequest = async (url, config) => {
|
|
119
|
+
const { params = {}, ...restConfig } = config ?? {};
|
|
120
|
+
const res = await instance.get(url, { params, ...restConfig });
|
|
121
|
+
return res.data;
|
|
122
|
+
};
|
|
123
|
+
return { patchRequest, postRequest, deleteRequest, getRequest };
|
|
124
|
+
};
|
|
125
|
+
export const apiClientV1 = createApiClient({
|
|
126
|
+
baseURL: apiV1BaseURL,
|
|
127
|
+
tenantId: envTenantId,
|
|
128
|
+
});
|
|
129
|
+
export const apiClientV2 = createApiClient({
|
|
130
|
+
baseURL: apiV2BaseURL,
|
|
131
|
+
isBearerToken: true,
|
|
132
|
+
tenantId: envTenantId,
|
|
133
|
+
});
|
|
134
|
+
export const apiClientTOTP = createApiClient({
|
|
135
|
+
baseURL: apiTOTPBaseURL,
|
|
136
|
+
isBearerToken: true,
|
|
137
|
+
tenantId: envTenantId,
|
|
138
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const convertPhoneToSupabaseFormat: (phone: string) => string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const convertPhoneToSupabaseFormat = (phone) => phone.replace(/\D/g, '');
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CipherKey } from 'crypto';
|
|
2
|
+
import { API } from '../api/types/types';
|
|
3
|
+
type MakeSecureRequestParams = {
|
|
4
|
+
callback: (props: API.Common.Encrypted.Request) => Promise<API.Common.Encrypted.Response>;
|
|
5
|
+
publicKey: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const generateSecretKey: () => Buffer<ArrayBufferLike>;
|
|
8
|
+
export declare const decryptAESData: (encryptedData: string, iv: string, secretKey: CipherKey) => Promise<any>;
|
|
9
|
+
export declare const makeSecureRequest: <T>({ callback, publicKey }: MakeSecureRequestParams) => Promise<T>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import NodeRSA from 'node-rsa';
|
|
3
|
+
export const generateSecretKey = () => {
|
|
4
|
+
const secretKey = crypto.randomBytes(32);
|
|
5
|
+
return secretKey;
|
|
6
|
+
};
|
|
7
|
+
export const decryptAESData = async (encryptedData, iv, secretKey) => {
|
|
8
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', secretKey, Buffer.from(iv, 'base64'));
|
|
9
|
+
let decrypted = decipher.update(encryptedData, 'base64', 'utf8');
|
|
10
|
+
decrypted += decipher.final('utf8');
|
|
11
|
+
return JSON.parse(decrypted);
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Очищает и валидирует публичный RSA ключ
|
|
15
|
+
* Исправляет ошибку InvalidAsn1Error: encoding too long
|
|
16
|
+
* путем удаления лишних символов и нормализации переносов строк
|
|
17
|
+
*/
|
|
18
|
+
const cleanAndValidatePublicKey = (publicKey) => {
|
|
19
|
+
try {
|
|
20
|
+
// Декодируем base64 ключ
|
|
21
|
+
const publicKeyBase64 = Buffer.from(publicKey, 'base64').toString('utf8');
|
|
22
|
+
// Удаляем лишние символы (переносы строк, пробелы в начале и конце)
|
|
23
|
+
// Нормализуем переносы строк для корректной обработки ASN.1
|
|
24
|
+
const cleanedKey = publicKeyBase64.trim().replace(/\r?\n|\r/g, '\n');
|
|
25
|
+
// Проверяем, что ключ содержит необходимые маркеры
|
|
26
|
+
if (!cleanedKey.includes('BEGIN') || !cleanedKey.includes('END')) {
|
|
27
|
+
throw new Error('Invalid public key format: missing BEGIN/END markers');
|
|
28
|
+
}
|
|
29
|
+
return cleanedKey;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
throw new Error(`Invalid public key format: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Автоматически определяет формат RSA ключа по заголовку
|
|
37
|
+
* Помогает избежать ошибок импорта при неправильном указании формата
|
|
38
|
+
*/
|
|
39
|
+
const detectKeyFormat = (key) => {
|
|
40
|
+
if (key.includes('BEGIN RSA PUBLIC KEY')) {
|
|
41
|
+
return 'pkcs1';
|
|
42
|
+
}
|
|
43
|
+
else if (key.includes('BEGIN PUBLIC KEY')) {
|
|
44
|
+
return 'pkcs8';
|
|
45
|
+
}
|
|
46
|
+
// По умолчанию возвращаем pkcs8, как было ранее
|
|
47
|
+
return 'pkcs8';
|
|
48
|
+
};
|
|
49
|
+
export const makeSecureRequest = async ({ callback, publicKey }) => {
|
|
50
|
+
const clientRsa = new NodeRSA();
|
|
51
|
+
try {
|
|
52
|
+
// Очищаем и валидируем публичный ключ
|
|
53
|
+
const cleanedPublicKey = cleanAndValidatePublicKey(publicKey);
|
|
54
|
+
// Определяем формат ключа автоматически
|
|
55
|
+
const keyFormat = detectKeyFormat(cleanedPublicKey);
|
|
56
|
+
// Импортируем ключ с правильным форматом
|
|
57
|
+
clientRsa.importKey(cleanedPublicKey, `${keyFormat}-public-pem`);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
61
|
+
throw new Error(`Failed to import RSA public key: ${errorMessage}`);
|
|
62
|
+
}
|
|
63
|
+
const clientSecretKey = generateSecretKey();
|
|
64
|
+
const clientPayload = {
|
|
65
|
+
key: clientSecretKey.toString('base64'),
|
|
66
|
+
timestamp: Date.now(),
|
|
67
|
+
};
|
|
68
|
+
const encrypted_key = clientRsa.encrypt(JSON.stringify(clientPayload), 'base64');
|
|
69
|
+
const { success, encrypted, data, iv } = await callback({ encrypted_key });
|
|
70
|
+
if (success && encrypted && data && iv) {
|
|
71
|
+
const decryptedData = await decryptAESData(data, iv, clientSecretKey);
|
|
72
|
+
return decryptedData.data;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
throw new Error('Failed to get encrypted secret key');
|
|
76
|
+
}
|
|
77
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Типы для работы с файловым хранилищем
|
|
3
|
+
*/
|
|
4
|
+
export interface UploadFileOptions {
|
|
5
|
+
file: File | Blob;
|
|
6
|
+
fileName: string;
|
|
7
|
+
bucket: string;
|
|
8
|
+
folder?: string;
|
|
9
|
+
contentType?: string;
|
|
10
|
+
cacheControl?: string;
|
|
11
|
+
upsert?: boolean;
|
|
12
|
+
authToken?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface UploadFileResult {
|
|
15
|
+
success: boolean;
|
|
16
|
+
publicUrl?: string;
|
|
17
|
+
signedUrl?: string;
|
|
18
|
+
path?: string;
|
|
19
|
+
error?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface GetFileUrlOptions {
|
|
22
|
+
path: string;
|
|
23
|
+
bucket: string;
|
|
24
|
+
expiresIn?: number;
|
|
25
|
+
authToken?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Названия бакетов по умолчанию
|
|
29
|
+
*/
|
|
30
|
+
export declare const ORDER_DOCS_BUCKET = "order_docs";
|
|
31
|
+
/**
|
|
32
|
+
* Загружает файл в Supabase Storage
|
|
33
|
+
* Файл сохраняется по пути: {folder}/{fileName} или {fileName}
|
|
34
|
+
*
|
|
35
|
+
* Папки создаются автоматически при загрузке файла, если их не существует.
|
|
36
|
+
* Можно указывать вложенные папки через слэш: 'images/avatars/2024'
|
|
37
|
+
*
|
|
38
|
+
* @param options - параметры загрузки файла
|
|
39
|
+
* @param options.folder - опциональная папка внутри бакета (например, 'documents', 'images/avatars')
|
|
40
|
+
* @returns результат загрузки с ссылкой на файл
|
|
41
|
+
*/
|
|
42
|
+
export declare const uploadFile: (options: UploadFileOptions) => Promise<UploadFileResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Получает подписанный URL для доступа к файлу
|
|
45
|
+
*
|
|
46
|
+
* @param options - параметры получения URL
|
|
47
|
+
* @returns подписанный URL или null при ошибке
|
|
48
|
+
*/
|
|
49
|
+
export declare const getSignedUrl: (options: GetFileUrlOptions) => Promise<string | null>;
|
|
50
|
+
/**
|
|
51
|
+
* Получает публичный URL для файла
|
|
52
|
+
*
|
|
53
|
+
* Для ПРИВАТНЫХ бакетов:
|
|
54
|
+
* - URL постоянный (не истекает)
|
|
55
|
+
* - Требует Authorization header с service role key для доступа
|
|
56
|
+
* - Используется на backend для суперадмина
|
|
57
|
+
*
|
|
58
|
+
* Для ПУБЛИЧНЫХ бакетов:
|
|
59
|
+
* - URL доступен всем без аутентификации
|
|
60
|
+
*
|
|
61
|
+
* @example Backend usage for private buckets:
|
|
62
|
+
* ```typescript
|
|
63
|
+
* const url = getPublicUrl(filePath, bucket);
|
|
64
|
+
*
|
|
65
|
+
* // Access with service role key:
|
|
66
|
+
* fetch(url, {
|
|
67
|
+
* headers: {
|
|
68
|
+
* 'Authorization': `Bearer ${SUPABASE_SERVICE_ROLE_KEY}`
|
|
69
|
+
* }
|
|
70
|
+
* })
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @param path - путь к файлу
|
|
74
|
+
* @param bucket - название бакета
|
|
75
|
+
* @returns постоянный URL
|
|
76
|
+
*/
|
|
77
|
+
export declare const getPublicUrl: (path: string, bucket: string) => string | null;
|
|
78
|
+
/**
|
|
79
|
+
* Удаляет файл из хранилища
|
|
80
|
+
*
|
|
81
|
+
* @param path - путь к файлу
|
|
82
|
+
* @param bucket - название бакета
|
|
83
|
+
* @param authToken - JWT token для авторизации
|
|
84
|
+
* @returns true при успешном удалении
|
|
85
|
+
*/
|
|
86
|
+
export declare const deleteFile: (path: string, bucket: string, authToken?: string) => Promise<boolean>;
|
|
87
|
+
/**
|
|
88
|
+
* Удаляет несколько файлов из хранилища
|
|
89
|
+
*
|
|
90
|
+
* @param paths - массив путей к файлам
|
|
91
|
+
* @param bucket - название бакета
|
|
92
|
+
* @param authToken - JWT token для авторизации
|
|
93
|
+
* @returns true при успешном удалении всех файлов
|
|
94
|
+
*/
|
|
95
|
+
export declare const deleteFiles: (paths: string[], bucket: string, authToken?: string) => Promise<boolean>;
|
|
96
|
+
/**
|
|
97
|
+
* Получает список файлов пользователя
|
|
98
|
+
*
|
|
99
|
+
* @param userId - ID пользователя
|
|
100
|
+
* @param bucket - название бакета
|
|
101
|
+
* @param authToken - JWT token для авторизации
|
|
102
|
+
* @returns список файлов или пустой массив при ошибке
|
|
103
|
+
*/
|
|
104
|
+
export declare const listUserFiles: (userId: string, bucket: string, authToken?: string) => Promise<Array<{
|
|
105
|
+
name: string;
|
|
106
|
+
id?: string;
|
|
107
|
+
updated_at?: string;
|
|
108
|
+
created_at?: string;
|
|
109
|
+
last_accessed_at?: string;
|
|
110
|
+
metadata?: Record<string, unknown>;
|
|
111
|
+
}>>;
|
|
112
|
+
/**
|
|
113
|
+
* Скачивает файл из хранилища
|
|
114
|
+
*
|
|
115
|
+
* @param path - путь к файлу
|
|
116
|
+
* @param bucket - название бакета
|
|
117
|
+
* @param authToken - JWT token для авторизации
|
|
118
|
+
* @returns Blob файла или null при ошибке
|
|
119
|
+
*/
|
|
120
|
+
export declare const downloadFile: (path: string, bucket: string, authToken?: string) => Promise<Blob | null>;
|