Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
itsnotrisky committed Feb 3, 2020
0 parents commit cda1f8c
Show file tree
Hide file tree
Showing 33 changed files with 1,981 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# @pozible/signed-upload
Use [Signed URLs](https://cloud.google.com/storage/docs/access-control/signed-urls) to upload a file straight to Cloud Storage. It returns `optimisticUrl` to access the file once it is uploaded.

## Usage
```
import signedUpload, { getUrls, upload } from '@pozible/signed-upload'
const {optimisticUrl, signedUrl} = await getUrls(metadata, filePath, config)
await upload(file, metadata, signedUrl)
// Or bootstrap the two commands with
const {optimisticUrl} = await signedUpload(file, metadata, filePath, config)
```

## Arguments
### `metadata`
`dimensions` attribute is used together with Cloud Function listening on bucket's changes to further process the image. Omit it when uploading generic files.
```
{
dimensions: {
imageUrl: {
width,
height
},
thumbUrl: {
...
},
...
},
filename: 'original-filename.ext',
mimetype: 'valid/type'
}
```

### `filePath`
A function to provide a final path to the file in your bucket with uuid as a parameter to give random value to the file name. No need to add extension as it will be automatically added based on given filename in `metadata`.
```
const filePath = randomId => `/path/to/your/file-with-${randomId}`
const filePath = () => `/path/to/your/original-file-name`
```

### `config`
- `baseUrl` (optional): Add baseUrl to be added to `optimisticPath` to form complete `optimisticUrl`.
- `bucketName`: When it is deployed to Google Cloud Platform it will use current project, else specify one here.
3 changes: 3 additions & 0 deletions dist/getOptimisticPath.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { IDictionary, Metadata } from '../module';
declare const _default: (metadata: Metadata, filePath: string) => IDictionary<string>;
export default _default;
15 changes: 15 additions & 0 deletions dist/getOptimisticPath.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dist/getOptimisticPath.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions dist/getOptimisticUrl.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { IDictionary, Config } from "../module";
declare const _default: ({ baseUrl, bucketName }: Config, optimisticPath: IDictionary<string>) => IDictionary<string>;
export default _default;
11 changes: 11 additions & 0 deletions dist/getOptimisticUrl.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dist/getOptimisticUrl.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions dist/getSignedUrl.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Metadata } from '../module';
declare const _default: (metadata: Metadata, filePathWithExt: string, bucketName: string) => Promise<import("@google-cloud/storage").GetSignedUrlResponse>;
export default _default;
15 changes: 15 additions & 0 deletions dist/getSignedUrl.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dist/getSignedUrl.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions dist/getUrls.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Metadata, Config } from '../module';
declare const _default: (metadata: Metadata, filePath: any, config: Config) => Promise<{
optimisticPath: import("../module").IDictionary<string>;
optimisticUrl: import("../module").IDictionary<string>;
signedUrl: string;
}>;
export default _default;
30 changes: 30 additions & 0 deletions dist/getUrls.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dist/getUrls.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/// <reference types="node" />
import { Metadata, Config } from '../module';
export declare const getUrls: (metadata: Metadata, filePath: any, config: Config) => Promise<{
optimisticPath: import("../module").IDictionary<string>;
optimisticUrl: import("../module").IDictionary<string>;
signedUrl: string;
}>;
export declare const upload: (file: any, metadata: Metadata, signedUrl: string) => Promise<import("node-fetch").Response>;
declare const _default: (file: File | NodeJS.ReadableStream, metadata: Metadata, filePath: any, config: Config) => Promise<{
optimisticPath: import("../module").IDictionary<string>;
optimisticUrl: import("../module").IDictionary<string>;
signedUrl: string;
}>;
export default _default;
24 changes: 24 additions & 0 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dist/index.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions dist/upload.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Metadata } from '../module';
declare const _default: (file: any, metadata: Metadata, signedUrl: string) => Promise<import("node-fetch").Response>;
export default _default;
17 changes: 17 additions & 0 deletions dist/upload.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dist/upload.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export {default as getUrls} from './dist/getUrls'
export {default as upload} from './dist/upload'
import _signedUpload from './dist/index'
export default _signedUpload
19 changes: 19 additions & 0 deletions module.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export interface IDictionary<TValue> {
[id: string]: TValue
}

export interface Dimension {
width: number
height: number
}

export interface Metadata {
dimensions?:IDictionary<Dimension>
filename:string
mimetype:string
}

export interface Config {
bucketName:string
baseUrl?:string
}
34 changes: 34 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "@pozible/signed-upload",
"version": "1.0.0",
"description": "Bootstrapped file upload.",
"main": "dist/index.js",
"types": "index.d.ts",
"repository": "[email protected]:pozi-team/signed-upload.git",
"author": "spondbob <[email protected]>",
"keywords": [
"signed urls",
"cloud storage"
],
"license": "MIT",
"devDependencies": {
"@types/chai": "^4.2.8",
"@types/mocha": "^7.0.1",
"@types/node-fetch": "^2.5.4",
"@types/uuid": "^3.4.7",
"chai": "^4.2.0",
"mocha": "^7.0.1",
"ts-node": "^8.6.2",
"typescript": "^3.7.5"
},
"dependencies": {
"@google-cloud/storage": "^4.3.0",
"@types/graphql-upload": "^8.0.3",
"node-fetch": "^2.6.0",
"uuid": "^3.4.0"
},
"scripts": {
"build": "tsc --declaration",
"test": "mocha -r ts-node/register tests/**/*.test.ts"
}
}
16 changes: 16 additions & 0 deletions src/getOptimisticPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { IDictionary, Metadata } from '../module'

const getExtension = fileName => fileName.split('.').pop()

export default (metadata:Metadata, filePath:string): IDictionary<string> => {
const ext = getExtension(metadata.filename)
const dimensions = metadata.dimensions || {}

if (!dimensions || Object.keys(dimensions).length === 0) return { url: `${filePath}.${ext}` }

return Object.keys(dimensions).reduce((acc, cur) => {
const { height, width } = dimensions[cur]
acc[cur] = `${filePath}@${width}x${height}.${ext}`
return acc
}, {})
}
10 changes: 10 additions & 0 deletions src/getOptimisticUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { IDictionary, Config } from "../module"

export default ({baseUrl, bucketName}:Config, optimisticPath:IDictionary<string>) => {
const storageUrl = 'https://storage.googleapis.com'
const url = baseUrl || `${storageUrl}/${bucketName}`
return Object.keys(optimisticPath).reduce((acc, cur) => {
acc[cur] = url+'/'+optimisticPath[cur]
return acc
}, {} as IDictionary<string>)
}
22 changes: 22 additions & 0 deletions src/getSignedUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Storage } from '@google-cloud/storage'
import { Metadata } from '../module'

const storage = new Storage()

export default (metadata:Metadata, filePathWithExt:string, bucketName:string) => {
const SECONDS = 10000
const expires = Date.now() + 30 * SECONDS
const extensionHeaders = metadata ? {
extensionHeaders: {'x-goog-meta-data': JSON.stringify(metadata)}
} : {}

const myBucket = storage.bucket(bucketName)
const bucketFile = myBucket.file(filePathWithExt)
return bucketFile.getSignedUrl({
action: 'write',
contentType: metadata.mimetype,
expires,
...extensionHeaders,
version: 'v4',
})
}
19 changes: 19 additions & 0 deletions src/getUrls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import uuidv1 from 'uuid/v1'

import getOptimisticPath from './getOptimisticPath'
import getOptimisticUrl from './getOptimisticUrl'
import getSignedUrl from './getSignedUrl'
import { Metadata, Config } from '../module'

export default async (metadata:Metadata, filePath, config:Config) => {
const fp = filePath(uuidv1())
const optimisticPath = getOptimisticPath(metadata, fp)
const optimisticUrl = getOptimisticUrl(config, optimisticPath)
const [signedUrl] = await getSignedUrl(metadata, optimisticPath.imageUrl || optimisticPath.url, config.bucketName)

return {
optimisticPath,
optimisticUrl,
signedUrl,
}
}
12 changes: 12 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Metadata, Config } from '../module'
import _getUrls from './getUrls'
import _upload from './upload'

export const getUrls = _getUrls
export const upload = _upload

export default async (file:File|NodeJS.ReadableStream, metadata:Metadata, filePath, config:Config) => {
const urls = await getUrls(metadata, filePath, config)
await upload(file, metadata, urls.signedUrl)
return urls
}
13 changes: 13 additions & 0 deletions src/upload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import fetch from 'node-fetch'
import { Metadata } from '../module'

export default (file, metadata:Metadata, signedUrl:string) => {
return fetch(signedUrl, {
body: file,
headers: {
'Content-Type': metadata.mimetype,
'x-goog-meta-data': JSON.stringify(metadata)
},
method: 'PUT',
})
}
Loading

0 comments on commit cda1f8c

Please sign in to comment.