Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5e43fc9cd9 | |||
| f053f42b6d |
@@ -1,39 +0,0 @@
|
|||||||
name: Mirror to GitHub (Archive Method)
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
mirror:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Mirror using git archive
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
# Configure git
|
|
||||||
git config --global user.name "Gitea Mirror Bot"
|
|
||||||
git config --global user.email "bot@noreply.gitea.local"
|
|
||||||
|
|
||||||
# Copy gitattributes
|
|
||||||
cp .gitattributes-github .gitattributes
|
|
||||||
|
|
||||||
# Create archive excluding files
|
|
||||||
git archive --format=tar HEAD | tar -x -C /tmp/export
|
|
||||||
|
|
||||||
# Initialize new repo in export directory
|
|
||||||
cd /tmp/export
|
|
||||||
git init
|
|
||||||
git add .
|
|
||||||
git commit -m "Mirror from Gitea: $(date '+%Y-%m-%d %H:%M:%S')"
|
|
||||||
|
|
||||||
# Push to GitHub
|
|
||||||
git remote add origin https://x-access-token:${GITHUB_TOKEN}@github.com/YOUR_GITHUB_USERNAME/YOUR_REPO_NAME.git
|
|
||||||
git push -f origin main
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
name: Mirror to GitHub (Rsync Method)
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
mirror:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Prepare mirror directory
|
|
||||||
run: |
|
|
||||||
# Create mirror directory
|
|
||||||
mkdir -p /tmp/github-mirror
|
|
||||||
|
|
||||||
# Use rsync to copy files, excluding sensitive ones
|
|
||||||
rsync -av --exclude-from='.github-mirror-exclude' ./ /tmp/github-mirror/
|
|
||||||
|
|
||||||
- name: Push to GitHub
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
cd /tmp/github-mirror
|
|
||||||
|
|
||||||
# Initialize git repo
|
|
||||||
git init
|
|
||||||
git config user.name "Gitea Mirror Bot"
|
|
||||||
git config user.email "bot@noreply.gitea.local"
|
|
||||||
|
|
||||||
# Add all files and commit
|
|
||||||
git add .
|
|
||||||
git commit -m "Mirror from Gitea: $(git --git-dir=$GITHUB_WORKSPACE/.git log -1 --format='%h %s')"
|
|
||||||
|
|
||||||
# Push to GitHub
|
|
||||||
git remote add origin https://x-access-token:${GITHUB_TOKEN}@github.com/YOUR_GITHUB_USERNAME/YOUR_REPO_NAME.git
|
|
||||||
git push -f origin main
|
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Push to GitHub
|
- name: Push to GitHub
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUBTOKEN }}
|
GITHUBTOKEN: ${{ secrets.GITHUBTOKEN }}
|
||||||
run: |
|
run: |
|
||||||
# Add GitHub remote
|
# Add GitHub remote
|
||||||
git remote add github https://x-access-token:${GITHUBTOKEN}@github.com/the-luap/picpeak.git
|
git remote add github https://x-access-token:${GITHUBTOKEN}@github.com/the-luap/picpeak.git
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* Ensure PostgreSQL compatibility for all insert operations
|
||||||
|
* This migration doesn't change the schema but ensures all tables
|
||||||
|
* are compatible with .returning() syntax
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.up = async function(knex) {
|
||||||
|
// This migration is informational only
|
||||||
|
// All insert operations should use .returning('id') going forward
|
||||||
|
|
||||||
|
console.log('PostgreSQL compatibility check:');
|
||||||
|
console.log('- All INSERT operations should use .returning("id")');
|
||||||
|
console.log('- All date operations should use ISO strings');
|
||||||
|
console.log('- Boolean values are handled automatically by Knex');
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = async function(knex) {
|
||||||
|
// No rollback needed
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "picpeak-backend",
|
"name": "picpeak-backend",
|
||||||
"version": "1.0.26",
|
"version": "1.0.27",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "picpeak-backend",
|
"name": "picpeak-backend",
|
||||||
"version": "1.0.26",
|
"version": "1.0.27",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"archiver": "^5.3.1",
|
"archiver": "^5.3.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "picpeak-backend",
|
"name": "picpeak-backend",
|
||||||
"version": "1.0.26",
|
"version": "1.0.27",
|
||||||
"description": "Backend for PicPeak event photo sharing platform",
|
"description": "Backend for PicPeak event photo sharing platform",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ async function createTestEvent() {
|
|||||||
await db('events').where('slug', eventData.slug).delete();
|
await db('events').where('slug', eventData.slug).delete();
|
||||||
|
|
||||||
// Insert new event
|
// Insert new event
|
||||||
const [eventId] = await db('events').insert(eventData);
|
const insertResult = await db('events').insert(eventData).returning('id');
|
||||||
|
const eventId = insertResult[0]?.id || insertResult[0];
|
||||||
console.log('Event created with ID:', eventId);
|
console.log('Event created with ID:', eventId);
|
||||||
|
|
||||||
console.log('\nTest event created successfully!');
|
console.log('\nTest event created successfully!');
|
||||||
|
|||||||
@@ -211,12 +211,14 @@ router.post('/:id/restore', adminAuth, async (req, res) => {
|
|||||||
categoriesMap.set(categoryName, existingCategory.id);
|
categoriesMap.set(categoryName, existingCategory.id);
|
||||||
} else {
|
} else {
|
||||||
// Create the category if it doesn't exist
|
// Create the category if it doesn't exist
|
||||||
const [newCategoryId] = await db('photo_categories').insert({
|
const insertResult = await db('photo_categories').insert({
|
||||||
event_id: archive.id,
|
event_id: archive.id,
|
||||||
name: categoryName,
|
name: categoryName,
|
||||||
slug: categoryName.toLowerCase().replace(/[^a-z0-9]/g, '-'),
|
slug: categoryName.toLowerCase().replace(/[^a-z0-9]/g, '-'),
|
||||||
created_at: new Date()
|
created_at: new Date()
|
||||||
});
|
}).returning('id');
|
||||||
|
|
||||||
|
const newCategoryId = insertResult[0]?.id || insertResult[0];
|
||||||
categoriesMap.set(categoryName, newCategoryId);
|
categoriesMap.set(categoryName, newCategoryId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -266,6 +268,9 @@ router.post('/:id/restore', adminAuth, async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update event status
|
// Update event status
|
||||||
|
const thirtyDaysFromNow = new Date();
|
||||||
|
thirtyDaysFromNow.setDate(thirtyDaysFromNow.getDate() + 30);
|
||||||
|
|
||||||
await db('events')
|
await db('events')
|
||||||
.where('id', req.params.id)
|
.where('id', req.params.id)
|
||||||
.update({
|
.update({
|
||||||
@@ -273,7 +278,7 @@ router.post('/:id/restore', adminAuth, async (req, res) => {
|
|||||||
is_active: true,
|
is_active: true,
|
||||||
archive_path: null,
|
archive_path: null,
|
||||||
archived_at: null,
|
archived_at: null,
|
||||||
expires_at: db.raw("datetime('now', '+30 days')") // Reset expiration
|
expires_at: thirtyDaysFromNow.toISOString() // Reset expiration - works on both DBs
|
||||||
});
|
});
|
||||||
|
|
||||||
// Log activity
|
// Log activity
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ router.post('/', adminAuth, [
|
|||||||
await fs.mkdir(path.join(eventPath, 'individual'), { recursive: true });
|
await fs.mkdir(path.join(eventPath, 'individual'), { recursive: true });
|
||||||
|
|
||||||
// Insert into database
|
// Insert into database
|
||||||
const [eventId] = await db('events').insert({
|
const insertResult = await db('events').insert({
|
||||||
slug,
|
slug,
|
||||||
event_type,
|
event_type,
|
||||||
event_name,
|
event_name,
|
||||||
@@ -99,7 +99,10 @@ router.post('/', adminAuth, [
|
|||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
allow_user_uploads,
|
allow_user_uploads,
|
||||||
upload_category_id
|
upload_category_id
|
||||||
});
|
}).returning('id');
|
||||||
|
|
||||||
|
// Handle both PostgreSQL (returns array of objects) and SQLite (returns array of IDs)
|
||||||
|
const eventId = insertResult[0]?.id || insertResult[0];
|
||||||
|
|
||||||
// Log activity
|
// Log activity
|
||||||
await logActivity('event_created',
|
await logActivity('event_created',
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ router.post('/', adminAuth, [
|
|||||||
await fs.mkdir(path.join(eventPath, 'individual'), { recursive: true });
|
await fs.mkdir(path.join(eventPath, 'individual'), { recursive: true });
|
||||||
|
|
||||||
// Insert into database
|
// Insert into database
|
||||||
const [eventId] = await db('events').insert({
|
const insertResult = await db('events').insert({
|
||||||
slug,
|
slug,
|
||||||
event_type,
|
event_type,
|
||||||
event_name,
|
event_name,
|
||||||
@@ -76,7 +76,10 @@ router.post('/', adminAuth, [
|
|||||||
color_theme,
|
color_theme,
|
||||||
share_link: shareLink,
|
share_link: shareLink,
|
||||||
expires_at
|
expires_at
|
||||||
});
|
}).returning('id');
|
||||||
|
|
||||||
|
// Handle both PostgreSQL (returns array of objects) and SQLite (returns array of IDs)
|
||||||
|
const eventId = insertResult[0]?.id || insertResult[0];
|
||||||
|
|
||||||
// Queue creation email
|
// Queue creation email
|
||||||
const { queueEmail } = require('../services/emailProcessor');
|
const { queueEmail } = require('../services/emailProcessor');
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
* Database Compatibility Utilities
|
||||||
|
* Handles differences between PostgreSQL and SQLite
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { db } = require('../database/db');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get database client type
|
||||||
|
* @returns {string} 'pg' or 'sqlite3'
|
||||||
|
*/
|
||||||
|
function getDbClient() {
|
||||||
|
return process.env.DATABASE_CLIENT || 'sqlite3';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if using PostgreSQL
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function isPostgreSQL() {
|
||||||
|
return getDbClient() === 'pg';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle insert operations that return IDs
|
||||||
|
* Works with both PostgreSQL and SQLite
|
||||||
|
* @param {object} query - Knex query builder
|
||||||
|
* @returns {Promise<number>} The inserted ID
|
||||||
|
*/
|
||||||
|
async function insertAndGetId(query) {
|
||||||
|
const result = await query.returning('id');
|
||||||
|
|
||||||
|
// PostgreSQL returns array of objects [{id: 1}]
|
||||||
|
// SQLite returns array of IDs [1]
|
||||||
|
return result[0]?.id || result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format date for database compatibility
|
||||||
|
* @param {Date} date - JavaScript Date object
|
||||||
|
* @returns {string} ISO string format that works on both databases
|
||||||
|
*/
|
||||||
|
function formatDateForDB(date) {
|
||||||
|
return date.toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add days to a date (database agnostic)
|
||||||
|
* @param {Date} date - Starting date
|
||||||
|
* @param {number} days - Number of days to add
|
||||||
|
* @returns {Date} New date
|
||||||
|
*/
|
||||||
|
function addDays(date, days) {
|
||||||
|
const result = new Date(date);
|
||||||
|
result.setDate(result.getDate() + days);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get date extraction SQL that works on both databases
|
||||||
|
* @param {string} column - Column name
|
||||||
|
* @returns {object} Knex raw query
|
||||||
|
*/
|
||||||
|
function dateExtractSQL(column) {
|
||||||
|
if (isPostgreSQL()) {
|
||||||
|
return db.raw(`DATE(${column})`);
|
||||||
|
} else {
|
||||||
|
// SQLite uses date() function
|
||||||
|
return db.raw(`date(${column})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get database size query
|
||||||
|
* @param {string} dbName - Database name
|
||||||
|
* @returns {Promise<number>} Size in bytes
|
||||||
|
*/
|
||||||
|
async function getDatabaseSize(dbName) {
|
||||||
|
if (isPostgreSQL()) {
|
||||||
|
const result = await db.raw('SELECT pg_database_size(?) as size', [dbName]);
|
||||||
|
return result.rows[0]?.size || 0;
|
||||||
|
} else {
|
||||||
|
// For SQLite, check file size
|
||||||
|
const fs = require('fs').promises;
|
||||||
|
const path = require('path');
|
||||||
|
const dbPath = process.env.DATABASE_PATH || path.join(__dirname, '../../data/photo_sharing.db');
|
||||||
|
try {
|
||||||
|
const stats = await fs.stat(dbPath);
|
||||||
|
return stats.size;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting SQLite database size:', error);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle boolean values for database compatibility
|
||||||
|
* @param {boolean} value - Boolean value
|
||||||
|
* @returns {any} Database-appropriate boolean representation
|
||||||
|
*/
|
||||||
|
function formatBoolean(value) {
|
||||||
|
if (isPostgreSQL()) {
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
// SQLite stores booleans as 0/1
|
||||||
|
return value ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse boolean from database
|
||||||
|
* @param {any} value - Database boolean value
|
||||||
|
* @returns {boolean} JavaScript boolean
|
||||||
|
*/
|
||||||
|
function parseBoolean(value) {
|
||||||
|
return Boolean(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getDbClient,
|
||||||
|
isPostgreSQL,
|
||||||
|
insertAndGetId,
|
||||||
|
formatDateForDB,
|
||||||
|
addDays,
|
||||||
|
dateExtractSQL,
|
||||||
|
getDatabaseSize,
|
||||||
|
formatBoolean,
|
||||||
|
parseBoolean
|
||||||
|
};
|
||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "picpeak-frontend",
|
"name": "picpeak-frontend",
|
||||||
"version": "1.0.26",
|
"version": "1.0.27",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "picpeak-frontend",
|
"name": "picpeak-frontend",
|
||||||
"version": "1.0.26",
|
"version": "1.0.27",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/react-query": "^5.0.0",
|
"@tanstack/react-query": "^5.0.0",
|
||||||
"@tiptap/extension-link": "^2.25.0",
|
"@tiptap/extension-link": "^2.25.0",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "picpeak-frontend",
|
"name": "picpeak-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.26",
|
"version": "1.0.27",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
Reference in New Issue
Block a user