UNPKG

@lucidcms/core

Version:

The core of the Lucid CMS. It's responsible for spinning up the API and serving the CMS.

1 lines 4.27 kB
{"version":3,"file":"signed-url.mjs","names":["constants"],"sources":["../../../src/libs/media/signed-url.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport constants from \"../../constants/constants.js\";\nimport urlAddPath from \"../../utils/helpers/url-add-path.js\";\n\ntype CreateSignedMediaUrlParams = {\n\thost: string;\n\tpath: string;\n\tkey: string;\n\tsecretKey: string;\n\tquery?: Record<string, string | number | undefined>;\n};\n\ntype CreateSignedMediaTokenParams = {\n\tpath: string;\n\tkey: string;\n\ttimestamp: string | number;\n\tsecretKey: string;\n\tquery?: Record<string, string | number | undefined>;\n};\n\ntype ValidateSignedMediaUrlParams = CreateSignedMediaTokenParams & {\n\ttoken: string;\n};\n\nconst normalizeQuery = (query?: Record<string, string | number | undefined>) =>\n\tObject.fromEntries(\n\t\tObject.entries(query ?? {})\n\t\t\t.filter(([, value]) => value !== undefined)\n\t\t\t.sort(([left], [right]) => left.localeCompare(right))\n\t\t\t.map(([key, value]) => [key, String(value)]),\n\t);\n\nconst serializeQuery = (query?: Record<string, string | number | undefined>) =>\n\tnew URLSearchParams(normalizeQuery(query)).toString();\n\nexport const createSignedMediaToken = (\n\tparams: CreateSignedMediaTokenParams,\n): string => {\n\treturn crypto\n\t\t.createHmac(\"sha256\", params.secretKey)\n\t\t.update(\n\t\t\t[\n\t\t\t\tparams.path,\n\t\t\t\tparams.key,\n\t\t\t\tString(params.timestamp),\n\t\t\t\tserializeQuery(params.query),\n\t\t\t].join(\"\\n\"),\n\t\t)\n\t\t.digest(\"hex\");\n};\n\nexport const createSignedMediaUrl = (\n\tparams: CreateSignedMediaUrlParams,\n): string => {\n\tconst timestamp = Date.now();\n\tconst normalizedQuery = normalizeQuery(params.query);\n\tconst token = createSignedMediaToken({\n\t\tpath: params.path,\n\t\tkey: params.key,\n\t\ttimestamp,\n\t\tsecretKey: params.secretKey,\n\t\tquery: normalizedQuery,\n\t});\n\n\tconst query = new URLSearchParams({\n\t\tkey: params.key,\n\t\ttoken,\n\t\ttimestamp: String(timestamp),\n\t});\n\n\tfor (const [key, value] of Object.entries(normalizedQuery)) {\n\t\tquery.set(key, value);\n\t}\n\n\tconst basePath = urlAddPath(\n\t\tparams.host,\n\t\t`${constants.directories.base}/api/v1/${params.path}`,\n\t);\n\n\treturn `${basePath}?${query.toString()}`;\n};\n\nexport const validateSignedMediaUrl = (\n\tparams: ValidateSignedMediaUrlParams,\n): boolean => {\n\tconst timestamp = Number.parseInt(String(params.timestamp), 10);\n\tif (!Number.isFinite(timestamp)) {\n\t\treturn false;\n\t}\n\n\tconst age = Date.now() - timestamp;\n\tif (age < 0 || age > constants.presignedUrlExpiration) {\n\t\treturn false;\n\t}\n\n\tconst expectedToken = createSignedMediaToken({\n\t\tpath: params.path,\n\t\tkey: params.key,\n\t\ttimestamp,\n\t\tsecretKey: params.secretKey,\n\t\tquery: params.query,\n\t});\n\n\tif (params.token.length !== expectedToken.length) {\n\t\treturn false;\n\t}\n\n\treturn crypto.timingSafeEqual(\n\t\tBuffer.from(params.token),\n\t\tBuffer.from(expectedToken),\n\t);\n};\n"],"mappings":"4HAwBA,MAAM,EAAkB,GACvB,OAAO,YACN,OAAO,QAAQ,GAAS,CAAC,CAAC,CAAC,CACzB,QAAQ,EAAG,KAAW,IAAU,IAAA,EAAS,CAAC,CAC1C,MAAM,CAAC,GAAO,CAAC,KAAW,EAAK,cAAc,CAAK,CAAC,CAAC,CACpD,KAAK,CAAC,EAAK,KAAW,CAAC,EAAK,OAAO,CAAK,CAAC,CAAC,CAC7C,EAEK,EAAkB,GACvB,IAAI,gBAAgB,EAAe,CAAK,CAAC,CAAC,CAAC,SAAS,EAExC,EACZ,GAEO,EACL,WAAW,SAAU,EAAO,SAAS,CAAC,CACtC,OACA,CACC,EAAO,KACP,EAAO,IACP,OAAO,EAAO,SAAS,EACvB,EAAe,EAAO,KAAK,CAC5B,CAAC,CAAC,KAAK;CAAI,CACZ,CAAC,CACA,OAAO,KAAK,EAGF,EACZ,GACY,CACZ,IAAM,EAAY,KAAK,IAAI,EACrB,EAAkB,EAAe,EAAO,KAAK,EAC7C,EAAQ,EAAuB,CACpC,KAAM,EAAO,KACb,IAAK,EAAO,IACZ,YACA,UAAW,EAAO,UAClB,MAAO,CACR,CAAC,EAEK,EAAQ,IAAI,gBAAgB,CACjC,IAAK,EAAO,IACZ,QACA,UAAW,OAAO,CAAS,CAC5B,CAAC,EAED,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAAe,EACxD,EAAM,IAAI,EAAK,CAAK,EAQrB,MAAO,GALU,EAChB,EAAO,KACP,GAAGA,EAAU,YAAY,KAAK,UAAU,EAAO,MAG/B,EAAE,GAAG,EAAM,SAAS,GACtC,EAEa,EACZ,GACa,CACb,IAAM,EAAY,OAAO,SAAS,OAAO,EAAO,SAAS,EAAG,EAAE,EAC9D,GAAI,CAAC,OAAO,SAAS,CAAS,EAC7B,MAAO,GAGR,IAAM,EAAM,KAAK,IAAI,EAAI,EACzB,GAAI,EAAM,GAAK,EAAMA,EAAU,uBAC9B,MAAO,GAGR,IAAM,EAAgB,EAAuB,CAC5C,KAAM,EAAO,KACb,IAAK,EAAO,IACZ,YACA,UAAW,EAAO,UAClB,MAAO,EAAO,KACf,CAAC,EAMD,OAJI,EAAO,MAAM,SAAW,EAAc,QAInC,EAAO,gBACb,OAAO,KAAK,EAAO,KAAK,EACxB,OAAO,KAAK,CAAa,CAC1B,CACD"}