chore: remove password-lock; switch to ADMIN_TOKEN env; update docs

This commit is contained in:
belisards
2026-05-03 16:34:41 -03:00
parent ea0d2c9370
commit ce7731cebb
4 changed files with 31 additions and 74 deletions

View File

@@ -1,12 +1,10 @@
# Admin Credentials (REQUIRED)
# Set a strong username and password for the admin account
ADMIN_USERNAME=admin
ADMIN_PASSWORD=changeme
# Admin Token (REQUIRED)
# Long random string used to authenticate the admin via ?adm=<token> in the URL.
# Generate with: openssl rand -hex 32
ADMIN_TOKEN=replace-with-32-byte-random-hex
# JWT Secret (Optional - auto-generated if not provided)
# For production, generate a secure random string:
# openssl rand -base64 32
SECRET=
# Public base URL used by the guest CLI when generating links
PUBLIC_BASE_URL=http://localhost:3000
# Application Settings (Optional)
NODE_ENV=production

View File

@@ -49,17 +49,28 @@ UI strings are hardcoded in the source. The PT-BR localization lives in:
- `app/layout.tsx``<html lang="pt-BR">` and metadata description
- `app/[slug]/page.tsx``Intl.NumberFormat('pt-BR', ...)` for prices
- `components/share-button.tsx` — "Compartilhar" label
- `app/lock/page.tsx`, `app/not-found.tsx`, `app/page.tsx` — page copy
- `app/not-found.tsx`, `app/page.tsx` — page copy
To switch language, update those files and rebuild the image.
### Admin credentials
### Admin token
Set via environment variables in `docker-compose.yml`:
The admin authenticates by visiting the site with `?adm=<token>` once. The token is set as an env var:
```yaml
- ADMIN_USERNAME=your_username
- ADMIN_PASSWORD=your_password
- ADMIN_TOKEN=long-random-hex-at-least-16-chars
```
The token is also accepted from the cookie `adm_token` after first visit.
### Guests
Each guest gets their own URL: `https://chadebebe.omeu.website/<slug>?usr=<token>`. Manage guests at `/admin/guests` or via CLI:
```bash
npm run guest:create -- --name="Martin"
npm run guest:list
npm run guest:delete -- --id=<guest-id>
```
## Deployment
@@ -80,7 +91,7 @@ cd chadebebe
docker compose up -d --build
```
The `data/db/wishlist.db` snapshot committed in the repo will be used as the initial database. On first run the app writes `data/secrets.json` (JWT keys) — this file is gitignored and must not be committed.
The `data/db/wishlist.db` snapshot committed in the repo will be used as the initial database.
### Updating the app

View File

@@ -2,20 +2,17 @@ import { NextRequest, NextResponse } from 'next/server';
import { eq } from 'drizzle-orm';
import { db, settings } from '@/lib/db';
import { verifyAdminToken } from '@/lib/auth/tokens';
import crypto from 'crypto';
// GET /api/settings - Get all settings (public endpoint for reading only)
export async function GET(request: NextRequest) {
export async function GET() {
try {
const allSettings = await db.select().from(settings);
// Convert to key-value object
const settingsObj = allSettings.reduce((acc, setting) => {
acc[setting.key] = setting.value;
return acc;
}, {} as Record<string, string | boolean>);
}, {} as Record<string, string>);
// Set defaults if not found
if (!settingsObj.siteTitle) {
settingsObj.siteTitle = 'Wishlist';
}
@@ -23,12 +20,12 @@ export async function GET(request: NextRequest) {
settingsObj.homepageSubtext = 'Browse and explore available wishlists';
}
// Convert passwordLockEnabled to boolean
(settingsObj as any).passwordLockEnabled = settingsObj.passwordLockEnabled === 'true';
return NextResponse.json({
success: true,
settings: settingsObj,
settings: {
siteTitle: settingsObj.siteTitle,
homepageSubtext: settingsObj.homepageSubtext,
},
});
} catch (error) {
console.error('Error fetching settings:', error);
@@ -47,9 +44,8 @@ export async function PUT(request: NextRequest) {
}
const body = await request.json();
const { siteTitle, homepageSubtext, passwordLockEnabled, passwordLock } = body;
const { siteTitle, homepageSubtext } = body;
// Update or insert siteTitle
if (siteTitle !== undefined) {
const existing = await db
.select()
@@ -70,7 +66,6 @@ export async function PUT(request: NextRequest) {
}
}
// Update or insert homepageSubtext
if (homepageSubtext !== undefined) {
const existing = await db
.select()
@@ -91,52 +86,6 @@ export async function PUT(request: NextRequest) {
}
}
// Update or insert passwordLockEnabled
if (passwordLockEnabled !== undefined) {
const value = passwordLockEnabled ? 'true' : 'false';
const existing = await db
.select()
.from(settings)
.where(eq(settings.key, 'passwordLockEnabled'))
.limit(1);
if (existing.length > 0) {
await db
.update(settings)
.set({ value, updatedAt: new Date() })
.where(eq(settings.key, 'passwordLockEnabled'));
} else {
await db.insert(settings).values({
key: 'passwordLockEnabled',
value,
});
}
}
// Update password hash if provided
if (passwordLock && passwordLock.trim() !== '') {
// Hash the password using SHA-256
const hash = crypto.createHash('sha256').update(passwordLock).digest('hex');
const existing = await db
.select()
.from(settings)
.where(eq(settings.key, 'passwordLockHash'))
.limit(1);
if (existing.length > 0) {
await db
.update(settings)
.set({ value: hash, updatedAt: new Date() })
.where(eq(settings.key, 'passwordLockHash'));
} else {
await db.insert(settings).values({
key: 'passwordLockHash',
value: hash,
});
}
}
return NextResponse.json({
success: true,
message: 'Settings updated successfully',

View File

@@ -5,8 +5,7 @@ services:
container_name: chadebebe
restart: unless-stopped
environment:
- ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-changeme}
- ADMIN_TOKEN=${ADMIN_TOKEN:?ADMIN_TOKEN must be set}
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- COOKIE_SECURE=true