|
| 1 | +import { joinURL } from "ufo"; |
| 2 | +// @ts-expect-error crypto-es missing types |
| 3 | +import { HMAC } from "crypto-es/lib/hmac.js"; |
| 4 | +// @ts-expect-error crypto-es missing types |
| 5 | +import { SHA256Algo } from "crypto-es/lib/sha256.js"; |
| 6 | +// @ts-expect-error crypto-es missing types |
| 7 | +import { Hex } from "crypto-es/lib/core.js"; |
| 8 | +// @ts-expect-error crypto-es missing types |
| 9 | +import { Base64 } from "crypto-es/lib/enc-base64.js"; |
| 10 | + |
| 11 | +export interface Modifiers { |
| 12 | + "min-height"?: string; |
| 13 | + "min-width"?: string; |
| 14 | + adjust?: string; |
| 15 | + auto_rotate?: string; |
| 16 | + autoquality?: string; |
| 17 | + background?: string; |
| 18 | + background_alpha?: string; |
| 19 | + blur?: string; |
| 20 | + blur_detections?: string; |
| 21 | + brightness?: string; |
| 22 | + cachebuster?: string; |
| 23 | + contrast?: string; |
| 24 | + crop?: string; |
| 25 | + disable_animation?: string; |
| 26 | + dpr?: string; |
| 27 | + draw_detections?: string; |
| 28 | + enforce_thumbnail?: string; |
| 29 | + enlarge?: string; |
| 30 | + expires?: string; |
| 31 | + extend?: string; |
| 32 | + fallback_image_url?: string; |
| 33 | + filename?: string; |
| 34 | + format?: string; |
| 35 | + format_quality?: string; |
| 36 | + gradient?: string; |
| 37 | + gravity?: string; |
| 38 | + height?: string; |
| 39 | + jpeg_options?: string; |
| 40 | + keep_copyright?: string; |
| 41 | + max_bytes?: string; |
| 42 | + padding?: string; |
| 43 | + page?: string; |
| 44 | + pixelate?: string; |
| 45 | + png_options?: string; |
| 46 | + preset?: string; |
| 47 | + quality?: string; |
| 48 | + raw?: string; |
| 49 | + resize?: string; |
| 50 | + resizing_algorithm?: string; |
| 51 | + resizing_type?: string; |
| 52 | + return_attachment?: string; |
| 53 | + rotate?: string; |
| 54 | + saturation?: string; |
| 55 | + sharpen?: string; |
| 56 | + size?: string; |
| 57 | + skip_processing?: string; |
| 58 | + strip_color_profile?: string; |
| 59 | + strip_metadata?: string; |
| 60 | + style?: string; |
| 61 | + trim?: string; |
| 62 | + unsharpening?: string; |
| 63 | + video_thumbnail_second?: string; |
| 64 | + watermark?: string; |
| 65 | + watermark_shadow?: string; |
| 66 | + watermark_size?: string; |
| 67 | + watermark_text?: string; |
| 68 | + watermark_url?: string; |
| 69 | + width?: string; |
| 70 | + zoom?: string; |
| 71 | +} |
| 72 | + |
| 73 | +const modifiersKeyMap: Modifiers = { |
| 74 | + "min-height": "mh", |
| 75 | + "min-width": "mw", |
| 76 | + adjust: "a", |
| 77 | + auto_rotate: "ar", |
| 78 | + autoquality: "aq", |
| 79 | + background: "bg", |
| 80 | + background_alpha: "bga", |
| 81 | + blur: "bl", |
| 82 | + blur_detections: "bd", |
| 83 | + brightness: "br", |
| 84 | + cachebuster: "cb", |
| 85 | + contrast: "co", |
| 86 | + crop: "c", |
| 87 | + disable_animation: "da", |
| 88 | + dpr: "dpr", |
| 89 | + draw_detections: "dd", |
| 90 | + enforce_thumbnail: "eth", |
| 91 | + enlarge: "el", |
| 92 | + expires: "exp", |
| 93 | + extend: "ex", |
| 94 | + fallback_image_url: "fiu", |
| 95 | + filename: "fn", |
| 96 | + format: "f", |
| 97 | + format_quality: "fq", |
| 98 | + gradient: "gr", |
| 99 | + gravity: "g", |
| 100 | + height: "h", |
| 101 | + jpeg_options: "jpgo", |
| 102 | + keep_copyright: "kcr", |
| 103 | + max_bytes: "mb", |
| 104 | + padding: "pd", |
| 105 | + page: "pg", |
| 106 | + pixelate: "pix", |
| 107 | + png_options: "pngo", |
| 108 | + preset: "pr", |
| 109 | + quality: "q", |
| 110 | + raw: "raw", |
| 111 | + resize: "rs", |
| 112 | + resizing_algorithm: "ra", |
| 113 | + resizing_type: "rt", |
| 114 | + return_attachment: "att", |
| 115 | + rotate: "rot", |
| 116 | + saturation: "sa", |
| 117 | + sharpen: "sh", |
| 118 | + size: "s", |
| 119 | + skip_processing: "skp", |
| 120 | + strip_color_profile: "scp", |
| 121 | + strip_metadata: "sm", |
| 122 | + style: "st", |
| 123 | + trim: "t", |
| 124 | + unsharpening: "ush", |
| 125 | + video_thumbnail_second: "vts", |
| 126 | + watermark: "wm", |
| 127 | + watermark_shadow: "wmsh", |
| 128 | + watermark_size: "wms", |
| 129 | + watermark_text: "wmt", |
| 130 | + watermark_url: "wmu", |
| 131 | + width: "w", |
| 132 | + zoom: "z", |
| 133 | +}; |
| 134 | + |
| 135 | +const isBrowser = typeof window !== "undefined"; |
| 136 | + |
| 137 | +export const toBase64 = (value: string) => |
| 138 | + isBrowser |
| 139 | + ? window.btoa(value) |
| 140 | + : Buffer.from(value, "binary").toString("base64"); |
| 141 | + |
| 142 | +export const trimBase64 = (value: string) => |
| 143 | + value.replaceAll("=", "").replaceAll("+", "-").replaceAll("/", "_"); |
| 144 | + |
| 145 | +export const generateHmac = (secret: string, salt: string) => { |
| 146 | + const hmac = HMAC.create(SHA256Algo, Hex.parse(secret)); |
| 147 | + hmac.update(Hex.parse(salt)); |
| 148 | + |
| 149 | + return hmac; |
| 150 | +}; |
| 151 | + |
| 152 | +export const generateSignature = (hmac: any, path: string): string => { |
| 153 | + const hash = Base64.stringify(hmac.finalize(path)); |
| 154 | + |
| 155 | + return trimBase64(hash); |
| 156 | +}; |
| 157 | + |
| 158 | +export const modifiersGenerator = (modifiers: Modifiers) => { |
| 159 | + return Object.entries(Object.fromEntries(Object.entries(modifiers).sort())) |
| 160 | + .map((modifier) => { |
| 161 | + return `${(modifiersKeyMap as Record<string, string>)[modifier[0]]}:${ |
| 162 | + modifier[1] |
| 163 | + }`; |
| 164 | + }) |
| 165 | + .join("/"); |
| 166 | +}; |
| 167 | + |
| 168 | +export function getImageUrl( |
| 169 | + imageSource: string, |
| 170 | + options: { |
| 171 | + secret: string; |
| 172 | + salt: string; |
| 173 | + baseURL: string; |
| 174 | + modifiers: Modifiers; |
| 175 | + } |
| 176 | +) { |
| 177 | + const encodedUrl = trimBase64(toBase64(imageSource)); |
| 178 | + const operations = modifiersGenerator(options.modifiers); |
| 179 | + const encodedUrlWithModifiers = joinURL("/", operations, encodedUrl); |
| 180 | + const hmac = generateHmac(options.secret, options.salt); |
| 181 | + const signature = generateSignature(hmac, encodedUrlWithModifiers); |
| 182 | + |
| 183 | + return joinURL(options.baseURL, signature, encodedUrlWithModifiers); |
| 184 | +} |
0 commit comments