Public badge for restored photos and commenter link retrieval/regeneration

This commit is contained in:
Alex Assistant 2026-02-20 15:13:46 +03:00
parent 9207c739fc
commit 0995989b56
5 changed files with 37 additions and 5 deletions

View File

@ -132,7 +132,7 @@ BRANCH=master bash scripts/deploy.sh
- после загрузки автоматический prefill имени (code_name) из имени файла,
- для каждой карточки фото можно отредактировать: имя, сортировку, комментарий и добавить/заменить фото "после",
- запись в таблицы `sections`, `photos`, `photo_files`,
- персональные комментаторы (генерация ссылок),
- персональные комментаторы (генерация ссылок + повторный просмотр/перевыпуск ссылки),
- плоские комментарии к фото,
- удаление комментариев админом,
- watermark на выдаче версии "после".

View File

@ -114,6 +114,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
}
if ($action === 'regenerate_commenter_token') {
$id = (int)($_POST['id'] ?? 0);
if ($id > 0) {
$token = commenterRegenerateToken($id);
$link = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http') . '://' . ($_SERVER['HTTP_HOST'] ?? 'localhost') . '/?viewer=' . urlencode($token);
$message = 'Токен обновлён | ссылка: ' . $link;
}
}
if ($action === 'delete_comment') {
$id = (int)($_POST['id'] ?? 0);
if ($id > 0) {
@ -371,9 +380,20 @@ function nextUniqueCodeName(string $base): string
<?php if ($adminMode === 'comments'): ?>
<section class="card">
<h3>Комментаторы и комментарии</h3>
<table class="tbl"><tr><th>Пользователь</th><th>Действие</th></tr>
<table class="tbl"><tr><th>Пользователь</th><th>Ссылка</th><th>Действия</th></tr>
<?php foreach($commenters as $u): ?>
<?php $viewerLink = !empty($u['token_plain']) ? ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http') . '://' . ($_SERVER['HTTP_HOST'] ?? 'localhost') . '/?viewer=' . urlencode((string)$u['token_plain'])) : ''; ?>
<tr><td><?= h((string)$u['display_name']) ?></td><td>
<?php if ($viewerLink !== ''): ?>
<input class="in" type="text" readonly value="<?= h($viewerLink) ?>" onclick="this.select()" style="min-width:320px">
<?php else: ?>
<span class="small">Нет сохранённой ссылки (старый пользователь)</span>
<?php endif; ?>
</td><td style="display:flex;gap:8px;flex-wrap:wrap">
<form method="post" action="?token=<?= urlencode($tokenIncoming) ?>&mode=comments">
<input type="hidden" name="action" value="regenerate_commenter_token"><input type="hidden" name="token" value="<?= h($tokenIncoming) ?>"><input type="hidden" name="id" value="<?= (int)$u['id'] ?>">
<button class="btn" type="submit">Новая ссылка</button>
</form>
<form method="post" action="?token=<?= urlencode($tokenIncoming) ?>&mode=comments" onsubmit="return confirm('Удалить пользователя?')">
<input type="hidden" name="action" value="delete_commenter"><input type="hidden" name="token" value="<?= h($tokenIncoming) ?>"><input type="hidden" name="id" value="<?= (int)$u['id'] ?>">
<button class="btn btn-danger" type="submit">Удалить доступ</button>

View File

@ -178,8 +178,9 @@ function outputWatermarked(string $path, string $mime): never
<?php else: ?>
<div class="cards">
<?php foreach($photos as $p): ?>
<a class="card" href="?photo_id=<?= (int)$p['id'] ?>&section_id=<?= (int)$activeSectionId ?><?= $viewerToken!=='' ? '&viewer=' . urlencode($viewerToken) : '' ?>" style="text-decoration:none;color:inherit">
<a class="card" href="?photo_id=<?= (int)$p['id'] ?>&section_id=<?= (int)$activeSectionId ?><?= $viewerToken!=='' ? '&viewer=' . urlencode($viewerToken) : '' ?>" style="text-decoration:none;color:inherit;position:relative">
<?php if (!empty($p['before_file_id'])): ?><img src="?action=image&file_id=<?= (int)$p['before_file_id'] ?>" alt=""><?php endif; ?>
<?php if (!empty($p['after_file_id'])): ?><span title="Есть обработанная версия" style="position:absolute;top:8px;right:8px;background:rgba(31,111,235,.92);color:#fff;font-size:11px;line-height:1;padding:6px 7px;border-radius:999px">AI</span><?php endif; ?>
<div class="cap"><strong><?= h((string)$p['code_name']) ?></strong><br><span class="muted"><?= h((string)($p['description'] ?? '')) ?></span></div>
</a>
<?php endforeach; ?>

View File

@ -100,8 +100,8 @@ function commenterCreate(string $displayName): array
{
$token = bin2hex(random_bytes(16));
$hash = hash('sha256', $token);
$st = db()->prepare('INSERT INTO comment_users(display_name, token_hash, is_active) VALUES (:n,:h,1)');
$st->execute(['n' => $displayName, 'h' => $hash]);
$st = db()->prepare('INSERT INTO comment_users(display_name, token_hash, token_plain, is_active) VALUES (:n,:h,:p,1)');
$st->execute(['n' => $displayName, 'h' => $hash, 'p' => $token]);
return [
'id' => (int)db()->lastInsertId(),
@ -121,6 +121,15 @@ function commenterDelete(int $id): void
$st->execute(['id' => $id]);
}
function commenterRegenerateToken(int $id): string
{
$token = bin2hex(random_bytes(16));
$hash = hash('sha256', $token);
$st = db()->prepare('UPDATE comment_users SET token_hash=:h, token_plain=:p WHERE id=:id');
$st->execute(['h' => $hash, 'p' => $token, 'id' => $id]);
return $token;
}
function commentsByPhoto(int $photoId): array
{
$sql = 'SELECT c.*, u.display_name

View File

@ -0,0 +1,2 @@
ALTER TABLE comment_users
ADD COLUMN token_plain VARCHAR(128) NULL AFTER token_hash;