@genoacms/adapter-gcp 0.3.9 → 0.4.1-fix.1

Sign up to get free protection for your applications and to get access to all the features.
package/dist/config.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- declare const _default: Config<object, object, object>;
1
+ declare const _default: Config<object, object, object, object>;
2
2
  export default _default;
@@ -0,0 +1,3 @@
1
+ import type { isEmailAdmins as isEmailAdminsT } from '@genoacms/cloudabstraction/authorization';
2
+ declare const isEmailAdmins: isEmailAdminsT;
3
+ export { isEmailAdmins };
@@ -0,0 +1,22 @@
1
+ import { ProjectsClient } from '@google-cloud/resource-manager';
2
+ import config from '../../config.js';
3
+ const resourceManager = new ProjectsClient({
4
+ projectId: config.authorization.projectId,
5
+ credentials: config.authorization.credentials
6
+ });
7
+ const projectId = config.authorization.projectId;
8
+ const isEmailAdmins = async (email) => {
9
+ const resource = `projects/${projectId}`;
10
+ const role = resource + '/roles/genoacms';
11
+ const data = await resourceManager.getIamPolicy({ resource });
12
+ const policy = data[0];
13
+ if ((policy.bindings) == null)
14
+ throw new Error('no-bindings');
15
+ const adminRole = policy.bindings.find(binding => binding.role === role);
16
+ if (adminRole == null)
17
+ throw new Error('no-admin-role');
18
+ if (adminRole.members == null)
19
+ throw new Error('no-principals');
20
+ return adminRole.members.includes(`user:${email}`);
21
+ };
22
+ export { isEmailAdmins };
@@ -1,7 +1,9 @@
1
- import type { storage as storageT } from '@genoacms/cloudabstraction';
2
- declare const getObject: storageT.getObject;
3
- declare const uploadObject: storageT.uploadObject;
4
- declare const deleteObject: storageT.deleteObject;
5
- declare const listDirectory: storageT.listDirectory;
6
- declare const createDirectory: storageT.createDirectory;
7
- export { getObject, uploadObject, deleteObject, listDirectory, createDirectory };
1
+ import type { Adapter } from '@genoacms/cloudabstraction/storage';
2
+ declare const getObject: Adapter['getObject'];
3
+ declare const getPublicURL: Adapter['getPublicURL'];
4
+ declare const getSignedURL: Adapter['getSignedURL'];
5
+ declare const uploadObject: Adapter['uploadObject'];
6
+ declare const deleteObject: Adapter['deleteObject'];
7
+ declare const listDirectory: Adapter['listDirectory'];
8
+ declare const createDirectory: Adapter['createDirectory'];
9
+ export { getObject, getPublicURL, getSignedURL, uploadObject, deleteObject, listDirectory, createDirectory };
@@ -16,6 +16,22 @@ const getObject = async ({ bucket, name }) => {
16
16
  data: file.createReadStream()
17
17
  };
18
18
  };
19
+ const getPublicURL = async ({ bucket, name }) => {
20
+ const bucketInstance = getBucket(bucket);
21
+ const file = bucketInstance.file(name);
22
+ return file.publicUrl();
23
+ };
24
+ const getSignedURL = async ({ bucket, name }) => {
25
+ const bucketInstance = getBucket(bucket);
26
+ const file = bucketInstance.file(name);
27
+ const expires = new Date();
28
+ expires.setTime(expires.getTime() + 60 * 60 * 1_000);
29
+ const [url] = await file.getSignedUrl({
30
+ action: 'read',
31
+ expires
32
+ });
33
+ return url;
34
+ };
19
35
  const uploadObject = async ({ bucket, name }, stream) => {
20
36
  const bucketInstance = getBucket(bucket);
21
37
  const file = bucketInstance.file(name);
@@ -35,7 +51,8 @@ const listDirectory = async ({ bucket, name }, listingParams = {}) => {
35
51
  startOffset: listingParams?.startAfter,
36
52
  delimiter: '/'
37
53
  };
38
- const [files, , apiResponse] = (await bucketInstance.getFiles(options));
54
+ let [files, , apiResponse] = (await bucketInstance.getFiles(options));
55
+ files = files.filter((file) => !file.name.endsWith('.folderPlaceholder'));
39
56
  return {
40
57
  files: files.map((file) => {
41
58
  return {
@@ -53,4 +70,4 @@ const createDirectory = async ({ bucket, name }) => {
53
70
  const file = bucketInstance.file(`${name}/.folderPlaceholder`);
54
71
  await file.save('');
55
72
  };
56
- export { getObject, uploadObject, deleteObject, listDirectory, createDirectory };
73
+ export { getObject, getPublicURL, getSignedURL, uploadObject, deleteObject, listDirectory, createDirectory };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genoacms/adapter-gcp",
3
- "version": "0.3.9",
3
+ "version": "0.4.1-fix.1",
4
4
  "description": "Implementation of abstraction layer of GenoaCMS for GCP",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,8 +17,9 @@
17
17
  "homepage": "https://github.com/GenoaCMS/adapter-gcp#readme",
18
18
  "type": "module",
19
19
  "dependencies": {
20
- "@genoacms/cloudabstraction": "^0.3.9",
20
+ "@genoacms/cloudabstraction": "^0.4.0",
21
21
  "@google-cloud/firestore": "^7.1.0",
22
+ "@google-cloud/resource-manager": "^5.1.0",
22
23
  "@google-cloud/storage": "^7.7.0"
23
24
  },
24
25
  "devDependencies": {
@@ -37,9 +38,9 @@
37
38
  "dist"
38
39
  ],
39
40
  "exports": {
40
- "./auth": {
41
- "import": "./dist/services/auth/index.js",
42
- "types": "./dist/services/auth/index.d.ts"
41
+ "./authorization": {
42
+ "import": "./dist/services/authorization/index.js",
43
+ "types": "./dist/services/authorization/index.d.ts"
43
44
  },
44
45
  "./database": {
45
46
  "import": "./dist/services/database/index.js",
@@ -0,0 +1,25 @@
1
+ import { ProjectsClient } from '@google-cloud/resource-manager'
2
+ import config from '../../config.js'
3
+ import type { isEmailAdmins as isEmailAdminsT } from '@genoacms/cloudabstraction/authorization'
4
+
5
+ const resourceManager = new ProjectsClient({
6
+ projectId: config.authorization.projectId,
7
+ credentials: config.authorization.credentials
8
+ })
9
+ const projectId = config.authorization.projectId
10
+
11
+ const isEmailAdmins: isEmailAdminsT = async (email: string) => {
12
+ const resource = `projects/${projectId}`
13
+ const role = resource + '/roles/genoacms'
14
+ const data = await resourceManager.getIamPolicy({ resource })
15
+ const policy = data[0]
16
+ if ((policy.bindings) == null) throw new Error('no-bindings')
17
+ const adminRole = policy.bindings.find(binding => binding.role === role)
18
+ if (adminRole == null) throw new Error('no-admin-role')
19
+ if (adminRole.members == null) throw new Error('no-principals')
20
+ return adminRole.members.includes(`user:${email}`)
21
+ }
22
+
23
+ export {
24
+ isEmailAdmins
25
+ }
@@ -1,6 +1,7 @@
1
1
  import type {
2
- storage as storageT
3
- } from '@genoacms/cloudabstraction'
2
+ StorageObject,
3
+ Adapter
4
+ } from '@genoacms/cloudabstraction/storage'
4
5
  import { type Bucket, Storage, type File } from '@google-cloud/storage'
5
6
  import config from '../../config.js'
6
7
 
@@ -14,7 +15,7 @@ const getBucket = (name: string): Bucket => {
14
15
  return bucket
15
16
  }
16
17
 
17
- const getObject: storageT.getObject = async ({ bucket, name }) => {
18
+ const getObject: Adapter['getObject'] = async ({ bucket, name }) => {
18
19
  const bucketInstance = getBucket(bucket)
19
20
  const file = bucketInstance.file(name)
20
21
 
@@ -23,19 +24,37 @@ const getObject: storageT.getObject = async ({ bucket, name }) => {
23
24
  }
24
25
  }
25
26
 
26
- const uploadObject: storageT.uploadObject = async ({ bucket, name }, stream) => {
27
+ const getPublicURL: Adapter['getPublicURL'] = async ({ bucket, name }) => {
28
+ const bucketInstance = getBucket(bucket)
29
+ const file = bucketInstance.file(name)
30
+ return file.publicUrl()
31
+ }
32
+
33
+ const getSignedURL: Adapter['getSignedURL'] = async ({ bucket, name }) => {
34
+ const bucketInstance = getBucket(bucket)
35
+ const file = bucketInstance.file(name)
36
+ const expires = new Date()
37
+ expires.setTime(expires.getTime() + 60 * 60 * 1_000)
38
+ const [url] = await file.getSignedUrl({
39
+ action: 'read',
40
+ expires
41
+ })
42
+ return url
43
+ }
44
+
45
+ const uploadObject: Adapter['uploadObject'] = async ({ bucket, name }, stream) => {
27
46
  const bucketInstance = getBucket(bucket)
28
47
  const file = bucketInstance.file(name)
29
48
  await file.save(stream)
30
49
  }
31
50
 
32
- const deleteObject: storageT.deleteObject = async ({ bucket, name }) => {
51
+ const deleteObject: Adapter['deleteObject'] = async ({ bucket, name }) => {
33
52
  const bucketInstance = getBucket(bucket)
34
53
  const file = bucketInstance.file(name)
35
54
  await file.delete()
36
55
  }
37
56
 
38
- const listDirectory: storageT.listDirectory = async ({ bucket, name }, listingParams = {}) => {
57
+ const listDirectory: Adapter['listDirectory'] = async ({ bucket, name }, listingParams = {}) => {
39
58
  const bucketInstance = getBucket(bucket)
40
59
  const options = {
41
60
  autoPaginate: false,
@@ -43,9 +62,12 @@ const listDirectory: storageT.listDirectory = async ({ bucket, name }, listingPa
43
62
  maxResults: listingParams?.limit,
44
63
  startOffset: listingParams?.startAfter,
45
64
  delimiter: '/'
65
+
46
66
  }
47
- const [files, , apiResponse] =
48
- (await bucketInstance.getFiles(options)) as [File[], object, { prefixes: string[] } | undefined ]
67
+ let [files, , apiResponse] =
68
+ (await bucketInstance.getFiles(options)) as [File[], object, { prefixes: string[] } | undefined]
69
+ files = files.filter((file) => !file.name.endsWith('.folderPlaceholder'))
70
+
49
71
  return {
50
72
  files: files.map((file) => {
51
73
  return {
@@ -53,13 +75,13 @@ const listDirectory: storageT.listDirectory = async ({ bucket, name }, listingPa
53
75
  // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
54
76
  size: file.metadata.size ? parseInt(file.metadata.size as string) : 0,
55
77
  lastModified: new Date(file.metadata.updated as string)
56
- } satisfies storageT.StorageObject
78
+ } satisfies StorageObject
57
79
  }),
58
80
  directories: apiResponse?.prefixes ?? []
59
81
  }
60
82
  }
61
83
 
62
- const createDirectory: storageT.createDirectory = async ({ bucket, name }) => {
84
+ const createDirectory: Adapter['createDirectory'] = async ({ bucket, name }) => {
63
85
  const bucketInstance = getBucket(bucket)
64
86
  const file = bucketInstance.file(`${name}/.folderPlaceholder`)
65
87
  await file.save('')
@@ -67,6 +89,8 @@ const createDirectory: storageT.createDirectory = async ({ bucket, name }) => {
67
89
 
68
90
  export {
69
91
  getObject,
92
+ getPublicURL,
93
+ getSignedURL,
70
94
  uploadObject,
71
95
  deleteObject,
72
96
  listDirectory,
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
File without changes