Compare commits

...

8 Commits

Author SHA1 Message Date
Gitea Actions Bot 448882cfef chore: bump version to 1.0.113 (backend + frontend) 2025-09-09 13:31:10 +00:00
paul 7f9cb33a40 chore(native): ensure SQLite data dir exists in setup and at runtime; keep native paths consistent under /opt/picpeak/app
Mirror to GitHub / mirror (push) Successful in 44s
Test and Lint / backend-test (push) Successful in 1m38s
Test and Lint / frontend-test (push) Successful in 2m3s
Version and Release / version-bump (push) Successful in 59s
Version and Release / trigger-drone (push) Successful in 3s
2025-09-09 15:25:04 +02:00
Gitea Actions Bot 798f6211e0 chore: bump version to 1.0.112 (backend + frontend) 2025-09-09 09:46:52 +00:00
paul b992b151d3 fix(native): correct setup paths to /opt/picpeak/app, update repo URL, add sqlite prod support; docs path fixes
Mirror to GitHub / mirror (push) Successful in 1m27s
Test and Lint / backend-test (push) Successful in 1m49s
Test and Lint / frontend-test (push) Successful in 2m9s
Version and Release / version-bump (push) Successful in 1m8s
Version and Release / trigger-drone (push) Successful in 3s
2025-09-09 11:25:41 +02:00
paul 87b8414e44 fix(setup/native): correct repo URL, paths, and systemd for native install; support sqlite in production knex config 2025-09-09 11:13:01 +02:00
paul ee13556c5c docs(readme): reflect new External Media reference mode and update roadmap (gallery feedback status)
Mirror to GitHub / mirror (push) Successful in 36s
Test and Lint / backend-test (push) Successful in 1m31s
Test and Lint / frontend-test (push) Successful in 2m14s
2025-09-06 10:16:08 +02:00
Gitea Actions Bot afeb35a446 chore: bump version to 1.0.111 (backend + frontend) 2025-09-06 07:22:38 +00:00
paul ab324f1928 fix(frontend): add missing externalMedia service and mount admin external-media routes; verify Vite build
Mirror to GitHub / mirror (push) Successful in 44s
Test and Lint / backend-test (push) Successful in 1m39s
Test and Lint / frontend-test (push) Successful in 2m16s
Version and Release / version-bump (push) Successful in 1m37s
Version and Release / trigger-drone (push) Successful in 3s
2025-09-06 09:15:49 +02:00
11 changed files with 130 additions and 64 deletions
+6 -2
View File
@@ -28,6 +28,7 @@ Unlike expensive SaaS solutions, PicPeak gives you:
### For Photographers
- 📁 **Drag & Drop Upload** - Simply drop photos into folders
- 🔗 **External Media (Reference Mode)** - Browse and import from a readonly external folder library without copying originals
-**Auto-Expiring Galleries** - Set expiration dates (default: 30 days)
- 🔐 **Password Protection** - Secure client galleries
- 📧 **Automated Emails** - Creation confirmations and expiration warnings
@@ -45,6 +46,7 @@ Unlike expensive SaaS solutions, PicPeak gives you:
### Technical Excellence
- 🐳 **Docker Ready** - Deploy in minutes
- 🔄 **Auto-Processing** - Automatic thumbnail generation
- 🗂️ **Reference Library Support** - Point PicPeak at `EXTERNAL_MEDIA_ROOT` to reference existing originals, index quickly, and generate thumbnails on demand
- 💾 **Smart Storage** - Automatic archiving of expired galleries
- 🛡️ **Security First** - JWT auth, rate limiting, CORS protection
- 📈 **Scalable** - From small studios to large agencies
@@ -73,6 +75,7 @@ docker-compose up -d
## 📖 Documentation
- 📘 [**Deployment Guide**](DEPLOYMENT_GUIDE.md) - Detailed installation instructions
- Includes the new [External Media Library](DEPLOYMENT_GUIDE.md#external-media-library) reference mode
- 🤝 [**Contributing**](CONTRIBUTING.md) - How to contribute
- 📜 [**License**](LICENSE) - MIT License
- 🔒 [**Security**](SECURITY.md) - Security policies
@@ -192,9 +195,10 @@ These features are currently in beta testing and may have limited functionality
| Feature | Description | Priority | Status |
|---------|-------------|----------|---------|
| **Backup & Restore** | Comprehensive backup system with S3/MinIO support, automated scheduling, and safe restore functionality | High | ✅ Implemented |
| **External Media Library (Reference Mode)** | Use an external folder library as a readonly source with import and ondemand thumbnail generation | High | ✅ Implemented |
| **Gallery Templates** | Additional gallery layouts and themes (masonry, slideshow, story-style) for different event types | Medium | 🔄 Open |
| **Face Recognition** | AI-powered face detection to help guests find their photos and create automatic person-based albums | Low | 🔄 Open |
| **Gallery Feedback** | Allow guests to like, rate, and comment on photos with admin notifications and moderation | Medium | ✅ Implemented (not tested) |
| **Gallery Feedback** | Allow guests to like, rate, and comment on photos with admin notifications and moderation | Medium | ✅ Implemented |
| **Video Support** | Upload and display videos alongside photos in galleries with streaming support | Low | 🔄 Open |
| **Multiple Administrators** | Support for multiple admin accounts with role-based permissions and activity tracking | Low | 📋 Planned |
@@ -233,4 +237,4 @@ PicPeak is released under the [MIT License](LICENSE). Use it freely for personal
<a href="https://github.com/the-luap/picpeak">GitHub</a> •
<a href="DEPLOYMENT_GUIDE.md">Documentation</a> •
<a href="https://github.com/the-luap/picpeak/issues">Support</a>
</p>
</p>
+9 -9
View File
@@ -8,7 +8,7 @@ This guide provides easy installation instructions for PicPeak on Linux servers
```bash
# Download and run the unified setup script
curl -fsSL https://raw.githubusercontent.com/yourusername/wedding-photo-sharing/main/scripts/setup.sh -o setup.sh && \
curl -fsSL https://raw.githubusercontent.com/the-luap/picpeak/main/scripts/setup.sh -o setup.sh && \
chmod +x setup.sh && \
sudo ./setup.sh
```
@@ -305,9 +305,9 @@ docker compose restart
```
### Native Configuration
Edit `/opt/picpeak/backend/.env`:
Edit `/opt/picpeak/app/backend/.env`:
```bash
sudo nano /opt/picpeak/backend/.env
sudo nano /opt/picpeak/app/backend/.env
sudo systemctl restart picpeak-backend
```
@@ -369,7 +369,7 @@ tar -czf photos-backup.tar.gz storage/events/
#### Native:
```bash
# Database backup
sudo cp /opt/picpeak/backend/database.sqlite /backup/database-$(date +%Y%m%d).sqlite
sudo cp /opt/picpeak/app/backend/data/photo_sharing.db /backup/database-$(date +%Y%m%d).sqlite
# Photos backup
sudo tar -czf /backup/photos-$(date +%Y%m%d).tar.gz /opt/picpeak/events/
@@ -446,7 +446,7 @@ ls -la ~/picpeak/storage/events/
docker exec picpeak-backend node scripts/reset-admin-password.js
# Native
cd /opt/picpeak/backend
cd /opt/picpeak/app/backend
sudo -u picpeak node scripts/reset-admin-password.js
```
@@ -458,11 +458,11 @@ sudo -u picpeak node scripts/reset-admin-password.js
- Installation: `/tmp/picpeak-setup-*.log`
2. **Documentation:**
- [Full Documentation](https://github.com/yourusername/wedding-photo-sharing)
- [Full Documentation](https://github.com/the-luap/picpeak)
- [Deployment Guide](./DEPLOYMENT_GUIDE.md)
3. **Support:**
- [GitHub Issues](https://github.com/yourusername/wedding-photo-sharing/issues)
- [GitHub Issues](https://github.com/the-luap/picpeak/issues)
- Include: Error messages, system info (`uname -a`), installation method
## 🔒 Security Best Practices
@@ -498,7 +498,7 @@ services:
### Native Optimization
```bash
# Increase Node.js memory
echo "NODE_OPTIONS=--max-old-space-size=2048" >> /opt/picpeak/backend/.env
echo "NODE_OPTIONS=--max-old-space-size=2048" >> /opt/picpeak/app/backend/.env
sudo systemctl restart picpeak-backend
```
@@ -540,4 +540,4 @@ sudo ./setup.sh --native \
---
**PicPeak Setup v1.0** | [Documentation](https://github.com/yourusername/wedding-photo-sharing) | [Support](https://github.com/yourusername/wedding-photo-sharing/issues)
**PicPeak Setup v1.0** | [Documentation](https://github.com/the-luap/picpeak) | [Support](https://github.com/the-luap/picpeak/issues)
+32 -24
View File
@@ -26,29 +26,37 @@ const config = {
production: {
client: process.env.DATABASE_CLIENT || 'pg',
connection: {
host: process.env.DB_HOST || 'db',
port: process.env.DB_PORT || 5432,
user: process.env.DB_USER || 'picpeak',
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME || 'picpeak',
ssl: process.env.DB_SSL === 'true' ? { rejectUnauthorized: false } : false,
// Connection stability settings
connectionTimeoutMillis: 30000,
idleTimeoutMillis: 30000,
keepAlive: true,
keepAliveInitialDelayMillis: 0
},
pool: {
min: 5,
max: 25,
acquireTimeoutMillis: 60000,
createTimeoutMillis: 60000,
idleTimeoutMillis: 30000,
reapIntervalMillis: 1000,
createRetryIntervalMillis: 200,
propagateCreateError: false
},
// Support both Postgres and SQLite in production based on DATABASE_CLIENT
connection: (process.env.DATABASE_CLIENT || 'pg') === 'pg'
? {
host: process.env.DB_HOST || 'db',
port: process.env.DB_PORT || 5432,
user: process.env.DB_USER || 'picpeak',
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME || 'picpeak',
ssl: process.env.DB_SSL === 'true' ? { rejectUnauthorized: false } : false,
// Connection stability settings
connectionTimeoutMillis: 30000,
idleTimeoutMillis: 30000,
keepAlive: true,
keepAliveInitialDelayMillis: 0
}
: {
filename: path.join(__dirname, process.env.DATABASE_PATH || './data/photo_sharing.db')
},
useNullAsDefault: (process.env.DATABASE_CLIENT || 'pg') !== 'pg',
pool: (process.env.DATABASE_CLIENT || 'pg') === 'pg'
? {
min: 5,
max: 25,
acquireTimeoutMillis: 60000,
createTimeoutMillis: 60000,
idleTimeoutMillis: 30000,
reapIntervalMillis: 1000,
createRetryIntervalMillis: 200,
propagateCreateError: false
}
: undefined,
migrations: {
directory: './migrations'
},
@@ -56,4 +64,4 @@ const config = {
}
};
module.exports = config[process.env.NODE_ENV || 'development'];
module.exports = config[process.env.NODE_ENV || 'development'];
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "picpeak-backend",
"version": "1.0.110",
"version": "1.0.113",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "picpeak-backend",
"version": "1.0.110",
"version": "1.0.113",
"dependencies": {
"@aws-sdk/client-s3": "^3.850.0",
"@aws-sdk/lib-storage": "^3.850.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "picpeak-backend",
"version": "1.0.110",
"version": "1.0.113",
"description": "Backend for PicPeak event photo sharing platform",
"main": "server.js",
"scripts": {
+3 -1
View File
@@ -143,6 +143,7 @@ const secureStatic = require('./src/middleware/secureStatic');
// Get storage path from environment or use default
const storagePath = process.env.STORAGE_PATH || path.join(__dirname, '../storage');
process.env.EXTERNAL_MEDIA_ROOT = process.env.EXTERNAL_MEDIA_ROOT || '/external-media';
// Static file serving for photos (protected)
app.use('/photos', require('./src/middleware/photoAuth'), setCorsHeaders, secureStatic(path.join(storagePath, 'events/active')));
@@ -199,7 +200,8 @@ app.get('/health', async (req, res) => {
// Routes
app.use('/api/auth', authRoutes);
app.use('/api/events', eventRoutes);
app.use('/api/events', eventRoutes);
app.use('/api/admin/external-media', require('./src/routes/adminExternalMedia'));
// Gallery routes - main routes first, then feedback routes
app.use('/api/gallery', galleryRoutes);
app.use('/api/gallery', require('./src/routes/galleryFeedback'));
+22 -1
View File
@@ -1,7 +1,28 @@
const fs = require('fs');
const path = require('path');
const knex = require('knex');
const knexConfig = require('../../knexfile');
const logger = require('../utils/logger');
// Ensure SQLite directory exists when using file-based DB (native installs)
try {
const isPostgres = knexConfig && knexConfig.client === 'pg';
if (!isPostgres && knexConfig && knexConfig.connection) {
const filename = typeof knexConfig.connection === 'object'
? knexConfig.connection.filename
: (typeof knexConfig.connection === 'string' ? knexConfig.connection : null);
if (filename && typeof filename === 'string') {
const dir = path.dirname(filename);
if (dir && dir !== '.') {
fs.mkdirSync(dir, { recursive: true });
}
}
}
} catch (e) {
// Non-fatal: log and continue; SQLite will fail later if still missing
try { logger.warn('SQLite directory ensure failed', { error: e.message }); } catch (_) {}
}
// Create database connection with built-in retry logic
const db = knex(knexConfig);
@@ -312,4 +333,4 @@ async function logActivity(activityType, metadata = {}, eventId = null, actor =
}
}
module.exports = { db, initializeDatabase, logActivity, withRetry };
module.exports = { db, initializeDatabase, logActivity, withRetry };
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "picpeak-frontend",
"version": "1.0.110",
"version": "1.0.113",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "picpeak-frontend",
"version": "1.0.110",
"version": "1.0.113",
"dependencies": {
"@tanstack/react-query": "^5.0.0",
"@tiptap/extension-character-count": "^2.26.1",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "picpeak-frontend",
"private": true,
"version": "1.0.110",
"version": "1.0.113",
"type": "module",
"scripts": {
"dev": "vite",
@@ -0,0 +1,22 @@
import { api } from '../config/api';
export interface ExternalEntry { name: string; type: 'dir' | 'file'; size?: number; mtime?: string }
export const externalMediaService = {
async list(pathRel: string = ''): Promise<{ path: string; entries: ExternalEntry[]; canNavigateUp: boolean }> {
const params = new URLSearchParams();
if (pathRel) params.set('path', pathRel);
const res = await api.get(`/admin/external-media/list?${params.toString()}`);
return res.data;
},
async importEvent(eventId: number, externalPath: string, options?: { recursive?: boolean; map?: { individual?: string; collages?: string } }): Promise<{ imported: number; skipped: number; thumbnailsQueued: number }> {
const res = await api.post(`/admin/external-media/events/${eventId}/import-external`, {
external_path: externalPath,
recursive: options?.recursive ?? true,
map: options?.map
});
return res.data;
}
}
+30 -21
View File
@@ -13,7 +13,7 @@ IFS=$'\n\t'
# Script configuration
readonly SCRIPT_VERSION="2.0.0"
readonly APP_NAME="PicPeak"
readonly REPO_URL="https://github.com/yourusername/wedding-photo-sharing"
readonly REPO_URL="https://github.com/the-luap/picpeak.git"
readonly NODE_VERSION="20"
readonly MIN_RAM_DOCKER=2048
readonly MIN_RAM_NATIVE=1024
@@ -551,29 +551,34 @@ setup_native_installation() {
# Create application directory
log_step "Creating application directory..."
mkdir -p "$NATIVE_APP_DIR"/{backend,events/{active,archived},logs,config}
mkdir -p "$NATIVE_APP_DIR"/{app,events/{active,archived},logs,config}
# Clone repository
log_step "Downloading PicPeak..."
if [[ -d "$NATIVE_APP_DIR/backend/.git" ]]; then
cd "$NATIVE_APP_DIR/backend"
if [[ -d "$NATIVE_APP_DIR/app/.git" ]]; then
cd "$NATIVE_APP_DIR/app"
git pull
else
git clone "$REPO_URL" "$NATIVE_APP_DIR/backend"
git clone "$REPO_URL" "$NATIVE_APP_DIR/app"
fi
# Install dependencies
log_step "Installing dependencies..."
cd "$NATIVE_APP_DIR/backend"
# The repository root contains both backend/ and frontend/
# Install backend production dependencies
cd "$NATIVE_APP_DIR/app/backend"
npm install --production
# Ensure SQLite data directory exists for native installs
mkdir -p "$NATIVE_APP_DIR/app/backend/data"
# Generate secrets
local jwt_secret=$(generate_jwt_secret)
[[ -z "$ADMIN_PASSWORD" ]] && ADMIN_PASSWORD=$(generate_password)
# Create .env file
log_step "Creating configuration..."
cat > "$NATIVE_APP_DIR/backend/.env" <<EOF
cat > "$NATIVE_APP_DIR/app/backend/.env" <<EOF
# PicPeak Native Configuration
# Generated: $(date)
@@ -587,11 +592,12 @@ ADMIN_USERNAME=admin
ADMIN_PASSWORD=$ADMIN_PASSWORD
ADMIN_EMAIL=$ADMIN_EMAIL
# Database
DATABASE_PATH=$NATIVE_APP_DIR/backend/database.sqlite
# Database (native uses SQLite by default)
DATABASE_CLIENT=sqlite3
DATABASE_PATH=$NATIVE_APP_DIR/app/backend/data/photo_sharing.db
# Storage
PHOTOS_DIR=$NATIVE_APP_DIR/events
# Storage root (thumbnails/uploads live under this path)
STORAGE_PATH=$NATIVE_APP_DIR
# Email
SMTP_ENABLED=${SMTP_HOST:+true}
@@ -618,11 +624,11 @@ EOF
# Set permissions
chown -R $NATIVE_APP_USER:$NATIVE_APP_USER "$NATIVE_APP_DIR"
chmod 600 "$NATIVE_APP_DIR/backend/.env"
chmod 600 "$NATIVE_APP_DIR/app/backend/.env"
# Run database migrations
log_step "Initializing database..."
cd "$NATIVE_APP_DIR/backend"
cd "$NATIVE_APP_DIR/app/backend"
sudo -u $NATIVE_APP_USER npm run migrate
# Create systemd services
@@ -654,7 +660,7 @@ After=network.target
[Service]
Type=simple
User=$NATIVE_APP_USER
WorkingDirectory=$NATIVE_APP_DIR/backend
WorkingDirectory=$NATIVE_APP_DIR/app/backend
Environment="NODE_ENV=production"
ExecStart=/usr/bin/node server.js
Restart=always
@@ -675,7 +681,7 @@ After=network.target picpeak-backend.service
[Service]
Type=simple
User=$NATIVE_APP_USER
WorkingDirectory=$NATIVE_APP_DIR/backend
WorkingDirectory=$NATIVE_APP_DIR/app/backend
Environment="NODE_ENV=production"
ExecStart=/usr/bin/node src/services/workerManager.js
Restart=always
@@ -878,8 +884,8 @@ print_success_message() {
echo
echo "📚 Documentation:"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Setup Guide: https://github.com/yourusername/wedding-photo-sharing/blob/main/SIMPLE_SETUP.md"
echo "Full Docs: https://github.com/yourusername/wedding-photo-sharing"
echo "Setup Guide: https://github.com/the-luap/picpeak/blob/main/SIMPLE_SETUP.md"
echo "Full Docs: https://github.com/the-luap/picpeak"
echo
echo -e "${GREEN}✨ Setup complete! Visit the admin panel to start creating galleries.${NC}"
}
@@ -935,13 +941,16 @@ update_native_installation() {
systemctl stop picpeak-backend picpeak-workers
# Backup current configuration
cp "$NATIVE_APP_DIR/backend/.env" "$NATIVE_APP_DIR/backend/.env.backup-$(date +%Y%m%d-%H%M%S)"
if [[ -f "$NATIVE_APP_DIR/app/backend/.env" ]]; then
cp "$NATIVE_APP_DIR/app/backend/.env" "$NATIVE_APP_DIR/app/backend/.env.backup-$(date +%Y%m%d-%H%M%S)"
fi
# Pull latest code
cd "$NATIVE_APP_DIR/backend"
cd "$NATIVE_APP_DIR/app"
sudo -u $NATIVE_APP_USER git pull
# Update dependencies
# Update backend dependencies
cd "$NATIVE_APP_DIR/app/backend"
sudo -u $NATIVE_APP_USER npm install --production
# Run migrations
@@ -1181,4 +1190,4 @@ main() {
}
# Run main function
main "$@"
main "$@"