@flashbacktech/flashbackclient 0.0.93 → 0.0.96
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/client.d.ts +4 -0
- package/dist/api/client.js +38 -0
- package/dist/api/interfaces.d.ts +3 -0
- package/dist/api/types/stats.d.ts +20 -0
- package/dist/api/types/stats.js +2 -0
- package/dist/gcs/storage.d.ts +2 -0
- package/dist/gcs/storage.js +46 -9
- package/dist/utils/blob.d.ts +8 -0
- package/dist/utils/blob.js +23 -0
- package/package.json +3 -2
package/dist/api/client.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CreateUnitRequest, CreateUnitResponse, CreateRepoRequest, CreateRepoResponse, StorageUnit, CreateRepoKeyRequest, CreateRepoKeyResponse, GetUnitsResponse, GetReposResponse, GetRepoKeysResponse, UpdateUnitRequest, UpdateUnitResponse, ActionResponse, UpdateRepoRequest, UpdateRepoResponse, UpdateRepoKeyRequest, UpdateRepoKeyResponse, ValidateUnitRequest, ValidateUnitResponse, ValidateRepoUnitsRequest, ValidateRepoUnitsResponse, StorageUnitStatusResponse, GetUnitNodeStatsResponse, GetUnitNodeStatsRequest } from './types/storage';
|
|
2
2
|
import { IApiClient, ProviderType } from './interfaces';
|
|
3
3
|
import { ActivateResponse, DeactivateResponse, LoginBody, LoginResponse, LogoutResponse, OAuth2ResponseDTO, RefreshTokenErrorResponse, RefreshTokenResponse, RegisterBody, RegisterResponse } from './types/auth';
|
|
4
|
+
import { StatsQueryParams, StatsResponse } from './types/stats';
|
|
4
5
|
interface ErrorResponse {
|
|
5
6
|
message?: string;
|
|
6
7
|
[key: string]: any;
|
|
@@ -59,5 +60,8 @@ export declare class ApiClient implements IApiClient {
|
|
|
59
60
|
userLogout: (refreshToken: string) => Promise<LogoutResponse>;
|
|
60
61
|
userActivate: () => Promise<ActivateResponse>;
|
|
61
62
|
userDeactivate: () => Promise<DeactivateResponse>;
|
|
63
|
+
private validateDateRange;
|
|
64
|
+
getDailyStats: (params: StatsQueryParams) => Promise<StatsResponse>;
|
|
65
|
+
getMinuteStats: (params: StatsQueryParams) => Promise<StatsResponse>;
|
|
62
66
|
}
|
|
63
67
|
export {};
|
package/dist/api/client.js
CHANGED
|
@@ -218,9 +218,47 @@ class ApiClient {
|
|
|
218
218
|
this.userDeactivate = async () => {
|
|
219
219
|
return this.makeRequest('user/deactivate', 'POST', null);
|
|
220
220
|
};
|
|
221
|
+
this.getDailyStats = async (params) => {
|
|
222
|
+
this.validateDateRange(params.startDate, params.endDate);
|
|
223
|
+
const queryParams = new URLSearchParams();
|
|
224
|
+
if (params.startDate) {
|
|
225
|
+
queryParams.append('startDate', params.startDate.toISOString());
|
|
226
|
+
}
|
|
227
|
+
if (params.endDate) {
|
|
228
|
+
queryParams.append('endDate', params.endDate.toISOString());
|
|
229
|
+
}
|
|
230
|
+
if (params.repoId && params.repoId.length > 0)
|
|
231
|
+
queryParams.append('repoId', params.repoId.join(','));
|
|
232
|
+
if (params.unitId && params.unitId.length > 0)
|
|
233
|
+
queryParams.append('unitId', params.unitId.join(','));
|
|
234
|
+
return this.makeRequest(`stats/daily?${queryParams.toString()}`, 'GET', null);
|
|
235
|
+
};
|
|
236
|
+
this.getMinuteStats = async (params) => {
|
|
237
|
+
this.validateDateRange(params.startDate, params.endDate);
|
|
238
|
+
const queryParams = new URLSearchParams();
|
|
239
|
+
if (params.startDate) {
|
|
240
|
+
queryParams.append('startDate', params.startDate.toISOString());
|
|
241
|
+
}
|
|
242
|
+
if (params.endDate) {
|
|
243
|
+
queryParams.append('endDate', params.endDate.toISOString());
|
|
244
|
+
}
|
|
245
|
+
if (params.repoId && params.repoId.length > 0)
|
|
246
|
+
queryParams.append('repoId', params.repoId.join(','));
|
|
247
|
+
if (params.unitId && params.unitId.length > 0)
|
|
248
|
+
queryParams.append('unitId', params.unitId.join(','));
|
|
249
|
+
return this.makeRequest(`stats/minute?${queryParams.toString()}`, 'GET', null);
|
|
250
|
+
};
|
|
221
251
|
this.baseURL = baseURL;
|
|
222
252
|
this.headers = {};
|
|
223
253
|
this.debug = false;
|
|
224
254
|
}
|
|
255
|
+
////// Stats API
|
|
256
|
+
validateDateRange(startDate, endDate) {
|
|
257
|
+
if (startDate && endDate) {
|
|
258
|
+
if (startDate > endDate) {
|
|
259
|
+
throw new Error('startDate cannot be greater than endDate');
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
225
263
|
}
|
|
226
264
|
exports.ApiClient = ApiClient;
|
package/dist/api/interfaces.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { StorageUnit, CreateUnitRequest, CreateUnitResponse, CreateRepoRequest, CreateRepoResponse, CreateRepoKeyRequest, CreateRepoKeyResponse, GetUnitsResponse, GetReposResponse, GetRepoKeysResponse, UpdateUnitRequest, UpdateUnitResponse, ActionResponse, UpdateRepoResponse, UpdateRepoRequest, UpdateRepoKeyRequest, UpdateRepoKeyResponse, ValidateUnitRequest, ValidateUnitResponse, ValidateRepoUnitsRequest, ValidateRepoUnitsResponse, GetUnitNodeStatsRequest, GetUnitNodeStatsResponse } from "./types/storage";
|
|
2
2
|
import { RegisterBody, LoginBody, RegisterResponse, LoginResponse, LogoutResponse, ActivateResponse, DeactivateResponse, RefreshTokenResponse, RefreshTokenErrorResponse } from "./types/auth";
|
|
3
|
+
import { StatsQueryParams, StatsResponse } from "./types/stats";
|
|
3
4
|
export declare enum ProviderType {
|
|
4
5
|
GOOGLE = "GOOGLE",
|
|
5
6
|
GITHUB = "GITHUB",
|
|
@@ -31,4 +32,6 @@ export interface IApiClient {
|
|
|
31
32
|
userLogout(refreshToken: string): Promise<LogoutResponse>;
|
|
32
33
|
userActivate(): Promise<ActivateResponse>;
|
|
33
34
|
userDeactivate(): Promise<DeactivateResponse>;
|
|
35
|
+
getDailyStats(params: StatsQueryParams): Promise<StatsResponse>;
|
|
36
|
+
getMinuteStats(params: StatsQueryParams): Promise<StatsResponse>;
|
|
34
37
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface StatsQueryParams {
|
|
2
|
+
startDate?: Date;
|
|
3
|
+
endDate?: Date;
|
|
4
|
+
repoId?: string[];
|
|
5
|
+
unitId?: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface StatsResponse {
|
|
8
|
+
success: boolean;
|
|
9
|
+
data: StatsData[];
|
|
10
|
+
message?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface StatsData {
|
|
13
|
+
timestamp: number;
|
|
14
|
+
repoId: string;
|
|
15
|
+
unitId: string;
|
|
16
|
+
upl_bytes: bigint;
|
|
17
|
+
dwl_bytes: bigint;
|
|
18
|
+
size_change: bigint;
|
|
19
|
+
latency_ms: number;
|
|
20
|
+
}
|
package/dist/gcs/storage.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export interface SignedUrlOptions {
|
|
|
18
18
|
export declare class FlashbackGCSStorage extends Storage {
|
|
19
19
|
protected credentials: ServiceCredentials;
|
|
20
20
|
private debug;
|
|
21
|
+
apiEndpoint: string;
|
|
22
|
+
private currentUploadContentType;
|
|
21
23
|
constructor(opts: FlashbackStorageOptions);
|
|
22
24
|
cleanup(): void;
|
|
23
25
|
bucket(name: string): import("@google-cloud/storage").Bucket;
|
package/dist/gcs/storage.js
CHANGED
|
@@ -7,14 +7,18 @@ exports.FlashbackGCSStorage = void 0;
|
|
|
7
7
|
const storage_1 = require("@google-cloud/storage");
|
|
8
8
|
const oauth2_1 = require("./oauth2");
|
|
9
9
|
const crypto_1 = __importDefault(require("crypto"));
|
|
10
|
+
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
10
11
|
const originalRequest = require('gaxios').instance.request;
|
|
12
|
+
const originalFetch = node_fetch_1.default;
|
|
11
13
|
class FlashbackGCSStorage extends storage_1.Storage {
|
|
12
14
|
constructor(opts) {
|
|
13
15
|
const { credentials, apiEndpoint = 'https://gcs-us-east-1-gcp.flashback.tech', tokenScopes = ['READ', 'WRITE'], ...rest } = opts;
|
|
14
|
-
|
|
16
|
+
// Ensure the endpoint doesn't have a trailing slash
|
|
17
|
+
const cleanEndpoint = apiEndpoint.replace(/\/$/, '');
|
|
18
|
+
const authClient = new oauth2_1.FlashbackAuthClient(cleanEndpoint + '/token', tokenScopes, credentials);
|
|
15
19
|
super({
|
|
16
20
|
...rest,
|
|
17
|
-
apiEndpoint,
|
|
21
|
+
apiEndpoint: cleanEndpoint,
|
|
18
22
|
authClient,
|
|
19
23
|
useAuthWithCustomEndpoint: true,
|
|
20
24
|
retryOptions: {
|
|
@@ -24,6 +28,7 @@ class FlashbackGCSStorage extends storage_1.Storage {
|
|
|
24
28
|
});
|
|
25
29
|
this.debug = false;
|
|
26
30
|
this.credentials = credentials;
|
|
31
|
+
this.apiEndpoint = cleanEndpoint;
|
|
27
32
|
// Intercept Gaxios instance creation
|
|
28
33
|
require('gaxios').instance.request = async function (opts) {
|
|
29
34
|
// Add auth headers to all requests
|
|
@@ -32,15 +37,45 @@ class FlashbackGCSStorage extends storage_1.Storage {
|
|
|
32
37
|
...(opts.headers || {}),
|
|
33
38
|
...headers,
|
|
34
39
|
};
|
|
40
|
+
// Ensure the base URL is used and properly handle query parameters
|
|
41
|
+
if (!opts.url.startsWith('http')) {
|
|
42
|
+
// Remove any trailing question marks
|
|
43
|
+
const cleanUrl = opts.url.replace(/\?+$/, '');
|
|
44
|
+
opts.url = `${cleanEndpoint}${cleanUrl}`;
|
|
45
|
+
}
|
|
35
46
|
return originalRequest.call(this, opts);
|
|
36
47
|
};
|
|
48
|
+
// Intercept node-fetch
|
|
49
|
+
global.fetch = async (url, options = {}) => {
|
|
50
|
+
const headers = await authClient.getRequestHeaders();
|
|
51
|
+
const finalUrl = url.startsWith('http') ? url : `${cleanEndpoint}${url}`;
|
|
52
|
+
return originalFetch(finalUrl, {
|
|
53
|
+
...options,
|
|
54
|
+
headers: {
|
|
55
|
+
...(options.headers || {}),
|
|
56
|
+
...headers,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
};
|
|
37
60
|
}
|
|
38
61
|
cleanup() {
|
|
39
62
|
require('gaxios').instance.request = originalRequest;
|
|
63
|
+
global.fetch = originalFetch;
|
|
40
64
|
}
|
|
41
65
|
// Override the bucket method to ensure we pass the auth client
|
|
42
66
|
bucket(name) {
|
|
43
|
-
|
|
67
|
+
const bucket = super.bucket(name);
|
|
68
|
+
const originalUpload = bucket.upload.bind(bucket);
|
|
69
|
+
bucket.upload = async (filePath, options) => {
|
|
70
|
+
this.currentUploadContentType = options?.contentType;
|
|
71
|
+
try {
|
|
72
|
+
return await originalUpload(filePath, options);
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
this.currentUploadContentType = undefined;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
return bucket;
|
|
44
79
|
}
|
|
45
80
|
setDebug(debug) {
|
|
46
81
|
this.debug = debug;
|
|
@@ -71,12 +106,13 @@ class FlashbackGCSStorage extends storage_1.Storage {
|
|
|
71
106
|
if (contentType) {
|
|
72
107
|
extensionHeaders['content-type'] = contentType;
|
|
73
108
|
}
|
|
74
|
-
|
|
109
|
+
// Sort headers once and use the same order for both signedHeaders and canonicalHeaders
|
|
110
|
+
const sortedHeaderKeys = Object.keys(extensionHeaders)
|
|
75
111
|
.map(header => header.toLowerCase())
|
|
76
|
-
.sort()
|
|
77
|
-
|
|
78
|
-
const canonicalHeaders =
|
|
79
|
-
.map(
|
|
112
|
+
.sort();
|
|
113
|
+
const signedHeaders = sortedHeaderKeys.join(';');
|
|
114
|
+
const canonicalHeaders = sortedHeaderKeys
|
|
115
|
+
.map(key => `${key}:${extensionHeaders[key.toLowerCase()]}`)
|
|
80
116
|
.join('\n') + '\n';
|
|
81
117
|
const datestamp = accessibleAt.toISOString().split('T')[0];
|
|
82
118
|
const credentialScope = `${datestamp}/auto/storage/goog4_request`;
|
|
@@ -120,7 +156,8 @@ class FlashbackGCSStorage extends storage_1.Storage {
|
|
|
120
156
|
credentialScope,
|
|
121
157
|
crypto_1.default.createHash('sha256').update(canonicalRequest).digest('hex'),
|
|
122
158
|
].join('\n');
|
|
123
|
-
|
|
159
|
+
const canonicalRequestHex = Buffer.from(canonicalRequest).toString('hex');
|
|
160
|
+
this.doLog('String to Sign:', stringToSign, canonicalRequestHex);
|
|
124
161
|
const sign = crypto_1.default.createSign('RSA-SHA256');
|
|
125
162
|
sign.update(stringToSign);
|
|
126
163
|
const signature = sign.sign(this.credentials.private_key, 'hex');
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateBlockID = generateBlockID;
|
|
4
|
+
/**
|
|
5
|
+
* Generate a 64 bytes base64 block ID string.
|
|
6
|
+
*
|
|
7
|
+
* @export
|
|
8
|
+
* @param {number} blockIndex
|
|
9
|
+
* @returns {string}
|
|
10
|
+
*/
|
|
11
|
+
function generateBlockID(blockIDPrefix, blockIndex) {
|
|
12
|
+
// To generate a 64 bytes base64 string, source string should be 48
|
|
13
|
+
const maxSourceStringLength = 48;
|
|
14
|
+
// A blob can have a maximum of 100,000 uncommitted blocks at any given time
|
|
15
|
+
const maxBlockIndexLength = 6;
|
|
16
|
+
const maxAllowedBlockIDPrefixLength = maxSourceStringLength - maxBlockIndexLength;
|
|
17
|
+
if (blockIDPrefix.length > maxAllowedBlockIDPrefixLength) {
|
|
18
|
+
blockIDPrefix = blockIDPrefix.slice(0, maxAllowedBlockIDPrefixLength);
|
|
19
|
+
}
|
|
20
|
+
const res = blockIDPrefix +
|
|
21
|
+
blockIndex.toString().padStart(maxSourceStringLength - blockIDPrefix.length, "0");
|
|
22
|
+
return Buffer.from(res).toString("base64");
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flashbacktech/flashbackclient",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.96",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@aws-sdk/client-sts": "^3.777.0",
|
|
27
|
+
"@google-cloud/storage": "^7.15.0",
|
|
27
28
|
"@stellar/stellar-sdk": "^13.0.0",
|
|
28
29
|
"formdata-node": "^6.0.3",
|
|
29
|
-
"@google-cloud/storage": "^7.15.0",
|
|
30
30
|
"google-auth-library": "^9.15.1"
|
|
31
31
|
},
|
|
32
32
|
"bin": {
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@aws-sdk/client-s3": "^3.777.0",
|
|
57
57
|
"@aws-sdk/s3-request-presigner": "^3.782.0",
|
|
58
|
+
"@azure/storage-blob": "^12.27.0",
|
|
58
59
|
"@eslint/js": "^8.56.0",
|
|
59
60
|
"@types/jest": "^29.5.14",
|
|
60
61
|
"@types/node": "^22.10.1",
|