Compare commits
No commits in common. "main" and "v0.4.0-alpha.4" have entirely different histories.
main
...
v0.4.0-alp
421
.drone.yml
Normal file
421
.drone.yml
Normal file
|
@ -0,0 +1,421 @@
|
|||
kind: pipeline
|
||||
type: docker
|
||||
name: clippy
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: clippy
|
||||
image: asonix/rust-builder:latest-linux-amd64
|
||||
pull: always
|
||||
commands:
|
||||
- rustup component add clippy
|
||||
- cargo clippy -- -D warnings
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
||||
- tag
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: tests
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: tests
|
||||
image: asonix/rust-builder:latest-linux-amd64
|
||||
pull: always
|
||||
commands:
|
||||
- cargo test
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
||||
- tag
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: check-amd64
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: check
|
||||
image: asonix/rust-builder:latest-linux-amd64
|
||||
pull: always
|
||||
commands:
|
||||
- cargo check --target=$TARGET
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: build-amd64
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: build
|
||||
image: asonix/rust-builder:latest-linux-amd64
|
||||
pull: always
|
||||
commands:
|
||||
- cargo build --target=$TARGET --release
|
||||
- $TOOL-strip target/$TARGET/release/pict-rs-proxy
|
||||
- cp target/$TARGET/release/pict-rs-proxy .
|
||||
- cp pict-rs-proxy pict-rs-proxy-linux-amd64
|
||||
|
||||
- name: push
|
||||
image: plugins/docker:20
|
||||
settings:
|
||||
username: asonix
|
||||
password:
|
||||
from_secret: dockerhub_token
|
||||
repo: asonix/pictrs-proxy
|
||||
dockerfile: docker/drone/Dockerfile
|
||||
auto_tag: true
|
||||
auto_tag_suffix: linux-amd64
|
||||
build_args:
|
||||
- REPO_ARCH=amd64
|
||||
|
||||
- name: publish
|
||||
image: plugins/gitea-release:1
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: gitea_token
|
||||
base_url: https://git.asonix.dog
|
||||
files:
|
||||
- pict-rs-proxy-linux-amd64
|
||||
|
||||
depends_on:
|
||||
- clippy
|
||||
- tests
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- tag
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: check-arm64v8
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: check
|
||||
image: asonix/rust-builder:latest-linux-arm64v8
|
||||
pull: always
|
||||
commands:
|
||||
- cargo check --target=$TARGET
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: build-arm64v8
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: build
|
||||
image: asonix/rust-builder:latest-linux-arm64v8
|
||||
pull: always
|
||||
commands:
|
||||
- cargo build --target=$TARGET --release
|
||||
- $TOOL-strip target/$TARGET/release/pict-rs-proxy
|
||||
- cp target/$TARGET/release/pict-rs-proxy .
|
||||
- cp pict-rs-proxy pict-rs-proxy-linux-arm64v8
|
||||
|
||||
- name: push
|
||||
image: plugins/docker:20
|
||||
settings:
|
||||
username: asonix
|
||||
password:
|
||||
from_secret: dockerhub_token
|
||||
repo: asonix/pictrs-proxy
|
||||
dockerfile: docker/drone/Dockerfile
|
||||
auto_tag: true
|
||||
auto_tag_suffix: linux-arm64v8
|
||||
build_args:
|
||||
- REPO_ARCH=arm64v8
|
||||
|
||||
- name: publish
|
||||
image: plugins/gitea-release:1
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: gitea_token
|
||||
base_url: https://git.asonix.dog
|
||||
files:
|
||||
- pict-rs-proxy-linux-arm64v8
|
||||
|
||||
depends_on:
|
||||
- clippy
|
||||
- tests
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- tag
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: check-arm32v7
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: check
|
||||
image: asonix/rust-builder:latest-linux-arm32v7
|
||||
pull: always
|
||||
commands:
|
||||
- cargo check --target=$TARGET
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: build-arm32v7
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: build
|
||||
image: asonix/rust-builder:latest-linux-arm32v7
|
||||
pull: always
|
||||
commands:
|
||||
- cargo build --target=$TARGET --release
|
||||
- $TOOL-strip target/$TARGET/release/pict-rs-proxy
|
||||
- cp target/$TARGET/release/pict-rs-proxy .
|
||||
- cp pict-rs-proxy pict-rs-proxy-linux-arm32v7
|
||||
|
||||
- name: push
|
||||
image: plugins/docker:20
|
||||
settings:
|
||||
username: asonix
|
||||
password:
|
||||
from_secret: dockerhub_token
|
||||
repo: asonix/pictrs-proxy
|
||||
dockerfile: docker/drone/Dockerfile
|
||||
auto_tag: true
|
||||
auto_tag_suffix: linux-arm32v7
|
||||
build_args:
|
||||
- REPO_ARCH=arm32v7
|
||||
|
||||
- name: publish
|
||||
image: plugins/gitea-release:1
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: gitea_token
|
||||
base_url: https://git.asonix.dog
|
||||
files:
|
||||
- pict-rs-proxy-linux-arm32v7
|
||||
|
||||
depends_on:
|
||||
- clippy
|
||||
- tests
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- tag
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: manifest
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: manifest
|
||||
image: plugins/manifest:1
|
||||
settings:
|
||||
username: asonix
|
||||
password:
|
||||
from_secret: dockerhub_token
|
||||
dump: true
|
||||
auto_tag: true
|
||||
ignore_missing: true
|
||||
spec: docker/drone/manifest.tmpl
|
||||
|
||||
|
||||
depends_on:
|
||||
- build-amd64
|
||||
- build-arm64v8
|
||||
- build-arm32v7
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- tag
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: publish-crate
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: publish
|
||||
image: asonix/rust-builder:latest-linux-amd64
|
||||
pull: always
|
||||
environment:
|
||||
CRATES_IO_TOKEN:
|
||||
from_secret: crates_io_token
|
||||
commands:
|
||||
- cargo publish --token $CRATES_IO_TOKEN
|
||||
|
||||
depends_on:
|
||||
- build-amd64
|
||||
- build-arm64v8
|
||||
- build-arm32v7
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- tag
|
|
@ -1,61 +0,0 @@
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
clippy:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: docker.io/asonix/actions-base-image:0.1
|
||||
steps:
|
||||
-
|
||||
name: Checkout pict-rs-proxy
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
-
|
||||
name: Cargo Cache
|
||||
uses: https://git.asonix.dog/asonix/actions/cache-rust-dependencies@main
|
||||
-
|
||||
name: Clippy
|
||||
run: |
|
||||
cargo clippy --no-default-features -- -D warnings
|
||||
|
||||
tests:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: docker.io/asonix/actions-base-image:0.1
|
||||
steps:
|
||||
-
|
||||
name: Checkout pict-rs-proxy
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
-
|
||||
name: Cargo Cache
|
||||
uses: https://git.asonix.dog/asonix/actions/cache-rust-dependencies@main
|
||||
-
|
||||
name: Test
|
||||
run: cargo test
|
||||
|
||||
check:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
- x86_64-unknown-linux-musl
|
||||
- armv7-unknown-linux-musleabihf
|
||||
- aarch64-unknown-linux-musl
|
||||
runs-on: docker
|
||||
container:
|
||||
image: docker.io/asonix/actions-base-image:0.1
|
||||
steps:
|
||||
-
|
||||
name: Checkout pict-rs-proxy
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
-
|
||||
name: Cargo Cache
|
||||
uses: https://git.asonix.dog/asonix/actions/cache-rust-dependencies@main
|
||||
-
|
||||
name: Debug builds
|
||||
run: cargo zigbuild --target ${{ matrix.target }}
|
|
@ -1,225 +0,0 @@
|
|||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
|
||||
env:
|
||||
REGISTRY_IMAGE: asonix/pictrs-proxy
|
||||
|
||||
jobs:
|
||||
clippy:
|
||||
runs-on: base-image
|
||||
container:
|
||||
image: docker.io/asonix/actions-base-image:0.1
|
||||
steps:
|
||||
-
|
||||
name: Checkout pict-rs-proxy
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
-
|
||||
name: Cargo Cache
|
||||
uses: https://git.asonix.dog/asonix/actions/cache-rust-dependencies@main
|
||||
-
|
||||
name: Clippy
|
||||
run: |
|
||||
cargo clippy --no-default-features -- -D warnings
|
||||
|
||||
tests:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: docker.io/asonix/actions-base-image:0.1
|
||||
steps:
|
||||
-
|
||||
name: Checkout pict-rs-proxy
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
-
|
||||
name: Cargo Cache
|
||||
uses: https://git.asonix.dog/asonix/actions/cache-rust-dependencies@main
|
||||
-
|
||||
name: Test
|
||||
run: cargo test
|
||||
|
||||
build:
|
||||
needs:
|
||||
- clippy
|
||||
- tests
|
||||
runs-on: docker
|
||||
container:
|
||||
image: docker.io/asonix/actions-base-image:0.1
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
info:
|
||||
- target: x86_64-unknown-linux-musl
|
||||
artifact: linux-amd64
|
||||
platform: linux/amd64
|
||||
- target: armv7-unknown-linux-musleabihf
|
||||
artifact: linux-arm32v7
|
||||
platform: linux/arm/v7
|
||||
- target: aarch64-unknown-linux-musl
|
||||
artifact: linux-arm64v8
|
||||
platform: linux/arm64
|
||||
steps:
|
||||
-
|
||||
name: Checkout pict-rs-proxy
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
-
|
||||
name: Cargo Cache
|
||||
uses: https://git.asonix.dog/asonix/actions/cache-rust-dependencies@main
|
||||
-
|
||||
name: Prepare Platform
|
||||
run: |
|
||||
platform=${{ matrix.info.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
-
|
||||
name: Docker meta
|
||||
id: meta
|
||||
uses: https://github.com/docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY_IMAGE }}
|
||||
flavor: |
|
||||
latest=auto
|
||||
suffix=-${{ matrix.info.artifact }}
|
||||
tags: |
|
||||
type=raw,value=latest,enable={{ is_default_branch }}
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: https://github.com/docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: https://github.com/docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Docker login
|
||||
uses: https://github.com/docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Compile pict-rs-proxy
|
||||
run: cargo zigbuild --target ${{ matrix.info.target }} --release
|
||||
-
|
||||
name: Prepare artifacts
|
||||
run: |
|
||||
mkdir artifacts
|
||||
cp target/${{ matrix.info.target }}/release/pict-rs-proxy artifacts/pict-rs-proxy-${{ matrix.info.artifact }}
|
||||
-
|
||||
uses: https://github.com/actions/upload-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: artifacts/
|
||||
-
|
||||
name: Prepare binary
|
||||
run: |
|
||||
cp target/${{ matrix.info.target }}/release/pict-rs-proxy docker/forgejo/pict-rs-proxy
|
||||
-
|
||||
name: Build and push ${{ matrix.info.platform }} docker image
|
||||
id: build
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./docker/forgejo
|
||||
platforms: ${{ matrix.info.platform }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},name-canonical=true,push=true
|
||||
-
|
||||
name: Export digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
echo "Created /tmp/digests/${digest#sha256:}"
|
||||
shell: bash
|
||||
-
|
||||
name: Upload ${{ matrix.info.platform }} digest
|
||||
uses: https://github.com/actions/upload-artifact@v3
|
||||
with:
|
||||
name: digests
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
publish-docker:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: docker.io/asonix/actions-base-image:0.1
|
||||
needs: [build]
|
||||
steps:
|
||||
-
|
||||
name: Download digests
|
||||
uses: https://github.com/actions/download-artifact@v3
|
||||
with:
|
||||
name: digests
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Docker login
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Docker meta
|
||||
id: meta
|
||||
uses: https://github.com/docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY_IMAGE }}
|
||||
flavor: |
|
||||
latest=auto
|
||||
tags: |
|
||||
type=raw,value=latest,enable={{ is_default_branch }}
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
-
|
||||
name: Create manifest list and push
|
||||
working-directory: /tmp/digests
|
||||
run: |
|
||||
tags=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "${DOCKER_METADATA_OUTPUT_JSON}")
|
||||
images=$(printf "${{ env.REGISTRY_IMAGE }}@sha256:%s " *)
|
||||
echo "Running 'docker buildx imagetools create ${tags[@]} ${images[@]}'"
|
||||
docker buildx imagetools create ${tags[@]} ${images[@]}
|
||||
shell: bash
|
||||
-
|
||||
name: Inspect Image
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
|
||||
|
||||
publish-forgejo:
|
||||
needs: [build]
|
||||
runs-on: docker
|
||||
container:
|
||||
image: docker.io/asonix/actions-base-image:0.1
|
||||
steps:
|
||||
- uses: https://github.com/actions/download-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: artifacts/
|
||||
merge-multiple: true
|
||||
- uses: actions/forgejo-release@v1
|
||||
with:
|
||||
direction: upload
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
release-dir: artifacts/
|
||||
|
||||
publish-crate:
|
||||
needs: [build]
|
||||
runs-on: docker
|
||||
container:
|
||||
image: docker.io/asonix/actions-base-image:0.1
|
||||
steps:
|
||||
-
|
||||
name: Checkout pict-rs-proxy
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
-
|
||||
name: Cargo Cache
|
||||
uses: https://git.asonix.dog/asonix/actions/cache-rust-dependencies@main
|
||||
-
|
||||
name: Publish Crate
|
||||
run: cargo publish --token ${{ secrets.CRATES_IO_TOKEN }}
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,5 +1,2 @@
|
|||
/target
|
||||
/docker/dev/volumes
|
||||
/.envrc
|
||||
/.direnv
|
||||
/result
|
||||
|
|
2214
Cargo.lock
generated
2214
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
44
Cargo.toml
44
Cargo.toml
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "pict-rs-proxy"
|
||||
description = "A simple web frontend for pict-rs"
|
||||
version = "0.5.0"
|
||||
version = "0.4.0-alpha.4"
|
||||
authors = ["asonix <asonix@asonix.dog>"]
|
||||
license = "AGPL-3.0"
|
||||
readme = "README.md"
|
||||
|
@ -10,58 +10,50 @@ edition = "2021"
|
|||
|
||||
build = "src/build.rs"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
actix-rt = "2.7.0"
|
||||
actix-web = { version = "4.0.0", default-features = false, features = ["rustls-0_21"] }
|
||||
actix-rt = "2.6.0"
|
||||
actix-web = { version = "4.0.0", default-features = false }
|
||||
anyhow = "1.0"
|
||||
awc = { version = "3.0.0", default-features = false, features = ["rustls-0_21"] }
|
||||
clap = { version = "4.0.2", features = ["derive", "env"] }
|
||||
console-subscriber = "0.2"
|
||||
awc = { version = "3.0.0", default-features = false }
|
||||
clap = { version = "3.1.15", features = ["derive", "env"] }
|
||||
console-subscriber = "0.1"
|
||||
dotenv = "0.15.0"
|
||||
mime = "0.3"
|
||||
minify-html = "0.15.0"
|
||||
opentelemetry = "0.21"
|
||||
opentelemetry_sdk = { version = "0.21", features = ["rt-tokio"] }
|
||||
opentelemetry-otlp = "0.14"
|
||||
rustls = "0.21"
|
||||
rustls-pemfile = "2.0.0"
|
||||
minify-html = "0.8.0"
|
||||
once_cell = "1.4"
|
||||
opentelemetry = { version = "0.17", features = ["rt-tokio"] }
|
||||
opentelemetry-otlp = "0.10"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_qs = { version = "0.12", features = ["actix4"] }
|
||||
serde_qs = { version = "0.9", features = ["actix4"] }
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1", features = ["fs"] }
|
||||
tracing = "0.1"
|
||||
tracing-error = "0.2"
|
||||
tracing-futures = "0.2"
|
||||
tracing-log = "0.2"
|
||||
tracing-opentelemetry = "0.22"
|
||||
tracing-log = "0.1"
|
||||
tracing-opentelemetry = "0.17"
|
||||
tracing-subscriber = { version = "0.3", features = [
|
||||
"ansi",
|
||||
"env-filter",
|
||||
"fmt",
|
||||
] }
|
||||
url = "2.1"
|
||||
webpki-roots = "0.26"
|
||||
rustls-channel-resolver = "0.1.0"
|
||||
|
||||
[dependencies.tracing-actix-web]
|
||||
version = "0.7.9"
|
||||
version = "0.5.0"
|
||||
default-features = false
|
||||
features = ["emit_event_on_error", "opentelemetry_0_21"]
|
||||
features = ["emit_event_on_error", "opentelemetry_0_17"]
|
||||
|
||||
[dependencies.tracing-awc]
|
||||
version = "0.1.9"
|
||||
version = "0.1.0"
|
||||
default-features = false
|
||||
features = ["emit_event_on_error", "opentelemetry_0_21"]
|
||||
features = ["emit_event_on_error", "opentelemetry_0_17"]
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1.0"
|
||||
dotenv = "0.15.0"
|
||||
ructe = { version = "0.17.0", features = ["sass", "mime03"] }
|
||||
ructe = { version = "0.14.0", features = ["sass", "mime03"] }
|
||||
|
|
40
README.md
40
README.md
|
@ -7,26 +7,30 @@ _a demo frontend for pict-rs supporting noscript_
|
|||
## Usage
|
||||
### Running
|
||||
```
|
||||
$ pict-rs-proxy -h
|
||||
A simple web frontend for pict-rs
|
||||
pict-rs-proxy 0.3.0-rc.3
|
||||
|
||||
Usage: pict-rs-proxy [OPTIONS]
|
||||
USAGE:
|
||||
pict-rs-proxy [OPTIONS]
|
||||
|
||||
Options:
|
||||
-a, --addr <ADDR>
|
||||
The address and port the server binds to [env: PICTRS_PROXY_ADDR=] [default: 0.0.0.0:8081]
|
||||
-u, --upstream <UPSTREAM>
|
||||
The url of the upstream pict-rs server [env: PICTRS_PROXY_UPSTREAM=] [default: http://localhost:8080]
|
||||
-d, --domain <DOMAIN>
|
||||
The scheme, domain, and optional port of the pict-rs proxy server [env: PICTRS_PROXY_DOMAIN=] [default: http://localhost:8081]
|
||||
--console-event-buffer-size <CONSOLE_EVENT_BUFFER_SIZE>
|
||||
Number of events to buffer for the console subscriber. When unset, console will be disabled [env: PICTRS_PROXY_CONSOLE_BUFFER_SIZE=]
|
||||
-o, --opentelemetry-url <OPENTELEMETRY_URL>
|
||||
URL of OpenTelemetry Collector [env: PICTRS_PROXY_OPENTELEMETRY_URL=]
|
||||
-h, --help
|
||||
Print help information
|
||||
-V, --version
|
||||
Print version information
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-a, --addr <addr>
|
||||
The address and port the server binds to [env: PICTRS_PROXY_ADDR=] [default: 0.0.0.0:8081]
|
||||
|
||||
--console-event-buffer-size <console-event-buffer-size>
|
||||
Number of events to buffer for the console subscriber. When unset, console will be disabled [env:
|
||||
PICTRS_PROXY_CONSOLE_BUFFER_SIZE=]
|
||||
-d, --domain <domain>
|
||||
The scheme, domain, and optional port of the pict-rs proxy server [env: PICTRS_PROXY_DOMAIN=] [default:
|
||||
http://localhost:8081]
|
||||
-o, --opentelemetry-url <opentelemetry-url>
|
||||
URL of OpenTelemetry Collector [env: PICTRS_PROXY_OPENTELEMETRY_URL=]
|
||||
|
||||
-u, --upstream <upstream>
|
||||
The url of the upstream pict-rs server [env: PICTRS_PROXY_UPSTREAM=] [default: http://localhost:8080]
|
||||
```
|
||||
|
||||
#### Examples
|
||||
|
|
12
docker/drone/Dockerfile
Normal file
12
docker/drone/Dockerfile
Normal file
|
@ -0,0 +1,12 @@
|
|||
ARG REPO_ARCH
|
||||
|
||||
FROM asonix/rust-runner:latest-linux-$REPO_ARCH
|
||||
|
||||
COPY pict-rs-proxy /usr/local/bin/pict-rs-proxy
|
||||
|
||||
USER app
|
||||
EXPOSE 8080
|
||||
EXPOSE 6669
|
||||
VOLUME /mnt
|
||||
ENTRYPOINT ["/sbin/tini", "--"]
|
||||
CMD ["/usr/local/bin/pict-rs-proxy"]
|
25
docker/drone/manifest.tmpl
Normal file
25
docker/drone/manifest.tmpl
Normal file
|
@ -0,0 +1,25 @@
|
|||
image: asonix/pictrs-proxy:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
-
|
||||
image: asonix/pictrs-proxy:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
-
|
||||
image: asonix/pictrs-proxy:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64v8
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
||||
-
|
||||
image: asonix/pictrs-proxy:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm32v7
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
||||
variant: v7
|
|
@ -1,24 +0,0 @@
|
|||
FROM alpine:3.19
|
||||
|
||||
ARG UID=991
|
||||
ARG GID=991
|
||||
|
||||
ENV \
|
||||
UID=${UID} \
|
||||
GID=${GID}
|
||||
|
||||
USER root
|
||||
RUN \
|
||||
addgroup -g "${GID}" app && \
|
||||
adduser -D -G app -u "${UID}" -g "" -h /opt/app app && \
|
||||
apk add tini && \
|
||||
chown -R app:app /mnt
|
||||
|
||||
COPY pict-rs-proxy /usr/local/bin/pict-rs-proxy
|
||||
|
||||
USER app
|
||||
EXPOSE 6669
|
||||
EXPOSE 8080
|
||||
VOLUME /mnt
|
||||
ENTRYPOINT ["/sbin/tini", "--"]
|
||||
CMD ["/usr/local/bin/pict-rs-proxy"]
|
41
docker/prod/Dockerfile
Normal file
41
docker/prod/Dockerfile
Normal file
|
@ -0,0 +1,41 @@
|
|||
ARG REPO_ARCH=amd64
|
||||
|
||||
# cross-build environment
|
||||
FROM asonix/rust-builder:$REPO_ARCH-latest AS builder
|
||||
|
||||
ARG TAG=main
|
||||
ARG BINARY=pict-rs-proxy
|
||||
ARG PROJECT=pict-rs-proxy
|
||||
ARG GIT_REPOSITORY=https://git.asonix.dog/asonix/$PROJECT
|
||||
|
||||
ENV \
|
||||
BINARY=${BINARY}
|
||||
|
||||
ADD \
|
||||
--chown=build:build \
|
||||
$GIT_REPOSITORY/archive/$TAG.tar.gz \
|
||||
/opt/build/repo.tar.gz
|
||||
|
||||
RUN \
|
||||
tar zxf repo.tar.gz
|
||||
|
||||
WORKDIR /opt/build/$PROJECT
|
||||
|
||||
RUN \
|
||||
build
|
||||
|
||||
# production environment
|
||||
FROM asonix/rust-runner:$REPO_ARCH-latest
|
||||
|
||||
ARG BINARY=pict-rs-proxy
|
||||
|
||||
ENV \
|
||||
BINARY=${BINARY}
|
||||
|
||||
COPY \
|
||||
--from=builder \
|
||||
/opt/build/binary \
|
||||
/usr/bin/${BINARY}
|
||||
|
||||
ENTRYPOINT ["/sbin/tini", "--"]
|
||||
CMD /usr/bin/${BINARY}
|
37
docker/prod/build-image.sh
Executable file
37
docker/prod/build-image.sh
Executable file
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
function require() {
|
||||
if [ "$1" = "" ]; then
|
||||
echo "input '$2' required"
|
||||
print_help
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function print_help() {
|
||||
echo "deploy.sh"
|
||||
echo ""
|
||||
echo "Usage:"
|
||||
echo " deploy.sh [repo] [tag] [arch]"
|
||||
echo ""
|
||||
echo "Args:"
|
||||
echo " repo: The docker repository to publish the image"
|
||||
echo " tag: The tag applied to the docker image"
|
||||
echo " arch: The architecuture of the doker image"
|
||||
}
|
||||
|
||||
REPO=$1
|
||||
TAG=$2
|
||||
ARCH=$3
|
||||
|
||||
require "$REPO" repo
|
||||
require "$TAG" tag
|
||||
require "$ARCH" arch
|
||||
|
||||
sudo docker build \
|
||||
--pull \
|
||||
--build-arg TAG=$TAG \
|
||||
--build-arg REPO_ARCH=$ARCH \
|
||||
-t $REPO:$ARCH-$TAG \
|
||||
-f Dockerfile \
|
||||
.
|
87
docker/prod/deploy.sh
Executable file
87
docker/prod/deploy.sh
Executable file
|
@ -0,0 +1,87 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
function require() {
|
||||
if [ "$1" = "" ]; then
|
||||
echo "input '$2' required"
|
||||
print_help
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function print_help() {
|
||||
echo "deploy.sh"
|
||||
echo ""
|
||||
echo "Usage:"
|
||||
echo " deploy.sh [tag] [branch] [push]"
|
||||
echo ""
|
||||
echo "Args:"
|
||||
echo " tag: The git tag to be applied to the repository and docker build"
|
||||
echo " branch: The git branch to use for tagging and publishing"
|
||||
echo " push: Whether or not to push the image"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " ./deploy.sh v0.3.0-alpha.13 main true"
|
||||
echo " ./deploy.sh v0.3.0-alpha.13-shell-out asonix/shell-out false"
|
||||
}
|
||||
|
||||
function build_image() {
|
||||
tag=$1
|
||||
arch=$2
|
||||
push=$3
|
||||
|
||||
./build-image.sh asonix/pictrs-proxy $tag $arch
|
||||
|
||||
sudo docker tag asonix/pictrs-proxy:$arch-$tag asonix/pictrs-proxy:$arch-latest
|
||||
|
||||
if [ "$push" == "true" ]; then
|
||||
sudo docker push asonix/pictrs-proxy:$arch-$tag
|
||||
sudo docker push asonix/pictrs-proxy:$arch-latest
|
||||
fi
|
||||
}
|
||||
|
||||
# Creating the new tag
|
||||
new_tag="$1"
|
||||
branch="$2"
|
||||
push=$3
|
||||
|
||||
require "$new_tag" "tag"
|
||||
require "$branch" "branch"
|
||||
require "$push" "push"
|
||||
|
||||
if ! sudo docker run --rm -it arm64v8/alpine:3.11 /bin/sh -c 'echo "docker is configured correctly"'
|
||||
then
|
||||
echo "docker is not configured to run on qemu-emulated architectures, fixing will require sudo"
|
||||
sudo docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||
fi
|
||||
|
||||
set -xe
|
||||
|
||||
git checkout $branch
|
||||
|
||||
# Changing the docker-compose prod
|
||||
sed -i "s/asonix\/pictrs-proxy:.*/asonix\/pictrs-proxy:$new_tag/" docker-compose.yml
|
||||
git add ../prod/docker-compose.yml
|
||||
# The commit
|
||||
git commit -m"Version $new_tag"
|
||||
git tag $new_tag
|
||||
|
||||
# Push
|
||||
git push origin $new_tag
|
||||
git push
|
||||
|
||||
# Build for arm64v8, arm32v7 and amd64
|
||||
build_image $new_tag arm64v8 $push
|
||||
build_image $new_tag arm32v7 $push
|
||||
build_image $new_tag amd64 $push
|
||||
|
||||
# Build for other archs
|
||||
# TODO
|
||||
|
||||
if [ "$push" == "true" ]; then
|
||||
./manifest.sh pictrs-proxy $new_tag
|
||||
./manifest.sh pictrs-proxy latest
|
||||
|
||||
pushd ../../
|
||||
cargo publish
|
||||
popd
|
||||
fi
|
|
@ -8,7 +8,7 @@ services:
|
|||
- ./volumes/pictrs:/mnt
|
||||
|
||||
pictrs-proxy:
|
||||
image: asonix/pictrs-proxy:0.3
|
||||
image: asonix/pictrs-proxy:v0.3.0-alpha.14
|
||||
ports:
|
||||
- "8081:8081"
|
||||
restart: always
|
||||
|
|
43
docker/prod/manifest.sh
Executable file
43
docker/prod/manifest.sh
Executable file
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
function require() {
|
||||
if [ "$1" = "" ]; then
|
||||
echo "input '$2' required"
|
||||
print_help
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
function print_help() {
|
||||
echo "deploy.sh"
|
||||
echo ""
|
||||
echo "Usage:"
|
||||
echo " manifest.sh [repo] [tag]"
|
||||
echo ""
|
||||
echo "Args:"
|
||||
echo " repo: The docker repository to update"
|
||||
echo " tag: The git tag to be applied to the image manifest"
|
||||
}
|
||||
|
||||
REPO=$1
|
||||
TAG=$2
|
||||
|
||||
require "$REPO" "repo"
|
||||
require "$TAG" "tag"
|
||||
|
||||
set -xe
|
||||
|
||||
sudo docker manifest create asonix/$REPO:$TAG \
|
||||
-a asonix/$REPO:arm64v8-$TAG \
|
||||
-a asonix/$REPO:arm32v7-$TAG \
|
||||
-a asonix/$REPO:amd64-$TAG
|
||||
|
||||
sudo docker manifest annotate asonix/$REPO:$TAG \
|
||||
asonix/$REPO:arm64v8-$TAG --os linux --arch arm64 --variant v8
|
||||
|
||||
sudo docker manifest annotate asonix/$REPO:$TAG \
|
||||
asonix/$REPO:arm32v7-$TAG --os linux --arch arm --variant v7
|
||||
|
||||
sudo docker manifest annotate asonix/$REPO:$TAG \
|
||||
asonix/$REPO:amd64-$TAG --os linux --arch amd64
|
||||
|
||||
sudo docker manifest push asonix/$REPO:$TAG --purge
|
61
flake.lock
61
flake.lock
|
@ -1,61 +0,0 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1705309234,
|
||||
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1706550542,
|
||||
"narHash": "sha256-UcsnCG6wx++23yeER4Hg18CXWbgNpqNXcHIo5/1Y+hc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "97b17f32362e475016f942bbdfda4a4a72a8a652",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
34
flake.nix
34
flake.nix
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
description = "pict-rs-proxy";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
};
|
||||
in
|
||||
{
|
||||
packages = rec {
|
||||
pict-rs-proxy = pkgs.callPackage ./pict-rs-proxy.nix { };
|
||||
|
||||
default = pict-rs-proxy;
|
||||
};
|
||||
|
||||
apps = rec {
|
||||
dev = flake-utils.lib.mkApp { drv = self.packages.${system}.pict-rs-proxy; };
|
||||
default = dev;
|
||||
};
|
||||
|
||||
devShell = with pkgs; mkShell {
|
||||
nativeBuildInputs = [ cargo cargo-outdated clippy gcc rust-analyzer rustc rustfmt ];
|
||||
|
||||
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
|
||||
};
|
||||
});
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
{ lib
|
||||
, nixosTests
|
||||
, rustPlatform
|
||||
}:
|
||||
|
||||
rustPlatform.buildRustPackage {
|
||||
pname = "pict-rs-proxy";
|
||||
version = "0.5.0";
|
||||
src = ./.;
|
||||
cargoLock.lockFile = ./Cargo.lock;
|
||||
|
||||
nativeBuildInputs = [ ];
|
||||
|
||||
passthru.tests = { inherit (nixosTests) pict-rs-proxy; };
|
||||
|
||||
meta = with lib; {
|
||||
description = "A simple image hosting service";
|
||||
homepage = "https://git.asonix.dog/asonix/pict-rs-proxy";
|
||||
license = with licenses; [ agpl3Plus ];
|
||||
};
|
||||
}
|
304
src/main.rs
304
src/main.rs
|
@ -10,22 +10,18 @@ use actix_web::{
|
|||
middleware::NormalizePath,
|
||||
web, App, HttpRequest, HttpResponse, HttpResponseBuilder, HttpServer, ResponseError,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use awc::{Client, Connector};
|
||||
use awc::Client;
|
||||
use clap::Parser;
|
||||
use console_subscriber::ConsoleLayer;
|
||||
use opentelemetry::KeyValue;
|
||||
use opentelemetry_otlp::WithExportConfig;
|
||||
use opentelemetry_sdk::{propagation::TraceContextPropagator, Resource};
|
||||
use rustls::{
|
||||
sign::CertifiedKey, Certificate, ClientConfig, OwnedTrustAnchor, PrivateKey, RootCertStore,
|
||||
ServerConfig,
|
||||
use once_cell::sync::Lazy;
|
||||
use opentelemetry::{
|
||||
sdk::{propagation::TraceContextPropagator, Resource},
|
||||
KeyValue,
|
||||
};
|
||||
use opentelemetry_otlp::WithExportConfig;
|
||||
use std::{
|
||||
io::Cursor,
|
||||
net::SocketAddr,
|
||||
path::PathBuf,
|
||||
sync::{Arc, OnceLock},
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
use tracing_actix_web::TracingLogger;
|
||||
|
@ -33,7 +29,8 @@ use tracing_awc::Tracing;
|
|||
use tracing_error::{ErrorLayer, SpanTrace};
|
||||
use tracing_log::LogTracer;
|
||||
use tracing_subscriber::{
|
||||
filter::Targets, layer::SubscriberExt, registry::LookupSpan, Layer, Registry,
|
||||
filter::Targets, fmt::format::FmtSpan, layer::SubscriberExt, registry::LookupSpan, Layer,
|
||||
Registry,
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
|
@ -42,20 +39,18 @@ include!(concat!(env!("OUT_DIR"), "/templates.rs"));
|
|||
const HOURS: u32 = 60 * 60;
|
||||
const DAYS: u32 = 24 * HOURS;
|
||||
|
||||
/// Simple proxy service to demonstrate pict-rs's functionality
|
||||
#[derive(Clone, Debug, Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Config {
|
||||
#[arg(
|
||||
#[clap(
|
||||
short,
|
||||
long,
|
||||
env = "PICTRS_PROXY_ADDR",
|
||||
default_value = "[::]:8081",
|
||||
default_value = "0.0.0.0:8081",
|
||||
help = "The address and port the server binds to"
|
||||
)]
|
||||
addr: SocketAddr,
|
||||
|
||||
#[arg(
|
||||
#[clap(
|
||||
short,
|
||||
long,
|
||||
env = "PICTRS_PROXY_UPSTREAM",
|
||||
|
@ -64,7 +59,7 @@ struct Config {
|
|||
)]
|
||||
upstream: Url,
|
||||
|
||||
#[arg(
|
||||
#[clap(
|
||||
short,
|
||||
long,
|
||||
env = "PICTRS_PROXY_DOMAIN",
|
||||
|
@ -73,54 +68,25 @@ struct Config {
|
|||
)]
|
||||
domain: Url,
|
||||
|
||||
#[arg(
|
||||
#[clap(
|
||||
long,
|
||||
env = "PICTRS_PROXY_CONSOLE_BUFFER_SIZE",
|
||||
help = "Number of events to buffer for the console subscriber"
|
||||
help = "Number of events to buffer for the console subscriber. When unset, console will be disabled"
|
||||
)]
|
||||
console_event_buffer_size: Option<usize>,
|
||||
|
||||
#[arg(
|
||||
long,
|
||||
env = "PICTRS_PROXY_CONSOLE",
|
||||
help = "Enable the tokio-console at the specified address"
|
||||
)]
|
||||
console_addr: Option<SocketAddr>,
|
||||
|
||||
#[arg(
|
||||
#[clap(
|
||||
short,
|
||||
long,
|
||||
env = "PICTRS_PROXY_OPENTELEMETRY_URL",
|
||||
help = "URL of OpenTelemetry Collector"
|
||||
)]
|
||||
opentelemetry_url: Option<Url>,
|
||||
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
env = "PICTRS_PROXY_CERTIFICATE",
|
||||
help = "Path to the certificate file to connect to pict-rs over TLS"
|
||||
)]
|
||||
certificate: Option<PathBuf>,
|
||||
|
||||
#[arg(
|
||||
long,
|
||||
env = "PICTRS_PROXY_SERVER_CERTIFICATE",
|
||||
help = "Path to the certificate file to serve pict-rs-proxy over TLS"
|
||||
)]
|
||||
server_certificate: Option<PathBuf>,
|
||||
|
||||
#[arg(
|
||||
long,
|
||||
env = "PICTRS_PROXY_SERVER_PRIVATE_KEY",
|
||||
help = "Path to the private key file to serve pict-rs-proxy over TLS"
|
||||
)]
|
||||
server_private_key: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn domain(&self) -> Option<&str> {
|
||||
config().domain.domain()
|
||||
CONFIG.domain.domain()
|
||||
}
|
||||
|
||||
fn upstream_upload_url(&self) -> String {
|
||||
|
@ -217,11 +183,7 @@ impl Config {
|
|||
}
|
||||
}
|
||||
|
||||
static CONFIG: OnceLock<Config> = OnceLock::new();
|
||||
|
||||
fn config() -> &'static Config {
|
||||
CONFIG.get_or_init(Config::parse)
|
||||
}
|
||||
static CONFIG: Lazy<Config> = Lazy::new(Config::parse);
|
||||
|
||||
pub enum UploadResult<'a> {
|
||||
Image(Image),
|
||||
|
@ -231,8 +193,6 @@ pub enum UploadResult<'a> {
|
|||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
enum FileType {
|
||||
#[serde(rename = "avif")]
|
||||
Avif,
|
||||
#[serde(rename = "jpg")]
|
||||
Jpg,
|
||||
#[serde(rename = "webp")]
|
||||
|
@ -242,7 +202,6 @@ enum FileType {
|
|||
impl FileType {
|
||||
fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Avif => "avif",
|
||||
Self::Jpg => "jpg",
|
||||
Self::Webp => "webp",
|
||||
}
|
||||
|
@ -347,27 +306,27 @@ impl Image {
|
|||
}
|
||||
|
||||
fn link(&self) -> String {
|
||||
config().image_url(&self.file)
|
||||
CONFIG.image_url(&self.file)
|
||||
}
|
||||
|
||||
fn thumbnails(&self) -> String {
|
||||
config().thumbnails_url(&self.file)
|
||||
CONFIG.thumbnails_url(&self.file)
|
||||
}
|
||||
|
||||
fn view(&self, size: Option<u64>) -> String {
|
||||
config().view_url(size, &self.file)
|
||||
CONFIG.view_url(size, &self.file)
|
||||
}
|
||||
|
||||
fn thumb(&self, size: u64, filetype: FileType) -> String {
|
||||
config().thumbnail_url(size, &self.file, filetype)
|
||||
CONFIG.thumbnail_url(size, &self.file, filetype)
|
||||
}
|
||||
|
||||
fn delete(&self) -> String {
|
||||
config().delete_url(&self.delete_token, &self.file)
|
||||
CONFIG.delete_url(&self.delete_token, &self.file)
|
||||
}
|
||||
|
||||
fn confirm_delete(&self) -> String {
|
||||
config().confirm_delete_url(&self.delete_token, &self.file)
|
||||
CONFIG.confirm_delete_url(&self.delete_token, &self.file)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -381,15 +340,6 @@ pub struct Error {
|
|||
kind: ErrorKind,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub(crate) fn upstream_error(&self) -> Option<&str> {
|
||||
match self.kind {
|
||||
ErrorKind::UploadFailed(ref msg) => Some(msg),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for Error
|
||||
where
|
||||
ErrorKind: From<T>,
|
||||
|
@ -427,7 +377,7 @@ impl ResponseError for Error {
|
|||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
match render(HttpResponse::build(self.status_code()), |cursor| {
|
||||
self::templates::error_html(cursor, &self.kind.to_string())
|
||||
self::templates::error(cursor, &self.kind.to_string())
|
||||
}) {
|
||||
Ok(res) => res,
|
||||
Err(_) => HttpResponse::build(self.status_code())
|
||||
|
@ -461,7 +411,7 @@ enum ErrorKind {
|
|||
#[tracing::instrument(name = "Upload Page")]
|
||||
async fn index() -> Result<HttpResponse, Error> {
|
||||
render(HttpResponse::Ok(), |cursor| {
|
||||
self::templates::index_html(cursor, "/upload", "images[]")
|
||||
self::templates::index(cursor, "/upload", "images[]")
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -476,7 +426,7 @@ async fn list_uploads(
|
|||
let mut details_handles = Vec::new();
|
||||
|
||||
for upload_id in &query.uploads {
|
||||
let claim_url = config().upstream_claim_url(upload_id.as_str());
|
||||
let claim_url = CONFIG.upstream_claim_url(upload_id.as_str());
|
||||
let client = client.clone();
|
||||
|
||||
upload_handles.push(actix_rt::spawn(async move {
|
||||
|
@ -497,7 +447,7 @@ async fn list_uploads(
|
|||
}
|
||||
|
||||
for (file, delete_token) in &query.files {
|
||||
let details_url = config().upstream_details_url(file);
|
||||
let details_url = CONFIG.upstream_details_url(file);
|
||||
|
||||
let file = file.clone();
|
||||
let delete_token = delete_token.clone();
|
||||
|
@ -574,12 +524,12 @@ async fn list_uploads(
|
|||
}
|
||||
|
||||
return render(HttpResponse::Ok(), |cursor| {
|
||||
self::templates::uploads_html(cursor)
|
||||
self::templates::uploads(cursor)
|
||||
});
|
||||
}
|
||||
|
||||
render(HttpResponse::Ok(), |cursor| {
|
||||
self::templates::finished_uploads_html(cursor, results)
|
||||
self::templates::finished_uploads(cursor, results)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -589,7 +539,7 @@ async fn upload(
|
|||
body: web::Payload,
|
||||
client: web::Data<Client>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
let client_request = client.request_from(config().upstream_upload_url(), req.head());
|
||||
let client_request = client.request_from(CONFIG.upstream_upload_url(), req.head());
|
||||
|
||||
let client_request = if let Some(addr) = req.head().peer_addr {
|
||||
client_request.append_header(("X-Forwarded-For", addr.to_string()))
|
||||
|
@ -624,7 +574,7 @@ async fn thumbs(
|
|||
) -> Result<HttpResponse, Error> {
|
||||
let file = query.into_inner().image;
|
||||
|
||||
let url = config().upstream_details_url(&file);
|
||||
let url = CONFIG.upstream_details_url(&file);
|
||||
let mut res = client.get(url).send().await?;
|
||||
|
||||
if res.status() == StatusCode::NOT_FOUND {
|
||||
|
@ -640,7 +590,7 @@ async fn thumbs(
|
|||
};
|
||||
|
||||
render(HttpResponse::Ok(), |cursor| {
|
||||
self::templates::thumbnails_html(cursor, image, THUMBNAIL_SIZES)
|
||||
self::templates::thumbnails(cursor, image, THUMBNAIL_SIZES)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -679,7 +629,7 @@ async fn view_original(
|
|||
) -> Result<HttpResponse, Error> {
|
||||
let file = file.into_inner();
|
||||
|
||||
let url = config().upstream_details_url(&file);
|
||||
let url = CONFIG.upstream_details_url(&file);
|
||||
let mut res = client.get(url).send().await?;
|
||||
|
||||
if res.status() == StatusCode::NOT_FOUND {
|
||||
|
@ -695,7 +645,7 @@ async fn view_original(
|
|||
};
|
||||
|
||||
render(HttpResponse::Ok(), |cursor| {
|
||||
self::templates::view_html(cursor, image, None, THUMBNAIL_SIZES.last())
|
||||
self::templates::view(cursor, image, None, THUMBNAIL_SIZES.last())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -710,7 +660,7 @@ async fn view(
|
|||
return Ok(to_404());
|
||||
}
|
||||
|
||||
let url = config().upstream_details_url(&file);
|
||||
let url = CONFIG.upstream_details_url(&file);
|
||||
let mut res = client.get(url).send().await?;
|
||||
|
||||
if res.status() == StatusCode::NOT_FOUND {
|
||||
|
@ -726,7 +676,7 @@ async fn view(
|
|||
};
|
||||
|
||||
render(HttpResponse::Ok(), |cursor| {
|
||||
self::templates::view_html(cursor, image, Some(size), THUMBNAIL_SIZES.last())
|
||||
self::templates::view(cursor, image, Some(size), THUMBNAIL_SIZES.last())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -739,7 +689,7 @@ async fn thumbnail(
|
|||
let (size, filetype, file) = parts.into_inner();
|
||||
|
||||
if valid_thumbnail_size(size) {
|
||||
let url = config().upstream_thumbnail_url(size, &file, filetype);
|
||||
let url = CONFIG.upstream_thumbnail_url(size, &file, filetype);
|
||||
|
||||
return image(url, req, client).await;
|
||||
}
|
||||
|
@ -757,7 +707,7 @@ async fn full_res(
|
|||
req: HttpRequest,
|
||||
client: web::Data<Client>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
let url = config().upstream_image_url(&filename.into_inner());
|
||||
let url = CONFIG.upstream_image_url(&filename.into_inner());
|
||||
|
||||
image(url, req, client).await
|
||||
}
|
||||
|
@ -802,7 +752,7 @@ async fn delete(
|
|||
confirm,
|
||||
} = query.into_inner();
|
||||
|
||||
let url = config().upstream_details_url(&file);
|
||||
let url = CONFIG.upstream_details_url(&file);
|
||||
let mut res = client.get(url).send().await?;
|
||||
|
||||
if res.status() == StatusCode::NOT_FOUND {
|
||||
|
@ -810,17 +760,17 @@ async fn delete(
|
|||
}
|
||||
|
||||
if confirm {
|
||||
let url = config().upstream_delete_url(&token, &file);
|
||||
let url = CONFIG.upstream_delete_url(&token, &file);
|
||||
client.delete(url).send().await?;
|
||||
|
||||
render(HttpResponse::Ok(), |cursor| {
|
||||
self::templates::deleted_html(cursor, &file)
|
||||
self::templates::deleted(cursor, &file)
|
||||
})
|
||||
} else {
|
||||
let details: Details = res.json().await?;
|
||||
|
||||
render(HttpResponse::Ok(), move |cursor| {
|
||||
self::templates::confirm_delete_html(
|
||||
self::templates::confirm_delete(
|
||||
cursor,
|
||||
&Image {
|
||||
file,
|
||||
|
@ -841,7 +791,7 @@ fn to_404() -> HttpResponse {
|
|||
#[tracing::instrument(name = "Not Found")]
|
||||
async fn not_found() -> Result<HttpResponse, Error> {
|
||||
render(HttpResponse::NotFound(), |cursor| {
|
||||
self::templates::not_found_html(cursor)
|
||||
self::templates::not_found(cursor)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -870,7 +820,6 @@ fn render(
|
|||
fn init_tracing(
|
||||
service_name: &'static str,
|
||||
opentelemetry_url: Option<&Url>,
|
||||
console_addr: Option<SocketAddr>,
|
||||
console_event_buffer_size: Option<usize>,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
opentelemetry::global::set_text_map_propagator(TraceContextPropagator::new());
|
||||
|
@ -881,32 +830,27 @@ fn init_tracing(
|
|||
.unwrap_or_else(|_| "info".into())
|
||||
.parse()?;
|
||||
|
||||
let format_layer = tracing_subscriber::fmt::layer().with_filter(targets.clone());
|
||||
let format_layer = tracing_subscriber::fmt::layer()
|
||||
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
|
||||
.with_filter(targets.clone());
|
||||
|
||||
let subscriber = Registry::default()
|
||||
.with(format_layer)
|
||||
.with(ErrorLayer::default());
|
||||
|
||||
if let Some(console_addr) = console_addr {
|
||||
let console_builder = ConsoleLayer::builder()
|
||||
if let Some(buffer_size) = console_event_buffer_size {
|
||||
let console_layer = ConsoleLayer::builder()
|
||||
.with_default_env()
|
||||
.server_addr(console_addr);
|
||||
|
||||
let console_layer = if let Some(buffer_size) = console_event_buffer_size {
|
||||
console_builder.event_buffer_capacity(buffer_size).spawn()
|
||||
} else {
|
||||
console_builder.spawn()
|
||||
};
|
||||
.server_addr(([0, 0, 0, 0], 6669))
|
||||
.event_buffer_capacity(buffer_size)
|
||||
.spawn();
|
||||
|
||||
let subscriber = subscriber.with(console_layer);
|
||||
|
||||
init_subscriber(subscriber, targets, opentelemetry_url, service_name)?;
|
||||
tracing::info!("Serving tokio-console endpoint on {console_addr}");
|
||||
init_subscriber(subscriber, targets, opentelemetry_url, service_name)
|
||||
} else {
|
||||
init_subscriber(subscriber, targets, opentelemetry_url, service_name)?;
|
||||
init_subscriber(subscriber, targets, opentelemetry_url, service_name)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_subscriber<S>(
|
||||
|
@ -920,19 +864,18 @@ where
|
|||
for<'a> S: LookupSpan<'a>,
|
||||
{
|
||||
if let Some(url) = opentelemetry_url {
|
||||
let tracer = opentelemetry_otlp::new_pipeline()
|
||||
.tracing()
|
||||
.with_trace_config(
|
||||
opentelemetry_sdk::trace::config().with_resource(Resource::new(vec![
|
||||
KeyValue::new("service.name", service_name),
|
||||
])),
|
||||
)
|
||||
.with_exporter(
|
||||
opentelemetry_otlp::new_exporter()
|
||||
.tonic()
|
||||
.with_endpoint(url.as_str()),
|
||||
)
|
||||
.install_batch(opentelemetry_sdk::runtime::Tokio)?;
|
||||
let tracer =
|
||||
opentelemetry_otlp::new_pipeline()
|
||||
.tracing()
|
||||
.with_trace_config(opentelemetry::sdk::trace::config().with_resource(
|
||||
Resource::new(vec![KeyValue::new("service.name", service_name)]),
|
||||
))
|
||||
.with_exporter(
|
||||
opentelemetry_otlp::new_exporter()
|
||||
.tonic()
|
||||
.with_endpoint(url.as_str()),
|
||||
)
|
||||
.install_batch(opentelemetry::runtime::Tokio)?;
|
||||
|
||||
let otel_layer = tracing_opentelemetry::layer()
|
||||
.with_tracer(tracer)
|
||||
|
@ -948,85 +891,21 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn rustls_client_config() -> anyhow::Result<ClientConfig> {
|
||||
let mut cert_store = RootCertStore {
|
||||
roots: webpki_roots::TLS_SERVER_ROOTS
|
||||
.iter()
|
||||
.map(|anchor| {
|
||||
OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
anchor.subject.to_vec(),
|
||||
anchor.subject_public_key_info.to_vec(),
|
||||
anchor.name_constraints.as_ref().map(|d| d.to_vec()),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
|
||||
if let Some(cert) = config().certificate.as_ref() {
|
||||
let cert_bytes = tokio::fs::read(&cert).await?;
|
||||
|
||||
for res in rustls_pemfile::certs(&mut cert_bytes.as_slice()) {
|
||||
cert_store.add(&Certificate(res?.to_vec()))?;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_root_certificates(cert_store)
|
||||
.with_no_client_auth())
|
||||
}
|
||||
|
||||
async fn rustls_server_key() -> anyhow::Result<Option<CertifiedKey>> {
|
||||
let certificate_path = if let Some(c) = &config().server_certificate {
|
||||
c
|
||||
} else {
|
||||
tracing::info!("No server certificate");
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let private_key_path = if let Some(p) = &config().server_private_key {
|
||||
p
|
||||
} else {
|
||||
tracing::info!("No server private_key");
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let cert_bytes = tokio::fs::read(certificate_path).await?;
|
||||
|
||||
let certs = rustls_pemfile::certs(&mut cert_bytes.as_slice())
|
||||
.map(|res| res.map(|c| Certificate(c.to_vec())))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let key_bytes = tokio::fs::read(private_key_path).await?;
|
||||
|
||||
let key =
|
||||
rustls_pemfile::private_key(&mut key_bytes.as_slice())?.context("No key in keyfile")?;
|
||||
|
||||
let signing_key = rustls::sign::any_supported_type(&PrivateKey(Vec::from(key.secret_der())))?;
|
||||
|
||||
Ok(Some(CertifiedKey::new(certs, signing_key)))
|
||||
}
|
||||
|
||||
#[actix_rt::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
async fn main() -> Result<(), anyhow::Error> {
|
||||
dotenv::dotenv().ok();
|
||||
|
||||
init_tracing(
|
||||
"pict-rs-proxy",
|
||||
config().opentelemetry_url.as_ref(),
|
||||
config().console_addr,
|
||||
config().console_event_buffer_size,
|
||||
CONFIG.opentelemetry_url.as_ref(),
|
||||
CONFIG.console_event_buffer_size,
|
||||
)?;
|
||||
|
||||
let client_config = rustls_client_config().await?;
|
||||
|
||||
let server = HttpServer::new(move || {
|
||||
HttpServer::new(move || {
|
||||
let client = Client::builder()
|
||||
.wrap(Tracing)
|
||||
.add_default_header(("User-Agent", "pict-rs-frontend, v0.5.0"))
|
||||
.add_default_header(("User-Agent", "pict-rs-frontend, v0.1.0"))
|
||||
.timeout(Duration::from_secs(30))
|
||||
.disable_redirects()
|
||||
.connector(Connector::new().rustls_021(Arc::new(client_config.clone())))
|
||||
.finish();
|
||||
|
||||
App::new()
|
||||
|
@ -1051,45 +930,10 @@ async fn main() -> anyhow::Result<()> {
|
|||
.service(web::resource("/delete").route(web::get().to(delete)))
|
||||
.service(web::resource("/404").route(web::get().to(not_found)))
|
||||
.default_service(web::get().to(go_home))
|
||||
});
|
||||
|
||||
if let Some(key) = rustls_server_key().await? {
|
||||
tracing::info!("Serving pict-rs-proxy over TLS on {}", config().addr);
|
||||
|
||||
let (tx, rx) = rustls_channel_resolver::channel::<32>(key);
|
||||
|
||||
let handle = actix_rt::spawn(async move {
|
||||
let mut interval = actix_rt::time::interval(Duration::from_secs(30));
|
||||
interval.tick().await;
|
||||
|
||||
loop {
|
||||
interval.tick().await;
|
||||
|
||||
match rustls_server_key().await {
|
||||
Ok(Some(key)) => tx.update(key),
|
||||
Ok(None) => tracing::warn!("Missing server certificates"),
|
||||
Err(e) => tracing::error!("Failed to read server certificates {e}"),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let server_config = ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_no_client_auth()
|
||||
.with_cert_resolver(rx);
|
||||
|
||||
server
|
||||
.bind_rustls_021(config().addr, server_config)?
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
handle.abort();
|
||||
let _ = handle.await;
|
||||
} else {
|
||||
tracing::info!("Serving pict-rs-proxy on {}", config().addr);
|
||||
|
||||
server.bind(config().addr)?.run().await?;
|
||||
}
|
||||
})
|
||||
.bind(CONFIG.addr)?
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
<div class="imagebox">
|
||||
<picture>
|
||||
<source type="image/webp" srcset="@image.thumb(800, FileType::Webp)" />
|
||||
<source type="image/avif" srcset="@image.thumb(800, FileType::Avif)" />
|
||||
<img src="@image.thumb(800, FileType::Jpg)" alt="@image.filename()" title="@image.filename()" />
|
||||
</picture>
|
||||
</div>
|
||||
|
@ -48,11 +47,7 @@
|
|||
UploadResult::Error(error) => {
|
||||
<p>
|
||||
Error:<br />
|
||||
@if let Some(msg) = error.upstream_error() {
|
||||
@msg
|
||||
} else {
|
||||
@error.to_string()
|
||||
}
|
||||
</p>
|
||||
}
|
||||
UploadResult::UploadId(_) => {
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<div class="button plain">
|
||||
<span id="file-upload-text">Select Files</span>
|
||||
<input id="file-upload-input" type="file" name="@name"
|
||||
accept="image/apng,image/avif,image/png,image/gif,image/jpeg,image/jxl,image/webp,video/mp4,video/quicktime,video/webm,.apng,.avif,.jpg,.jpeg,.jxl,.png,.gif,.webp,.m4v,.mov,.mp4,.webm"
|
||||
accept="image/png,image/gif,image/jpeg,image/webp,video/mp4,video/quicktime,.jpg,.jpeg,.png,.gif,.webp,.m4v,.mp4"
|
||||
multiple />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@use super::statics::{layout_css, favicon_ico};
|
||||
@use crate::config;
|
||||
@use crate::CONFIG;
|
||||
|
||||
@(title: &str, description: Option<&str>, head: Content, body: Content)
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
|||
<meta property="og:description" content="Upload and share image files" />
|
||||
<meta property="twitter:description" content="Upload and share image files" />
|
||||
}
|
||||
@if let Some(domain) = config().domain() {
|
||||
@if let Some(domain) = CONFIG.domain() {
|
||||
<meta property="twitter:domain" content="@domain" />
|
||||
}
|
||||
<link rel="stylesheet" href="@crate::statics(layout_css.name)" type="text/css" />
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
<article>
|
||||
<picture>
|
||||
<source type="image/webp" srcset="@image.thumb(*size, FileType::Webp)" />
|
||||
<source type="image/avif" srcset="@image.thumb(*size, FileType::Avif)" />
|
||||
<img src="@image.thumb(*size, FileType::Jpg)" alt="@image.filename()" title="@image.filename()" />
|
||||
</picture>
|
||||
<p>@size x @size</p>
|
||||
|
|
Loading…
Reference in a new issue