diff --git a/Dockerfile b/Dockerfile index 142184e..185687e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,7 @@ RUN npm exec pnpm i --frozen-lockfile --prod COPY --from=builder /app/dist ./dist -ENV ASSETS=/data +ENV LOCAL_ASSETS=/data ENV ADDRESS=0.0.0.0 EXPOSE 80 diff --git a/README.md b/README.md index cc585f8..3563a33 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # morphus 🖼 +A lightweight image resizing and effect proxy that caches image transformations. +The heavy lifting is done by [`libvips`](https://github.com/libvips/libvips) and [`sharp`](https://github.com/lovell/sharp) + ## 🌈 Features - Config driven @@ -7,6 +10,126 @@ - Host verification - Multiple storage adapters (Local, S3, GCS) - Caniuse based automatic formatting -- Highly performant +- ETag caching -Allowed hosts -> crossorigin on img tag +## 🏗 Installation + +The easies way to run is using docker + +```yaml +allowedDomains: + - !regexp ^https?:\/\/images.unsplash.com +``` + +```yaml +version: '3.8' + +services: + app: + image: cupcakearmy/morphus + ports: + - '80:80' +``` + +```bash +docker-compose up +``` + +## 💻 Usage + +###### Example + +```html + +``` + +| Parameter | Syntax | Example | +| --------- | -------------------------------------------------------------- | ---------------------------------------------------------- | +| url | URL | `?url=https://cdn.example.org/dog-full-res.png` | +| format | ComplexParameter | `?format=webp` `?format=webp\|quality:90,progressive:true` | +| resize | [sharp.fit](https://sharp.pixelplumbing.com/api-resize#resize) | `?resize=contain` | +| width | number | `?width=500` | +| height | number | `?width=500` | +| op | ComplexParameter[] | `?op=rotate\|angle:90&op=sharpen\|sigma:1,flat:2` | + +### ComplexParameter + +The syntax for the `ComplexParameter` is as follows: + +###### Without options + +``` +?param= +``` + +###### With two options + +``` +?param=|optionA:1,optionB:true +``` + +## ⚙️ Configuration + +Config files are searched in the current working directory under `morphus.yaml`. + +Configuration can be done either thorough config files or env variables. The usage of a config file is recommended. Below is a table of available configuration options, for more details see below. + +| Config | Environment | Default | Description | +| ---------------- | ---------------- | ---------- | -------------------------------------------------------------------------------------- | +| `port` | `PORT` | 80 | The port to bind | +| `address` | `ADDRESS` | 127.0.0.1 | The address to bind | +| `allowedDomains` | `ALLOWED_DOMAIN` | null | The domains that are allowed to be used as image sources | +| `allowedHosts` | `ALLOWED_HOSTS` | null | The hosts that are allowed to access the images | +| `cleanUrls` | `CLEAN_URL` | Fragment | Whether source URLs are cleaned | +| `maxAge` | `MAX_AGE` | 1d | How long the served images are marked as cached, after that ETag is used to revalidate | +| `storage` | `STORAGE` | Local | The storage driver to use | +| `local_assets` | `LOCAL_ASSETS` | `./assets` | Directory where the local storage driver persists files | + +### Allowed Domains + +Allowed domains are a way to secure the service by only allowing certain remote domains as possible sources of images. + +You can provide a `string` which will match as prefix or `RegExp` that allow for more powerful control. + +If omitted every domain is allowed. + +```yaml +allowedDomains: + # This will match any URL that starts with the string. + - https://my.cloud.org + + # For regexp you need to add the !regexp tag in from of it. + - !regexp ^https?:\/\/images.unsplash.com +``` + +### Allowed Hosts + +Same syntax as for allowed domains. + +Allowed hosts enables you to whitelist a number of `origins`. + +If ommtted any origin is allowed. + +```yaml +allowedHosts: + - https://my.cloud.org +``` + +###### Note + +When using the url in an `` tag you need to add the `` attribute to enable sending the `origin` header to the server. Read more [here](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-crossorigin) + +## Clean URLs + +This option allows cleaning the source URLs to remove duplicates. allowed options are `off`, `fragment`, `query`. + +###### Example + +| Type | URL | +| ---------- | ------------------------------------------------------------------------------- | +| Original | `https://images.unsplash.com/photo-1636839270984-1f7cbc2b4c4b?lang=en#chapter1` | +| `off` | `https://images.unsplash.com/photo-1636839270984-1f7cbc2b4c4b?lang=en#chapter1` | +| `fragment` | `https://images.unsplash.com/photo-1636839270984-1f7cbc2b4c4b?lang=en` | +| `query` | `https://images.unsplash.com/photo-1636839270984-1f7cbc2b4c4b` | diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..cdd0409 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,11 @@ +version: '3.8' + +services: + app: + image: cupcakearmy/morphus + ports: + - "80:80" + volumes: + - ./morphus.yaml:/app/morphus.yaml:ro + - ./data:/data + diff --git a/src/config.ts b/src/config.ts index 1b0141e..3e46031 100644 --- a/src/config.ts +++ b/src/config.ts @@ -91,15 +91,15 @@ const config = convict({ }, // Local storage - assets: { + localAssets: { doc: 'The path to the assets folder', format: String, default: './assets', - env: 'ASSETS', + env: 'LOCAL_ASSETS', }, }) -for (const file of ['morphus.yaml', 'morphus.yaml', 'morphus.json']) { +for (const file of ['morphus.yaml', 'morphus.yaml']) { try { config.loadFile(file) break diff --git a/src/storage/index.ts b/src/storage/index.ts index ff01d64..df0a8e1 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -20,7 +20,7 @@ export async function init() { if (!storage) { switch (Config.storage) { case StorageType.Local: - storage = new Local(Config.assets) + storage = new Local(Config.localAssets) break default: throw new Error(`Unknown storage type: ${Config.storage}`)