Initial release: self-hosted client photo gallery
ci / docker (push) Failing after 52s

Rust (axum + sqlx) API and worker sharing a Postgres-backed job queue
(SKIP LOCKED, heartbeat, reaper, typed statuses), S3 storage with derived
keys and a fully private bucket, OIDC photographer login with per-request
allowlist checks, client share links with argon2 passwords and lockout,
cookie-based image authorization with sliding expiry, hand-rolled
spec-compliant streaming ZIP downloads with exact Content-Length,
React + Vite gallery frontend, single Docker image, Helm chart for
external S3 + Postgres, and Gitea CI.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-17 13:04:39 +02:00
co-authored by Claude
commit 3ab8ff8dd7
55 changed files with 11958 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
import { useState } from 'react'
export default function Stars({ value = 0, onChange, small }) {
const [hover, setHover] = useState(0)
const shown = hover || value || 0
return (
<span
className={`stars${small ? ' stars-small' : ''}${onChange ? '' : ' stars-readonly'}`}
onMouseLeave={() => setHover(0)}
>
{[1, 2, 3, 4, 5].map((n) => (
<button
key={n}
type="button"
className={n <= shown ? 'star filled' : 'star'}
disabled={!onChange}
onMouseEnter={() => onChange && setHover(n)}
onClick={(e) => {
e.stopPropagation()
// Clicking the current rating clears it.
onChange(n === value ? 0 : n)
}}
title={onChange ? `${n} star${n > 1 ? 's' : ''}` : undefined}
>
</button>
))}
</span>
)
}