Skip to content

Commit 646bbc9

Browse files
committed
Merge branch 'feature/signed-urls'
2 parents 69fab91 + e65d8e4 commit 646bbc9

5 files changed

Lines changed: 121 additions & 2 deletions

File tree

README.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,39 @@ const isValid = cloudConvert.webhooks.verify(
140140
); // returns true or false
141141
```
142142

143-
## Using Sandbox
143+
## Signed URLs
144+
145+
Signed URLs allow converting files on demand only using URL query parameters. The node.js SDK allows to generate such URLs. Therefore, you need to obtain a signed URL base and a signing secret on the [CloudConvert Dashboard](https://cloudconvert.com/dashboard/api/v2/signed-urls).
146+
147+
```js
148+
const signedUrlBase = 'https://s.cloudconvert.com/...'; // You can find it in your signed URL settings.
149+
const signingSecret = '...'; // You can find it in your signed URL settings.
150+
const cacheKey = 'cache-key'; // Allows caching of the result file for 24h
151+
152+
const job = {
153+
tasks: {
154+
'import-it': {
155+
operation: 'import/url',
156+
url: 'https://some.url',
157+
filename: 'logo.png'
158+
},
159+
'export-it': {
160+
operation: 'export/url',
161+
input: 'import-it',
162+
inline: true
163+
}
164+
}
165+
};
166+
167+
const url = cloudConvert.signedUrls.sign(
168+
signedUrlBase,
169+
signingSecret,
170+
job,
171+
cacheKey
172+
); // returns the generated URL
173+
```
174+
175+
## Using the Sandbox
144176

145177
You can use the Sandbox to avoid consuming your quota while testing your application. The node SDK allows you to do that.
146178

lib/CloudConvert.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import TasksResource, { TaskEventData } from './TasksResource';
55
import UsersResource from './UsersResource';
66
import WebhooksResource from './WebhooksResource';
77
import { version } from '../package.json';
8+
import SignedUrlResource from './SignedUrlResource';
89

910
export default class CloudConvert {
1011
private socket: SocketIOClient.Socket | undefined;
@@ -18,6 +19,7 @@ export default class CloudConvert {
1819
public jobs!: JobsResource;
1920
public users!: UsersResource;
2021
public webhooks!: WebhooksResource;
22+
public signedUrls!: SignedUrlResource;
2123

2224
constructor(apiKey: string, useSandbox = false) {
2325
this.apiKey = apiKey;
@@ -47,6 +49,7 @@ export default class CloudConvert {
4749
this.jobs = new JobsResource(this);
4850
this.users = new UsersResource(this);
4951
this.webhooks = new WebhooksResource(this);
52+
this.signedUrls = new SignedUrlResource(this);
5053
}
5154

5255
subscribe(

lib/JobsResource.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ interface TaskContainer {
127127
[name: string]: PossibleOperations;
128128
}
129129
// Add the other properties that are required for job creation
130-
interface JobTemplate {
130+
export interface JobTemplate {
131131
tasks: TaskContainer;
132132
tag?: string;
133133
}

lib/SignedUrlResource.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import * as crypto from 'crypto';
2+
import CloudConvert from './CloudConvert';
3+
import { JobTemplate } from './JobsResource';
4+
5+
export default class SignedUrlResource {
6+
private readonly cloudConvert: CloudConvert;
7+
8+
constructor(cloudConvert: CloudConvert) {
9+
this.cloudConvert = cloudConvert;
10+
}
11+
12+
sign(
13+
base: string,
14+
signingSecret: string,
15+
job: JobTemplate,
16+
cacheKey: string | null
17+
): string {
18+
const json = JSON.stringify(job);
19+
const base64 = new Buffer(json || '').toString('base64');
20+
const base64UrlSafe = base64
21+
.replace('+', '-')
22+
.replace('/', '_')
23+
.replace(/=+$/, '');
24+
25+
let url = base + '?job=' + base64UrlSafe;
26+
27+
if (cacheKey) {
28+
url += '&cache_key=' + cacheKey;
29+
}
30+
31+
const hmac = crypto.createHmac('sha256', signingSecret);
32+
const signature = hmac.update(Buffer.from(url, 'utf-8')).digest('hex');
33+
34+
url += '&s=' + signature;
35+
36+
return url;
37+
}
38+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import CloudConvert from '../../built/lib/CloudConvert.js';
2+
import { assert } from 'chai';
3+
4+
describe('SignedUrlResource', () => {
5+
beforeEach(() => {
6+
this.cloudConvert = new CloudConvert('test');
7+
});
8+
9+
describe('create()', () => {
10+
it('should create a signed URL', async () => {
11+
const base =
12+
'https://s.cloudconvert.com/b3d85428-584e-4639-bc11-76b7dee9c109';
13+
const signingSecret = 'NT8dpJkttEyfSk3qlRgUJtvTkx64vhyX';
14+
15+
const job = {
16+
tasks: {
17+
'import-it': {
18+
operation: 'import/url',
19+
url: 'https://some.url',
20+
filename: 'logo.png'
21+
},
22+
'export-it': {
23+
operation: 'export/url',
24+
input: 'import-it',
25+
inline: true
26+
}
27+
}
28+
};
29+
30+
const url = this.cloudConvert.signedUrls.sign(
31+
base,
32+
signingSecret,
33+
job,
34+
'mykey'
35+
);
36+
37+
assert.include(url, base);
38+
assert.include(url, '?job=');
39+
assert.include(url, '&cache_key=mykey');
40+
assert.include(
41+
url,
42+
'&s=209d54e4454a407de71a07e6e500f45155fecf58a4e53d68329fbf358efcd823'
43+
);
44+
});
45+
});
46+
});

0 commit comments

Comments
 (0)