@@ -5,12 +5,26 @@ import path from 'node:path'
55import process from 'node:process'
66import { fileURLToPath } from 'node:url'
77import { type ZodIssue , z } from 'zod'
8+ import {
9+ assemblyAuthInstructionsSchema ,
10+ assemblyInstructionsSchema ,
11+ } from './alphalib/types/template.ts'
12+ import type { OptionalAuthParams } from './apiTypes.ts'
813import { Transloadit } from './Transloadit.ts'
914
1015type UrlParamPrimitive = string | number | boolean
1116type UrlParamArray = UrlParamPrimitive [ ]
1217type NormalizedUrlParams = Record < string , UrlParamPrimitive | UrlParamArray >
1318
19+ interface RunSigOptions {
20+ providedInput ?: string
21+ algorithm ?: string
22+ }
23+
24+ interface RunSmartSigOptions {
25+ providedInput ?: string
26+ }
27+
1428const smartCdnParamsSchema = z
1529 . object ( {
1630 workspace : z . string ( ) . min ( 1 , 'workspace is required' ) ,
@@ -21,6 +35,11 @@ const smartCdnParamsSchema = z
2135 } )
2236 . passthrough ( )
2337
38+ const cliSignatureParamsSchema = assemblyInstructionsSchema
39+ . extend ( { auth : assemblyAuthInstructionsSchema . partial ( ) . optional ( ) } )
40+ . partial ( )
41+ . passthrough ( )
42+
2443export async function readStdin ( ) : Promise < string > {
2544 if ( process . stdin . isTTY ) return ''
2645
@@ -75,19 +94,85 @@ function normalizeUrlParams(params?: Record<string, unknown>): NormalizedUrlPara
7594 return normalized
7695}
7796
78- export async function runSmartSig ( providedInput ?: string ) : Promise < void > {
97+ function ensureCredentials ( ) : { authKey: string ; authSecret: string } | null {
7998 const authKey = process . env . TRANSLOADIT_KEY || process . env . TRANSLOADIT_AUTH_KEY
8099 const authSecret = process . env . TRANSLOADIT_SECRET || process . env . TRANSLOADIT_AUTH_SECRET
81100
82101 if ( ! authKey || ! authSecret ) {
83102 fail (
84103 'Missing credentials. Please set TRANSLOADIT_KEY and TRANSLOADIT_SECRET environment variables.' ,
85104 )
86- return
105+ return null
87106 }
88107
108+ return { authKey, authSecret }
109+ }
110+
111+ export async function runSig ( options : RunSigOptions = { } ) : Promise < void > {
112+ const credentials = ensureCredentials ( )
113+ if ( credentials == null ) return
114+ const { authKey, authSecret } = credentials
115+ const { providedInput, algorithm } = options
116+
89117 const rawInput = providedInput ?? ( await readStdin ( ) )
90118 const input = rawInput . trim ( )
119+ let params : Record < string , unknown >
120+
121+ if ( input === '' ) {
122+ params = { auth : { key : authKey } }
123+ } else {
124+ let parsed : unknown
125+ try {
126+ parsed = JSON . parse ( input )
127+ } catch ( error ) {
128+ fail ( `Failed to parse JSON from stdin: ${ ( error as Error ) . message } ` )
129+ return
130+ }
131+
132+ if ( parsed == null || typeof parsed !== 'object' || Array . isArray ( parsed ) ) {
133+ fail ( 'Invalid params provided via stdin. Expected a JSON object.' )
134+ return
135+ }
136+
137+ const parsedResult = cliSignatureParamsSchema . safeParse ( parsed )
138+ if ( ! parsedResult . success ) {
139+ fail ( `Invalid params: ${ formatIssues ( parsedResult . error . issues ) } ` )
140+ return
141+ }
142+
143+ const parsedParams = parsedResult . data as Record < string , unknown >
144+ const existingAuth =
145+ typeof parsedParams . auth === 'object' &&
146+ parsedParams . auth != null &&
147+ ! Array . isArray ( parsedParams . auth )
148+ ? ( parsedParams . auth as Record < string , unknown > )
149+ : { }
150+
151+ params = {
152+ ...parsedParams ,
153+ auth : {
154+ ...existingAuth ,
155+ key : authKey ,
156+ } ,
157+ }
158+ }
159+
160+ const client = new Transloadit ( { authKey, authSecret } )
161+ try {
162+ const signature = client . calcSignature ( params as OptionalAuthParams , algorithm )
163+ process . stdout . write ( `${ JSON . stringify ( signature ) } \n` )
164+ } catch ( error ) {
165+ fail ( `Failed to generate signature: ${ ( error as Error ) . message } ` )
166+ }
167+ }
168+
169+ export async function runSmartSig ( options : RunSmartSigOptions = { } ) : Promise < void > {
170+ const credentials = ensureCredentials ( )
171+ if ( credentials == null ) return
172+ const { authKey , authSecret } = credentials
173+
174+ const rawInput = options . providedInput ?? ( await readStdin ( ) )
175+ const input = rawInput . trim ( )
91176 if ( input === '' ) {
92177 fail (
93178 'Missing params provided via stdin. Expected a JSON object with workspace, template, input, and optional Smart CDN parameters.' ,
@@ -115,7 +200,6 @@ export async function runSmartSig(providedInput?: string): Promise<void> {
115200 }
116201
117202 const { workspace , template , input : inputFieldRaw , url_params , expire_at_ms } = parsedResult . data
118-
119203 const urlParams = normalizeUrlParams ( url_params as Record < string , unknown > | undefined )
120204
121205 let expiresAt : number | undefined
@@ -133,37 +217,83 @@ export async function runSmartSig(providedInput?: string): Promise<void> {
133217 const inputField = typeof inputFieldRaw === 'string' ? inputFieldRaw : String ( inputFieldRaw )
134218
135219 const client = new Transloadit ( { authKey, authSecret } )
136- const signedUrl = client . getSignedSmartCDNUrl ( {
137- workspace,
138- template,
139- input : inputField ,
140- urlParams,
141- expiresAt,
142- } )
143- process . stdout . write ( `${ signedUrl } \n` )
220+ try {
221+ const signedUrl = client . getSignedSmartCDNUrl ( {
222+ workspace,
223+ template,
224+ input : inputField ,
225+ urlParams,
226+ expiresAt,
227+ } )
228+ process . stdout . write ( `${ signedUrl } \n` )
229+ } catch ( error ) {
230+ fail ( `Failed to generate Smart CDN URL: ${ ( error as Error ) . message } ` )
231+ }
232+ }
233+
234+ function parseSigArguments ( args : string [ ] ) : { algorithm ?: string } {
235+ let algorithm : string | undefined
236+ let index = 0
237+ while ( index < args . length ) {
238+ const arg = args [ index ]
239+ if ( arg === '--algorithm' || arg === '-a' ) {
240+ const next = args [ index + 1 ]
241+ if ( next == null || next . startsWith ( '-' ) ) {
242+ throw new Error ( 'Missing value for --algorithm option' )
243+ }
244+ algorithm = next
245+ index += 2
246+ continue
247+ }
248+ if ( arg . startsWith ( '--algorithm=' ) ) {
249+ const [ , value ] = arg . split ( '=' , 2 )
250+ if ( value === undefined || value === '' ) {
251+ throw new Error ( 'Missing value for --algorithm option' )
252+ }
253+ algorithm = value
254+ index += 1
255+ continue
256+ }
257+ throw new Error ( `Unknown option: ${ arg } ` )
258+ }
259+
260+ return { algorithm }
144261}
145262
146263export async function main ( args = process . argv . slice ( 2 ) ) : Promise < void > {
147- const [ command ] = args
264+ const [ command , ... commandArgs ] = args
148265
149266 switch ( command ) {
150267 case 'smart_sig' : {
151268 await runSmartSig ( )
152269 break
153270 }
154271
272+ case 'sig ': {
273+ try {
274+ const { algorithm } = parseSigArguments ( commandArgs )
275+ await runSig ( { algorithm } )
276+ } catch ( error ) {
277+ fail ( ( error as Error ) . message )
278+ }
279+ break
280+ }
281+
155282 case '-h' :
156283 case '--help' :
157284 case undefined : {
158285 process . stdout . write (
159286 [
160287 'Usage:' ,
161288 ' npx transloadit smart_sig Read Smart CDN params JSON from stdin and output a signed URL.' ,
289+ ' npx transloadit sig [--algorithm <name>] Read params JSON from stdin and output signed payload JSON.' ,
162290 '' ,
163291 'Required JSON fields:' ,
164- ' workspace, template, input' ,
292+ ' smart_sig: workspace, template, input' ,
293+ ' sig: none (object is optional)' ,
165294 'Optional JSON fields:' ,
166- ' expire_at_ms, url_params' ,
295+ ' smart_sig: expire_at_ms, url_params' ,
296+ ' sig: auth.expires and any supported assembly params' ,
167297 '' ,
168298 'Environment variables:' ,
169299 ' TRANSLOADIT_KEY, TRANSLOADIT_SECRET' ,
0 commit comments