Datenbank von SQLite auf Postgres umgestellt

Verbindung über KVER_DSN; Tabelle user heißt jetzt account (reserviertes
Wort in Postgres). kver import-sqlite übernimmt die alte Datei einmalig in
einer Transaktion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiXsPUqw7oeomZ8wZrQW5q
This commit is contained in:
irrlicht
2026-09-26 22:22:07 +02:00
co-authored by Claude Opus 5.5
parent c6ab082f8c
commit 2714fdb1a5
15 changed files with 412 additions and 301 deletions
+7 -7
View File
@@ -192,7 +192,7 @@ func stopImpressionWorker() {
// Netz. Gezielt nur auf den HTML-Seiten-Routen, nicht auf API oder Assets.
//
// Die Erfassung läuft asynchron über den Impression-Worker: der Seitenaufruf
// darf nicht auf den DB-Schreib warten (SQLite serialisiert Schreiber). r ist
// darf nicht auf den DB-Schreibvorgang warten. r ist
// nach Rückkehr des Handlers nicht mehr sicher nutzbar -> wir kopieren die zwei
// Werte vorher heraus.
func trackImpression(next http.Handler) http.Handler {
@@ -214,8 +214,8 @@ func recordImpression(path, anonIP, asn string) {
}
day := time.Now().UTC().Format("2006-01-02")
db.Exec(
`INSERT INTO impression (day, anon_ip, path, asn, hits) VALUES (?, ?, ?, ?, 1)
ON CONFLICT(day, anon_ip, path) DO UPDATE SET hits = hits + 1`,
`INSERT INTO impression (day, anon_ip, path, asn, hits) VALUES ($1, $2, $3, $4, 1)
ON CONFLICT (day, anon_ip, path) DO UPDATE SET hits = impression.hits + 1`,
day, anonIP, path, asn,
)
}
@@ -225,21 +225,21 @@ func recordImpression(path, anonIP, asn string) {
// rechnen. Öffentlich, kein Auth.
func handleStatsDetail(w http.ResponseWriter, r *http.Request) {
var users, entries, toplevel, votesLeft, votesRight int64
db.QueryRow(`SELECT COUNT(*) FROM user`).Scan(&users)
db.QueryRow(`SELECT COUNT(*) FROM account`).Scan(&users)
db.QueryRow(`SELECT COUNT(*) FROM entry WHERE deleted = 0`).Scan(&entries)
db.QueryRow(`SELECT COUNT(*) FROM entry WHERE deleted = 0 AND reply_to = 0`).Scan(&toplevel)
db.QueryRow(`SELECT COUNT(*) FROM vote WHERE mode = 'left'`).Scan(&votesLeft)
db.QueryRow(`SELECT COUNT(*) FROM vote WHERE mode = 'right'`).Scan(&votesRight)
var imprTotal, visitorsTotal int64
db.QueryRow(`SELECT COALESCE(SUM(hits), 0), COUNT(DISTINCT anon_ip) FROM impression`).
db.QueryRow(`SELECT COALESCE(SUM(hits), 0)::bigint, COUNT(DISTINCT anon_ip) FROM impression`).
Scan(&imprTotal, &visitorsTotal)
// Tägliche Impressions + grobe Unique-Netze, jüngste 30 Tage zuerst. Das
// Frontend dreht für die Achse auf aufsteigend.
daily := []map[string]any{}
if rows, err := db.Query(
`SELECT day, SUM(hits), COUNT(DISTINCT anon_ip)
`SELECT day, SUM(hits)::bigint, COUNT(DISTINCT anon_ip)
FROM impression
GROUP BY day
ORDER BY day DESC
@@ -257,7 +257,7 @@ func handleStatsDetail(w http.ResponseWriter, r *http.Request) {
topASNs := []map[string]any{}
if rows, err := db.Query(
`SELECT CASE WHEN asn = '' THEN '(unbekannt)' ELSE asn END AS a,
SUM(hits) AS h,
SUM(hits)::bigint AS h,
COUNT(DISTINCT anon_ip)
FROM impression
GROUP BY a