Self-hosted Express + Postgres app for organizing STL/3MF/OBJ/etc. with: - Model/file library, tags, collections, print settings - URL import: Thingiverse (API), Printables (GraphQL), MakerWorld (FlareSolverr + token), cubee3d/other (metadata + manual files) - In-browser 3D viewer; 3MF rendered via Python/trimesh GLB conversion - Auto thumbnails, license/commercial-use tracking, in-app editing - Settings (API tokens / FlareSolverr) stored in DB with .env fallback Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
39 lines
988 B
JavaScript
39 lines
988 B
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
const { Pool } = require('pg');
|
|
|
|
const pool = new Pool({
|
|
connectionString: process.env.DATABASE_URL,
|
|
});
|
|
|
|
pool.on('error', (err) => {
|
|
console.error('Unexpected Postgres pool error:', err);
|
|
});
|
|
|
|
// Apply schema.sql on startup (idempotent — uses CREATE TABLE IF NOT EXISTS).
|
|
async function init() {
|
|
const schema = fs.readFileSync(path.join(__dirname, '..', 'db', 'schema.sql'), 'utf8');
|
|
await pool.query(schema);
|
|
}
|
|
|
|
function query(text, params) {
|
|
return pool.query(text, params);
|
|
}
|
|
|
|
// Run a function inside a transaction, passing it a dedicated client.
|
|
async function transaction(fn) {
|
|
const client = await pool.connect();
|
|
try {
|
|
await client.query('BEGIN');
|
|
const result = await fn(client);
|
|
await client.query('COMMIT');
|
|
return result;
|
|
} catch (err) {
|
|
await client.query('ROLLBACK');
|
|
throw err;
|
|
} finally {
|
|
client.release();
|
|
}
|
|
}
|
|
|
|
module.exports = { pool, query, transaction, init };
|