@nu-art/firebase-backend 0.400.5

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.
Files changed (65) hide show
  1. package/ModuleBE_Firebase.d.ts +20 -0
  2. package/ModuleBE_Firebase.js +69 -0
  3. package/auth/FirebaseBaseWrapper.d.ts +19 -0
  4. package/auth/FirebaseBaseWrapper.js +39 -0
  5. package/auth/FirebaseSession_Admin.d.ts +19 -0
  6. package/auth/FirebaseSession_Admin.js +45 -0
  7. package/auth/firebase-session.d.ts +61 -0
  8. package/auth/firebase-session.js +93 -0
  9. package/database/DatabaseWrapperBE.d.ts +71 -0
  10. package/database/DatabaseWrapperBE.js +182 -0
  11. package/database/types.d.ts +4 -0
  12. package/database/types.js +18 -0
  13. package/firestore/FirestoreCollection.d.ts +71 -0
  14. package/firestore/FirestoreCollection.js +184 -0
  15. package/firestore/FirestoreInterface.d.ts +11 -0
  16. package/firestore/FirestoreInterface.js +111 -0
  17. package/firestore/FirestoreTransaction.d.ts +30 -0
  18. package/firestore/FirestoreTransaction.js +153 -0
  19. package/firestore/FirestoreWrapperBE.d.ts +16 -0
  20. package/firestore/FirestoreWrapperBE.js +53 -0
  21. package/firestore/types.d.ts +6 -0
  22. package/firestore/types.js +25 -0
  23. package/firestore-v3/DocWrapperV3.d.ts +32 -0
  24. package/firestore-v3/DocWrapperV3.js +148 -0
  25. package/firestore-v3/FirestoreCollectionV3.d.ts +154 -0
  26. package/firestore-v3/FirestoreCollectionV3.js +470 -0
  27. package/firestore-v3/FirestoreInterfaceV3.d.ts +10 -0
  28. package/firestore-v3/FirestoreInterfaceV3.js +107 -0
  29. package/firestore-v3/FirestoreWrapperBEV3.d.ts +16 -0
  30. package/firestore-v3/FirestoreWrapperBEV3.js +154 -0
  31. package/firestore-v3/consts.d.ts +13 -0
  32. package/firestore-v3/consts.js +18 -0
  33. package/firestore-v3/types.d.ts +6 -0
  34. package/firestore-v3/types.js +1 -0
  35. package/functions/firebase-function.d.ts +38 -0
  36. package/functions/firebase-function.js +53 -0
  37. package/functions-v2/ModuleBE_BaseFunction.d.ts +11 -0
  38. package/functions-v2/ModuleBE_BaseFunction.js +32 -0
  39. package/functions-v2/ModuleBE_ExpressFunction_V2.d.ts +11 -0
  40. package/functions-v2/ModuleBE_ExpressFunction_V2.js +29 -0
  41. package/functions-v2/ModuleBE_FirebaseDBListener.d.ts +10 -0
  42. package/functions-v2/ModuleBE_FirebaseDBListener.js +24 -0
  43. package/functions-v2/ModuleBE_FirebaseScheduler.d.ts +32 -0
  44. package/functions-v2/ModuleBE_FirebaseScheduler.js +57 -0
  45. package/functions-v2/ModuleBE_FirestoreListener.d.ts +13 -0
  46. package/functions-v2/ModuleBE_FirestoreListener.js +21 -0
  47. package/functions-v2/ModuleBE_PubSubFunction.d.ts +13 -0
  48. package/functions-v2/ModuleBE_PubSubFunction.js +47 -0
  49. package/functions-v2/ModuleBE_StorageListener.d.ts +13 -0
  50. package/functions-v2/ModuleBE_StorageListener.js +34 -0
  51. package/index.d.ts +21 -0
  52. package/index.js +38 -0
  53. package/package.json +75 -0
  54. package/push/PushMessagesWrapperBE.d.ts +14 -0
  55. package/push/PushMessagesWrapperBE.js +44 -0
  56. package/push/types.d.ts +3 -0
  57. package/push/types.js +18 -0
  58. package/storage/StorageWrapperBE.d.ts +63 -0
  59. package/storage/StorageWrapperBE.js +246 -0
  60. package/storage/emulator.d.ts +4 -0
  61. package/storage/emulator.js +46 -0
  62. package/storage/types.d.ts +4 -0
  63. package/storage/types.js +18 -0
  64. package/tools/lock-operation.d.ts +10 -0
  65. package/tools/lock-operation.js +35 -0
package/index.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ export * from './auth/firebase-session.js';
2
+ export * from './auth/FirebaseSession_Admin.js';
3
+ export * from './functions/firebase-function.js';
4
+ export * from './ModuleBE_Firebase.js';
5
+ export * from './firestore/FirestoreInterface.js';
6
+ export * from './firestore/FirestoreCollection.js';
7
+ export * from './firestore/FirestoreTransaction.js';
8
+ export * from './firestore/FirestoreWrapperBE.js';
9
+ export * from './firestore/types.js';
10
+ export * from './database/DatabaseWrapperBE.js';
11
+ export * from './storage/types.js';
12
+ export * from './storage/StorageWrapperBE.js';
13
+ export * from './push/PushMessagesWrapperBE.js';
14
+ export * from './push/types.js';
15
+ export * from './functions-v2/ModuleBE_BaseFunction.js';
16
+ export * from './functions-v2/ModuleBE_ExpressFunction_V2.js';
17
+ export * from './functions-v2/ModuleBE_FirebaseDBListener.js';
18
+ export * from './functions-v2/ModuleBE_FirebaseScheduler.js';
19
+ export * from './functions-v2/ModuleBE_FirestoreListener.js';
20
+ export * from './functions-v2/ModuleBE_PubSubFunction.js';
21
+ export * from './functions-v2/ModuleBE_StorageListener.js';
package/index.js ADDED
@@ -0,0 +1,38 @@
1
+ /*
2
+ * Firebase is a simpler Typescript wrapper to all of firebase services.
3
+ *
4
+ * Copyright (C) 2020 Adam van der Kruk aka TacB0sS
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+ export * from './auth/firebase-session.js';
19
+ export * from './auth/FirebaseSession_Admin.js';
20
+ export * from './functions/firebase-function.js';
21
+ export * from './ModuleBE_Firebase.js';
22
+ export * from './firestore/FirestoreInterface.js';
23
+ export * from './firestore/FirestoreCollection.js';
24
+ export * from './firestore/FirestoreTransaction.js';
25
+ export * from './firestore/FirestoreWrapperBE.js';
26
+ export * from './firestore/types.js';
27
+ export * from './database/DatabaseWrapperBE.js';
28
+ export * from './storage/types.js';
29
+ export * from './storage/StorageWrapperBE.js';
30
+ export * from './push/PushMessagesWrapperBE.js';
31
+ export * from './push/types.js';
32
+ export * from './functions-v2/ModuleBE_BaseFunction.js';
33
+ export * from './functions-v2/ModuleBE_ExpressFunction_V2.js';
34
+ export * from './functions-v2/ModuleBE_FirebaseDBListener.js';
35
+ export * from './functions-v2/ModuleBE_FirebaseScheduler.js';
36
+ export * from './functions-v2/ModuleBE_FirestoreListener.js';
37
+ export * from './functions-v2/ModuleBE_PubSubFunction.js';
38
+ export * from './functions-v2/ModuleBE_StorageListener.js';
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@nu-art/firebase-backend",
3
+ "version": "0.400.5",
4
+ "description": "Storm - Express & Typescript based backend framework Backend",
5
+ "keywords": [
6
+ "TacB0sS",
7
+ "infra",
8
+ "nu-art",
9
+ "storm",
10
+ "thunderstorm",
11
+ "typescript"
12
+ ],
13
+ "homepage": "https://github.com/nu-art-js/firebase",
14
+ "bugs": {
15
+ "url": "https://github.com/nu-art-js/firebase/issues"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+ssh://git@github.com:nu-art-js/firebase.git"
20
+ },
21
+ "publishConfig": {
22
+ "directory": "dist",
23
+ "linkDirectory": true
24
+ },
25
+ "license": "Apache-2.0",
26
+ "author": "TacB0sS",
27
+ "files": [
28
+ "**/*"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsc",
32
+ "run-tests": "firebase emulators:exec \"npm run test\"",
33
+ "test": "ts-mocha -w -p src/test/tsconfig.json --timeout 0 --inspect=8107 --watch-files '**/*.ts' src/test/firestore-v2/__test.ts",
34
+ "rtv2": "firebase emulators:exec \"npm run test-v2\"",
35
+ "test-v2": "ts-mocha -w -p src/test/tsconfig.json --timeout 0 --inspect=8107 --watch-files '**/*.ts' src/test/firestore-v2/__test.ts"
36
+ },
37
+ "dependencies": {
38
+ "@nu-art/firebase-shared": "0.400.5",
39
+ "@google-cloud/common": "^5.0.2",
40
+ "@google-cloud/firestore": "^7.11.0",
41
+ "@google-cloud/storage": "^7.15.0",
42
+ "@nu-art/google-services-backend": "0.400.5",
43
+ "@nu-art/ts-common": "0.400.5",
44
+ "express": "^4.18.2",
45
+ "firebase": "^11.9.0",
46
+ "firebase-admin": "13.4.0",
47
+ "firebase-functions": "6.3.2",
48
+ "google-auth-library": "^10.0.0",
49
+ "http-proxy": "1.18.1",
50
+ "url": "0.11.3",
51
+ "ws": "^8.13.0"
52
+ },
53
+ "devDependencies": {
54
+ "@types/compression": "^1.0.1",
55
+ "@types/chai": "^4.3.4",
56
+ "@types/mocha": "^10.0.1",
57
+ "@types/http-proxy": "1.17.14",
58
+ "teeny-request": "~7.2.0",
59
+ "@types/ws": "^8.5.5"
60
+ },
61
+ "unitConfig": {
62
+ "type": "typescript-lib"
63
+ },
64
+ "type": "module",
65
+ "exports": {
66
+ ".": {
67
+ "types": "./index.d.ts",
68
+ "import": "./index.js"
69
+ },
70
+ "./*": {
71
+ "types": "./*.d.ts",
72
+ "import": "./*.js"
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,14 @@
1
+ import { FirebaseType_BatchResponse, FirebaseType_SubscriptionResponse } from './types.js';
2
+ import { FirebaseBaseWrapper } from '../auth/FirebaseBaseWrapper.js';
3
+ import { FirebaseSession } from '../auth/firebase-session.js';
4
+ import { Message, MulticastMessage } from 'firebase-admin/messaging';
5
+ export declare class PushMessagesWrapperBE extends FirebaseBaseWrapper {
6
+ private readonly messaging;
7
+ constructor(firebaseSession: FirebaseSession<any>);
8
+ send(message: Message, dryRun?: boolean): Promise<string>;
9
+ sendAll(messages: Message[], dryRun?: boolean): Promise<FirebaseType_BatchResponse>;
10
+ sendMultiCast(messages: MulticastMessage, dryRun?: boolean): Promise<FirebaseType_BatchResponse>;
11
+ subscribeToTopic(tokens: string[], topic: string): Promise<FirebaseType_SubscriptionResponse>;
12
+ unsubscribeFromTopic(tokens: string[], topic: string): Promise<FirebaseType_SubscriptionResponse>;
13
+ isEmulator(): boolean;
14
+ }
@@ -0,0 +1,44 @@
1
+ /*
2
+ * Firebase is a simpler Typescript wrapper to all of firebase services.
3
+ *
4
+ * Copyright (C) 2020 Adam van der Kruk aka TacB0sS
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+ import { FirebaseBaseWrapper } from '../auth/FirebaseBaseWrapper.js';
19
+ import { getMessaging } from 'firebase-admin/messaging';
20
+ export class PushMessagesWrapperBE extends FirebaseBaseWrapper {
21
+ messaging;
22
+ constructor(firebaseSession) {
23
+ super(firebaseSession);
24
+ this.messaging = getMessaging(firebaseSession.app);
25
+ }
26
+ async send(message, dryRun) {
27
+ return this.messaging.send(message, dryRun);
28
+ }
29
+ async sendAll(messages, dryRun) {
30
+ return this.messaging.sendEach(messages, dryRun);
31
+ }
32
+ async sendMultiCast(messages, dryRun) {
33
+ return this.messaging.sendEachForMulticast(messages, dryRun);
34
+ }
35
+ async subscribeToTopic(tokens, topic) {
36
+ return this.messaging.subscribeToTopic(tokens, topic);
37
+ }
38
+ async unsubscribeFromTopic(tokens, topic) {
39
+ return this.messaging.unsubscribeFromTopic(tokens, topic);
40
+ }
41
+ isEmulator() {
42
+ return false;
43
+ }
44
+ }
@@ -0,0 +1,3 @@
1
+ import { BatchResponse, MessagingTopicManagementResponse } from 'firebase-admin/messaging';
2
+ export type FirebaseType_BatchResponse = BatchResponse;
3
+ export type FirebaseType_SubscriptionResponse = MessagingTopicManagementResponse;
package/push/types.js ADDED
@@ -0,0 +1,18 @@
1
+ /*
2
+ * Firebase is a simpler Typescript wrapper to all of firebase services.
3
+ *
4
+ * Copyright (C) 2020 Adam van der Kruk aka TacB0sS
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+ export {};
@@ -0,0 +1,63 @@
1
+ import { Bucket, CreateReadStreamOptions, CreateWriteStreamOptions, File, FileMetadata, MakeFilePublicResponse } from '@google-cloud/storage';
2
+ import { FirebaseSession } from '../auth/firebase-session.js';
3
+ import { FirebaseBaseWrapper } from '../auth/FirebaseBaseWrapper.js';
4
+ import { Response } from 'teeny-request';
5
+ import { Writable } from 'stream';
6
+ export declare const END_OF_STREAM: {
7
+ END_OF_STREAM: string;
8
+ };
9
+ export declare class StorageWrapperBE extends FirebaseBaseWrapper {
10
+ private storage;
11
+ constructor(firebaseSession: FirebaseSession<any>);
12
+ getMainBucket(): Promise<BucketWrapper>;
13
+ getOrCreateBucket(bucketName?: string): Promise<BucketWrapper>;
14
+ getFile(pathToRemoteFile: string, bucketName?: string): Promise<FileWrapper>;
15
+ isEmulator(): boolean;
16
+ }
17
+ export declare class BucketWrapper {
18
+ readonly bucketName: string;
19
+ readonly bucket: Bucket;
20
+ readonly storage: StorageWrapperBE;
21
+ private constructor();
22
+ getFile(pathToRemoteFile: string): Promise<FileWrapper>;
23
+ listFiles(folder?: string, filter?: (file: File) => boolean): Promise<File[]>;
24
+ getBucketName(): string;
25
+ deleteFiles(folder?: string, filter?: (file: File) => boolean): Promise<void>;
26
+ private iterateOverFiles;
27
+ }
28
+ export declare class FileWrapper {
29
+ static emulatorStorageProxy: string;
30
+ readonly file: File;
31
+ readonly path: string;
32
+ readonly bucket: BucketWrapper;
33
+ private readonly isEmulator?;
34
+ constructor(path: string, file: File, bucket: BucketWrapper, isEmulator?: boolean);
35
+ getWriteSignedUrl(contentType: string, expiresInMs: number): Promise<{
36
+ fileName: string;
37
+ signedUrl: string;
38
+ publicUrl: string;
39
+ } | {
40
+ fileName: string;
41
+ signedUrl: string;
42
+ }>;
43
+ getReadSignedUrl(expiresInMs?: number, contentType?: string): Promise<{
44
+ fileName: string;
45
+ signedUrl: string;
46
+ publicUrl: string;
47
+ }>;
48
+ exists(): Promise<boolean>;
49
+ write(data: string | number | object | boolean): Promise<void>;
50
+ read(): Promise<Buffer>;
51
+ copy(destination: string | BucketWrapper | FileWrapper): Promise<File>;
52
+ private copyImpl;
53
+ private copyByStream;
54
+ move(destination: string | BucketWrapper | FileWrapper): Promise<void>;
55
+ delete(): Promise<[Response<any>] | undefined>;
56
+ private getSignedUrl;
57
+ writeToStream(feeder: (writable: Writable) => Promise<void | typeof END_OF_STREAM>): Promise<void>;
58
+ createWriteStream(options?: CreateWriteStreamOptions): Writable;
59
+ createReadStream(options?: CreateReadStreamOptions): import("stream").Readable;
60
+ makePublic(): Promise<MakeFilePublicResponse>;
61
+ setMetadata(metadata: FileMetadata, options?: object): Promise<FileMetadata>;
62
+ getMetadata(options?: object): Promise<FileMetadata>;
63
+ }
@@ -0,0 +1,246 @@
1
+ /*
2
+ * Firebase is a simpler Typescript wrapper to all of firebase services.
3
+ *
4
+ * Copyright (C) 2020 Adam van der Kruk aka TacB0sS
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+ import { BadImplementationException, currentTimeMillis, Minute, ThisShouldNotHappenException } from '@nu-art/ts-common';
19
+ import { FirebaseBaseWrapper } from '../auth/FirebaseBaseWrapper.js';
20
+ import { getStorage } from 'firebase-admin/storage';
21
+ export const END_OF_STREAM = { END_OF_STREAM: 'END_OF_STREAM' };
22
+ export class StorageWrapperBE extends FirebaseBaseWrapper {
23
+ // readonly storage: FirebaseType_Storage;
24
+ storage;
25
+ constructor(firebaseSession) {
26
+ super(firebaseSession);
27
+ this.storage = getStorage(firebaseSession.app);
28
+ }
29
+ async getMainBucket() {
30
+ return this.getOrCreateBucket();
31
+ }
32
+ async getOrCreateBucket(bucketName) {
33
+ const originBucketName = bucketName;
34
+ const gcpBucketPrefix = 'gs://';
35
+ bucketName ??= `${gcpBucketPrefix}${process.env.GCLOUD_PROJECT}.appspot.com`;
36
+ if (!bucketName.startsWith(gcpBucketPrefix))
37
+ throw new BadImplementationException(`Bucket name MUST start with '${gcpBucketPrefix}', received '${bucketName}'`);
38
+ let bucket = this.storage.bucket(bucketName);
39
+ if (!this.isEmulator())
40
+ bucket = (await bucket.get({ autoCreate: true }))[0];
41
+ this.logWarningBold(`Creating bucket name: ${bucketName},\norigin bucket name: ${originBucketName}\nactual bucket name: ${bucket.name}`);
42
+ // @ts-ignore
43
+ return new BucketWrapper(bucketName, bucket, this);
44
+ }
45
+ async getFile(pathToRemoteFile, bucketName) {
46
+ const bucket = await this.getOrCreateBucket(bucketName);
47
+ return bucket.getFile(pathToRemoteFile);
48
+ }
49
+ isEmulator() {
50
+ return !!(process.env.FIREBASE_STORAGE_EMULATOR_HOST || process.env.FUNCTIONS_EMULATOR) || false;
51
+ }
52
+ }
53
+ export class BucketWrapper {
54
+ bucketName;
55
+ bucket;
56
+ storage;
57
+ constructor(bucketName, bucket, storage) {
58
+ this.bucketName = bucketName;
59
+ this.bucket = bucket;
60
+ this.storage = storage;
61
+ }
62
+ async getFile(pathToRemoteFile) {
63
+ const emulator = this.storage.isEmulator();
64
+ return new FileWrapper(pathToRemoteFile, this.bucket.file(pathToRemoteFile), this, emulator);
65
+ }
66
+ async listFiles(folder = '', filter = () => true) {
67
+ const filteredFiles = [];
68
+ await this.iterateOverFiles(folder, filter, async (file) => filteredFiles.push(file));
69
+ return filteredFiles;
70
+ }
71
+ getBucketName() {
72
+ return this.bucket.name;
73
+ }
74
+ async deleteFiles(folder = '', filter = () => true) {
75
+ await this.iterateOverFiles(folder, filter, (file) => file.delete());
76
+ }
77
+ async iterateOverFiles(folder, filter, action) {
78
+ return new Promise((resolve, reject) => {
79
+ const callback = async (err, files, nextQuery) => {
80
+ if (err)
81
+ return reject(err);
82
+ if (files) {
83
+ await Promise.all(files.filter(filter).map(file => action(file)));
84
+ }
85
+ if (!nextQuery)
86
+ return resolve();
87
+ this.bucket.getFiles(nextQuery, callback);
88
+ };
89
+ this.bucket.getFiles({
90
+ prefix: folder,
91
+ autoPaginate: false
92
+ }, callback);
93
+ });
94
+ }
95
+ }
96
+ export class FileWrapper {
97
+ static emulatorStorageProxy;
98
+ file;
99
+ path;
100
+ bucket;
101
+ isEmulator;
102
+ constructor(path, file, bucket, isEmulator) {
103
+ this.file = file;
104
+ this.bucket = bucket;
105
+ this.path = path;
106
+ this.isEmulator = isEmulator;
107
+ }
108
+ async getWriteSignedUrl(contentType, expiresInMs) {
109
+ const options = {
110
+ action: 'write',
111
+ contentType: contentType,
112
+ expires: currentTimeMillis() + expiresInMs,
113
+ };
114
+ if (this.isEmulator) {
115
+ const signedUrl = `${(FileWrapper.emulatorStorageProxy)}/emulatorUpload?path=${encodeURIComponent(this.path)}`;
116
+ return {
117
+ fileName: this.path,
118
+ signedUrl: signedUrl,
119
+ };
120
+ }
121
+ return this.getSignedUrl(options);
122
+ }
123
+ async getReadSignedUrl(expiresInMs = 5 * Minute, contentType) {
124
+ const options = {
125
+ action: 'read',
126
+ contentType,
127
+ expires: currentTimeMillis() + expiresInMs,
128
+ };
129
+ if (this.isEmulator) {
130
+ return {
131
+ fileName: this.path,
132
+ signedUrl: this.file.publicUrl(), // this works for read operations locally
133
+ publicUrl: this.file.publicUrl()
134
+ };
135
+ }
136
+ return this.getSignedUrl(options);
137
+ }
138
+ async exists() {
139
+ return (await this.file.exists())[0];
140
+ }
141
+ async write(data) {
142
+ if (Buffer.isBuffer(data))
143
+ return this.file.save(data);
144
+ switch (typeof data) {
145
+ case 'function':
146
+ case 'symbol':
147
+ case 'bigint':
148
+ case 'undefined':
149
+ throw new BadImplementationException(`Cannot save file: ${this.file.name}, data is ${typeof data}`);
150
+ case 'object':
151
+ return this.file.save(JSON.stringify(data));
152
+ case 'boolean':
153
+ case 'number':
154
+ case 'string':
155
+ return this.file.save(`${data}`);
156
+ }
157
+ }
158
+ async read() {
159
+ const downloadResponse = await this.file.download();
160
+ return downloadResponse[0];
161
+ }
162
+ async copy(destination) {
163
+ const copy = await this.copyImpl(destination);
164
+ return copy[0];
165
+ }
166
+ copyImpl(destination) {
167
+ if (typeof destination === 'string')
168
+ return this.file.copy(destination);
169
+ const bucketWrapper = destination instanceof FileWrapper ? destination.bucket : destination;
170
+ if (this.bucket.storage.firebaseSession !== bucketWrapper.storage.firebaseSession)
171
+ return this.copyByStream(destination);
172
+ if (destination instanceof FileWrapper)
173
+ return this.file.copy(destination.file);
174
+ return this.file.copy(destination.bucket);
175
+ }
176
+ async copyByStream(destination) {
177
+ const destinationFile = destination instanceof FileWrapper ? destination.file : (await destination.getFile(this.path)).file;
178
+ return new Promise((resolve, reject) => {
179
+ this
180
+ .file
181
+ .createReadStream()
182
+ .pipe(destinationFile
183
+ .createWriteStream()
184
+ .on('error', reject)
185
+ .on('finish', () => resolve([destinationFile, undefined])))
186
+ .on('error', reject);
187
+ });
188
+ }
189
+ async move(destination) {
190
+ const file = await this.copy(destination);
191
+ try {
192
+ await this.delete();
193
+ }
194
+ catch (e) {
195
+ try {
196
+ await file.delete();
197
+ }
198
+ catch (err) {
199
+ throw new ThisShouldNotHappenException('Error during the deletion of the recently copied file, check the attached error', err);
200
+ }
201
+ throw new BadImplementationException('Error during the deletion of the file after a successful copy, attached error stack', e);
202
+ }
203
+ }
204
+ async delete() {
205
+ if (!await this.file.exists())
206
+ return;
207
+ return this.file.delete();
208
+ }
209
+ async getSignedUrl(options) {
210
+ const results = await this.file.getSignedUrl(options);
211
+ const url = results[0]; // An array with a single string is returned
212
+ return {
213
+ fileName: this.path,
214
+ signedUrl: url,
215
+ publicUrl: encodeURI(`https://storage.googleapis.com/${this.bucket.bucketName.replace(`gs://`, '')}${this.path}`)
216
+ };
217
+ }
218
+ async writeToStream(feeder) {
219
+ const writeable = this.createWriteStream({ gzip: true });
220
+ const promise = new Promise((resolve, reject) => {
221
+ writeable.on('close', () => resolve());
222
+ writeable.on('error', (e) => reject(e));
223
+ });
224
+ let data;
225
+ do {
226
+ data = await feeder(writeable);
227
+ } while (data !== END_OF_STREAM);
228
+ writeable.end();
229
+ return promise;
230
+ }
231
+ createWriteStream(options) {
232
+ return this.file.createWriteStream(options);
233
+ }
234
+ createReadStream(options) {
235
+ return this.file.createReadStream(options);
236
+ }
237
+ async makePublic() {
238
+ return this.file.makePublic();
239
+ }
240
+ async setMetadata(metadata, options) {
241
+ return (await this.file.setMetadata(metadata, options))[0];
242
+ }
243
+ async getMetadata(options) {
244
+ return (await this.file.getMetadata(options))[0];
245
+ }
246
+ }
@@ -0,0 +1,4 @@
1
+ export declare const firebaseStorageEmulatorProxy: {
2
+ emulatorUpload: import("firebase-functions/https").HttpsFunction;
3
+ emulatorDownload: import("firebase-functions/https").HttpsFunction;
4
+ };
@@ -0,0 +1,46 @@
1
+ import { onRequest } from 'firebase-functions/v2/https';
2
+ import { ModuleBE_Firebase } from '../ModuleBE_Firebase.js';
3
+ import { HttpCodes } from '@nu-art/ts-common/core/exceptions/http-codes';
4
+ export const firebaseStorageEmulatorProxy = {
5
+ emulatorUpload: onRequest({ maxInstances: 10, concurrency: 1, }, async (request, response) => {
6
+ if (request.method.toLowerCase() !== 'put')
7
+ response.send('method not supported').end(HttpCodes._4XX.METHOD_NOT_ALLOWED.code);
8
+ const pathToFile = request.query['path'];
9
+ const file = await ModuleBE_Firebase.createAdminSession().getStorage().getFile(String(pathToFile));
10
+ const writable = file.createWriteStream({ gzip: true });
11
+ request.pipe(writable)
12
+ .on('error', (err) => {
13
+ console.error('Stream error:', err);
14
+ response.status(500).send('Error uploading file');
15
+ })
16
+ .on('finish', () => {
17
+ response.status(200).send('File uploaded successfully');
18
+ });
19
+ }),
20
+ emulatorDownload: onRequest({ maxInstances: 10, concurrency: 1, }, async (request, response) => {
21
+ if (request.method.toLowerCase() !== 'get')
22
+ response.send('method not supported').end(HttpCodes._4XX.METHOD_NOT_ALLOWED.code);
23
+ const pathToFile = request.query['path'];
24
+ const contentType = request.query['content-type'];
25
+ const fileName = request.query['file-name'];
26
+ const file = await ModuleBE_Firebase.createAdminSession().getStorage().getFile(String(pathToFile));
27
+ file.createReadStream({ decompress: false })
28
+ .on('error', (err) => {
29
+ console.error('Stream error:', err);
30
+ response.status(500).send('Error downloading file');
31
+ })
32
+ .on('response', (fileResponse) => {
33
+ console.log('response', fileResponse);
34
+ Object.keys(fileResponse.headers).forEach(key => {
35
+ response.set(key, fileResponse.headers[key]);
36
+ });
37
+ response.set('content-disposition', `attachment; filename=${fileName}`);
38
+ response.set('content-type', contentType || fileResponse.headers['content-type']);
39
+ })
40
+ .on('end', () => {
41
+ console.log('File download completed.');
42
+ response.end();
43
+ })
44
+ .pipe(response);
45
+ })
46
+ };
@@ -0,0 +1,4 @@
1
+ import { CopyResponse } from '@google-cloud/storage';
2
+ import { Storage } from 'firebase-admin/storage';
3
+ export type FirebaseType_Storage = Storage;
4
+ export type Firebase_CopyResponse = [CopyResponse[0], CopyResponse[1] | undefined];
@@ -0,0 +1,18 @@
1
+ /*
2
+ * Firebase is a simpler Typescript wrapper to all of firebase services.
3
+ *
4
+ * Copyright (C) 2020 Adam van der Kruk aka TacB0sS
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+ export {};
@@ -0,0 +1,10 @@
1
+ import { CustomException } from '@nu-art/ts-common';
2
+ /**
3
+ * # <ins>OperationLockedException</ins>
4
+ * This class inherits {@link CustomException} and functions like it, after setting the exceptionType property as "OperationLockedException",
5
+ * @category - Exceptions
6
+ */
7
+ export declare class OperationLockedException extends CustomException {
8
+ constructor(message: string, cause?: Error);
9
+ }
10
+ export declare function lockedOperation<T>(key: string, timeout: number, action: () => Promise<T>, exception?: CustomException): Promise<T>;
@@ -0,0 +1,35 @@
1
+ import { currentTimeMillis, CustomException } from '@nu-art/ts-common';
2
+ import { ModuleBE_Firebase } from '../ModuleBE_Firebase.js';
3
+ /**
4
+ * # <ins>OperationLockedException</ins>
5
+ * This class inherits {@link CustomException} and functions like it, after setting the exceptionType property as "OperationLockedException",
6
+ * @category - Exceptions
7
+ */
8
+ export class OperationLockedException extends CustomException {
9
+ constructor(message, cause) {
10
+ super(OperationLockedException, message, cause);
11
+ }
12
+ }
13
+ const FirebaseLockState_LOCKED = 'LOCKED';
14
+ const FirebaseLockState_UNLOCKED = 'UNLOCKED';
15
+ export async function lockedOperation(key, timeout, action, exception = new OperationLockedException('This action is already in progress')) {
16
+ const ref = ModuleBE_Firebase.createAdminSession().getDatabase().ref(`/lock/${key}`);
17
+ const now = currentTimeMillis();
18
+ const transactionResult = await ref.transaction(current => {
19
+ // Check if the lock is held and not expired
20
+ if (current && current.status === FirebaseLockState_LOCKED && now < current.timeout) {
21
+ return current; // Returning current to keep the existing value
22
+ }
23
+ // Lock is either not held or expired, so we can acquire it
24
+ return { status: FirebaseLockState_LOCKED, timeout: now + timeout };
25
+ });
26
+ // Check if the transaction was committed and lock was acquired
27
+ if (!transactionResult.committed || transactionResult.value.status !== FirebaseLockState_LOCKED)
28
+ throw exception; // Lock could not be acquired
29
+ try {
30
+ return await action();
31
+ }
32
+ finally {
33
+ await ref.patch({ status: FirebaseLockState_UNLOCKED }); // Setting the status to 'UNLOCKED'
34
+ }
35
+ }