mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-28 03:06:38 +08:00
d5cf5fb4b2
Adopt the cropflre/nowen-video tech stack and rebuild the project from
scratch:
- Backend: Go 1.25 + Gin + GORM + SQLite (WAL) + JWT + WebSocket hub.
Layered packages config / database / model / repository / service /
middleware / handler. Default admin (admin/admin123) seeded on first
run; /api routes for auth, libraries, media, stream and admin
panels. WebSocket scan-progress events at /api/ws.
- Frontend: React 18 + Vite 5 + Tailwind 3.4 + Zustand + axios +
react-router 6 + lucide-react + framer-motion + hls.js. Pages for
Login / Home / Library / Search / MediaDetail / Player / Admin
(Library, Users, Settings tabs).
- Distribution: multi-arch Dockerfile (frontend -> backend -> Alpine
runtime), docker-compose.yml, GitHub Actions for CI and GHCR
publish, Makefile, env-prefixed config (MEDIASTATION_*).
- Docs: README, CONTRIBUTING, .env.example, config.example.yaml.
Backend builds, vets and tests pass. Frontend builds via tsc -b && vite
build (250 kB JS / 16 kB CSS, gzipped 84 / 4 kB).
84 lines
2.3 KiB
Go
84 lines
2.3 KiB
Go
// Package database wires up GORM against SQLite (WAL mode) and exposes the
|
|
// auto-migration entry point used at startup.
|
|
package database
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
|
|
"github.com/glebarez/sqlite"
|
|
"go.uber.org/zap"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
|
|
"github.com/ShukeBta/MediaStationGo/internal/config"
|
|
"github.com/ShukeBta/MediaStationGo/internal/model"
|
|
)
|
|
|
|
// Open initialises the SQLite database file applying WAL pragmas for
|
|
// better concurrent read performance — same defaults as nowen-video.
|
|
func Open(cfg *config.Config, log *zap.Logger) (*gorm.DB, error) {
|
|
dsn := buildDSN(cfg)
|
|
|
|
gormLogger := logger.New(
|
|
zapStdLogger{log: log},
|
|
logger.Config{
|
|
SlowThreshold: 0,
|
|
LogLevel: logger.Warn,
|
|
IgnoreRecordNotFoundError: true,
|
|
Colorful: false,
|
|
},
|
|
)
|
|
|
|
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{
|
|
Logger: gormLogger,
|
|
PrepareStmt: true,
|
|
DisableForeignKeyConstraintWhenMigrating: false,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("gorm open: %w", err)
|
|
}
|
|
sqlDB, err := db.DB()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("gorm sqldb: %w", err)
|
|
}
|
|
if cfg.Database.MaxOpenConns > 0 {
|
|
sqlDB.SetMaxOpenConns(cfg.Database.MaxOpenConns)
|
|
}
|
|
if cfg.Database.MaxIdleConns > 0 {
|
|
sqlDB.SetMaxIdleConns(cfg.Database.MaxIdleConns)
|
|
}
|
|
return db, nil
|
|
}
|
|
|
|
func buildDSN(cfg *config.Config) string {
|
|
dbPath := cfg.Database.DBPath
|
|
if !filepath.IsAbs(dbPath) {
|
|
// keep as-is to respect user-provided relative paths.
|
|
dbPath = filepath.Clean(dbPath)
|
|
}
|
|
dsn := dbPath + "?_pragma=foreign_keys(1)"
|
|
if cfg.Database.WALMode {
|
|
dsn += "&_pragma=journal_mode(WAL)"
|
|
}
|
|
if cfg.Database.BusyTimeout > 0 {
|
|
dsn += fmt.Sprintf("&_pragma=busy_timeout(%d)", cfg.Database.BusyTimeout)
|
|
}
|
|
if cfg.Database.CacheSize != 0 {
|
|
dsn += fmt.Sprintf("&_pragma=cache_size(%d)", cfg.Database.CacheSize)
|
|
}
|
|
return dsn
|
|
}
|
|
|
|
// AutoMigrate creates tables for every model registered in the model package.
|
|
func AutoMigrate(db *gorm.DB) error {
|
|
return db.AutoMigrate(model.AllModels()...)
|
|
}
|
|
|
|
// zapStdLogger adapts a *zap.Logger to GORM's tiny logger interface.
|
|
type zapStdLogger struct{ log *zap.Logger }
|
|
|
|
func (z zapStdLogger) Printf(format string, args ...interface{}) {
|
|
z.log.Sugar().Infof(format, args...)
|
|
}
|