fix: enforce gallery access and consolidate gallery workflows (#1357)
Harden gallery authentication and authorization, consolidate gallery workflows, and prevent token-bearing URLs from leaking through nginx request error logs.
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
const knex = require('knex');
|
||||
const migration = require('../../migrations/core/210_events_updated_at');
|
||||
const { toTimestamp } = require('../../src/utils/dateNormalize');
|
||||
const { randomUUID } = require('crypto');
|
||||
|
||||
const engines = [['sqlite', null], ...(process.env.PICPEAK_PG_TEST_URL ? [['pg', process.env.PICPEAK_PG_TEST_URL]] : [])];
|
||||
describe.each(engines)('event timestamp migration contract (%s)', (engine, connection) => {
|
||||
let db, owner, schema;
|
||||
beforeEach(async () => {
|
||||
if (engine === 'pg') {
|
||||
schema = `event_contract_${randomUUID().replace(/-/g, '')}`;
|
||||
owner = knex({ client: 'pg', connection });
|
||||
await owner.schema.createSchema(schema);
|
||||
db = knex({ client: 'pg', connection, searchPath: [schema] });
|
||||
} else db = knex({ client: 'sqlite3', connection: { filename: ':memory:' }, useNullAsDefault: true });
|
||||
});
|
||||
afterEach(async () => {
|
||||
await db.destroy();
|
||||
if (owner) { await owner.schema.dropSchema(schema, true); await owner.destroy(); }
|
||||
});
|
||||
it('upgrades legacy data, is repeatable and preserves subsequent edits', async () => {
|
||||
await db.schema.createTable('events', table => {
|
||||
table.increments('id'); table.timestamp('created_at').defaultTo(db.fn.now()); table.boolean('is_active').defaultTo(true);
|
||||
});
|
||||
const created = '2026-01-02T03:04:05.000Z';
|
||||
await db('events').insert({ created_at: created });
|
||||
await migration.up(db); await migration.up(db);
|
||||
let row = await db('events').first();
|
||||
expect(toTimestamp(row.updated_at)).toBe(Date.parse(created));
|
||||
await db('events').where({ id: row.id }).update({ updated_at: db.fn.now(), is_active: engine === 'pg' ? false : 0 });
|
||||
const changed = (await db('events').first()).updated_at;
|
||||
await migration.up(db); row = await db('events').first();
|
||||
expect(toTimestamp(row.updated_at)).toBe(toTimestamp(changed)); expect([false, 0]).toContain(row.is_active);
|
||||
});
|
||||
it('handles a fresh table and an already present updated_at column', async () => {
|
||||
await db.schema.createTable('events', table => { table.increments('id'); table.timestamp('created_at'); table.timestamp('updated_at'); });
|
||||
await migration.up(db);
|
||||
expect(await db.schema.hasColumn('events', 'updated_at')).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
const knex = require('knex');
|
||||
const { randomUUID } = require('crypto');
|
||||
const fs = require('fs/promises');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const request = require('supertest');
|
||||
const pgUrl = process.env.PICPEAK_PG_TEST_URL;
|
||||
(pgUrl ? describe : describe.skip)('fresh PostgreSQL gallery contract', () => {
|
||||
let owner, db, schema, tmpDir, cleanup, previousClient;
|
||||
beforeAll(async () => {
|
||||
schema = `fresh_gallery_${randomUUID().replace(/-/g, '')}`;
|
||||
owner = knex({ client: 'pg', connection: pgUrl });
|
||||
await owner.schema.createSchema(schema);
|
||||
previousClient = process.env.DATABASE_CLIENT;
|
||||
process.env.DATABASE_CLIENT = 'pg';
|
||||
process.env.JWT_SECRET = 'fresh-pg-gallery-test-secret-at-least-32-characters';
|
||||
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'picpeak-fresh-pg-'));
|
||||
process.env.STORAGE_PATH = path.join(tmpDir, 'storage');
|
||||
jest.doMock('../../knexfile', () => ({ client: 'pg', connection: pgUrl, searchPath: [schema] }));
|
||||
({ db } = require('../../src/database/db'));
|
||||
// bootCrmDb runs the complete core chain against the shared db singleton.
|
||||
({ cleanup } = await require('../integration/helpers/crmDb').bootCrmDb());
|
||||
}, 120000);
|
||||
afterAll(async () => {
|
||||
await require('../../src/services/serviceShutdown').stopServices();
|
||||
if (cleanup) await cleanup(); else if (db) await db.destroy();
|
||||
if (owner) { await owner.schema.dropSchema(schema, true); await owner.destroy(); }
|
||||
if (tmpDir) await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
if (previousClient === undefined) delete process.env.DATABASE_CLIENT; else process.env.DATABASE_CLIENT = previousClient;
|
||||
jest.dontMock('../../knexfile');
|
||||
});
|
||||
it('creates through the real admin route, then toggles a typed boolean and timestamp', async () => {
|
||||
const { seedMinimal, assignAdminRole, mintAdminToken, buildRouteApp } = require('../integration/helpers/crmDb');
|
||||
const { adminId } = await seedMinimal(db);
|
||||
await assignAdminRole(db, adminId);
|
||||
const app = buildRouteApp('/api/admin/events', require('../../src/routes/adminEvents'));
|
||||
const bearer = `Bearer ${mintAdminToken(adminId)}`;
|
||||
const created = await request(app).post('/api/admin/events').set('Authorization', bearer).send({
|
||||
event_type: 'wedding', event_name: 'Fresh PostgreSQL', event_date: '2026-10-01',
|
||||
customer_name: 'Customer', customer_email: '[email protected]', admin_email: '[email protected]',
|
||||
password: 'Strong-Test-Photo-Pass-924!', expiration_days: 30, feedback_enabled: true,
|
||||
});
|
||||
expect(created.status).toBe(200);
|
||||
const event = await db('events').where({ event_name: 'Fresh PostgreSQL' }).first();
|
||||
expect(event.created_by).toBe(adminId);
|
||||
expect(event.is_active).toBe(true);
|
||||
expect(event.updated_at).toBeInstanceOf(Date);
|
||||
expect(await db('event_feedback_settings').where({ event_id: event.id }).first()).toBeTruthy();
|
||||
const toggled = await request(app).post(`/api/admin/events/${event.id}/toggle-status`).set('Authorization', bearer).send({});
|
||||
expect(toggled.status).toBe(200);
|
||||
const row = await db('events').where({ id: event.id }).first();
|
||||
expect(row.is_active).toBe(false);
|
||||
expect(row.updated_at).toBeInstanceOf(Date);
|
||||
await require('../../migrations/core/210_events_updated_at').up(db);
|
||||
expect((await db('events').where({ id: event.id }).first()).updated_at).toEqual(row.updated_at);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user