diff --git a/docker-compose.yml b/docker-compose.yml index ae38ad6..6ee0ab8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,13 +36,10 @@ services: environment: - LISTEN_ADDR=:8080 - DATABASE_URL=postgres://trade:trade@postgres:5432/futures?sslmode=disable - - AUTH_DB_PATH=/app/auth/auth.db depends_on: - postgres ports: - "4000:8080" - volumes: - - ./data:/app/auth restart: unless-stopped volumes: diff --git a/web/backend/Dockerfile b/web/backend/Dockerfile index 87b588d..4b045fd 100644 --- a/web/backend/Dockerfile +++ b/web/backend/Dockerfile @@ -21,7 +21,6 @@ WORKDIR /src COPY backend ./ COPY --from=ui /ui/dist ./dist -# 用 modernc.org/sqlite 纯 Go 驱动,无 CGO,无需 gcc/musl-dev ENV CGO_ENABLED=0 GOOS=linux RUN go mod tidy && \ @@ -36,7 +35,7 @@ RUN apk add --no-cache tzdata ca-certificates && \ echo "Asia/Shanghai" > /etc/timezone && \ apk del tzdata && \ adduser -D -u 1000 app && \ - mkdir -p /app/data /app/auth && \ + mkdir -p /app/data && \ chown -R app:app /app WORKDIR /app @@ -45,8 +44,7 @@ USER app COPY --from=api --chown=app:app /out/web /app/web ENV TZ=Asia/Shanghai \ - LISTEN_ADDR=:8080 \ - AUTH_DB_PATH=/app/auth/auth.db + LISTEN_ADDR=:8080 EXPOSE 8080 diff --git a/web/backend/go.mod b/web/backend/go.mod index 2f0eadb..383716a 100644 --- a/web/backend/go.mod +++ b/web/backend/go.mod @@ -7,5 +7,4 @@ require ( github.com/golang-jwt/jwt/v5 v5.2.1 github.com/lib/pq v1.10.9 golang.org/x/crypto v0.27.0 - modernc.org/sqlite v1.32.0 ) diff --git a/web/backend/go.sum b/web/backend/go.sum index 88d1e28..ac842d6 100644 --- a/web/backend/go.sum +++ b/web/backend/go.sum @@ -2,4 +2,3 @@ github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITL github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA= diff --git a/web/backend/internal/config/config.go b/web/backend/internal/config/config.go index bc5a28d..3d657a8 100644 --- a/web/backend/internal/config/config.go +++ b/web/backend/internal/config/config.go @@ -8,7 +8,6 @@ import ( type Config struct { ListenAddr string DatabaseURL string - AuthDBPath string TushareAPIURL string } @@ -16,7 +15,6 @@ func Load() (*Config, error) { cfg := &Config{ ListenAddr: getenv("LISTEN_ADDR", ":8080"), DatabaseURL: os.Getenv("DATABASE_URL"), - AuthDBPath: getenv("AUTH_DB_PATH", "/app/auth/auth.db"), TushareAPIURL: getenv("TUSHARE_API_URL", "http://tushare:8000"), } if cfg.DatabaseURL == "" { diff --git a/web/backend/internal/store/auth.go b/web/backend/internal/store/auth.go index 3acb8ad..df5ded9 100644 --- a/web/backend/internal/store/auth.go +++ b/web/backend/internal/store/auth.go @@ -4,10 +4,9 @@ import ( "database/sql" "errors" "fmt" - "path/filepath" "time" - _ "modernc.org/sqlite" + _ "github.com/lib/pq" ) type AuthStore struct{ db *sql.DB } @@ -30,18 +29,14 @@ const ( var ErrNotFound = errors.New("user not found") -func OpenAuth(path string) (*AuthStore, error) { - if dir := filepath.Dir(path); dir != "" { - _ = ensureDir(dir) - } - dsn := fmt.Sprintf("file:%s?_pragma=journal_mode(WAL)&_pragma=foreign_keys(1)&_pragma=busy_timeout(5000)", path) - db, err := sql.Open("sqlite", dsn) +func OpenAuth(databaseURL string) (*AuthStore, error) { + db, err := sql.Open("postgres", databaseURL) if err != nil { - return nil, fmt.Errorf("open auth.db: %w", err) + return nil, fmt.Errorf("open auth db: %w", err) } - db.SetMaxOpenConns(1) // sqlite write 单连接更稳 + db.SetMaxOpenConns(8) if err := db.Ping(); err != nil { - return nil, fmt.Errorf("ping auth.db: %w", err) + return nil, fmt.Errorf("ping auth db: %w", err) } s := &AuthStore{db: db} if err := s.init(); err != nil { @@ -55,11 +50,11 @@ func (s *AuthStore) Close() error { return s.db.Close() } func (s *AuthStore) init() error { _, err := s.db.Exec(` CREATE TABLE IF NOT EXISTS users ( - id INTEGER PRIMARY KEY AUTOINCREMENT, + id SERIAL PRIMARY KEY, username TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, role TEXT NOT NULL CHECK(role IN ('admin','user')), - disabled INTEGER NOT NULL DEFAULT 0, + disabled BOOLEAN NOT NULL DEFAULT FALSE, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); @@ -69,7 +64,7 @@ func (s *AuthStore) init() error { return err } // 兼容旧表:添加 force_password_change 列(已存在则忽略错误) - _, _ = s.db.Exec(`ALTER TABLE users ADD COLUMN force_password_change INTEGER NOT NULL DEFAULT 0`) + _, _ = s.db.Exec(`ALTER TABLE users ADD COLUMN IF NOT EXISTS force_password_change BOOLEAN NOT NULL DEFAULT FALSE`) return nil } @@ -81,28 +76,28 @@ func (s *AuthStore) CountAdmins() (int, error) { func (s *AuthStore) CreateUser(username, passwordHash, role string) (*User, error) { now := time.Now().Format("2006-01-02 15:04:05") - res, err := s.db.Exec( + var id int64 + err := s.db.QueryRow( `INSERT INTO users(username, password_hash, role, disabled, created_at, updated_at) - VALUES (?, ?, ?, 0, ?, ?)`, + VALUES ($1, $2, $3, FALSE, $4, $5) RETURNING id`, username, passwordHash, role, now, now, - ) + ).Scan(&id) if err != nil { return nil, err } - id, _ := res.LastInsertId() return &User{ID: id, Username: username, PasswordHash: passwordHash, Role: role, CreatedAt: now, UpdatedAt: now}, nil } func (s *AuthStore) GetByUsername(username string) (*User, error) { row := s.db.QueryRow(`SELECT id, username, password_hash, role, disabled, force_password_change, created_at, updated_at - FROM users WHERE username = ?`, username) + FROM users WHERE username = $1`, username) return scanUser(row) } func (s *AuthStore) GetByID(id int64) (*User, error) { row := s.db.QueryRow(`SELECT id, username, password_hash, role, disabled, force_password_change, created_at, updated_at - FROM users WHERE id = ?`, id) + FROM users WHERE id = $1`, id) return scanUser(row) } @@ -126,7 +121,7 @@ func (s *AuthStore) ListUsers() ([]User, error) { func (s *AuthStore) UpdatePassword(id int64, hash string) error { now := time.Now().Format("2006-01-02 15:04:05") - res, err := s.db.Exec(`UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?`, hash, now, id) + res, err := s.db.Exec(`UPDATE users SET password_hash = $1, updated_at = $2 WHERE id = $3`, hash, now, id) if err != nil { return err } @@ -139,11 +134,7 @@ func (s *AuthStore) UpdatePassword(id int64, hash string) error { func (s *AuthStore) SetForcePasswordChange(id int64, v bool) error { now := time.Now().Format("2006-01-02 15:04:05") - val := 0 - if v { - val = 1 - } - res, err := s.db.Exec(`UPDATE users SET force_password_change = ?, updated_at = ? WHERE id = ?`, val, now, id) + res, err := s.db.Exec(`UPDATE users SET force_password_change = $1, updated_at = $2 WHERE id = $3`, v, now, id) if err != nil { return err } @@ -156,11 +147,7 @@ func (s *AuthStore) SetForcePasswordChange(id int64, v bool) error { func (s *AuthStore) SetDisabled(id int64, disabled bool) error { now := time.Now().Format("2006-01-02 15:04:05") - v := 0 - if disabled { - v = 1 - } - res, err := s.db.Exec(`UPDATE users SET disabled = ?, updated_at = ? WHERE id = ?`, v, now, id) + res, err := s.db.Exec(`UPDATE users SET disabled = $1, updated_at = $2 WHERE id = $3`, disabled, now, id) if err != nil { return err } @@ -172,7 +159,7 @@ func (s *AuthStore) SetDisabled(id int64, disabled bool) error { } func (s *AuthStore) DeleteUser(id int64) error { - res, err := s.db.Exec(`DELETE FROM users WHERE id = ?`, id) + res, err := s.db.Exec(`DELETE FROM users WHERE id = $1`, id) if err != nil { return err } @@ -189,16 +176,12 @@ type rowScanner interface { func scanUser(r rowScanner) (*User, error) { var u User - var disabled int - var forceChange int - if err := r.Scan(&u.ID, &u.Username, &u.PasswordHash, &u.Role, &disabled, &forceChange, &u.CreatedAt, &u.UpdatedAt); err != nil { + if err := r.Scan(&u.ID, &u.Username, &u.PasswordHash, &u.Role, &u.Disabled, &u.ForcePasswordChange, &u.CreatedAt, &u.UpdatedAt); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound } return nil, err } - u.Disabled = disabled != 0 - u.ForcePasswordChange = forceChange != 0 return &u, nil } diff --git a/web/backend/internal/store/util.go b/web/backend/internal/store/util.go deleted file mode 100644 index 2857eac..0000000 --- a/web/backend/internal/store/util.go +++ /dev/null @@ -1,10 +0,0 @@ -package store - -import "os" - -func ensureDir(dir string) error { - if _, err := os.Stat(dir); err == nil { - return nil - } - return os.MkdirAll(dir, 0o755) -} diff --git a/web/backend/main.go b/web/backend/main.go index e1f9379..8cdca60 100644 --- a/web/backend/main.go +++ b/web/backend/main.go @@ -30,7 +30,7 @@ func main() { } defer futures.Close() - authDB, err := store.OpenAuth(cfg.AuthDBPath) + authDB, err := store.OpenAuth(cfg.DatabaseURL) if err != nil { log.Fatalf("open auth: %v", err) }