first commit
This commit is contained in:
commit
d35821b915
4
.dockerignore
Normal file
4
.dockerignore
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules
|
||||||
|
docs
|
||||||
|
data
|
||||||
|
.gitea
|
||||||
12
.editorConfig
Normal file
12
.editorConfig
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
[{*.css,*.scss,*.less,*.overrides,*.variables}]
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[{*.js,*.jsx,*.json,*.ts,*.tsx}]
|
||||||
|
indent_size = 2
|
||||||
63
.gitea/workflows/publish.yml
Normal file
63
.gitea/workflows/publish.yml
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
name: Docker CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
env:
|
||||||
|
node-version: 20.x
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: catthehacker/ubuntu:act-latest
|
||||||
|
env:
|
||||||
|
DOCKER_ORG: nzambello
|
||||||
|
DOCKER_LATEST: nightly
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Docker meta
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v4
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
git.nzambello.dev/nzambello/resize-img-api
|
||||||
|
labels: |
|
||||||
|
org.label-schema.docker.cmd=docker run -d -p 8787:8787 git.nzambello.dev/nzambello/resize-img-api:latest
|
||||||
|
flavor: latest=false
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
type=sha
|
||||||
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
|
|
||||||
|
- name: Login to Container Registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: git.nzambello.dev
|
||||||
|
username: ${{ gitea.repository_owner }}
|
||||||
|
password: ${{ secrets.ACTIONS_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v4
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: ${{ gitea.event_name != 'pull_request' }}
|
||||||
|
tags: |
|
||||||
|
git.nzambello.dev/nzambello/resize-img-api:latest
|
||||||
|
labels: $${{ steps.meta.outputs.labels }}
|
||||||
175
.gitignore
vendored
Normal file
175
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
|
||||||
|
logs
|
||||||
|
_.log
|
||||||
|
npm-debug.log_
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Caches
|
||||||
|
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
|
||||||
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
|
||||||
|
pids
|
||||||
|
_.pid
|
||||||
|
_.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
|
||||||
|
.temp
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
# IntelliJ based IDEs
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Finder (MacOS) folder config
|
||||||
|
.DS_Store
|
||||||
6
Dockerfile
Normal file
6
Dockerfile
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
FROM oven/bun:alpine
|
||||||
|
WORKDIR /app
|
||||||
|
COPY . .
|
||||||
|
RUN bun install --production --frozen-lockfile
|
||||||
|
CMD ["bun", "start"]
|
||||||
|
EXPOSE 8787
|
||||||
67
README.md
Normal file
67
README.md
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
# resize-img-api
|
||||||
|
|
||||||
|
The structure of the API path is:
|
||||||
|
|
||||||
|
```
|
||||||
|
/api/imgresize/:width/:height/:url
|
||||||
|
```
|
||||||
|
|
||||||
|
Where `:width` and `:height` can be numbers in pixels or `auto`.
|
||||||
|
|
||||||
|
The `:url` is the URL of the image to be resized and should be URL encoded, which can be done in JS with `encodeURIComponent`(). See [MDN ref](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent).
|
||||||
|
|
||||||
|
Example encoded URL for [https://memori.ai/logo.png](https://memori.ai/logo.png):
|
||||||
|
|
||||||
|
```
|
||||||
|
https%3A%2F%2Fmemori.ai%2Flogo.png
|
||||||
|
```
|
||||||
|
|
||||||
|
Then call the API as, for example:
|
||||||
|
|
||||||
|
```
|
||||||
|
/api/imgresize/200/200/https%3A%2F%2Fmemori.ai%2Flogo.png
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also specify a format using the querystring `?format=` and indicating one of the following: avif, gif, heif, jpeg, jpg, jp2, pdf, png, svg, tiff, webp.
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
To build the Docker image:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t resize-img-api .
|
||||||
|
```
|
||||||
|
|
||||||
|
To run the Docker container:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -p 8787:8787 resize-img-api
|
||||||
|
```
|
||||||
|
|
||||||
|
Using the published image:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -p 8787:8787 git.nzambello.dev/nzambello/resize-img-api:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
To install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
To run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun start
|
||||||
|
```
|
||||||
|
|
||||||
|
Or in development mode with hot reloading:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun dev
|
||||||
|
```
|
||||||
|
|
||||||
|
This project was created using `bun init` in bun v1.0.26. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
|
||||||
10
docker-compose.yml
Normal file
10
docker-compose.yml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
resize-image-api:
|
||||||
|
image: nzambello/resize-image-api:latest
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "8787:8787"
|
||||||
99
index.ts
Normal file
99
index.ts
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
import { Hono } from "hono";
|
||||||
|
import { cors } from "hono/cors";
|
||||||
|
import { etag } from "hono/etag";
|
||||||
|
import { logger } from "hono/logger";
|
||||||
|
import { prettyJSON } from "hono/pretty-json";
|
||||||
|
import { html } from "hono/html";
|
||||||
|
import sharp from "sharp";
|
||||||
|
|
||||||
|
const homepage = html`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="dark">
|
||||||
|
<head>
|
||||||
|
<meta charSet="utf-8" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="og:type" content="website" />
|
||||||
|
|
||||||
|
<title>Resize Images API</title>
|
||||||
|
<meta name="description" content="Resize images via API">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="container">
|
||||||
|
<h1>Resize images API</h1>
|
||||||
|
<style>
|
||||||
|
h1 {
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<article style="max-width: 600px; margin-left: auto; margin-right: auto; padding: 3rem 2rem;">
|
||||||
|
<p>The structure of the API path is:</p>
|
||||||
|
<p><pre>/api/imgresize/:width/:height/:url</pre></p>
|
||||||
|
<p>Where <code>width</code> and <code>height</code> can be numbers in pixels or <code>auto</code>.</p>
|
||||||
|
<p>The URL should be URL encoded, which can be done in JS with <code>encodeURIComponent</code>. See <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent" target="_blank" rel="noopener noreferrer">MDN ref</a>.</p>
|
||||||
|
<p>Example for <a href="https://memori.ai/logo.png" target="_blank" rel="noopener noreferrer">https://memori.ai/logo.png</a>:</p>
|
||||||
|
<p><pre>https%3A%2F%2Fmemori.ai%2Flogo.png</pre></p>
|
||||||
|
<p>Then call the API as, for example:</p>
|
||||||
|
<p><pre>/api/imgresize/200/200/https%3A%2F%2Fmemori.ai%2Flogo.png</pre></p>
|
||||||
|
<p>You can also specify a format using the querystring <code>?format=</code> and indicating one of the following: avif, gif, heif, jpeg, jpg, jp2, pdf, png, svg, tiff, webp.</p>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const app = new Hono();
|
||||||
|
app.use(prettyJSON());
|
||||||
|
app.use(etag(), logger());
|
||||||
|
|
||||||
|
app.use("/api/*", cors());
|
||||||
|
|
||||||
|
app.get("/", (c) => {
|
||||||
|
return c.html(homepage);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/api/imgresize/:width/:height/:url", async (c) => {
|
||||||
|
const { width, height, url } = c.req.param();
|
||||||
|
const format = c.req.query("format");
|
||||||
|
|
||||||
|
c.header("Cache-Control", "s-maxage=31536000, stale-while-revalidate");
|
||||||
|
c.header("Content-Type", `image/jpeg`);
|
||||||
|
|
||||||
|
const decodedUrl = decodeURIComponent(url as string);
|
||||||
|
|
||||||
|
const readStream = await fetch(decodedUrl, { cache: "no-cache" });
|
||||||
|
const input = Buffer.from(await readStream.arrayBuffer());
|
||||||
|
|
||||||
|
const w = width && width !== "auto" ? Number(width) : undefined;
|
||||||
|
const h = height && height !== "auto" ? Number(height) : undefined;
|
||||||
|
|
||||||
|
const outputBuffer = await sharp(input)
|
||||||
|
// outputBuffer contains JPEG image data
|
||||||
|
// no wider and no higher than w and h pixels
|
||||||
|
// and no larger than the input image
|
||||||
|
.resize({
|
||||||
|
width: w,
|
||||||
|
height: h,
|
||||||
|
fit: sharp.fit.inside,
|
||||||
|
withoutEnlargement: true,
|
||||||
|
})
|
||||||
|
.toFormat(
|
||||||
|
format && sharp.format[format as keyof typeof sharp.format] !== undefined
|
||||||
|
? sharp.format[format as keyof typeof sharp.format]
|
||||||
|
: sharp.format.jpeg
|
||||||
|
)
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
c.status(200);
|
||||||
|
return c.body(outputBuffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default {
|
||||||
|
port: Bun.env.PORT || 8787,
|
||||||
|
fetch: app.fetch,
|
||||||
|
};
|
||||||
17
package.json
Normal file
17
package.json
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"name": "resize-img-api",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "bun run --hot index.ts",
|
||||||
|
"start": "bun run index.ts"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"hono": "^4.1.4",
|
||||||
|
"sharp": "^0.33.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
7
tsconfig.json
Normal file
7
tsconfig.json
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "hono/jsx"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue