Privater Bereich /api/privat fuer Behoerdenfotos (d4rk-rp-planung#227 H1)
Polizeifotos aus dem Spiel duerfen nicht unter /f/ liegen, wo jeder sie mit der Adresse bekommt. Neu: - eigener Ordner DATA_DIR/privat neben files/ (fuer /f/ und /t/ durch Bauart unerreichbar), eigene Tabelle privat_media, eigene Vorschauen - PUT/GET /api/privat/datei/<pfad>, ?vorschau=1; kein Ueberschreiben (409), kein Loeschweg; Cache-Control private, no-store - Token-Fessel "privat": ein privater Token kommt an keinen oeffentlichen Weg (upload, Fivemanage, Liste), ein oeffentlicher an keinen privaten - Dashboard: Kaestchen "nur privater Bereich" beim Anlegen und Bearbeiten, Marke in der Liste - checkPathUnter: dieselbe Pfadregel fuer beide Ablagen - tools/sichern.ps1 packt privat/ mit und zaehlt Dateien gegen Eintraege Geprueft mit tools/privat-pruefen.mjs gegen einen Wegwerf-Container des Abbilds: 21/21, mit Kontrollen (oeffentliches Bild MUSS unter /f/ kommen). Dashboard gemessen (kopfloser Edge): 7/7. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
c8080fb2eb
commit
6630def7b2
14 files changed
+641
-15
No files matched your search
@@ -183,6 +183,8 @@ export type TokenRow = {
|
||||
/** Erlaubte Arten, mit Komma. Leer = alle. */
|
||||
arten: string
|
||||
expires_at: number | null
|
||||
/** 1 = nur privater Bereich (/api/privat), kein öffentlicher Upload. */
|
||||
privat: number
|
||||
abgelaufen: boolean
|
||||
/** Wieviel mit DIESEM Token abgelegt wurde. */
|
||||
dateien: number
|
||||
@@ -199,6 +201,7 @@ export type NewToken = {
|
||||
name: string
|
||||
prefix: string
|
||||
canDelete: boolean
|
||||
privat: boolean
|
||||
token: string
|
||||
}
|
||||
|
||||
@@ -584,6 +587,7 @@ export const api = {
|
||||
maxKante?: string | null
|
||||
arten?: string[] | null
|
||||
gueltigTage?: string | null
|
||||
privat?: boolean
|
||||
},
|
||||
) => request<TokenRow & { geaendert: string[] }>(`/api/dash/tokens/${id}`, {
|
||||
method: 'PATCH',
|
||||
@@ -625,6 +629,7 @@ export const api = {
|
||||
quotaMb?: string
|
||||
arten?: string[]
|
||||
gueltigTage?: string
|
||||
privat?: boolean
|
||||
}) => post<NewToken>('/api/dash/tokens', daten),
|
||||
deleteToken: (id: number) => del<{ ok: true }>(`/api/dash/tokens/${id}`),
|
||||
}
|
||||
@@ -36,6 +36,7 @@ export function TokenBearbeiten({
|
||||
const [name, setName] = useState(token.name)
|
||||
const [prefix, setPrefix] = useState(token.prefix)
|
||||
const [canDelete, setCanDelete] = useState(Boolean(token.can_delete))
|
||||
const [privat, setPrivat] = useState(Boolean(token.privat))
|
||||
const [maxMb, setMaxMb] = useState(
|
||||
token.max_bytes ? String(Math.round((token.max_bytes / 1048576) * 10) / 10) : '',
|
||||
)
|
||||
@@ -65,6 +66,7 @@ export function TokenBearbeiten({
|
||||
name: name.trim(),
|
||||
prefix,
|
||||
canDelete,
|
||||
privat,
|
||||
// Leer heißt ausdrücklich „keine Grenze" und nicht
|
||||
// „nicht angefasst" — sonst ließe sich eine einmal
|
||||
// gesetzte Grenze nie wieder loswerden.
|
||||
@@ -148,6 +150,22 @@ export function TokenBearbeiten({
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label className="flex items-start gap-2 text-sm">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={privat}
|
||||
onChange={(e) => setPrivat(e.target.checked)}
|
||||
className="mt-0.5 accent-akzent"
|
||||
/>
|
||||
<span>
|
||||
nur <strong className="text-schrift">privater Bereich</strong>
|
||||
<span className="block text-xs text-schrift-leise">
|
||||
Wirkt sofort: angehakt kann der Token nicht mehr öffentlich
|
||||
hochladen, abgehakt nicht mehr privat lesen.
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div>
|
||||
<span className="mb-1 block text-xs font-medium text-schrift-leise">
|
||||
Erlaubte Arten (nichts angekreuzt = alle)
|
||||
|
||||
@@ -14,6 +14,7 @@ export function Tokens() {
|
||||
const [name, setName] = useState('')
|
||||
const [prefix, setPrefix] = useState('')
|
||||
const [darfLoeschen, setDarfLoeschen] = useState(false)
|
||||
const [privat, setPrivat] = useState(false)
|
||||
const [maxMb, setMaxMb] = useState('')
|
||||
const [quotaMb, setQuotaMb] = useState('')
|
||||
const [maxKante, setMaxKante] = useState('')
|
||||
@@ -60,6 +61,7 @@ export function Tokens() {
|
||||
maxKante: maxKante.trim(),
|
||||
arten,
|
||||
gueltigTage: gueltigTage.trim(),
|
||||
privat,
|
||||
}),
|
||||
(t) => `Token "${t.name}" angelegt.`,
|
||||
)
|
||||
@@ -69,6 +71,7 @@ export function Tokens() {
|
||||
setName('')
|
||||
setPrefix('')
|
||||
setDarfLoeschen(false)
|
||||
setPrivat(false)
|
||||
setMaxMb('')
|
||||
setGueltigTage('')
|
||||
setArten([])
|
||||
@@ -205,6 +208,24 @@ export function Tokens() {
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label className="flex items-start gap-2 text-sm">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={privat}
|
||||
onChange={(e) => setPrivat(e.target.checked)}
|
||||
className="mt-0.5 accent-akzent"
|
||||
/>
|
||||
<span>
|
||||
nur privater Bereich
|
||||
<span className="block text-xs text-schrift-leise">
|
||||
Für Fotos, die nicht öffentlich sein dürfen (Behörden). Der Token
|
||||
legt nur unter <code className="font-mono">/api/privat</code> ab
|
||||
und liest nur dort — nicht unter /f/, ohne Liste, ohne Freigabe.
|
||||
Öffentlich hochladen kann er dann nicht.
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div>
|
||||
<Knopf
|
||||
art="haupt"
|
||||
@@ -274,6 +295,11 @@ export function Tokens() {
|
||||
: '— (überall)'}
|
||||
</td>
|
||||
<td className="py-2">
|
||||
{t.privat ? (
|
||||
<span className="mr-1 rounded bg-akzent/15 px-1.5 py-0.5 text-xs text-schrift">
|
||||
privat
|
||||
</span>
|
||||
) : null}
|
||||
{t.can_delete ? (
|
||||
<span className="rounded bg-warn/15 px-1.5 py-0.5 text-xs text-warn">
|
||||
schreiben + löschen
|
||||
@@ -411,6 +437,7 @@ export function Tokens() {
|
||||
<p className="mt-3 text-xs text-schrift-leise">
|
||||
Präfix: <code className="font-mono">{frisch.prefix || '—'}</code>{' '}
|
||||
· Löschen: {frisch.canDelete ? 'erlaubt' : 'nicht erlaubt'}
|
||||
{frisch.privat ? ' · nur privater Bereich' : ''}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in new issue
Block a user