Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4264026bbe | |||
| 24b4a314a9 | |||
| ba825823a0 | |||
| fb16b7bbb8 | |||
| 8404125ff0 | |||
| 61ad2d61c1 | |||
| 9fd6b44487 | |||
| 9fe10bcce2 | |||
| f2abb40987 | |||
| 3697344cd0 |
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "picpeak-backend",
|
"name": "picpeak-backend",
|
||||||
"version": "1.0.114",
|
"version": "1.0.119",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "picpeak-backend",
|
"name": "picpeak-backend",
|
||||||
"version": "1.0.114",
|
"version": "1.0.119",
|
||||||
"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.114",
|
"version": "1.0.119",
|
||||||
"description": "Backend for PicPeak event photo sharing platform",
|
"description": "Backend for PicPeak event photo sharing platform",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+46
-13
@@ -12,6 +12,7 @@ logger.info('Server starting up', {
|
|||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const helmet = require('helmet');
|
const helmet = require('helmet');
|
||||||
const cors = require('cors');
|
const cors = require('cors');
|
||||||
@@ -42,25 +43,36 @@ const PORT = process.env.PORT || 3000;
|
|||||||
app.set('trust proxy', 'loopback, linklocal, uniquelocal');
|
app.set('trust proxy', 'loopback, linklocal, uniquelocal');
|
||||||
|
|
||||||
// Security middleware with custom CSP
|
// Security middleware with custom CSP
|
||||||
|
// In native HTTP installs, do NOT force HTTPS for subresources.
|
||||||
|
const enableHsts = process.env.ENABLE_HSTS === 'true';
|
||||||
|
const cspDirectives = {
|
||||||
|
defaultSrc: ["'self'"],
|
||||||
|
scriptSrc: ["'self'", "'unsafe-inline'"], // Required for React
|
||||||
|
styleSrc: ["'self'", "'unsafe-inline'", "https:"], // Required for styled components
|
||||||
|
imgSrc: ["'self'", "data:", "https:", "blob:"], // Allow data URLs and external images
|
||||||
|
connectSrc: ["'self'"], // API connections
|
||||||
|
fontSrc: ["'self'", "https:", "data:"], // Web fonts
|
||||||
|
objectSrc: ["'none'"], // Disable plugins
|
||||||
|
mediaSrc: ["'self'"], // Audio/video
|
||||||
|
frameSrc: ["'none'"], // Disable iframes
|
||||||
|
};
|
||||||
|
// Only upgrade insecure requests when HSTS explicitly enabled (HTTPS deployment)
|
||||||
|
if (enableHsts) {
|
||||||
|
// In helmet, an empty array enables the directive
|
||||||
|
cspDirectives.upgradeInsecureRequests = [];
|
||||||
|
}
|
||||||
|
|
||||||
app.use(helmet({
|
app.use(helmet({
|
||||||
contentSecurityPolicy: {
|
contentSecurityPolicy: {
|
||||||
directives: {
|
// Avoid helmet adding defaults like upgrade-insecure-requests when not desired
|
||||||
defaultSrc: ["'self'"],
|
useDefaults: false,
|
||||||
scriptSrc: ["'self'", "'unsafe-inline'"], // Required for React
|
directives: cspDirectives,
|
||||||
styleSrc: ["'self'", "'unsafe-inline'", "https:"], // Required for styled components
|
|
||||||
imgSrc: ["'self'", "data:", "https:", "blob:"], // Allow data URLs and external images
|
|
||||||
connectSrc: ["'self'"], // API connections
|
|
||||||
fontSrc: ["'self'", "https:", "data:"], // Web fonts
|
|
||||||
objectSrc: ["'none'"], // Disable plugins
|
|
||||||
mediaSrc: ["'self'"], // Audio/video
|
|
||||||
frameSrc: ["'none'"], // Disable iframes
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
hsts: {
|
hsts: enableHsts ? {
|
||||||
maxAge: 31536000, // 1 year
|
maxAge: 31536000, // 1 year
|
||||||
includeSubDomains: true,
|
includeSubDomains: true,
|
||||||
preload: true
|
preload: true
|
||||||
},
|
} : false,
|
||||||
permittedCrossDomainPolicies: false,
|
permittedCrossDomainPolicies: false,
|
||||||
referrerPolicy: { policy: "strict-origin-when-cross-origin" }
|
referrerPolicy: { policy: "strict-origin-when-cross-origin" }
|
||||||
}));
|
}));
|
||||||
@@ -219,6 +231,27 @@ app.use('/api/public', require('./src/routes/publicCMS'));
|
|||||||
app.use('/api/images', require('./src/routes/protectedImages'));
|
app.use('/api/images', require('./src/routes/protectedImages'));
|
||||||
app.use('/api/secure-images', secureImagesRoutes);
|
app.use('/api/secure-images', secureImagesRoutes);
|
||||||
|
|
||||||
|
// Optional: Serve built frontend (native installs)
|
||||||
|
try {
|
||||||
|
const serveFrontendEnv = process.env.SERVE_FRONTEND; // 'true' | 'false' | undefined
|
||||||
|
const frontendDir = process.env.FRONTEND_DIR || path.join(__dirname, '../frontend/dist');
|
||||||
|
const indexPath = path.join(frontendDir, 'index.html');
|
||||||
|
// Auto-serve when dist exists unless explicitly disabled
|
||||||
|
const shouldServe = (serveFrontendEnv === 'true') || ((serveFrontendEnv === undefined || serveFrontendEnv === 'auto') && fs.existsSync(indexPath));
|
||||||
|
if (shouldServe) {
|
||||||
|
logger.info(`Serving frontend from ${frontendDir}`);
|
||||||
|
app.use(express.static(frontendDir));
|
||||||
|
// SPA fallback for non-API routes
|
||||||
|
app.get([ '/', '/admin', '/admin/*', '/gallery/*' ], (req, res) => {
|
||||||
|
res.sendFile(indexPath);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.info('Frontend static serving disabled or dist not found', { serveFrontendEnv, frontendDir });
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.warn('Failed to enable frontend static serving', { error: e.message });
|
||||||
|
}
|
||||||
|
|
||||||
// Error handling middleware
|
// Error handling middleware
|
||||||
app.use((err, req, res, next) => {
|
app.use((err, req, res, next) => {
|
||||||
console.error('EXPRESS ERROR HANDLER:', err);
|
console.error('EXPRESS ERROR HANDLER:', err);
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "picpeak-frontend",
|
"name": "picpeak-frontend",
|
||||||
"version": "1.0.114",
|
"version": "1.0.117",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "picpeak-frontend",
|
"name": "picpeak-frontend",
|
||||||
"version": "1.0.114",
|
"version": "1.0.117",
|
||||||
"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.114",
|
"version": "1.0.117",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
+48
-7
@@ -580,10 +580,15 @@ setup_native_installation() {
|
|||||||
log_step "Downloading PicPeak..."
|
log_step "Downloading PicPeak..."
|
||||||
if [[ -d "$NATIVE_APP_DIR/app/.git" ]]; then
|
if [[ -d "$NATIVE_APP_DIR/app/.git" ]]; then
|
||||||
cd "$NATIVE_APP_DIR/app"
|
cd "$NATIVE_APP_DIR/app"
|
||||||
run_as_user "git pull --ff-only" || {
|
# Ensure correct remote and update even if history was rewritten
|
||||||
run_as_user "git config --global --add safe.directory $NATIVE_APP_DIR/app"
|
run_as_user "git config --global --add safe.directory $NATIVE_APP_DIR/app" || true
|
||||||
run_as_user "git pull --ff-only"
|
run_as_user "git remote set-url origin $REPO_URL" || true
|
||||||
}
|
run_as_user "git fetch --all --prune" || true
|
||||||
|
# Prefer checking out remote main and hard resetting to avoid merge prompts
|
||||||
|
if ! run_as_user "git checkout -B main origin/main"; then
|
||||||
|
run_as_user "git checkout main" || true
|
||||||
|
run_as_user "git reset --hard origin/main"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
run_as_user "git clone $REPO_URL $NATIVE_APP_DIR/app" || {
|
run_as_user "git clone $REPO_URL $NATIVE_APP_DIR/app" || {
|
||||||
run_as_user "git config --global --add safe.directory $NATIVE_APP_DIR/app"
|
run_as_user "git config --global --add safe.directory $NATIVE_APP_DIR/app"
|
||||||
@@ -597,9 +602,19 @@ setup_native_installation() {
|
|||||||
# Install backend production dependencies
|
# Install backend production dependencies
|
||||||
cd "$NATIVE_APP_DIR/app/backend"
|
cd "$NATIVE_APP_DIR/app/backend"
|
||||||
npm install --production
|
npm install --production
|
||||||
|
|
||||||
# Ensure SQLite data directory exists for native installs
|
# Ensure SQLite data directory exists for native installs
|
||||||
mkdir -p "$NATIVE_APP_DIR/app/backend/data"
|
mkdir -p "$NATIVE_APP_DIR/app/backend/data"
|
||||||
|
|
||||||
|
# Build frontend for native serving
|
||||||
|
log_step "Building frontend..."
|
||||||
|
if [[ -d "$NATIVE_APP_DIR/app/frontend" ]]; then
|
||||||
|
cd "$NATIVE_APP_DIR/app/frontend"
|
||||||
|
# Try ci (faster/clean) then fallback to install
|
||||||
|
run_as_user "npm ci --include=dev" || run_as_user "npm install"
|
||||||
|
run_as_user "npm run build"
|
||||||
|
else
|
||||||
|
log_warn "Frontend directory not found; admin UI will not be served by backend"
|
||||||
|
fi
|
||||||
|
|
||||||
# Generate secrets
|
# Generate secrets
|
||||||
local jwt_secret=$(generate_jwt_secret)
|
local jwt_secret=$(generate_jwt_secret)
|
||||||
@@ -649,6 +664,10 @@ DEFAULT_EXPIRY_DAYS=30
|
|||||||
# Logging
|
# Logging
|
||||||
LOG_DIR=$NATIVE_APP_DIR/logs
|
LOG_DIR=$NATIVE_APP_DIR/logs
|
||||||
LOG_LEVEL=info
|
LOG_LEVEL=info
|
||||||
|
|
||||||
|
# Frontend serving (native installs)
|
||||||
|
SERVE_FRONTEND=true
|
||||||
|
FRONTEND_DIR=$NATIVE_APP_DIR/app/frontend/dist
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Set permissions
|
# Set permissions
|
||||||
@@ -976,7 +995,13 @@ update_native_installation() {
|
|||||||
|
|
||||||
# Pull latest code
|
# Pull latest code
|
||||||
cd "$NATIVE_APP_DIR/app"
|
cd "$NATIVE_APP_DIR/app"
|
||||||
run_as_user "git pull --ff-only" || run_as_user "git pull"
|
run_as_user "git config --global --add safe.directory $NATIVE_APP_DIR/app" || true
|
||||||
|
run_as_user "git remote set-url origin $REPO_URL" || true
|
||||||
|
run_as_user "git fetch --all --prune"
|
||||||
|
if ! run_as_user "git checkout -B main origin/main"; then
|
||||||
|
run_as_user "git checkout main" || true
|
||||||
|
run_as_user "git reset --hard origin/main"
|
||||||
|
fi
|
||||||
|
|
||||||
# Update backend dependencies
|
# Update backend dependencies
|
||||||
cd "$NATIVE_APP_DIR/app/backend"
|
cd "$NATIVE_APP_DIR/app/backend"
|
||||||
@@ -984,9 +1009,25 @@ update_native_installation() {
|
|||||||
|
|
||||||
# Run migrations
|
# Run migrations
|
||||||
run_as_user "npm run migrate"
|
run_as_user "npm run migrate"
|
||||||
|
|
||||||
|
# Rebuild frontend (ensure admin UI for native installs)
|
||||||
|
if [[ -d "$NATIVE_APP_DIR/app/frontend" ]]; then
|
||||||
|
log_step "Rebuilding frontend..."
|
||||||
|
cd "$NATIVE_APP_DIR/app/frontend"
|
||||||
|
run_as_user "npm ci --include=dev" || run_as_user "npm install"
|
||||||
|
run_as_user "npm run build"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure env has frontend serving flags
|
||||||
|
if ! grep -q '^SERVE_FRONTEND=' "$NATIVE_APP_DIR/app/backend/.env"; then
|
||||||
|
echo "SERVE_FRONTEND=true" >> "$NATIVE_APP_DIR/app/backend/.env"
|
||||||
|
fi
|
||||||
|
if ! grep -q '^FRONTEND_DIR=' "$NATIVE_APP_DIR/app/backend/.env"; then
|
||||||
|
echo "FRONTEND_DIR=$NATIVE_APP_DIR/app/frontend/dist" >> "$NATIVE_APP_DIR/app/backend/.env"
|
||||||
|
fi
|
||||||
|
|
||||||
# Restart services
|
# Restart services
|
||||||
systemctl start picpeak-backend picpeak-workers
|
systemctl restart picpeak-backend picpeak-workers
|
||||||
|
|
||||||
log_success "Native installation updated successfully!"
|
log_success "Native installation updated successfully!"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user