Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 840b8870ec | |||
| ad495a92c4 | |||
| b428543452 | |||
| 6492cb9ec8 | |||
| 0e0a0b91d1 | |||
| f8fb1c3f4b | |||
| b108f6fe1c | |||
| 61299a33c4 |
@@ -1,39 +0,0 @@
|
|||||||
# Development Environment with PostgreSQL
|
|
||||||
# Copy this to .env for PostgreSQL development with Docker Compose
|
|
||||||
|
|
||||||
# JWT Secret (development only)
|
|
||||||
JWT_SECRET=dev-secret-key-do-not-use-in-production
|
|
||||||
|
|
||||||
# Database Configuration (PostgreSQL)
|
|
||||||
DATABASE_CLIENT=pg
|
|
||||||
DB_USER=picpeak_dev
|
|
||||||
DB_PASSWORD=dev_password_123
|
|
||||||
DB_NAME=picpeak_dev
|
|
||||||
|
|
||||||
# Redis Configuration
|
|
||||||
REDIS_PASSWORD=dev_redis_pass
|
|
||||||
|
|
||||||
# Admin Account (initial setup)
|
|
||||||
ADMIN_USERNAME=admin
|
|
||||||
ADMIN_EMAIL=admin@localhost
|
|
||||||
|
|
||||||
# Email Configuration (Mailhog for development)
|
|
||||||
SMTP_HOST=mailhog
|
|
||||||
SMTP_PORT=1025
|
|
||||||
SMTP_SECURE=false
|
|
||||||
SMTP_USER=
|
|
||||||
SMTP_PASS=
|
|
||||||
EMAIL_FROM=noreply@picpeak.local
|
|
||||||
|
|
||||||
# Application URLs
|
|
||||||
FRONTEND_URL=http://localhost:3000
|
|
||||||
ADMIN_URL=http://localhost:3001
|
|
||||||
VITE_API_URL=http://localhost:3001/api
|
|
||||||
|
|
||||||
# Timezone
|
|
||||||
TZ=UTC
|
|
||||||
|
|
||||||
# Analytics (Optional - leave empty for development)
|
|
||||||
VITE_UMAMI_URL=
|
|
||||||
VITE_UMAMI_WEBSITE_ID=
|
|
||||||
VITE_UMAMI_SHARE_URL=
|
|
||||||
+38
-53
@@ -1,65 +1,50 @@
|
|||||||
# PicPeak Development Environment Configuration
|
# PicPeak Environment Configuration
|
||||||
# Copy this file to .env for local development
|
# Copy this file to .env and update with your values
|
||||||
|
|
||||||
# SECURITY WARNING: This configuration is for development only!
|
# Environment
|
||||||
# For production, use .env.production.example
|
NODE_ENV=production
|
||||||
|
|
||||||
# JWT Secret (Change in production!)
|
# JWT Secret (generate with: openssl rand -base64 64)
|
||||||
# Generate secure secret with: openssl rand -base64 32
|
JWT_SECRET=your_very_long_random_jwt_secret_here
|
||||||
JWT_SECRET=dev-secret-DO-NOT-USE-IN-PRODUCTION
|
|
||||||
|
|
||||||
# Application URLs (Docker Compose development setup)
|
# Database Configuration (PostgreSQL)
|
||||||
ADMIN_URL=http://localhost:3005
|
|
||||||
FRONTEND_URL=http://localhost:3005
|
|
||||||
BACKEND_URL=http://localhost:3001
|
|
||||||
|
|
||||||
# Database Configuration (PostgreSQL for development)
|
|
||||||
DATABASE_CLIENT=pg
|
DATABASE_CLIENT=pg
|
||||||
DB_HOST=db
|
|
||||||
DB_PORT=5432
|
|
||||||
DB_NAME=picpeak
|
|
||||||
DB_USER=picpeak
|
DB_USER=picpeak
|
||||||
DB_PASSWORD=picpeak
|
DB_PASSWORD=your_secure_postgres_password_here
|
||||||
|
DB_NAME=picpeak_prod
|
||||||
|
|
||||||
|
# Redis Configuration
|
||||||
|
REDIS_PASSWORD=your_secure_redis_password_here
|
||||||
|
|
||||||
|
# Admin Account (initial setup)
|
||||||
|
ADMIN_USERNAME=admin
|
||||||
|
ADMIN_EMAIL=admin@yourdomain.com
|
||||||
|
|
||||||
# Email Configuration
|
# Email Configuration
|
||||||
# For development with docker-compose.dev.yml:
|
# For Gmail: use app-specific password
|
||||||
# Access Mailhog UI at: http://localhost:8025
|
# For SendGrid: SMTP_USER=apikey, SMTP_PASS=your-api-key
|
||||||
SMTP_HOST=mailhog
|
SMTP_HOST=smtp.gmail.com
|
||||||
SMTP_PORT=1025
|
SMTP_PORT=587
|
||||||
SMTP_SECURE=false
|
SMTP_SECURE=false
|
||||||
SMTP_USER=
|
SMTP_USER=your-email@gmail.com
|
||||||
SMTP_PASS=
|
SMTP_PASS=your-app-specific-password
|
||||||
EMAIL_FROM=noreply@localhost
|
EMAIL_FROM=noreply@yourdomain.com
|
||||||
|
|
||||||
# For development without Docker, use real SMTP:
|
# Application URLs
|
||||||
# SMTP_HOST=smtp.gmail.com
|
FRONTEND_URL=https://yourdomain.com
|
||||||
# SMTP_PORT=587
|
ADMIN_URL=https://yourdomain.com:3001
|
||||||
# SMTP_SECURE=false
|
VITE_API_URL=https://yourdomain.com:3001/api
|
||||||
# SMTP_USER=your-email@gmail.com
|
|
||||||
# SMTP_PASS=your-app-password
|
|
||||||
# EMAIL_FROM=PicPeak Dev <your-email@gmail.com>
|
|
||||||
|
|
||||||
# Backend Port Configuration
|
# Port Configuration (optional)
|
||||||
PORT=3001
|
# BACKEND_PORT=3001
|
||||||
|
# FRONTEND_PORT=3000
|
||||||
|
# DB_PORT=5432
|
||||||
|
# REDIS_PORT=6379
|
||||||
|
|
||||||
# Optional: Umami Analytics Backend Config
|
# Timezone
|
||||||
# NOTE: Primary configuration through Admin UI > Settings > Analytics
|
TZ=UTC
|
||||||
# These are fallback values for server-side tracking
|
|
||||||
# UMAMI_URL=https://analytics.example.com
|
|
||||||
# UMAMI_WEBSITE_ID=your-website-id
|
|
||||||
# UMAMI_HASH_SALT=your-hash-salt
|
|
||||||
|
|
||||||
# Development Features
|
# Analytics (Optional - Umami)
|
||||||
NODE_ENV=development
|
VITE_UMAMI_URL=
|
||||||
LOG_LEVEL=debug
|
VITE_UMAMI_WEBSITE_ID=
|
||||||
|
VITE_UMAMI_SHARE_URL=
|
||||||
# Storage Settings (optional)
|
|
||||||
DEFAULT_EXPIRATION_DAYS=30
|
|
||||||
WARNING_DAYS_BEFORE_EXPIRY=7
|
|
||||||
|
|
||||||
# Admin Setup Notes:
|
|
||||||
# 1. Run 'docker-compose -f docker-compose.dev.yml up -d'
|
|
||||||
# 2. Run 'docker-compose -f docker-compose.dev.yml exec backend npm run migrate'
|
|
||||||
# 3. Admin credentials will be auto-generated
|
|
||||||
# 4. Check backend/ADMIN_CREDENTIALS.txt for login details
|
|
||||||
# 5. Change password on first login (required)
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
# PicPeak Production Configuration
|
|
||||||
# Copy this file to .env and update with your production values
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# CRITICAL SECURITY - MUST CHANGE ALL VALUES!
|
|
||||||
# ============================================
|
|
||||||
|
|
||||||
# JWT Secret - REQUIRED (minimum 32 characters)
|
|
||||||
# Generate with: openssl rand -base64 32
|
|
||||||
JWT_SECRET=CHANGE-THIS-PRODUCTION-SECRET-USE-OPENSSL-COMMAND
|
|
||||||
|
|
||||||
# Application URLs - REQUIRED (your actual domain)
|
|
||||||
FRONTEND_URL=https://your-domain.com
|
|
||||||
BACKEND_URL=https://your-domain.com
|
|
||||||
ADMIN_URL=https://your-domain.com
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# DATABASE CONFIGURATION - REQUIRED
|
|
||||||
# ============================================
|
|
||||||
|
|
||||||
# PostgreSQL Configuration (Recommended for production)
|
|
||||||
DATABASE_CLIENT=pg
|
|
||||||
DB_HOST=db # Use 'db' for Docker Compose, or external host
|
|
||||||
DB_PORT=5432
|
|
||||||
DB_NAME=picpeak
|
|
||||||
DB_USER=picpeak
|
|
||||||
DB_PASSWORD=CHANGE-THIS-SECURE-DATABASE-PASSWORD
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# EMAIL CONFIGURATION - REQUIRED
|
|
||||||
# ============================================
|
|
||||||
|
|
||||||
# Example: Gmail with App Password
|
|
||||||
# SMTP_HOST=smtp.gmail.com
|
|
||||||
# SMTP_PORT=587
|
|
||||||
# SMTP_SECURE=false
|
|
||||||
# SMTP_USER=your-email@gmail.com
|
|
||||||
# SMTP_PASS=your-16-char-app-password
|
|
||||||
# EMAIL_FROM=Your Name <your-email@gmail.com>
|
|
||||||
|
|
||||||
# Example: SendGrid
|
|
||||||
SMTP_HOST=smtp.sendgrid.net
|
|
||||||
SMTP_PORT=587
|
|
||||||
SMTP_SECURE=false
|
|
||||||
SMTP_USER=apikey
|
|
||||||
SMTP_PASS=YOUR-SENDGRID-API-KEY
|
|
||||||
EMAIL_FROM=PicPeak <noreply@your-domain.com>
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# ADMIN SETUP - AUTO-GENERATED
|
|
||||||
# ============================================
|
|
||||||
# NOTE: Admin credentials are automatically generated during setup
|
|
||||||
# DO NOT set ADMIN_EMAIL or ADMIN_PASSWORD anymore!
|
|
||||||
# Run 'npm run migrate' and check ADMIN_CREDENTIALS.txt
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# OPTIONAL CONFIGURATION
|
|
||||||
# ============================================
|
|
||||||
|
|
||||||
# Umami Analytics (Optional - Fallback values)
|
|
||||||
# Primary config via Admin UI > Settings > Analytics
|
|
||||||
# UMAMI_URL=https://analytics.your-domain.com
|
|
||||||
# UMAMI_WEBSITE_ID=your-website-id
|
|
||||||
# UMAMI_HASH_SALT=your-hash-salt # Required if using Umami
|
|
||||||
|
|
||||||
# Frontend Analytics (Optional - Fallback values)
|
|
||||||
# VITE_UMAMI_URL=https://analytics.your-domain.com
|
|
||||||
# VITE_UMAMI_WEBSITE_ID=your-website-id
|
|
||||||
# VITE_UMAMI_SHARE_URL=https://analytics.your-domain.com/share/xyz/gallery
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# PERFORMANCE & SECURITY TUNING
|
|
||||||
# ============================================
|
|
||||||
|
|
||||||
NODE_ENV=production
|
|
||||||
PORT=3001
|
|
||||||
LOG_LEVEL=info
|
|
||||||
|
|
||||||
# Backend URL (if different from frontend)
|
|
||||||
# BACKEND_URL=https://api.your-domain.com
|
|
||||||
|
|
||||||
# Storage Settings
|
|
||||||
DEFAULT_EXPIRATION_DAYS=30
|
|
||||||
WARNING_DAYS_BEFORE_EXPIRY=7
|
|
||||||
|
|
||||||
# Security Settings (Defaults are secure)
|
|
||||||
BCRYPT_ROUNDS=12
|
|
||||||
SESSION_TIMEOUT_MINUTES=60
|
|
||||||
RATE_LIMIT_WINDOW_MS=900000 # 15 minutes
|
|
||||||
RATE_LIMIT_MAX_REQUESTS=100 # per window
|
|
||||||
|
|
||||||
# Connection Pool (Adjust based on load)
|
|
||||||
DB_POOL_MIN=5
|
|
||||||
DB_POOL_MAX=25
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# DOCKER COMPOSE SPECIFIC
|
|
||||||
# ============================================
|
|
||||||
|
|
||||||
# Traefik Configuration (if using Traefik)
|
|
||||||
DOMAIN=your-domain.com
|
|
||||||
LETSENCRYPT_EMAIL=admin@your-domain.com
|
|
||||||
|
|
||||||
# Volume Paths (Docker)
|
|
||||||
STORAGE_PATH=/app/storage
|
|
||||||
EVENTS_PATH=/app/storage/events
|
|
||||||
ARCHIVE_PATH=/app/storage/events/archived
|
|
||||||
+325
-642
File diff suppressed because it is too large
Load Diff
@@ -18,9 +18,13 @@ exports.up = async function(knex) {
|
|||||||
const generatedPassword = generateReadablePassword();
|
const generatedPassword = generateReadablePassword();
|
||||||
const passwordHash = await bcrypt.hash(generatedPassword, 12); // Increased rounds for better security
|
const passwordHash = await bcrypt.hash(generatedPassword, 12); // Increased rounds for better security
|
||||||
|
|
||||||
|
// Get admin credentials from environment or use defaults
|
||||||
|
const adminUsername = process.env.ADMIN_USERNAME || 'admin';
|
||||||
|
const adminEmail = process.env.ADMIN_EMAIL || 'admin@example.com';
|
||||||
|
|
||||||
await knex('admin_users').insert({
|
await knex('admin_users').insert({
|
||||||
username: 'admin',
|
username: adminUsername,
|
||||||
email: 'admin@example.com',
|
email: adminEmail,
|
||||||
password_hash: passwordHash,
|
password_hash: passwordHash,
|
||||||
created_at: new Date()
|
created_at: new Date()
|
||||||
});
|
});
|
||||||
@@ -36,7 +40,7 @@ PicPeak Admin Credentials
|
|||||||
|
|
||||||
Your admin account has been created with these credentials:
|
Your admin account has been created with these credentials:
|
||||||
|
|
||||||
Username: admin
|
Email: ${adminEmail}
|
||||||
Password: ${generatedPassword}
|
Password: ${generatedPassword}
|
||||||
|
|
||||||
IMPORTANT SECURITY NOTES:
|
IMPORTANT SECURITY NOTES:
|
||||||
@@ -47,6 +51,8 @@ IMPORTANT SECURITY NOTES:
|
|||||||
|
|
||||||
Login URL: ${process.env.ADMIN_URL || 'http://localhost:3001'}/admin
|
Login URL: ${process.env.ADMIN_URL || 'http://localhost:3001'}/admin
|
||||||
|
|
||||||
|
Login with the email address shown above
|
||||||
|
|
||||||
Generated on: ${new Date().toISOString()}
|
Generated on: ${new Date().toISOString()}
|
||||||
========================================
|
========================================
|
||||||
`;
|
`;
|
||||||
@@ -65,7 +71,7 @@ Generated on: ${new Date().toISOString()}
|
|||||||
console.log('\n========================================');
|
console.log('\n========================================');
|
||||||
console.log('✅ Admin user created successfully!');
|
console.log('✅ Admin user created successfully!');
|
||||||
console.log('========================================');
|
console.log('========================================');
|
||||||
console.log('Username: admin');
|
console.log(`Email: ${adminEmail}`);
|
||||||
console.log(`Password: ${generatedPassword}`);
|
console.log(`Password: ${generatedPassword}`);
|
||||||
console.log('\n⚠️ IMPORTANT:');
|
console.log('\n⚠️ IMPORTANT:');
|
||||||
console.log('1. Save these credentials securely');
|
console.log('1. Save these credentials securely');
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "picpeak-backend",
|
"name": "picpeak-backend",
|
||||||
"version": "1.0.95",
|
"version": "1.0.99",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "picpeak-backend",
|
"name": "picpeak-backend",
|
||||||
"version": "1.0.95",
|
"version": "1.0.99",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.850.0",
|
"@aws-sdk/client-s3": "^3.850.0",
|
||||||
"@aws-sdk/lib-storage": "^3.850.0",
|
"@aws-sdk/lib-storage": "^3.850.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "picpeak-backend",
|
"name": "picpeak-backend",
|
||||||
"version": "1.0.95",
|
"version": "1.0.99",
|
||||||
"description": "Backend for PicPeak event photo sharing platform",
|
"description": "Backend for PicPeak event photo sharing platform",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show or reset admin credentials
|
||||||
|
*/
|
||||||
|
|
||||||
|
const bcrypt = require('bcrypt');
|
||||||
|
const { db } = require('../src/database/db');
|
||||||
|
const { generateReadablePassword } = require('../src/utils/passwordGenerator');
|
||||||
|
|
||||||
|
async function showAdminCredentials(resetPassword = false) {
|
||||||
|
try {
|
||||||
|
// Get admin user
|
||||||
|
const admin = await db('admin_users')
|
||||||
|
.where('username', 'admin')
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if (!admin) {
|
||||||
|
console.error('❌ No admin user found in database');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n========================================');
|
||||||
|
console.log('PicPeak Admin Credentials');
|
||||||
|
console.log('========================================');
|
||||||
|
console.log(`Username: ${admin.username}`);
|
||||||
|
console.log(`Email: ${admin.email}`);
|
||||||
|
|
||||||
|
if (resetPassword) {
|
||||||
|
// Generate new password
|
||||||
|
const newPassword = generateReadablePassword();
|
||||||
|
const passwordHash = await bcrypt.hash(newPassword, 12);
|
||||||
|
|
||||||
|
// Update password
|
||||||
|
await db('admin_users')
|
||||||
|
.where('id', admin.id)
|
||||||
|
.update({
|
||||||
|
password_hash: passwordHash,
|
||||||
|
updated_at: new Date()
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Password: ${newPassword} (NEWLY RESET)`);
|
||||||
|
console.log('\n⚠️ IMPORTANT: Please save this password securely!');
|
||||||
|
} else {
|
||||||
|
console.log('Password: [hidden - use --reset flag to generate new password]');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\nLogin URL: http://localhost:3001/admin');
|
||||||
|
console.log('========================================\n');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error.message);
|
||||||
|
process.exit(1);
|
||||||
|
} finally {
|
||||||
|
await db.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for reset flag
|
||||||
|
const resetPassword = process.argv.includes('--reset');
|
||||||
|
|
||||||
|
if (resetPassword) {
|
||||||
|
console.log('🔄 Resetting admin password...');
|
||||||
|
}
|
||||||
|
|
||||||
|
showAdminCredentials(resetPassword);
|
||||||
@@ -20,6 +20,12 @@ const ATTEMPT_WINDOW = 15 * 60 * 1000; // 15 minutes window for counting attempt
|
|||||||
*/
|
*/
|
||||||
async function trackFailedAttempt(identifier, ipAddress, userAgent) {
|
async function trackFailedAttempt(identifier, ipAddress, userAgent) {
|
||||||
try {
|
try {
|
||||||
|
// Check if table exists first
|
||||||
|
const tableExists = await db.schema.hasTable('login_attempts');
|
||||||
|
if (!tableExists) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await db('login_attempts').insert({
|
await db('login_attempts').insert({
|
||||||
identifier,
|
identifier,
|
||||||
ip_address: ipAddress,
|
ip_address: ipAddress,
|
||||||
@@ -48,6 +54,12 @@ async function trackFailedAttempt(identifier, ipAddress, userAgent) {
|
|||||||
*/
|
*/
|
||||||
async function trackSuccessfulLogin(identifier, ipAddress, userAgent) {
|
async function trackSuccessfulLogin(identifier, ipAddress, userAgent) {
|
||||||
try {
|
try {
|
||||||
|
// Check if table exists first
|
||||||
|
const tableExists = await db.schema.hasTable('login_attempts');
|
||||||
|
if (!tableExists) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await db('login_attempts').insert({
|
await db('login_attempts').insert({
|
||||||
identifier,
|
identifier,
|
||||||
ip_address: ipAddress,
|
ip_address: ipAddress,
|
||||||
@@ -75,6 +87,12 @@ async function trackSuccessfulLogin(identifier, ipAddress, userAgent) {
|
|||||||
*/
|
*/
|
||||||
async function checkAccountLockout(identifier) {
|
async function checkAccountLockout(identifier) {
|
||||||
try {
|
try {
|
||||||
|
// Check if table exists first
|
||||||
|
const tableExists = await db.schema.hasTable('login_attempts');
|
||||||
|
if (!tableExists) {
|
||||||
|
return { isLocked: false };
|
||||||
|
}
|
||||||
|
|
||||||
const recentWindow = new Date(Date.now() - ATTEMPT_WINDOW);
|
const recentWindow = new Date(Date.now() - ATTEMPT_WINDOW);
|
||||||
|
|
||||||
// Get recent failed attempts
|
// Get recent failed attempts
|
||||||
@@ -114,6 +132,12 @@ async function checkAccountLockout(identifier) {
|
|||||||
*/
|
*/
|
||||||
async function checkSuspiciousActivity(identifier, ipAddress) {
|
async function checkSuspiciousActivity(identifier, ipAddress) {
|
||||||
try {
|
try {
|
||||||
|
// Check if table exists first
|
||||||
|
const tableExists = await db.schema.hasTable('login_attempts');
|
||||||
|
if (!tableExists) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for rapid attempts from different IPs
|
// Check for rapid attempts from different IPs
|
||||||
const recentWindow = new Date(Date.now() - 5 * 60 * 1000); // 5 minutes
|
const recentWindow = new Date(Date.now() - 5 * 60 * 1000); // 5 minutes
|
||||||
|
|
||||||
@@ -153,6 +177,13 @@ function getGenericAuthError() {
|
|||||||
*/
|
*/
|
||||||
async function cleanupOldAttempts() {
|
async function cleanupOldAttempts() {
|
||||||
try {
|
try {
|
||||||
|
// Check if table exists first
|
||||||
|
const tableExists = await db.schema.hasTable('login_attempts');
|
||||||
|
if (!tableExists) {
|
||||||
|
// Table doesn't exist, skip cleanup
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const cutoffDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); // 7 days
|
const cutoffDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); // 7 days
|
||||||
|
|
||||||
const deleted = await db('login_attempts')
|
const deleted = await db('login_attempts')
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
|
||||||
backend:
|
|
||||||
build:
|
|
||||||
context: ./backend
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
container_name: picpeak-backend-dev
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
- NODE_ENV=development
|
|
||||||
- PORT=3001
|
|
||||||
- JWT_SECRET=${JWT_SECRET}
|
|
||||||
- ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
|
|
||||||
- ADMIN_EMAIL=${ADMIN_EMAIL:-admin@example.com}
|
|
||||||
- DATABASE_CLIENT=pg
|
|
||||||
- DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
|
|
||||||
- DB_TYPE=postgresql
|
|
||||||
- DB_HOST=postgres
|
|
||||||
- DB_PORT=5432
|
|
||||||
- DB_USER=${DB_USER}
|
|
||||||
- DB_PASSWORD=${DB_PASSWORD}
|
|
||||||
- DB_NAME=${DB_NAME}
|
|
||||||
- SMTP_HOST=${SMTP_HOST}
|
|
||||||
- SMTP_PORT=${SMTP_PORT}
|
|
||||||
- SMTP_SECURE=${SMTP_SECURE:-false}
|
|
||||||
- SMTP_USER=${SMTP_USER}
|
|
||||||
- SMTP_PASS=${SMTP_PASS}
|
|
||||||
- EMAIL_FROM=${EMAIL_FROM:-noreply@picpeak.local}
|
|
||||||
- FRONTEND_URL=${FRONTEND_URL:-http://localhost:3000}
|
|
||||||
- ADMIN_URL=${ADMIN_URL:-http://localhost:3001}
|
|
||||||
- TZ=${TZ:-UTC}
|
|
||||||
volumes:
|
|
||||||
- ./events:/app/events
|
|
||||||
- ./data:/app/data
|
|
||||||
- ./logs:/app/logs
|
|
||||||
- ./backup:/backup
|
|
||||||
ports:
|
|
||||||
- "3001:3001"
|
|
||||||
depends_on:
|
|
||||||
postgres:
|
|
||||||
condition: service_healthy
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3001/api/health"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 40s
|
|
||||||
networks:
|
|
||||||
- picpeak-network
|
|
||||||
|
|
||||||
postgres:
|
|
||||||
image: postgres:15-alpine
|
|
||||||
container_name: picpeak-postgres-dev
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
- POSTGRES_USER=${DB_USER}
|
|
||||||
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
|
||||||
- POSTGRES_DB=${DB_NAME}
|
|
||||||
- PGDATA=/var/lib/postgresql/data/pgdata
|
|
||||||
- TZ=${TZ:-UTC}
|
|
||||||
volumes:
|
|
||||||
- postgres-data:/var/lib/postgresql/data
|
|
||||||
ports:
|
|
||||||
- "5432:5432"
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
start_period: 20s
|
|
||||||
networks:
|
|
||||||
- picpeak-network
|
|
||||||
|
|
||||||
redis:
|
|
||||||
image: redis:7-alpine
|
|
||||||
container_name: picpeak-redis-dev
|
|
||||||
restart: unless-stopped
|
|
||||||
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-picpeak_redis_pass}
|
|
||||||
volumes:
|
|
||||||
- redis-data:/data
|
|
||||||
ports:
|
|
||||||
- "6379:6379"
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 3
|
|
||||||
networks:
|
|
||||||
- picpeak-network
|
|
||||||
|
|
||||||
frontend:
|
|
||||||
build:
|
|
||||||
context: ./frontend
|
|
||||||
dockerfile: Dockerfile.dev
|
|
||||||
args:
|
|
||||||
- VITE_API_URL=${VITE_API_URL:-http://localhost:3001/api}
|
|
||||||
- VITE_UMAMI_URL=${VITE_UMAMI_URL:-}
|
|
||||||
- VITE_UMAMI_WEBSITE_ID=${VITE_UMAMI_WEBSITE_ID:-}
|
|
||||||
- VITE_UMAMI_SHARE_URL=${VITE_UMAMI_SHARE_URL:-}
|
|
||||||
container_name: picpeak-frontend-dev
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
- NODE_ENV=development
|
|
||||||
volumes:
|
|
||||||
- ./frontend:/app
|
|
||||||
- /app/node_modules
|
|
||||||
ports:
|
|
||||||
- "3000:3000"
|
|
||||||
command: npm run dev
|
|
||||||
depends_on:
|
|
||||||
- backend
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
networks:
|
|
||||||
- picpeak-network
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
postgres-data:
|
|
||||||
driver: local
|
|
||||||
redis-data:
|
|
||||||
driver: local
|
|
||||||
|
|
||||||
networks:
|
|
||||||
picpeak-network:
|
|
||||||
driver: bridge
|
|
||||||
+91
-91
@@ -1,125 +1,125 @@
|
|||||||
# docker-compose.yml - Production configuration
|
|
||||||
#
|
|
||||||
# For development, use: docker-compose -f docker-compose.dev.yml up -d
|
|
||||||
# For local customizations, create docker-compose.override.yml (see docker-compose.override.yml.example)
|
|
||||||
#
|
|
||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
backend:
|
backend:
|
||||||
image: picpeak-backend:latest
|
|
||||||
build:
|
build:
|
||||||
context: ./backend
|
context: ./backend
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
container_name: picpeak-backend
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
|
||||||
- db
|
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=${NODE_ENV:-production}
|
||||||
- PORT=3001
|
- PORT=3001
|
||||||
- JWT_SECRET=${JWT_SECRET}
|
- JWT_SECRET=${JWT_SECRET}
|
||||||
- ADMIN_URL=${ADMIN_URL}
|
- ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
|
||||||
- FRONTEND_URL=${FRONTEND_URL}
|
- ADMIN_EMAIL=${ADMIN_EMAIL:-admin@example.com}
|
||||||
- BACKEND_URL=${BACKEND_URL}
|
|
||||||
# Database
|
|
||||||
- DATABASE_CLIENT=pg
|
- DATABASE_CLIENT=pg
|
||||||
- DB_HOST=db
|
- DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
|
||||||
|
- DB_TYPE=postgresql
|
||||||
|
- DB_HOST=postgres
|
||||||
- DB_PORT=5432
|
- DB_PORT=5432
|
||||||
- DB_USER=${DB_USER:-picpeak}
|
- DB_USER=${DB_USER}
|
||||||
- DB_PASSWORD=${DB_PASSWORD}
|
- DB_PASSWORD=${DB_PASSWORD}
|
||||||
- DB_NAME=${DB_NAME:-picpeak}
|
- DB_NAME=${DB_NAME}
|
||||||
# Email
|
|
||||||
- SMTP_HOST=${SMTP_HOST}
|
- SMTP_HOST=${SMTP_HOST}
|
||||||
- SMTP_PORT=${SMTP_PORT}
|
- SMTP_PORT=${SMTP_PORT}
|
||||||
- SMTP_SECURE=${SMTP_SECURE}
|
- SMTP_SECURE=${SMTP_SECURE:-false}
|
||||||
- SMTP_USER=${SMTP_USER}
|
- SMTP_USER=${SMTP_USER}
|
||||||
- SMTP_PASS=${SMTP_PASS}
|
- SMTP_PASS=${SMTP_PASS}
|
||||||
- EMAIL_FROM=${EMAIL_FROM}
|
- EMAIL_FROM=${EMAIL_FROM:-noreply@picpeak.local}
|
||||||
# Analytics
|
- FRONTEND_URL=${FRONTEND_URL:-http://localhost:3000}
|
||||||
- UMAMI_URL=${UMAMI_URL}
|
- ADMIN_URL=${ADMIN_URL:-http://localhost:3001}
|
||||||
- UMAMI_WEBSITE_ID=${UMAMI_WEBSITE_ID}
|
- TZ=${TZ:-UTC}
|
||||||
# Storage paths
|
|
||||||
- STORAGE_PATH=/app/storage
|
- STORAGE_PATH=/app/storage
|
||||||
- EVENTS_PATH=/app/storage/events
|
|
||||||
- ARCHIVE_PATH=/app/storage/events/archived
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./storage:/app/storage
|
- ./events:/app/events
|
||||||
- ./data:/app/data
|
- ./data:/app/data
|
||||||
- ./logs:/app/logs
|
- ./logs:/app/logs
|
||||||
|
- ./backup:/backup
|
||||||
|
- ./storage:/app/storage
|
||||||
|
ports:
|
||||||
|
- "${BACKEND_PORT:-3001}:3001"
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3001/api/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
networks:
|
networks:
|
||||||
- picpeak
|
- picpeak-network
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
container_name: picpeak-postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=${DB_USER}
|
||||||
|
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
||||||
|
- POSTGRES_DB=${DB_NAME}
|
||||||
|
- PGDATA=/var/lib/postgresql/data/pgdata
|
||||||
|
- TZ=${TZ:-UTC}
|
||||||
|
volumes:
|
||||||
|
- postgres-data:/var/lib/postgresql/data
|
||||||
|
ports:
|
||||||
|
- "${DB_PORT:-5432}:5432"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
start_period: 20s
|
||||||
|
networks:
|
||||||
|
- picpeak-network
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: picpeak-redis
|
||||||
|
restart: unless-stopped
|
||||||
|
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-picpeak_redis_pass}
|
||||||
|
volumes:
|
||||||
|
- redis-data:/data
|
||||||
|
ports:
|
||||||
|
- "${REDIS_PORT:-6379}:6379"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
networks:
|
||||||
|
- picpeak-network
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: picpeak-frontend:latest
|
|
||||||
build:
|
build:
|
||||||
context: ./frontend
|
context: ./frontend
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
args:
|
args:
|
||||||
- VITE_API_URL=/api
|
- VITE_API_URL=${VITE_API_URL:-http://localhost:3001/api}
|
||||||
restart: unless-stopped
|
- VITE_UMAMI_URL=${VITE_UMAMI_URL:-}
|
||||||
depends_on:
|
- VITE_UMAMI_WEBSITE_ID=${VITE_UMAMI_WEBSITE_ID:-}
|
||||||
- backend
|
- VITE_UMAMI_SHARE_URL=${VITE_UMAMI_SHARE_URL:-}
|
||||||
networks:
|
container_name: picpeak-frontend
|
||||||
- picpeak
|
|
||||||
|
|
||||||
nginx:
|
|
||||||
image: nginx:alpine
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=${NODE_ENV:-production}
|
||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "${FRONTEND_PORT:-3000}:80"
|
||||||
- "443:443"
|
|
||||||
volumes:
|
|
||||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
|
|
||||||
- ./nginx/sites-enabled:/etc/nginx/sites-enabled
|
|
||||||
- ./certbot/conf:/etc/letsencrypt
|
|
||||||
- ./certbot/www:/var/www/certbot
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- frontend
|
|
||||||
- backend
|
- backend
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
networks:
|
networks:
|
||||||
- picpeak
|
- picpeak-network
|
||||||
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
|
|
||||||
|
|
||||||
certbot:
|
|
||||||
image: certbot/certbot
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
|
||||||
- ./certbot/conf:/etc/letsencrypt
|
|
||||||
- ./certbot/www:/var/www/certbot
|
|
||||||
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
|
|
||||||
|
|
||||||
db:
|
|
||||||
image: postgres:15-alpine
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
- POSTGRES_USER=${DB_USER:-picpeak}
|
|
||||||
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
|
||||||
- POSTGRES_DB=${DB_NAME:-picpeak}
|
|
||||||
# Allow connections from any host with password authentication
|
|
||||||
- POSTGRES_HOST_AUTH_METHOD=scram-sha-256
|
|
||||||
- POSTGRES_INITDB_ARGS=--auth-host=scram-sha-256 --auth-local=trust
|
|
||||||
volumes:
|
|
||||||
- postgres_data:/var/lib/postgresql/data
|
|
||||||
networks:
|
|
||||||
- picpeak
|
|
||||||
# Allow connections without SSL requirement from Docker network
|
|
||||||
command: postgres -c ssl=off
|
|
||||||
|
|
||||||
umami:
|
|
||||||
image: ghcr.io/umami-software/umami:postgresql-latest
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
DATABASE_URL: postgresql://${DB_USER:-picpeak}:${DB_PASSWORD}@db:5432/umami
|
|
||||||
DATABASE_TYPE: postgresql
|
|
||||||
HASH_SALT: ${UMAMI_HASH_SALT}
|
|
||||||
depends_on:
|
|
||||||
- db
|
|
||||||
networks:
|
|
||||||
- picpeak
|
|
||||||
|
|
||||||
networks:
|
|
||||||
picpeak:
|
|
||||||
driver: bridge
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres-data:
|
||||||
|
driver: local
|
||||||
|
redis-data:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
networks:
|
||||||
|
picpeak-network:
|
||||||
|
driver: bridge
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Dockerfile.dev - Development configuration for frontend
|
# Dockerfile.dev - Development configuration for frontend
|
||||||
FROM node:18-alpine
|
FROM node:20-alpine
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "picpeak-frontend",
|
"name": "picpeak-frontend",
|
||||||
"version": "1.0.95",
|
"version": "1.0.99",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "picpeak-frontend",
|
"name": "picpeak-frontend",
|
||||||
"version": "1.0.95",
|
"version": "1.0.99",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/react-query": "^5.0.0",
|
"@tanstack/react-query": "^5.0.0",
|
||||||
"@tiptap/extension-character-count": "^2.26.1",
|
"@tiptap/extension-character-count": "^2.26.1",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "picpeak-frontend",
|
"name": "picpeak-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.95",
|
"version": "1.0.99",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
Reference in New Issue
Block a user