Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 95a6ad505d | |||
| 9dc82c8490 | |||
| 554bf1d43c | |||
| f147bd6cfb | |||
| f564f40ed0 |
+25
-45
@@ -110,53 +110,33 @@ steps:
|
|||||||
|
|
||||||
# -------- NEW: Create GitHub Release --------
|
# -------- NEW: Create GitHub Release --------
|
||||||
- name: github-release
|
- name: github-release
|
||||||
image: plugins/github-release
|
image: curlimages/curl:latest
|
||||||
|
when:
|
||||||
|
event: tag
|
||||||
environment:
|
environment:
|
||||||
PLUGIN_API_KEY:
|
GITHUB_TOKEN:
|
||||||
from_secret: GITHUB_TOKEN
|
from_secret: GITHUB_TOKEN
|
||||||
DRONE_REMOTE_URL: https://github.com/the-luap/picpeak.git
|
commands:
|
||||||
settings:
|
- |
|
||||||
api_key:
|
# Create release payload
|
||||||
from_secret: GITHUB_TOKEN
|
cat > release.json <<EOF
|
||||||
repo: the-luap/picpeak
|
{
|
||||||
title: "PicPeak ${DRONE_TAG}"
|
"tag_name": "${DRONE_TAG}",
|
||||||
prerelease: false
|
"target_commitish": "main",
|
||||||
overwrite: true
|
"name": "PicPeak ${DRONE_TAG}",
|
||||||
note: |
|
"body": "# PicPeak ${DRONE_TAG}\n\n## 🐳 Docker Images\n\nThis release includes Docker images published to GitHub Container Registry:\n\n\`\`\`bash\n# Backend\ndocker pull ghcr.io/the-luap/picpeak-backend:${DRONE_TAG}\ndocker pull ghcr.io/the-luap/picpeak-backend:latest\n\n# Frontend\ndocker pull ghcr.io/the-luap/picpeak-frontend:${DRONE_TAG}\ndocker pull ghcr.io/the-luap/picpeak-frontend:latest\n\`\`\`\n\n## 📦 What's New\n\nSee the [README](https://github.com/the-luap/picpeak#readme) for features and documentation.\n\n## 🚀 Quick Start\n\n\`\`\`bash\n# Clone and deploy\ngit clone https://github.com/the-luap/picpeak.git\ncd picpeak\n\n# Use the tagged version\ndocker-compose -f docker-compose.prod.yml up -d\n\`\`\`\n\n---\n\nFor detailed deployment instructions, see the [Deployment Guide](https://github.com/the-luap/picpeak/blob/main/DEPLOYMENT.md).",
|
||||||
# PicPeak ${DRONE_TAG}
|
"draft": false,
|
||||||
|
"prerelease": false
|
||||||
## 🐳 Docker Images
|
}
|
||||||
|
EOF
|
||||||
This release includes Docker images published to GitHub Container Registry:
|
- |
|
||||||
|
# Create the release on GitHub
|
||||||
```bash
|
curl -X POST \
|
||||||
# Backend
|
-H "Accept: application/vnd.github+json" \
|
||||||
docker pull ghcr.io/the-luap/picpeak-backend:${DRONE_TAG}
|
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
||||||
docker pull ghcr.io/the-luap/picpeak-backend:latest
|
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||||
|
https://api.github.com/repos/the-luap/picpeak/releases \
|
||||||
# Frontend
|
-d @release.json
|
||||||
docker pull ghcr.io/the-luap/picpeak-frontend:${DRONE_TAG}
|
|
||||||
docker pull ghcr.io/the-luap/picpeak-frontend:latest
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📦 What's New
|
|
||||||
|
|
||||||
See the [README](https://github.com/the-luap/picpeak#readme) for features and documentation.
|
|
||||||
|
|
||||||
## 🚀 Quick Start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Clone and deploy
|
|
||||||
git clone https://github.com/the-luap/picpeak.git
|
|
||||||
cd picpeak
|
|
||||||
|
|
||||||
# Use the tagged version
|
|
||||||
docker-compose -f docker-compose.prod.yml up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
For detailed deployment instructions, see the [Deployment Guide](https://github.com/the-luap/picpeak/blob/main/DEPLOYMENT.md).
|
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
event:
|
event:
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ coverage/
|
|||||||
*.tmp
|
*.tmp
|
||||||
*.temp
|
*.temp
|
||||||
|
|
||||||
|
# Backup and test directories
|
||||||
|
backups/
|
||||||
|
test-archiver/
|
||||||
|
|
||||||
# Keep directory structure
|
# Keep directory structure
|
||||||
!storage/events/active/.gitkeep
|
!storage/events/active/.gitkeep
|
||||||
!storage/events/archived/.gitkeep
|
!storage/events/archived/.gitkeep
|
||||||
|
|||||||
@@ -125,9 +125,10 @@ exports.up = async function(knex) {
|
|||||||
// Add new email templates for restore notifications
|
// Add new email templates for restore notifications
|
||||||
const emailTemplates = [
|
const emailTemplates = [
|
||||||
{
|
{
|
||||||
name: 'restore_completed',
|
template_key: 'restore_completed',
|
||||||
subject: '✅ Restore Completed Successfully',
|
subject_en: '✅ Restore Completed Successfully',
|
||||||
body: `<h2>Restore Operation Completed</h2>
|
subject_de: '✅ Wiederherstellung erfolgreich abgeschlossen',
|
||||||
|
body_html_en: `<h2>Restore Operation Completed</h2>
|
||||||
<p>A restore operation has completed successfully.</p>
|
<p>A restore operation has completed successfully.</p>
|
||||||
|
|
||||||
<h3>Details:</h3>
|
<h3>Details:</h3>
|
||||||
@@ -140,32 +141,7 @@ exports.up = async function(knex) {
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>Please verify that all systems are functioning correctly after the restore.</p>`,
|
<p>Please verify that all systems are functioning correctly after the restore.</p>`,
|
||||||
language: 'en',
|
body_html_de: `<h2>Wiederherstellungsvorgang abgeschlossen</h2>
|
||||||
is_active: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'restore_failed',
|
|
||||||
subject: '❌ Restore Operation Failed',
|
|
||||||
body: `<h2>Restore Operation Failed</h2>
|
|
||||||
<p>A restore operation has failed and requires attention.</p>
|
|
||||||
|
|
||||||
<h3>Details:</h3>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Restore Type:</strong> {{restore_type}}</li>
|
|
||||||
<li><strong>Error:</strong> {{error_message}}</li>
|
|
||||||
<li><strong>Timestamp:</strong> {{timestamp}}</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p>Please check the system logs for more details and take appropriate action.</p>
|
|
||||||
|
|
||||||
<p><strong>Important:</strong> If a pre-restore backup was created, it may be used for recovery.</p>`,
|
|
||||||
language: 'en',
|
|
||||||
is_active: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'restore_completed',
|
|
||||||
subject: '✅ Wiederherstellung erfolgreich abgeschlossen',
|
|
||||||
body: `<h2>Wiederherstellungsvorgang abgeschlossen</h2>
|
|
||||||
<p>Ein Wiederherstellungsvorgang wurde erfolgreich abgeschlossen.</p>
|
<p>Ein Wiederherstellungsvorgang wurde erfolgreich abgeschlossen.</p>
|
||||||
|
|
||||||
<h3>Details:</h3>
|
<h3>Details:</h3>
|
||||||
@@ -178,13 +154,50 @@ exports.up = async function(knex) {
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>Bitte überprüfen Sie, ob alle Systeme nach der Wiederherstellung ordnungsgemäß funktionieren.</p>`,
|
<p>Bitte überprüfen Sie, ob alle Systeme nach der Wiederherstellung ordnungsgemäß funktionieren.</p>`,
|
||||||
language: 'de',
|
body_text_en: `Restore Operation Completed
|
||||||
is_active: true
|
|
||||||
|
A restore operation has completed successfully.
|
||||||
|
|
||||||
|
Details:
|
||||||
|
- Restore Type: {{restore_type}}
|
||||||
|
- Duration: {{duration}}
|
||||||
|
- Files Restored: {{files_restored}}
|
||||||
|
- Backup ID: {{backup_id}}
|
||||||
|
- Timestamp: {{timestamp}}
|
||||||
|
|
||||||
|
Please verify that all systems are functioning correctly after the restore.`,
|
||||||
|
body_text_de: `Wiederherstellungsvorgang abgeschlossen
|
||||||
|
|
||||||
|
Ein Wiederherstellungsvorgang wurde erfolgreich abgeschlossen.
|
||||||
|
|
||||||
|
Details:
|
||||||
|
- Wiederherstellungstyp: {{restore_type}}
|
||||||
|
- Dauer: {{duration}}
|
||||||
|
- Wiederhergestellte Dateien: {{files_restored}}
|
||||||
|
- Backup-ID: {{backup_id}}
|
||||||
|
- Zeitstempel: {{timestamp}}
|
||||||
|
|
||||||
|
Bitte überprüfen Sie, ob alle Systeme nach der Wiederherstellung ordnungsgemäß funktionieren.`,
|
||||||
|
variables: JSON.stringify(['restore_type', 'duration', 'files_restored', 'backup_id', 'timestamp'])
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'restore_failed',
|
template_key: 'restore_failed',
|
||||||
subject: '❌ Wiederherstellungsvorgang fehlgeschlagen',
|
subject_en: '❌ Restore Operation Failed',
|
||||||
body: `<h2>Wiederherstellungsvorgang fehlgeschlagen</h2>
|
subject_de: '❌ Wiederherstellungsvorgang fehlgeschlagen',
|
||||||
|
body_html_en: `<h2>Restore Operation Failed</h2>
|
||||||
|
<p>A restore operation has failed and requires attention.</p>
|
||||||
|
|
||||||
|
<h3>Details:</h3>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Restore Type:</strong> {{restore_type}}</li>
|
||||||
|
<li><strong>Error:</strong> {{error_message}}</li>
|
||||||
|
<li><strong>Timestamp:</strong> {{timestamp}}</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>Please check the system logs for more details and take appropriate action.</p>
|
||||||
|
|
||||||
|
<p><strong>Important:</strong> If a pre-restore backup was created, it may be used for recovery.</p>`,
|
||||||
|
body_html_de: `<h2>Wiederherstellungsvorgang fehlgeschlagen</h2>
|
||||||
<p>Ein Wiederherstellungsvorgang ist fehlgeschlagen und erfordert Ihre Aufmerksamkeit.</p>
|
<p>Ein Wiederherstellungsvorgang ist fehlgeschlagen und erfordert Ihre Aufmerksamkeit.</p>
|
||||||
|
|
||||||
<h3>Details:</h3>
|
<h3>Details:</h3>
|
||||||
@@ -197,8 +210,31 @@ exports.up = async function(knex) {
|
|||||||
<p>Bitte überprüfen Sie die Systemprotokolle für weitere Details und ergreifen Sie entsprechende Maßnahmen.</p>
|
<p>Bitte überprüfen Sie die Systemprotokolle für weitere Details und ergreifen Sie entsprechende Maßnahmen.</p>
|
||||||
|
|
||||||
<p><strong>Wichtig:</strong> Falls ein Backup vor der Wiederherstellung erstellt wurde, kann es zur Wiederherstellung verwendet werden.</p>`,
|
<p><strong>Wichtig:</strong> Falls ein Backup vor der Wiederherstellung erstellt wurde, kann es zur Wiederherstellung verwendet werden.</p>`,
|
||||||
language: 'de',
|
body_text_en: `Restore Operation Failed
|
||||||
is_active: true
|
|
||||||
|
A restore operation has failed and requires attention.
|
||||||
|
|
||||||
|
Details:
|
||||||
|
- Restore Type: {{restore_type}}
|
||||||
|
- Error: {{error_message}}
|
||||||
|
- Timestamp: {{timestamp}}
|
||||||
|
|
||||||
|
Please check the system logs for more details and take appropriate action.
|
||||||
|
|
||||||
|
Important: If a pre-restore backup was created, it may be used for recovery.`,
|
||||||
|
body_text_de: `Wiederherstellungsvorgang fehlgeschlagen
|
||||||
|
|
||||||
|
Ein Wiederherstellungsvorgang ist fehlgeschlagen und erfordert Ihre Aufmerksamkeit.
|
||||||
|
|
||||||
|
Details:
|
||||||
|
- Wiederherstellungstyp: {{restore_type}}
|
||||||
|
- Fehler: {{error_message}}
|
||||||
|
- Zeitstempel: {{timestamp}}
|
||||||
|
|
||||||
|
Bitte überprüfen Sie die Systemprotokolle für weitere Details und ergreifen Sie entsprechende Maßnahmen.
|
||||||
|
|
||||||
|
Wichtig: Falls ein Backup vor der Wiederherstellung erstellt wurde, kann es zur Wiederherstellung verwendet werden.`,
|
||||||
|
variables: JSON.stringify(['restore_type', 'error_message', 'timestamp'])
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -208,7 +244,7 @@ exports.up = async function(knex) {
|
|||||||
exports.down = async function(knex) {
|
exports.down = async function(knex) {
|
||||||
// Remove email templates
|
// Remove email templates
|
||||||
await knex('email_templates')
|
await knex('email_templates')
|
||||||
.whereIn('name', ['restore_completed', 'restore_failed'])
|
.whereIn('template_key', ['restore_completed', 'restore_failed'])
|
||||||
.delete();
|
.delete();
|
||||||
|
|
||||||
// Remove settings
|
// Remove settings
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "picpeak-backend",
|
"name": "picpeak-backend",
|
||||||
"version": "1.0.82",
|
"version": "1.0.84",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "picpeak-backend",
|
"name": "picpeak-backend",
|
||||||
"version": "1.0.82",
|
"version": "1.0.84",
|
||||||
"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.82",
|
"version": "1.0.84",
|
||||||
"description": "Backend for PicPeak event photo sharing platform",
|
"description": "Backend for PicPeak event photo sharing platform",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ router.get('/', async (req, res) => {
|
|||||||
theme_config: settingsObject.theme_config || null,
|
theme_config: settingsObject.theme_config || null,
|
||||||
default_language: settingsObject.general_default_language || 'en',
|
default_language: settingsObject.general_default_language || 'en',
|
||||||
enable_analytics: settingsObject.general_enable_analytics !== false,
|
enable_analytics: settingsObject.general_enable_analytics !== false,
|
||||||
|
general_date_format: settingsObject.general_date_format || 'PPP',
|
||||||
enable_recaptcha: settingsObject.security_enable_recaptcha === true || settingsObject.security_enable_recaptcha === 'true',
|
enable_recaptcha: settingsObject.security_enable_recaptcha === true || settingsObject.security_enable_recaptcha === 'true',
|
||||||
recaptcha_site_key: settingsObject.security_recaptcha_site_key || null,
|
recaptcha_site_key: settingsObject.security_recaptcha_site_key || null,
|
||||||
maintenance_mode: settingsObject.general_maintenance_mode === true || settingsObject.general_maintenance_mode === 'true',
|
maintenance_mode: settingsObject.general_maintenance_mode === true || settingsObject.general_maintenance_mode === 'true',
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "picpeak-frontend",
|
"name": "picpeak-frontend",
|
||||||
"version": "1.0.78",
|
"version": "1.0.84",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "picpeak-frontend",
|
"name": "picpeak-frontend",
|
||||||
"version": "1.0.78",
|
"version": "1.0.84",
|
||||||
"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.78",
|
"version": "1.0.84",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -83,8 +83,8 @@ api.interceptors.response.use(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (error.response?.status === 401) {
|
if (error.response?.status === 401) {
|
||||||
// Check if it's an admin route
|
// Check if it's an admin route (but not public endpoints)
|
||||||
const isAdminRoute = error.config?.url?.includes('/admin');
|
const isAdminRoute = error.config?.url?.includes('/admin') && !error.config?.url?.includes('/public/');
|
||||||
const currentPath = window.location.pathname;
|
const currentPath = window.location.pathname;
|
||||||
|
|
||||||
if (isAdminRoute) {
|
if (isAdminRoute) {
|
||||||
|
|||||||
@@ -2,16 +2,17 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { format as dateFnsFormat, formatDistanceToNow as dateFnsFormatDistanceToNow } from 'date-fns';
|
import { format as dateFnsFormat, formatDistanceToNow as dateFnsFormatDistanceToNow } from 'date-fns';
|
||||||
import { de, enUS } from 'date-fns/locale';
|
import { de, enUS } from 'date-fns/locale';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { settingsService } from '../services/settings.service';
|
import { publicSettingsService } from '../services/publicSettings.service';
|
||||||
|
|
||||||
export const useLocalizedDate = () => {
|
export const useLocalizedDate = () => {
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
|
|
||||||
// Fetch admin settings to get the date format
|
// Fetch public settings to get the date format
|
||||||
const { data: settings } = useQuery({
|
const { data: settings } = useQuery({
|
||||||
queryKey: ['admin-settings-general'],
|
queryKey: ['public-settings'],
|
||||||
queryFn: () => settingsService.getSettingsByType('general'),
|
queryFn: () => publicSettingsService.getPublicSettings(),
|
||||||
staleTime: 5 * 60 * 1000, // Cache for 5 minutes
|
staleTime: 5 * 60 * 1000, // Cache for 5 minutes
|
||||||
|
retry: 1, // Only retry once to avoid blocking the UI
|
||||||
});
|
});
|
||||||
|
|
||||||
const getLocale = () => {
|
const getLocale = () => {
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { api } from '../config/api';
|
||||||
|
|
||||||
|
export interface PublicSettings {
|
||||||
|
branding_company_name: string;
|
||||||
|
branding_company_tagline: string;
|
||||||
|
branding_support_email: string;
|
||||||
|
branding_footer_text: string;
|
||||||
|
branding_watermark_enabled: boolean;
|
||||||
|
branding_watermark_logo_url: string;
|
||||||
|
branding_watermark_position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | 'center';
|
||||||
|
branding_watermark_opacity: number;
|
||||||
|
branding_watermark_size: number;
|
||||||
|
branding_favicon_url: string;
|
||||||
|
branding_logo_url: string;
|
||||||
|
theme_config: any;
|
||||||
|
default_language: string;
|
||||||
|
enable_analytics: boolean;
|
||||||
|
general_date_format: string;
|
||||||
|
enable_recaptcha: boolean;
|
||||||
|
recaptcha_site_key: string | null;
|
||||||
|
maintenance_mode: boolean;
|
||||||
|
umami_enabled: boolean;
|
||||||
|
umami_url: string | null;
|
||||||
|
umami_website_id: string | null;
|
||||||
|
umami_share_url: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const publicSettingsService = {
|
||||||
|
// Get public settings (no authentication required)
|
||||||
|
async getPublicSettings(): Promise<PublicSettings> {
|
||||||
|
const response = await api.get<PublicSettings>('/public/settings');
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user