switch to sharp, dockerize
This commit is contained in:
parent
6511720e4a
commit
f72b775ee2
11
.dockerignore
Normal file
11
.dockerignore
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
build
|
||||||
|
.data
|
||||||
|
node_modules
|
||||||
|
readme
|
||||||
|
README.md
|
||||||
|
.npmrc
|
||||||
|
.prettierrc
|
||||||
|
.prettierignore
|
||||||
|
.gitignore
|
||||||
|
.env.example
|
||||||
|
.svelte-kit
|
|
@ -21,4 +21,4 @@ USERINFO__IDENTIFIER=preferred_username
|
||||||
ALLOWED_TYPES=image/jpg,image/jpeg,image/png,image/png,image/webp,image/gif
|
ALLOWED_TYPES=image/jpg,image/jpeg,image/png,image/png,image/webp,image/gif
|
||||||
|
|
||||||
# Prisma database URL
|
# Prisma database URL
|
||||||
DATABASE_URL="file:../.data/data.db"
|
DATABASE_URL=file:../.data/data.db
|
41
Dockerfile
Normal file
41
Dockerfile
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# skidded off https://git.madhouselabs.net/MadHouse-Labs/file-uploader/src/branch/master/Dockerfile
|
||||||
|
FROM node:lts-alpine AS base
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
FROM oven/bun:1-debian AS base2
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
FROM base AS install
|
||||||
|
RUN mkdir -p /temp/dev
|
||||||
|
COPY package.json package-lock.json /temp/dev/
|
||||||
|
RUN cd /temp/dev && npm install
|
||||||
|
|
||||||
|
RUN mkdir -p /temp/prod
|
||||||
|
COPY package.json package-lock.json /temp/prod/
|
||||||
|
RUN cd /temp/prod && npm install --omit dev
|
||||||
|
|
||||||
|
FROM base AS prisma
|
||||||
|
COPY --from=install /temp/prod/node_modules node_modules
|
||||||
|
COPY . .
|
||||||
|
RUN npx prisma generate
|
||||||
|
|
||||||
|
FROM base AS prisma-dev
|
||||||
|
COPY --from=install /temp/dev/node_modules node_modules
|
||||||
|
COPY . .
|
||||||
|
RUN npx prisma generate
|
||||||
|
|
||||||
|
FROM base2 AS build
|
||||||
|
COPY --from=prisma-dev /usr/src/app/node_modules node_modules
|
||||||
|
COPY . .
|
||||||
|
# vite build
|
||||||
|
RUN NODE_ENV=production bun --bun run build
|
||||||
|
|
||||||
|
FROM base2 AS release
|
||||||
|
COPY --from=prisma /usr/src/app/prisma prisma
|
||||||
|
COPY --from=prisma /usr/src/app/node_modules node_modules
|
||||||
|
COPY --from=build /usr/src/app/build build
|
||||||
|
COPY --from=build /usr/src/app/static static
|
||||||
|
COPY --from=build /usr/src/app/package.json .
|
||||||
|
|
||||||
|
EXPOSE 3000/tcp
|
||||||
|
CMD [ "bun", "run", "./build/index.js" ]
|
|
@ -1,134 +0,0 @@
|
||||||
<!--
|
|
||||||
|
|
||||||
Creating a security policy that fits your specific local environment
|
|
||||||
before making use of ImageMagick is highly advised. You can find guidance on
|
|
||||||
setting up this policy at https://imagemagick.org/script/security-policy.php,
|
|
||||||
and it's important to verify your policy using the validation tool located
|
|
||||||
at https://imagemagick-secevaluator.doyensec.com/.
|
|
||||||
|
|
||||||
|
|
||||||
Web-safe ImageMagick security policy:
|
|
||||||
|
|
||||||
This security protocol designed for web-safe usage focuses on situations
|
|
||||||
where ImageMagick is applied in publicly accessible contexts, like websites.
|
|
||||||
It deactivates the capability to read from or write to any image formats
|
|
||||||
other than web-safe formats like GIF, JPEG, and PNG. Additionally, this
|
|
||||||
policy prohibits the execution of image filters and indirect reads, thereby
|
|
||||||
thwarting potential security breaches. By implementing these limitations,
|
|
||||||
the web-safe policy fortifies the safeguarding of systems accessible to
|
|
||||||
the public, reducing the risk of exploiting ImageMagick's capabilities
|
|
||||||
for potential attacks.
|
|
||||||
-->
|
|
||||||
<policymap>
|
|
||||||
<!-- Set maximum parallel threads. -->
|
|
||||||
<policy domain="resource" name="thread" value="2"/>
|
|
||||||
<!--
|
|
||||||
Set maximum time to live in seconds or mnemonics, e.g. "2 minutes". When
|
|
||||||
this limit is exceeded, an exception is thrown and processing stops.
|
|
||||||
-->
|
|
||||||
<policy domain="resource" name="time" value="60"/>
|
|
||||||
<!--
|
|
||||||
Set maximum number of open pixel cache files. When this limit is
|
|
||||||
exceeded, any subsequent pixels cached to disk are closed and reopened
|
|
||||||
on demand.
|
|
||||||
-->
|
|
||||||
<policy domain="resource" name="file" value="768"/>
|
|
||||||
<!--
|
|
||||||
Set maximum amount of memory in bytes to allocate for the pixel cache
|
|
||||||
from the heap. When this limit is exceeded, the image pixels are cached
|
|
||||||
to memory-mapped disk.
|
|
||||||
-->
|
|
||||||
<policy domain="resource" name="memory" value="256MiB"/>
|
|
||||||
<!--
|
|
||||||
Set maximum amount of memory map in bytes to allocate for the pixel
|
|
||||||
cache. When this limit is exceeded, the image pixels are cached to
|
|
||||||
disk.
|
|
||||||
-->
|
|
||||||
<policy domain="resource" name="map" value="512MiB"/>
|
|
||||||
<!--
|
|
||||||
Set the maximum width * height of an image that can reside in the pixel
|
|
||||||
cache memory. Images that exceed the area limit are cached to disk.
|
|
||||||
-->
|
|
||||||
<policy domain="resource" name="area" value="16KP"/>
|
|
||||||
<!--
|
|
||||||
Set maximum amount of disk space in bytes permitted for use by the pixel
|
|
||||||
cache. When this limit is exceeded, the pixel cache is not be created
|
|
||||||
and an exception is thrown.
|
|
||||||
-->
|
|
||||||
<policy domain="resource" name="disk" value="1GiB"/>
|
|
||||||
<!--
|
|
||||||
Set the maximum length of an image sequence. When this limit is
|
|
||||||
exceeded, an exception is thrown.
|
|
||||||
-->
|
|
||||||
<policy domain="resource" name="list-length" value="16"/>
|
|
||||||
<!--
|
|
||||||
Set the maximum width of an image. When this limit is exceeded, an
|
|
||||||
exception is thrown.
|
|
||||||
-->
|
|
||||||
<policy domain="resource" name="width" value="4KP"/>
|
|
||||||
<!--
|
|
||||||
Set the maximum height of an image. When this limit is exceeded, an
|
|
||||||
exception is thrown.
|
|
||||||
-->
|
|
||||||
<policy domain="resource" name="height" value="4KP"/>
|
|
||||||
<!--
|
|
||||||
Periodically yield the CPU for at least the time specified in
|
|
||||||
milliseconds.
|
|
||||||
-->
|
|
||||||
<policy domain="resource" name="throttle" value="2"/>
|
|
||||||
<!--
|
|
||||||
Do not create temporary files in the default shared directories, instead
|
|
||||||
specify a private area to store only ImageMagick temporary files.
|
|
||||||
-->
|
|
||||||
<!--
|
|
||||||
<policy domain="resource" name="temporary-path" value="/magick/tmp/"/>
|
|
||||||
-->
|
|
||||||
<!--
|
|
||||||
Force memory initialization by memory mapping select memory
|
|
||||||
allocations.
|
|
||||||
-->
|
|
||||||
<policy domain="cache" name="memory-map" value="anonymous"/>
|
|
||||||
<!--
|
|
||||||
Ensure all image data is fully flushed and synchronized to disk.
|
|
||||||
-->
|
|
||||||
<policy domain="cache" name="synchronize" value="true"/>
|
|
||||||
<!--
|
|
||||||
Replace passphrase for secure distributed processing
|
|
||||||
-->
|
|
||||||
<!--
|
|
||||||
<policy domain="cache" name="shared-secret" value="secret-passphrase" stealth="true"/>
|
|
||||||
-->
|
|
||||||
<!-- Do not permit any delegates to execute. -->
|
|
||||||
<policy domain="delegate" rights="none" pattern="*"/>
|
|
||||||
<!-- Do not permit any image filters to load. -->
|
|
||||||
<policy domain="filter" rights="none" pattern="*"/>
|
|
||||||
<!-- Don't read/write from/to stdin/stdout. -->
|
|
||||||
<policy domain="path" rights="none" pattern="-"/>
|
|
||||||
<!-- don't read sensitive paths. -->
|
|
||||||
<policy domain="path" rights="none" pattern="/etc/*"/>
|
|
||||||
<!-- Indirect reads are not permitted. -->
|
|
||||||
<policy domain="path" rights="none" pattern="@*"/>
|
|
||||||
<!--
|
|
||||||
Deny all image modules and specifically exempt reading or writing
|
|
||||||
web-safe image formats.
|
|
||||||
-->
|
|
||||||
<policy domain="module" rights="none" pattern="*"/>
|
|
||||||
<policy domain="module" rights="read | write" pattern="{BMP,GIF,JPEG,PNG,TIFF,WEBP}"/>
|
|
||||||
<policy domain="module" rights="read | write" pattern="{MPC}" stealth="true"/>
|
|
||||||
<policy domain="module" rights="write" pattern="{JSON,INFO,PNM,PS}"/>
|
|
||||||
<!--
|
|
||||||
This policy sets the number of times to replace content of certain
|
|
||||||
memory buffers and temporary files before they are freed or deleted.
|
|
||||||
-->
|
|
||||||
<policy domain="system" name="shred" value="1"/>
|
|
||||||
<!--
|
|
||||||
Enable the initialization of buffers with zeros, resulting in a minor
|
|
||||||
performance penalty but with improved security.
|
|
||||||
-->
|
|
||||||
<policy domain="system" name="memory-map" value="anonymous"/>
|
|
||||||
<!--
|
|
||||||
Set the maximum amount of memory in bytes that are permitted for
|
|
||||||
allocation requests.
|
|
||||||
-->
|
|
||||||
<policy domain="system" name="max-memory-request" value="256MiB"/>
|
|
||||||
</policymap>
|
|
1117
package-lock.json
generated
1117
package-lock.json
generated
File diff suppressed because it is too large
Load diff
14
package.json
14
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "ava",
|
"name": "ava",
|
||||||
"version": "1.0.1",
|
"version": "1.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
|
@ -12,8 +12,6 @@
|
||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "^3.0.0",
|
|
||||||
"@sveltejs/kit": "^2.0.0",
|
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||||
"@types/bun": "^1.1.6",
|
"@types/bun": "^1.1.6",
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.1.1",
|
||||||
|
@ -31,10 +29,14 @@
|
||||||
"@fontsource-variable/inter": "^5.0.18",
|
"@fontsource-variable/inter": "^5.0.18",
|
||||||
"@fontsource-variable/noto-sans-mono": "^5.0.20",
|
"@fontsource-variable/noto-sans-mono": "^5.0.20",
|
||||||
"@prisma/client": "5.16.2",
|
"@prisma/client": "5.16.2",
|
||||||
"magickwand.js": "^1.1.0",
|
"mime": "^4.0.4",
|
||||||
"mime": "^4.0.4"
|
"sharp": "^0.33.4",
|
||||||
|
"@sveltejs/kit": "^2.0.0"
|
||||||
},
|
},
|
||||||
"trustedDependencies": [
|
"trustedDependencies": [
|
||||||
"magickwand.js"
|
"@prisma/client",
|
||||||
|
"@prisma/engines",
|
||||||
|
"prisma",
|
||||||
|
"sharp"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
|
binaryTargets = ["native", "debian-openssl-1.1.x"]
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
|
|
|
@ -3,11 +3,9 @@ import { existsSync } from "node:fs"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
import { prisma } from "./clientsingleton"
|
import { prisma } from "./clientsingleton"
|
||||||
import configuration from "./configuration"
|
import configuration from "./configuration"
|
||||||
import { Magick, MagickCore } from "magickwand.js"
|
import Sharp from "sharp"
|
||||||
import mime from "mime"
|
import mime from "mime"
|
||||||
|
|
||||||
Magick.SetSecurityPolicy(await Bun.file('./imagemagick.policy.xml').text())
|
|
||||||
|
|
||||||
// todo: make customizable
|
// todo: make customizable
|
||||||
export const avatarDirectory = "./.data/avatars"
|
export const avatarDirectory = "./.data/avatars"
|
||||||
export const defaultAvatarDirectory = "./static/default/"
|
export const defaultAvatarDirectory = "./static/default/"
|
||||||
|
@ -37,23 +35,18 @@ export async function getPathToAvatarForIdentifier(identifier: string, size: str
|
||||||
return getPathToAvatarForUid(user?.userId, size)
|
return getPathToAvatarForUid(user?.userId, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function rerenderAvatar(bin: ArrayBuffer, squareSize?: number) {
|
export async function rerenderAvatar(bin: ArrayBuffer, squareSize: number) {
|
||||||
let img = new Magick.Image;
|
let img = Sharp(bin);
|
||||||
// read file
|
let metadata = await img.metadata();
|
||||||
await img.readAsync(new Magick.Blob(bin),"")
|
squareSize = Math.min(...[metadata.width, metadata.height].filter(e => e) as number[], squareSize)
|
||||||
if (squareSize) {
|
|
||||||
// resize, but don't upscale, while filling without squishing
|
|
||||||
await img.scaleAsync(`${squareSize}x${squareSize}^>`)
|
|
||||||
}
|
|
||||||
// center crop
|
|
||||||
const size = img.size()
|
|
||||||
squareSize = Math.min(size.width(), size.height())
|
|
||||||
await img.extentAsync(`${squareSize}x${squareSize}`, MagickCore.CenterGravity)
|
|
||||||
|
|
||||||
// return avatar buffer
|
img.resize({
|
||||||
let tempBlob = new Magick.Blob()
|
width: squareSize,
|
||||||
await img.writeAsync(tempBlob)
|
height: squareSize,
|
||||||
return tempBlob.dataAsync()
|
fit: "cover"
|
||||||
|
})
|
||||||
|
|
||||||
|
return img.toBuffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setNewAvatar(uid: string, avatar?: File) {
|
export async function setNewAvatar(uid: string, avatar?: File) {
|
||||||
|
|
Loading…
Reference in a new issue