183 lines
6.4 KiB
JavaScript
183 lines
6.4 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.pool = void 0;
|
|
exports.runMigrations = runMigrations;
|
|
const pg_1 = require("pg");
|
|
const pool = new pg_1.Pool({
|
|
connectionString: process.env.DATABASE_URL,
|
|
});
|
|
exports.pool = pool;
|
|
async function runMigrations() {
|
|
const client = await pool.connect();
|
|
try {
|
|
await client.query('BEGIN');
|
|
// Users table
|
|
await client.query(`
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id SERIAL PRIMARY KEY,
|
|
email VARCHAR(255) UNIQUE NOT NULL,
|
|
password_hash VARCHAR(255) NOT NULL,
|
|
role VARCHAR(50) DEFAULT 'admin',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
// Stores table
|
|
await client.query(`
|
|
CREATE TABLE IF NOT EXISTS stores (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
slug VARCHAR(255) UNIQUE NOT NULL,
|
|
dutchie_url TEXT NOT NULL,
|
|
active BOOLEAN DEFAULT true,
|
|
scrape_enabled BOOLEAN DEFAULT true,
|
|
last_scraped_at TIMESTAMP,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
// Categories table (shop, brands, specials)
|
|
await client.query(`
|
|
CREATE TABLE IF NOT EXISTS categories (
|
|
id SERIAL PRIMARY KEY,
|
|
store_id INTEGER REFERENCES stores(id) ON DELETE CASCADE,
|
|
name VARCHAR(255) NOT NULL,
|
|
slug VARCHAR(255) NOT NULL,
|
|
dutchie_url TEXT NOT NULL,
|
|
scrape_enabled BOOLEAN DEFAULT true,
|
|
last_scraped_at TIMESTAMP,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(store_id, slug)
|
|
);
|
|
`);
|
|
// Products table
|
|
await client.query(`
|
|
CREATE TABLE IF NOT EXISTS products (
|
|
id SERIAL PRIMARY KEY,
|
|
store_id INTEGER REFERENCES stores(id) ON DELETE CASCADE,
|
|
category_id INTEGER REFERENCES categories(id) ON DELETE SET NULL,
|
|
dutchie_product_id VARCHAR(255),
|
|
name VARCHAR(500) NOT NULL,
|
|
slug VARCHAR(500),
|
|
description TEXT,
|
|
price DECIMAL(10, 2),
|
|
original_price DECIMAL(10, 2),
|
|
strain_type VARCHAR(100),
|
|
thc_percentage DECIMAL(5, 2),
|
|
cbd_percentage DECIMAL(5, 2),
|
|
brand VARCHAR(255),
|
|
weight VARCHAR(100),
|
|
image_url TEXT,
|
|
local_image_path TEXT,
|
|
dutchie_url TEXT NOT NULL,
|
|
in_stock BOOLEAN DEFAULT true,
|
|
is_special BOOLEAN DEFAULT false,
|
|
metadata JSONB,
|
|
first_seen_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
last_seen_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(store_id, dutchie_product_id)
|
|
);
|
|
`);
|
|
// Campaigns table
|
|
await client.query(`
|
|
CREATE TABLE IF NOT EXISTS campaigns (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
slug VARCHAR(255) UNIQUE NOT NULL,
|
|
description TEXT,
|
|
display_style VARCHAR(50) DEFAULT 'grid',
|
|
active BOOLEAN DEFAULT true,
|
|
start_date TIMESTAMP,
|
|
end_date TIMESTAMP,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
// Campaign products (many-to-many with ordering)
|
|
await client.query(`
|
|
CREATE TABLE IF NOT EXISTS campaign_products (
|
|
id SERIAL PRIMARY KEY,
|
|
campaign_id INTEGER REFERENCES campaigns(id) ON DELETE CASCADE,
|
|
product_id INTEGER REFERENCES products(id) ON DELETE CASCADE,
|
|
display_order INTEGER DEFAULT 0,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(campaign_id, product_id)
|
|
);
|
|
`);
|
|
// Click tracking
|
|
await client.query(`
|
|
CREATE TABLE IF NOT EXISTS clicks (
|
|
id SERIAL PRIMARY KEY,
|
|
product_id INTEGER REFERENCES products(id) ON DELETE CASCADE,
|
|
campaign_id INTEGER REFERENCES campaigns(id) ON DELETE SET NULL,
|
|
ip_address VARCHAR(45),
|
|
user_agent TEXT,
|
|
referrer TEXT,
|
|
clicked_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
// Create index on clicked_at for analytics queries
|
|
await client.query(`
|
|
CREATE INDEX IF NOT EXISTS idx_clicks_clicked_at ON clicks(clicked_at);
|
|
CREATE INDEX IF NOT EXISTS idx_clicks_product_id ON clicks(product_id);
|
|
CREATE INDEX IF NOT EXISTS idx_clicks_campaign_id ON clicks(campaign_id);
|
|
`);
|
|
// Proxies table
|
|
await client.query(`
|
|
CREATE TABLE IF NOT EXISTS proxies (
|
|
id SERIAL PRIMARY KEY,
|
|
host VARCHAR(255) NOT NULL,
|
|
port INTEGER NOT NULL,
|
|
protocol VARCHAR(10) NOT NULL,
|
|
username VARCHAR(255),
|
|
password VARCHAR(255),
|
|
active BOOLEAN DEFAULT true,
|
|
is_anonymous BOOLEAN DEFAULT false,
|
|
last_tested_at TIMESTAMP,
|
|
test_result VARCHAR(50),
|
|
response_time_ms INTEGER,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(host, port, protocol)
|
|
);
|
|
`);
|
|
// Settings table
|
|
await client.query(`
|
|
CREATE TABLE IF NOT EXISTS settings (
|
|
key VARCHAR(255) PRIMARY KEY,
|
|
value TEXT NOT NULL,
|
|
description TEXT,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
// Insert default settings
|
|
await client.query(`
|
|
INSERT INTO settings (key, value, description) VALUES
|
|
('scrape_interval_hours', '4', 'How often to scrape stores (in hours)'),
|
|
('scrape_specials_time', '00:01', 'Time to scrape specials daily (HH:MM in 24h format)'),
|
|
('analytics_retention_days', '365', 'How many days to keep analytics data'),
|
|
('proxy_timeout_ms', '3000', 'Proxy timeout in milliseconds'),
|
|
('proxy_test_url', 'https://httpbin.org/ip', 'URL to test proxies against')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
`);
|
|
await client.query('COMMIT');
|
|
console.log('✅ Migrations completed successfully');
|
|
}
|
|
catch (error) {
|
|
await client.query('ROLLBACK');
|
|
console.error('❌ Migration failed:', error);
|
|
throw error;
|
|
}
|
|
finally {
|
|
client.release();
|
|
}
|
|
}
|
|
// Run migrations if this file is executed directly
|
|
if (require.main === module) {
|
|
runMigrations()
|
|
.then(() => process.exit(0))
|
|
.catch(() => process.exit(1));
|
|
}
|