feat: database backups & analytics site management
- Added manual database backup functionality with pg_dump - Added cron-based automated backups to Google Drive and Dropbox - Added ability to manually add, edit, and delete sites in Analytics - Added 1-day timeframe filter in Analytics page - Updated Dockerfile to include postgresql-client
This commit is contained in:
+49
-1
@@ -33,6 +33,12 @@ pool.query(`
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_pv_domain ON pageviews(domain, ts DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_pv_ts ON pageviews(ts DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS analytics_sites (
|
||||
domain TEXT PRIMARY KEY,
|
||||
created_at BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM CURRENT_TIMESTAMP)::BIGINT)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS config_sites (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
@@ -59,6 +65,25 @@ pool.query(`
|
||||
icon TEXT,
|
||||
description TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS backup_configs (
|
||||
id TEXT PRIMARY KEY,
|
||||
db_id TEXT NOT NULL,
|
||||
schedule TEXT NOT NULL,
|
||||
cloud_type TEXT NOT NULL,
|
||||
credentials TEXT NOT NULL,
|
||||
created_at BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM CURRENT_TIMESTAMP)::BIGINT)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS backup_logs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
db_id TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
message TEXT,
|
||||
file_size BIGINT,
|
||||
ts BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM CURRENT_TIMESTAMP)::BIGINT)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_backup_db_ts ON backup_logs(db_id, ts DESC);
|
||||
`).catch(console.error)
|
||||
export type PingLog = {
|
||||
id: number
|
||||
@@ -192,8 +217,31 @@ export async function getAnalyticsStats(domain: string, days = 30) {
|
||||
}
|
||||
|
||||
export async function getAllDomains(): Promise<string[]> {
|
||||
const res = await pool.query(`SELECT DISTINCT domain FROM pageviews ORDER BY domain`)
|
||||
const res = await pool.query(`
|
||||
SELECT domain FROM analytics_sites
|
||||
UNION
|
||||
SELECT DISTINCT domain FROM pageviews
|
||||
ORDER BY domain
|
||||
`)
|
||||
return res.rows.map(r => r.domain)
|
||||
}
|
||||
|
||||
export async function addAnalyticsSite(domain: string) {
|
||||
await pool.query(
|
||||
`INSERT INTO analytics_sites (domain) VALUES ($1) ON CONFLICT DO NOTHING`,
|
||||
[domain]
|
||||
)
|
||||
}
|
||||
|
||||
export async function renameAnalyticsSite(oldDomain: string, newDomain: string) {
|
||||
// Update both tables
|
||||
await pool.query(`UPDATE analytics_sites SET domain = $1 WHERE domain = $2`, [newDomain, oldDomain])
|
||||
await pool.query(`UPDATE pageviews SET domain = $1 WHERE domain = $2`, [newDomain, oldDomain])
|
||||
}
|
||||
|
||||
export async function deleteAnalyticsSite(domain: string) {
|
||||
await pool.query(`DELETE FROM analytics_sites WHERE domain = $1`, [domain])
|
||||
await pool.query(`DELETE FROM pageviews WHERE domain = $1`, [domain])
|
||||
}
|
||||
|
||||
export default pool
|
||||
|
||||
Reference in New Issue
Block a user