MVP: 按 07 风格重写前端 + Go 后端落地(长文/短文、编辑器、后台管理)

This commit is contained in:
Sakurasan
2026-09-20 20:56:03 +08:00
parent edee708802
commit 4d8f2de3a4
96 changed files with 6005 additions and 2602 deletions
+101
View File
@@ -0,0 +1,101 @@
// Package db opens a *sql.DB for either SQLite or PostgreSQL and rewrites
// the shared `?` placeholders into PostgreSQL's `$n` form.
package db
import (
"database/sql"
"fmt"
"os"
"path/filepath"
"strings"
_ "github.com/lib/pq"
_ "modernc.org/sqlite"
)
type Dialect int
const (
SQLite Dialect = iota
Postgres
)
type DB struct {
*sql.DB
Dialect Dialect
}
func Open(driver, dsn string) (*DB, error) {
var d Dialect
switch driver {
case "sqlite":
d = SQLite
if err := os.MkdirAll(filepath.Dir(dsn), 0o755); err != nil {
return nil, err
}
dsn = addSQLiteParams(dsn)
case "postgres":
d = Postgres
default:
return nil, fmt.Errorf("unsupported driver %q", driver)
}
pool, err := sql.Open(driverName(driver), dsn)
if err != nil {
return nil, err
}
if d == SQLite {
// SQLite is single-writer; keep a small pool to avoid "database is locked".
pool.SetMaxOpenConns(1)
} else {
pool.SetMaxOpenConns(10)
}
if err := pool.Ping(); err != nil {
return nil, fmt.Errorf("connect %s: %w", driver, err)
}
return &DB{DB: pool, Dialect: d}, nil
}
func driverName(driver string) string {
if driver == "postgres" {
return "postgres"
}
return "sqlite"
}
func addSQLiteParams(dsn string) string {
if strings.HasPrefix(dsn, "file:") || strings.Contains(dsn, "?") {
return dsn
}
return dsn + "?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)&_pragma=foreign_keys(1)"
}
// Rebind converts `?` placeholders to `$1..$n` on PostgreSQL.
func (d *DB) Rebind(q string) string {
if d.Dialect != Postgres {
return q
}
var b strings.Builder
b.Grow(len(q) + 8)
n := 0
for _, r := range q {
if r == '?' {
n++
b.WriteString("$")
b.WriteString(fmt.Sprint(n))
continue
}
b.WriteRune(r)
}
return b.String()
}
func (d *DB) Q(q string) string { return d.Rebind(q) }
// AutoInc returns the column definition for an auto-incrementing primary key.
func (d *DB) AutoInc() string {
if d.Dialect == Postgres {
return "BIGSERIAL PRIMARY KEY"
}
return "INTEGER PRIMARY KEY AUTOINCREMENT"
}