transloadit 4.0.7 → 4.1.2
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/CHANGELOG.md +37 -0
- package/README.md +200 -21
- package/dist/ApiError.d.ts +1 -1
- package/dist/ApiError.d.ts.map +1 -1
- package/dist/ApiError.js.map +1 -1
- package/dist/Transloadit.d.ts +23 -4
- package/dist/Transloadit.d.ts.map +1 -1
- package/dist/Transloadit.js +62 -28
- package/dist/Transloadit.js.map +1 -1
- package/dist/apiTypes.d.ts +1 -1
- package/dist/apiTypes.d.ts.map +1 -1
- package/dist/cli/OutputCtl.d.ts +46 -0
- package/dist/cli/OutputCtl.d.ts.map +1 -0
- package/dist/cli/OutputCtl.js +85 -0
- package/dist/cli/OutputCtl.js.map +1 -0
- package/dist/cli/commands/BaseCommand.d.ts +23 -0
- package/dist/cli/commands/BaseCommand.d.ts.map +1 -0
- package/dist/cli/commands/BaseCommand.js +52 -0
- package/dist/cli/commands/BaseCommand.js.map +1 -0
- package/dist/cli/commands/assemblies.d.ts +93 -0
- package/dist/cli/commands/assemblies.d.ts.map +1 -0
- package/dist/cli/commands/assemblies.js +1021 -0
- package/dist/cli/commands/assemblies.js.map +1 -0
- package/dist/cli/commands/auth.d.ts +28 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +280 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/bills.d.ts +14 -0
- package/dist/cli/commands/bills.d.ts.map +1 -0
- package/dist/cli/commands/bills.js +69 -0
- package/dist/cli/commands/bills.js.map +1 -0
- package/dist/cli/commands/index.d.ts +3 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +39 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/notifications.d.ts +16 -0
- package/dist/cli/commands/notifications.d.ts.map +1 -0
- package/dist/cli/commands/notifications.js +44 -0
- package/dist/cli/commands/notifications.js.map +1 -0
- package/dist/cli/commands/templates.d.ts +81 -0
- package/dist/cli/commands/templates.d.ts.map +1 -0
- package/dist/cli/commands/templates.js +428 -0
- package/dist/cli/commands/templates.js.map +1 -0
- package/dist/cli/helpers.d.ts +13 -0
- package/dist/cli/helpers.d.ts.map +1 -0
- package/dist/cli/helpers.js +39 -0
- package/dist/cli/helpers.js.map +1 -0
- package/dist/cli/template-last-modified.d.ts +10 -0
- package/dist/cli/template-last-modified.d.ts.map +1 -0
- package/dist/cli/template-last-modified.js +134 -0
- package/dist/cli/template-last-modified.js.map +1 -0
- package/dist/cli/types.d.ts +152 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +64 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli.d.ts +2 -12
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -256
- package/dist/cli.js.map +1 -1
- package/dist/tus.d.ts +2 -1
- package/dist/tus.d.ts.map +1 -1
- package/dist/tus.js +33 -5
- package/dist/tus.js.map +1 -1
- package/package.json +12 -7
- package/src/ApiError.ts +2 -1
- package/src/Transloadit.ts +98 -39
- package/src/apiTypes.ts +1 -1
- package/src/cli/OutputCtl.ts +115 -0
- package/src/cli/commands/BaseCommand.ts +71 -0
- package/src/cli/commands/assemblies.ts +1373 -0
- package/src/cli/commands/auth.ts +354 -0
- package/src/cli/commands/bills.ts +91 -0
- package/src/cli/commands/index.ts +65 -0
- package/src/cli/commands/notifications.ts +63 -0
- package/src/cli/commands/templates.ts +556 -0
- package/src/cli/helpers.ts +50 -0
- package/src/cli/template-last-modified.ts +156 -0
- package/src/cli/types.ts +183 -0
- package/src/cli.ts +12 -305
- package/src/tus.ts +37 -5
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
2
|
+
import { Command, Option } from 'clipanion'
|
|
3
|
+
import type { ZodIssue } from 'zod'
|
|
4
|
+
import { z } from 'zod'
|
|
5
|
+
import {
|
|
6
|
+
assemblyAuthInstructionsSchema,
|
|
7
|
+
assemblyInstructionsSchema,
|
|
8
|
+
} from '../../alphalib/types/template.ts'
|
|
9
|
+
import type { OptionalAuthParams } from '../../apiTypes.ts'
|
|
10
|
+
import { Transloadit } from '../../Transloadit.ts'
|
|
11
|
+
import { getEnvCredentials } from '../helpers.ts'
|
|
12
|
+
import { UnauthenticatedCommand } from './BaseCommand.ts'
|
|
13
|
+
|
|
14
|
+
type UrlParamPrimitive = string | number | boolean
|
|
15
|
+
type UrlParamArray = UrlParamPrimitive[]
|
|
16
|
+
type NormalizedUrlParams = Record<string, UrlParamPrimitive | UrlParamArray>
|
|
17
|
+
|
|
18
|
+
const smartCdnParamsSchema = z
|
|
19
|
+
.object({
|
|
20
|
+
workspace: z.string().min(1, 'workspace is required'),
|
|
21
|
+
template: z.string().min(1, 'template is required'),
|
|
22
|
+
input: z.union([z.string(), z.number(), z.boolean()]),
|
|
23
|
+
url_params: z.record(z.unknown()).optional(),
|
|
24
|
+
expire_at_ms: z.union([z.number(), z.string()]).optional(),
|
|
25
|
+
})
|
|
26
|
+
.passthrough()
|
|
27
|
+
|
|
28
|
+
const cliSignatureParamsSchema = assemblyInstructionsSchema
|
|
29
|
+
.extend({ auth: assemblyAuthInstructionsSchema.partial().optional() })
|
|
30
|
+
.partial()
|
|
31
|
+
.passthrough()
|
|
32
|
+
|
|
33
|
+
type CliSignatureParams = z.infer<typeof cliSignatureParamsSchema>
|
|
34
|
+
|
|
35
|
+
function formatIssues(issues: ZodIssue[]): string {
|
|
36
|
+
return issues
|
|
37
|
+
.map((issue) => {
|
|
38
|
+
const path = issue.path.join('.') || '(root)'
|
|
39
|
+
return `${path}: ${issue.message}`
|
|
40
|
+
})
|
|
41
|
+
.join('; ')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function normalizeUrlParam(value: unknown): UrlParamPrimitive | UrlParamArray | undefined {
|
|
45
|
+
if (value == null) return undefined
|
|
46
|
+
if (Array.isArray(value)) {
|
|
47
|
+
const normalized = value.filter(
|
|
48
|
+
(item): item is UrlParamPrimitive =>
|
|
49
|
+
typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean',
|
|
50
|
+
)
|
|
51
|
+
return normalized.length > 0 ? normalized : undefined
|
|
52
|
+
}
|
|
53
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
54
|
+
return value
|
|
55
|
+
}
|
|
56
|
+
return undefined
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function normalizeUrlParams(params?: Record<string, unknown>): NormalizedUrlParams | undefined {
|
|
60
|
+
if (params == null) return undefined
|
|
61
|
+
let normalized: NormalizedUrlParams | undefined
|
|
62
|
+
for (const [key, value] of Object.entries(params)) {
|
|
63
|
+
const normalizedValue = normalizeUrlParam(value)
|
|
64
|
+
if (normalizedValue === undefined) continue
|
|
65
|
+
if (normalized == null) normalized = {}
|
|
66
|
+
normalized[key] = normalizedValue
|
|
67
|
+
}
|
|
68
|
+
return normalized
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function readStdin(): Promise<string> {
|
|
72
|
+
if (process.stdin.isTTY) return ''
|
|
73
|
+
|
|
74
|
+
process.stdin.setEncoding('utf8')
|
|
75
|
+
let data = ''
|
|
76
|
+
|
|
77
|
+
for await (const chunk of process.stdin) {
|
|
78
|
+
data += chunk
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return data
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const getCredentials = getEnvCredentials
|
|
85
|
+
|
|
86
|
+
// Result type for signature operations
|
|
87
|
+
type SigResult = { ok: true; output: string } | { ok: false; error: string }
|
|
88
|
+
|
|
89
|
+
// Core logic for signature generation
|
|
90
|
+
function generateSignature(
|
|
91
|
+
input: string,
|
|
92
|
+
credentials: { authKey: string; authSecret: string },
|
|
93
|
+
algorithm?: string,
|
|
94
|
+
): SigResult {
|
|
95
|
+
const { authKey, authSecret } = credentials
|
|
96
|
+
let params: CliSignatureParams
|
|
97
|
+
|
|
98
|
+
if (input === '') {
|
|
99
|
+
params = { auth: { key: authKey } }
|
|
100
|
+
} else {
|
|
101
|
+
let parsed: unknown
|
|
102
|
+
try {
|
|
103
|
+
parsed = JSON.parse(input)
|
|
104
|
+
} catch (error) {
|
|
105
|
+
return { ok: false, error: `Failed to parse JSON from stdin: ${(error as Error).message}` }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (parsed == null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
109
|
+
return { ok: false, error: 'Invalid params provided via stdin. Expected a JSON object.' }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const parsedResult = cliSignatureParamsSchema.safeParse(parsed)
|
|
113
|
+
if (!parsedResult.success) {
|
|
114
|
+
return { ok: false, error: `Invalid params: ${formatIssues(parsedResult.error.issues)}` }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const parsedParams = parsedResult.data
|
|
118
|
+
const existingAuth = parsedParams.auth ?? {}
|
|
119
|
+
|
|
120
|
+
params = {
|
|
121
|
+
...parsedParams,
|
|
122
|
+
auth: {
|
|
123
|
+
...existingAuth,
|
|
124
|
+
key: authKey,
|
|
125
|
+
},
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const client = new Transloadit({ authKey, authSecret })
|
|
130
|
+
try {
|
|
131
|
+
const signature = client.calcSignature(params as OptionalAuthParams, algorithm)
|
|
132
|
+
return { ok: true, output: JSON.stringify(signature) }
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return { ok: false, error: `Failed to generate signature: ${(error as Error).message}` }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Core logic for Smart CDN URL generation
|
|
139
|
+
function generateSmartCdnUrl(
|
|
140
|
+
input: string,
|
|
141
|
+
credentials: { authKey: string; authSecret: string },
|
|
142
|
+
): SigResult {
|
|
143
|
+
const { authKey, authSecret } = credentials
|
|
144
|
+
|
|
145
|
+
if (input === '') {
|
|
146
|
+
return {
|
|
147
|
+
ok: false,
|
|
148
|
+
error:
|
|
149
|
+
'Missing params provided via stdin. Expected a JSON object with workspace, template, input, and optional Smart CDN parameters.',
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
let parsed: unknown
|
|
154
|
+
try {
|
|
155
|
+
parsed = JSON.parse(input)
|
|
156
|
+
} catch (error) {
|
|
157
|
+
return { ok: false, error: `Failed to parse JSON from stdin: ${(error as Error).message}` }
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (parsed == null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
161
|
+
return { ok: false, error: 'Invalid params provided via stdin. Expected a JSON object.' }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const parsedResult = smartCdnParamsSchema.safeParse(parsed)
|
|
165
|
+
if (!parsedResult.success) {
|
|
166
|
+
return { ok: false, error: `Invalid params: ${formatIssues(parsedResult.error.issues)}` }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const { workspace, template, input: inputFieldRaw, url_params, expire_at_ms } = parsedResult.data
|
|
170
|
+
const urlParams = normalizeUrlParams(url_params)
|
|
171
|
+
|
|
172
|
+
let expiresAt: number | undefined
|
|
173
|
+
if (typeof expire_at_ms === 'string') {
|
|
174
|
+
const parsedNumber = Number.parseInt(expire_at_ms, 10)
|
|
175
|
+
if (Number.isNaN(parsedNumber)) {
|
|
176
|
+
return { ok: false, error: 'Invalid params: expire_at_ms must be a number.' }
|
|
177
|
+
}
|
|
178
|
+
expiresAt = parsedNumber
|
|
179
|
+
} else {
|
|
180
|
+
expiresAt = expire_at_ms
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const inputField = typeof inputFieldRaw === 'string' ? inputFieldRaw : String(inputFieldRaw)
|
|
184
|
+
|
|
185
|
+
const client = new Transloadit({ authKey, authSecret })
|
|
186
|
+
try {
|
|
187
|
+
const signedUrl = client.getSignedSmartCDNUrl({
|
|
188
|
+
workspace,
|
|
189
|
+
template,
|
|
190
|
+
input: inputField,
|
|
191
|
+
urlParams,
|
|
192
|
+
expiresAt,
|
|
193
|
+
})
|
|
194
|
+
return { ok: true, output: signedUrl }
|
|
195
|
+
} catch (error) {
|
|
196
|
+
return { ok: false, error: `Failed to generate Smart CDN URL: ${(error as Error).message}` }
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Testable helper functions exported for unit tests
|
|
201
|
+
export interface RunSigOptions {
|
|
202
|
+
providedInput?: string
|
|
203
|
+
algorithm?: string
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface RunSmartSigOptions {
|
|
207
|
+
providedInput?: string
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export async function runSig(options: RunSigOptions = {}): Promise<void> {
|
|
211
|
+
const credentials = getCredentials()
|
|
212
|
+
if (credentials == null) {
|
|
213
|
+
console.error(
|
|
214
|
+
'Missing credentials. Please set TRANSLOADIT_KEY and TRANSLOADIT_SECRET environment variables.',
|
|
215
|
+
)
|
|
216
|
+
process.exitCode = 1
|
|
217
|
+
return
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const rawInput = options.providedInput ?? (await readStdin())
|
|
221
|
+
const result = generateSignature(rawInput.trim(), credentials, options.algorithm)
|
|
222
|
+
|
|
223
|
+
if (result.ok) {
|
|
224
|
+
process.stdout.write(`${result.output}\n`)
|
|
225
|
+
} else {
|
|
226
|
+
console.error(result.error)
|
|
227
|
+
process.exitCode = 1
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export async function runSmartSig(options: RunSmartSigOptions = {}): Promise<void> {
|
|
232
|
+
const credentials = getCredentials()
|
|
233
|
+
if (credentials == null) {
|
|
234
|
+
console.error(
|
|
235
|
+
'Missing credentials. Please set TRANSLOADIT_KEY and TRANSLOADIT_SECRET environment variables.',
|
|
236
|
+
)
|
|
237
|
+
process.exitCode = 1
|
|
238
|
+
return
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const rawInput = options.providedInput ?? (await readStdin())
|
|
242
|
+
const result = generateSmartCdnUrl(rawInput.trim(), credentials)
|
|
243
|
+
|
|
244
|
+
if (result.ok) {
|
|
245
|
+
process.stdout.write(`${result.output}\n`)
|
|
246
|
+
} else {
|
|
247
|
+
console.error(result.error)
|
|
248
|
+
process.exitCode = 1
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Generate a signature for assembly params
|
|
254
|
+
*/
|
|
255
|
+
export class SignatureCommand extends UnauthenticatedCommand {
|
|
256
|
+
static override paths = [
|
|
257
|
+
['auth', 'signature'],
|
|
258
|
+
['auth', 'sig'],
|
|
259
|
+
['signature'],
|
|
260
|
+
['sig'], // BC alias
|
|
261
|
+
]
|
|
262
|
+
|
|
263
|
+
static override usage = Command.Usage({
|
|
264
|
+
category: 'Auth',
|
|
265
|
+
description: 'Generate a signature for assembly params',
|
|
266
|
+
details: `
|
|
267
|
+
Read params JSON from stdin and output signed payload JSON.
|
|
268
|
+
If no input is provided, generates a signature with default params.
|
|
269
|
+
`,
|
|
270
|
+
examples: [
|
|
271
|
+
['Generate signature', 'echo \'{"steps":{}}\' | transloadit signature'],
|
|
272
|
+
['With algorithm', 'echo \'{"steps":{}}\' | transloadit signature --algorithm sha384'],
|
|
273
|
+
['Using alias', 'echo \'{"steps":{}}\' | transloadit sig'],
|
|
274
|
+
],
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
algorithm = Option.String('--algorithm,-a', {
|
|
278
|
+
description: 'Signature algorithm to use (sha1, sha256, sha384, sha512)',
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
protected async run(): Promise<number | undefined> {
|
|
282
|
+
const credentials = getCredentials()
|
|
283
|
+
if (credentials == null) {
|
|
284
|
+
this.output.error(
|
|
285
|
+
'Missing credentials. Please set TRANSLOADIT_KEY and TRANSLOADIT_SECRET environment variables.',
|
|
286
|
+
)
|
|
287
|
+
return 1
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const rawInput = await readStdin()
|
|
291
|
+
const result = generateSignature(rawInput.trim(), credentials, this.algorithm)
|
|
292
|
+
|
|
293
|
+
if (result.ok) {
|
|
294
|
+
process.stdout.write(`${result.output}\n`)
|
|
295
|
+
return undefined
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
this.output.error(result.error)
|
|
299
|
+
return 1
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Generate a signed Smart CDN URL
|
|
305
|
+
*/
|
|
306
|
+
export class SmartCdnSignatureCommand extends UnauthenticatedCommand {
|
|
307
|
+
static override paths = [
|
|
308
|
+
['auth', 'smart-cdn'],
|
|
309
|
+
['auth', 'smart_cdn'],
|
|
310
|
+
['smart-cdn'],
|
|
311
|
+
['smart_sig'], // BC alias
|
|
312
|
+
]
|
|
313
|
+
|
|
314
|
+
static override usage = Command.Usage({
|
|
315
|
+
category: 'Auth',
|
|
316
|
+
description: 'Generate a signed Smart CDN URL',
|
|
317
|
+
details: `
|
|
318
|
+
Read Smart CDN params JSON from stdin and output a signed URL.
|
|
319
|
+
Required fields: workspace, template, input
|
|
320
|
+
Optional fields: expire_at_ms, url_params
|
|
321
|
+
`,
|
|
322
|
+
examples: [
|
|
323
|
+
[
|
|
324
|
+
'Generate Smart CDN URL',
|
|
325
|
+
'echo \'{"workspace":"w","template":"t","input":"i"}\' | transloadit smart-cdn',
|
|
326
|
+
],
|
|
327
|
+
[
|
|
328
|
+
'Using alias',
|
|
329
|
+
'echo \'{"workspace":"w","template":"t","input":"i"}\' | transloadit smart_sig',
|
|
330
|
+
],
|
|
331
|
+
],
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
protected async run(): Promise<number | undefined> {
|
|
335
|
+
const credentials = getCredentials()
|
|
336
|
+
if (credentials == null) {
|
|
337
|
+
this.output.error(
|
|
338
|
+
'Missing credentials. Please set TRANSLOADIT_KEY and TRANSLOADIT_SECRET environment variables.',
|
|
339
|
+
)
|
|
340
|
+
return 1
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const rawInput = await readStdin()
|
|
344
|
+
const result = generateSmartCdnUrl(rawInput.trim(), credentials)
|
|
345
|
+
|
|
346
|
+
if (result.ok) {
|
|
347
|
+
process.stdout.write(`${result.output}\n`)
|
|
348
|
+
return undefined
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
this.output.error(result.error)
|
|
352
|
+
return 1
|
|
353
|
+
}
|
|
354
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Command, Option } from 'clipanion'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { tryCatch } from '../../alphalib/tryCatch.ts'
|
|
4
|
+
import type { Transloadit } from '../../Transloadit.ts'
|
|
5
|
+
import { formatAPIError } from '../helpers.ts'
|
|
6
|
+
import type { IOutputCtl } from '../OutputCtl.ts'
|
|
7
|
+
import { AuthenticatedCommand } from './BaseCommand.ts'
|
|
8
|
+
|
|
9
|
+
// --- Types and business logic ---
|
|
10
|
+
|
|
11
|
+
export interface BillsGetOptions {
|
|
12
|
+
months: string[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const BillResponseSchema = z.object({
|
|
16
|
+
total: z.number(),
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export async function get(
|
|
20
|
+
output: IOutputCtl,
|
|
21
|
+
client: Transloadit,
|
|
22
|
+
{ months }: BillsGetOptions,
|
|
23
|
+
): Promise<void> {
|
|
24
|
+
const requests = months.map((month) => client.getBill(month))
|
|
25
|
+
|
|
26
|
+
const [err, results] = await tryCatch(Promise.all(requests))
|
|
27
|
+
if (err) {
|
|
28
|
+
output.error(formatAPIError(err))
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (const result of results) {
|
|
33
|
+
const parsed = BillResponseSchema.safeParse(result)
|
|
34
|
+
if (parsed.success) {
|
|
35
|
+
output.print(`$${parsed.data.total}`, result)
|
|
36
|
+
} else {
|
|
37
|
+
output.print('Unable to parse bill response', result)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// --- Command class ---
|
|
43
|
+
|
|
44
|
+
export class BillsGetCommand extends AuthenticatedCommand {
|
|
45
|
+
static override paths = [
|
|
46
|
+
['bills', 'get'],
|
|
47
|
+
['bill', 'get'],
|
|
48
|
+
['b', 'get'],
|
|
49
|
+
['b', 'g'],
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
static override usage = Command.Usage({
|
|
53
|
+
category: 'Bills',
|
|
54
|
+
description: 'Fetch billing information',
|
|
55
|
+
details: `
|
|
56
|
+
Fetch billing information for the specified months.
|
|
57
|
+
Months should be specified in YYYY-MM format.
|
|
58
|
+
If no month is specified, returns the current month.
|
|
59
|
+
`,
|
|
60
|
+
examples: [
|
|
61
|
+
['Get current month billing', 'transloadit bills get'],
|
|
62
|
+
['Get specific month', 'transloadit bills get 2024-01'],
|
|
63
|
+
['Get multiple months', 'transloadit bills get 2024-01 2024-02'],
|
|
64
|
+
],
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
months = Option.Rest()
|
|
68
|
+
|
|
69
|
+
protected async run(): Promise<number | undefined> {
|
|
70
|
+
const monthList: string[] = []
|
|
71
|
+
|
|
72
|
+
for (const month of this.months) {
|
|
73
|
+
if (!/^\d{4}-\d{1,2}$/.test(month)) {
|
|
74
|
+
this.output.error(`invalid date format '${month}' (YYYY-MM)`)
|
|
75
|
+
return 1
|
|
76
|
+
}
|
|
77
|
+
monthList.push(month)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Default to current month if none specified
|
|
81
|
+
if (monthList.length === 0) {
|
|
82
|
+
const d = new Date()
|
|
83
|
+
monthList.push(`${d.getUTCFullYear()}-${d.getUTCMonth() + 1}`)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
await get(this.output, this.client, {
|
|
87
|
+
months: monthList,
|
|
88
|
+
})
|
|
89
|
+
return undefined
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Builtins, Cli } from 'clipanion'
|
|
2
|
+
|
|
3
|
+
import packageJson from '../../../package.json' with { type: 'json' }
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
AssembliesCreateCommand,
|
|
7
|
+
AssembliesDeleteCommand,
|
|
8
|
+
AssembliesGetCommand,
|
|
9
|
+
AssembliesListCommand,
|
|
10
|
+
AssembliesReplayCommand,
|
|
11
|
+
} from './assemblies.ts'
|
|
12
|
+
|
|
13
|
+
import { SignatureCommand, SmartCdnSignatureCommand } from './auth.ts'
|
|
14
|
+
|
|
15
|
+
import { BillsGetCommand } from './bills.ts'
|
|
16
|
+
|
|
17
|
+
import { NotificationsReplayCommand } from './notifications.ts'
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
TemplatesCreateCommand,
|
|
21
|
+
TemplatesDeleteCommand,
|
|
22
|
+
TemplatesGetCommand,
|
|
23
|
+
TemplatesListCommand,
|
|
24
|
+
TemplatesModifyCommand,
|
|
25
|
+
TemplatesSyncCommand,
|
|
26
|
+
} from './templates.ts'
|
|
27
|
+
|
|
28
|
+
export function createCli(): Cli {
|
|
29
|
+
const cli = new Cli({
|
|
30
|
+
binaryLabel: 'Transloadit CLI',
|
|
31
|
+
binaryName: 'transloadit',
|
|
32
|
+
binaryVersion: packageJson.version,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// Built-in commands
|
|
36
|
+
cli.register(Builtins.HelpCommand)
|
|
37
|
+
cli.register(Builtins.VersionCommand)
|
|
38
|
+
|
|
39
|
+
// Auth commands (signature generation)
|
|
40
|
+
cli.register(SignatureCommand)
|
|
41
|
+
cli.register(SmartCdnSignatureCommand)
|
|
42
|
+
|
|
43
|
+
// Assemblies commands
|
|
44
|
+
cli.register(AssembliesCreateCommand)
|
|
45
|
+
cli.register(AssembliesListCommand)
|
|
46
|
+
cli.register(AssembliesGetCommand)
|
|
47
|
+
cli.register(AssembliesDeleteCommand)
|
|
48
|
+
cli.register(AssembliesReplayCommand)
|
|
49
|
+
|
|
50
|
+
// Templates commands
|
|
51
|
+
cli.register(TemplatesCreateCommand)
|
|
52
|
+
cli.register(TemplatesGetCommand)
|
|
53
|
+
cli.register(TemplatesModifyCommand)
|
|
54
|
+
cli.register(TemplatesDeleteCommand)
|
|
55
|
+
cli.register(TemplatesListCommand)
|
|
56
|
+
cli.register(TemplatesSyncCommand)
|
|
57
|
+
|
|
58
|
+
// Bills commands
|
|
59
|
+
cli.register(BillsGetCommand)
|
|
60
|
+
|
|
61
|
+
// Notifications commands
|
|
62
|
+
cli.register(NotificationsReplayCommand)
|
|
63
|
+
|
|
64
|
+
return cli
|
|
65
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Command, Option } from 'clipanion'
|
|
2
|
+
import { tryCatch } from '../../alphalib/tryCatch.ts'
|
|
3
|
+
import type { Transloadit } from '../../Transloadit.ts'
|
|
4
|
+
import type { IOutputCtl } from '../OutputCtl.ts'
|
|
5
|
+
import { ensureError } from '../types.ts'
|
|
6
|
+
import { AuthenticatedCommand } from './BaseCommand.ts'
|
|
7
|
+
|
|
8
|
+
// --- Types and business logic ---
|
|
9
|
+
|
|
10
|
+
export interface NotificationsReplayOptions {
|
|
11
|
+
notify_url?: string
|
|
12
|
+
assemblies: string[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function replay(
|
|
16
|
+
output: IOutputCtl,
|
|
17
|
+
client: Transloadit,
|
|
18
|
+
{ notify_url, assemblies }: NotificationsReplayOptions,
|
|
19
|
+
): Promise<void> {
|
|
20
|
+
const promises = assemblies.map((id) => client.replayAssemblyNotification(id, { notify_url }))
|
|
21
|
+
const [err] = await tryCatch(Promise.all(promises))
|
|
22
|
+
if (err) {
|
|
23
|
+
output.error(ensureError(err).message)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// --- Command class ---
|
|
28
|
+
|
|
29
|
+
export class NotificationsReplayCommand extends AuthenticatedCommand {
|
|
30
|
+
static override paths = [
|
|
31
|
+
['assembly-notifications', 'replay'],
|
|
32
|
+
['notifications', 'replay'],
|
|
33
|
+
['notification', 'replay'],
|
|
34
|
+
['n', 'replay'],
|
|
35
|
+
['n', 'r'],
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
static override usage = Command.Usage({
|
|
39
|
+
category: 'Notifications',
|
|
40
|
+
description: 'Replay notifications for assemblies',
|
|
41
|
+
examples: [
|
|
42
|
+
['Replay notifications', 'transloadit assembly-notifications replay ASSEMBLY_ID'],
|
|
43
|
+
[
|
|
44
|
+
'Replay to a new URL',
|
|
45
|
+
'transloadit assembly-notifications replay --notify-url https://example.com/notify ASSEMBLY_ID',
|
|
46
|
+
],
|
|
47
|
+
],
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
notifyUrl = Option.String('--notify-url', {
|
|
51
|
+
description: 'Specify a new URL to send the notifications to',
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
assemblyIds = Option.Rest({ required: 1 })
|
|
55
|
+
|
|
56
|
+
protected async run(): Promise<number | undefined> {
|
|
57
|
+
await replay(this.output, this.client, {
|
|
58
|
+
notify_url: this.notifyUrl,
|
|
59
|
+
assemblies: this.assemblyIds,
|
|
60
|
+
})
|
|
61
|
+
return undefined
|
|
62
|
+
}
|
|
63
|
+
}
|