mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-08-19 08:26:23 -07:00
implement all installer pages, add DBPREFIX to constraints (not necessary to update if already installed)
This commit is contained in:
parent
ccad76ff28
commit
d5f489ef22
20 changed files with 482 additions and 283 deletions
|
@ -1,45 +1,44 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"time"
|
||||
"strings"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
type pathsForm struct {
|
||||
ConfigDir string `form:"configdir,required,notempty" method:"POST"`
|
||||
ConfigPath string `form:"configdir,required,notempty" method:"POST"`
|
||||
TemplateDir string `form:"templatedir,required,notempty" method:"POST"`
|
||||
DocumentRoot string `form:"documentroot,required,notempty" method:"POST"`
|
||||
LogDir string `form:"logdir,required,notempty" method:"POST"`
|
||||
WebRoot string `form:"webroot,required,notempty" method:"POST"`
|
||||
WebRoot string `form:"webroot,required" method:"POST"`
|
||||
}
|
||||
|
||||
func (pf *pathsForm) validateDir(pDir *string, desc string) error {
|
||||
dir := *pDir
|
||||
if dir == "" {
|
||||
func (pf *pathsForm) validatePath(targetPath *string, desc string, expectDir bool) error {
|
||||
p := *targetPath
|
||||
if p == "" {
|
||||
return fmt.Errorf("%s is required", desc)
|
||||
}
|
||||
dir = path.Clean(dir)
|
||||
*pDir = dir
|
||||
p = path.Clean(p)
|
||||
*targetPath = p
|
||||
|
||||
fi, err := os.Stat(dir)
|
||||
fi, err := os.Stat(p)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return fmt.Errorf("%s %s does not exist", desc, dir)
|
||||
return fmt.Errorf("%s %s does not exist", desc, p)
|
||||
}
|
||||
if errors.Is(err, fs.ErrPermission) {
|
||||
return fmt.Errorf("permission denied to %s", dir)
|
||||
return fmt.Errorf("permission denied to %s", p)
|
||||
}
|
||||
if !fi.Mode().IsDir() {
|
||||
return fmt.Errorf("%s exists at %s but is not a directory", desc, dir)
|
||||
if expectDir && !fi.Mode().IsDir() {
|
||||
return fmt.Errorf("%s exists at %s but is not a directory", desc, p)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -48,43 +47,44 @@ func (pf *pathsForm) validate(warnEv, errEv *zerolog.Event) (err error) {
|
|||
pf.DocumentRoot = path.Clean(pf.DocumentRoot)
|
||||
pf.LogDir = path.Clean(pf.LogDir)
|
||||
|
||||
if pf.ConfigDir == "" {
|
||||
if pf.ConfigPath == "" {
|
||||
warnEv.Msg("Required config output directory not set")
|
||||
return errors.New("config output directory is required")
|
||||
}
|
||||
pf.ConfigDir = path.Clean(pf.ConfigDir)
|
||||
pf.ConfigPath = path.Clean(pf.ConfigPath)
|
||||
|
||||
validConfigDirs := []string{".", "/usr/local/etc/gochan", "/etc/gochan"}
|
||||
|
||||
if !slices.Contains(validConfigDirs, pf.ConfigDir) {
|
||||
warnEv.Str("configDir", pf.ConfigDir).
|
||||
Msg("Invalid config output directory")
|
||||
return fmt.Errorf("config output directory %s is not allowed. Valid values are ., /usr/local/etc/gochan, or /etc/gochan", pf.ConfigDir)
|
||||
validConfigPaths := cfgPaths
|
||||
pathsArr := zerolog.Arr()
|
||||
for _, p := range validConfigPaths {
|
||||
pathsArr.Str(p)
|
||||
}
|
||||
|
||||
if err = pf.validateDir(&pf.ConfigDir, "config output directory"); err != nil {
|
||||
if !slices.Contains(validConfigPaths, pf.ConfigPath) {
|
||||
warnEv.Str("configPath", pf.ConfigPath).
|
||||
Array("validConfigPaths", pathsArr).
|
||||
Msg("Invalid config output path")
|
||||
return fmt.Errorf("config output path %s is not allowed. Valid values are %s", strings.Join(cfgPaths, ", "), pf.ConfigPath)
|
||||
}
|
||||
|
||||
configDir := path.Dir(pf.ConfigPath)
|
||||
|
||||
if err = pf.validatePath(&configDir, "config output directory", true); err != nil {
|
||||
warnEv.Err(err).
|
||||
Msg("Invalid config output directory")
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = os.Stat(path.Join(pf.ConfigDir, "gochan.json")); err == nil {
|
||||
warnEv.Str("configDir", pf.ConfigDir).
|
||||
Msg("Config output directory already exists")
|
||||
return fmt.Errorf("gochan.json already exists in %s", pf.ConfigDir)
|
||||
}
|
||||
|
||||
if err = pf.validateDir(&pf.TemplateDir, "template directory"); err != nil {
|
||||
if err = pf.validatePath(&pf.TemplateDir, "template directory", true); err != nil {
|
||||
warnEv.Err(err).Str("templateDir", pf.TemplateDir).
|
||||
Msg("Invalid template directory")
|
||||
return err
|
||||
}
|
||||
if err = pf.validateDir(&pf.DocumentRoot, "document root"); err != nil {
|
||||
if err = pf.validatePath(&pf.DocumentRoot, "document root", true); err != nil {
|
||||
warnEv.Err(err).Str("documentRoot", pf.DocumentRoot).
|
||||
Msg("Invalid document root")
|
||||
return err
|
||||
}
|
||||
if err = pf.validateDir(&pf.LogDir, "log directory"); err != nil {
|
||||
if err = pf.validatePath(&pf.LogDir, "log directory", true); err != nil {
|
||||
warnEv.Err(err).Str("logDir", pf.LogDir).
|
||||
Msg("Invalid log directory")
|
||||
return err
|
||||
|
@ -103,49 +103,69 @@ type dbForm struct {
|
|||
DBuser string `form:"dbuser,required,notempty" method:"POST"`
|
||||
DBpass string `form:"dbpass" method:"POST"`
|
||||
DBprefix string `form:"dbprefix" method:"POST"`
|
||||
|
||||
TimeoutSeconds int `form:"timeoutseconds,required" method:"POST"`
|
||||
MaxOpenConns int `form:"maxopenconns,required" method:"POST"`
|
||||
MaxIdleConns int `form:"maxidleconns,required" method:"POST"`
|
||||
ConnMaxLifetimeMin int `form:"connmaxlifetimemin,required" method:"POST"`
|
||||
}
|
||||
|
||||
func (dbf *dbForm) validate() (tablesExist bool, err error) {
|
||||
var connStr string
|
||||
var query string
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
switch dbf.DBtype {
|
||||
case "mysql":
|
||||
connStr = fmt.Sprintf(gcsql.MySQLConnStr, dbf.DBuser, dbf.DBpass, dbf.DBhost, dbf.DBname)
|
||||
query = `SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`
|
||||
case "postgres":
|
||||
connStr = fmt.Sprintf(gcsql.PostgresConnStr, dbf.DBuser, dbf.DBpass, dbf.DBhost, dbf.DBname)
|
||||
query = `SELECT COUNT(*) FROM information_schema.TABLES WHERE table_catalog = CURRENT_DATABASE() AND table_name = ?`
|
||||
case "sqlite3":
|
||||
connStr = fmt.Sprintf(gcsql.SQLite3ConnStr, dbf.DBhost, dbf.DBuser, dbf.DBpass)
|
||||
query = `SELECT COUNT(*) FROM sqlite_master WHERE name = ? AND type = 'table'`
|
||||
default:
|
||||
return false, gcsql.ErrUnsupportedDB
|
||||
func (dbf *dbForm) validate() (status dbStatus, err error) {
|
||||
if dbf.DBprefix == "" {
|
||||
return dbStatusNoPrefix, nil
|
||||
}
|
||||
db, err := sql.Open(dbf.DBtype, connStr)
|
||||
if dbf.TimeoutSeconds <= 0 {
|
||||
return dbStatusUnknown, errors.New("request timeout must be greater than 0")
|
||||
}
|
||||
if dbf.MaxOpenConns <= 0 {
|
||||
return dbStatusUnknown, errors.New("max open connections must be greater than 0")
|
||||
}
|
||||
if dbf.MaxIdleConns <= 0 {
|
||||
return dbStatusUnknown, errors.New("max idle connections must be greater than 0")
|
||||
}
|
||||
if dbf.ConnMaxLifetimeMin <= 0 {
|
||||
return dbStatusUnknown, errors.New("max lifetime for connections must be greater than 0")
|
||||
}
|
||||
|
||||
if err := gcsql.ConnectToDB(&config.SQLConfig{
|
||||
// using a dummy config to test connection. It will be set as the main config later
|
||||
DBtype: dbf.DBtype,
|
||||
DBhost: dbf.DBhost,
|
||||
DBname: dbf.DBname,
|
||||
DBusername: dbf.DBuser,
|
||||
DBpassword: dbf.DBpass,
|
||||
DBprefix: dbf.DBprefix,
|
||||
|
||||
DBTimeoutSeconds: dbf.TimeoutSeconds,
|
||||
DBMaxOpenConnections: dbf.MaxOpenConns,
|
||||
DBMaxIdleConnections: dbf.MaxIdleConns,
|
||||
DBConnMaxLifetimeMin: dbf.ConnMaxLifetimeMin,
|
||||
}); err != nil {
|
||||
return dbStatusUnknown, err
|
||||
}
|
||||
tablesExist, err := gcsql.DoesGochanPrefixTableExist()
|
||||
if err != nil {
|
||||
return false, err
|
||||
return dbStatusUnknown, err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
var count int
|
||||
stmt, err := db.PrepareContext(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
if err = stmt.QueryRowContext(ctx, dbf.DBprefix+"database_version").Scan(&count); err != nil {
|
||||
return false, err
|
||||
}
|
||||
tablesExist = count > 0
|
||||
if err = stmt.Close(); err != nil {
|
||||
return
|
||||
}
|
||||
if err = db.Close(); err != nil {
|
||||
return
|
||||
if tablesExist {
|
||||
status = dbStatusTablesExist
|
||||
} else {
|
||||
status = dbStatusClean
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type staffForm struct {
|
||||
Username string `form:"username,required,notempty" method:"POST"`
|
||||
Password string `form:"password,required,notempty" method:"POST"`
|
||||
ConfirmPassword string `form:"confirmpassword,required,notempty" method:"POST"`
|
||||
ToMisc string `form:"to-misc" method:"POST"`
|
||||
}
|
||||
|
||||
func (sf *staffForm) validate() (err error) {
|
||||
if sf.Password != sf.ConfirmPassword {
|
||||
return errors.New("passwords do not match")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"html/template"
|
||||
|
@ -19,6 +21,7 @@ import (
|
|||
"github.com/Eggbertx/go-forms"
|
||||
"github.com/gochan-org/gochan/pkg/building"
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
_ "github.com/gochan-org/gochan/pkg/gcsql/initsql"
|
||||
"github.com/gochan-org/gochan/pkg/gctemplates"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
|
@ -35,9 +38,35 @@ var (
|
|||
installTemplate *template.Template
|
||||
installServerStopper chan int
|
||||
|
||||
configDir string
|
||||
configPath string
|
||||
|
||||
currentDBStatus = dbStatusUnknown
|
||||
adminUser *gcsql.Staff
|
||||
cfg *config.GochanConfig = config.GetDefaultConfig()
|
||||
)
|
||||
|
||||
const (
|
||||
dbStatusUnknown dbStatus = iota
|
||||
dbStatusClean
|
||||
dbStatusNoPrefix
|
||||
dbStatusTablesExist
|
||||
)
|
||||
|
||||
type dbStatus int
|
||||
|
||||
func (dbs dbStatus) String() string {
|
||||
switch dbs {
|
||||
case dbStatusClean:
|
||||
return "The database does not appear to contain any Gochan tables. It will be provisioned in the next step."
|
||||
case dbStatusNoPrefix:
|
||||
return "Since no prefix was specified, the installer will attempt to provision the database in the next step."
|
||||
case dbStatusTablesExist:
|
||||
return fmt.Sprintf("The database appears to contain Gochan tables with the prefix %s. The next step (database provisioning) may return errors", config.GetSystemCriticalConfig().DBprefix)
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
|
||||
|
@ -45,14 +74,12 @@ func main() {
|
|||
infoEv := gcutil.LogInfo()
|
||||
defer gcutil.LogDiscard(infoEv, fatalEv)
|
||||
|
||||
workingConfig := config.GetDefaultConfig()
|
||||
|
||||
flag.StringVar(&workingConfig.SiteHost, "host", "127.0.0.1", "Host to listen on")
|
||||
flag.IntVar(&workingConfig.Port, "port", 0, "Port to bind to (REQUIRED)")
|
||||
flag.BoolVar(&workingConfig.UseFastCGI, "fastcgi", false, "Use FastCGI instead of HTTP")
|
||||
flag.StringVar(&workingConfig.WebRoot, "webroot", "/", "Web root path")
|
||||
flag.StringVar(&workingConfig.TemplateDir, "template-dir", "", "Template directory (REQUIRED)")
|
||||
flag.StringVar(&workingConfig.DocumentRoot, "document-root", "", "Document root directory (REQUIRED)")
|
||||
flag.StringVar(&cfg.ListenAddress, "host", "127.0.0.1", "Host to listen on")
|
||||
flag.IntVar(&cfg.Port, "port", 0, "Port to bind to (REQUIRED)")
|
||||
flag.BoolVar(&cfg.UseFastCGI, "fastcgi", false, "Use FastCGI instead of HTTP")
|
||||
flag.StringVar(&cfg.WebRoot, "webroot", "/", "Web root path")
|
||||
flag.StringVar(&cfg.TemplateDir, "template-dir", "", "Template directory (REQUIRED)")
|
||||
flag.StringVar(&cfg.DocumentRoot, "document-root", "", "Document root directory (REQUIRED)")
|
||||
flag.Parse()
|
||||
|
||||
if jsonPath := config.GetGochanJSONPath(); jsonPath != "" {
|
||||
|
@ -61,8 +88,8 @@ func main() {
|
|||
os.Exit(0)
|
||||
}
|
||||
|
||||
config.SetSiteConfig(&workingConfig.SiteConfig)
|
||||
config.SetSystemCriticalConfig(&workingConfig.SystemCriticalConfig)
|
||||
config.SetSiteConfig(&cfg.SiteConfig)
|
||||
config.SetSystemCriticalConfig(&cfg.SystemCriticalConfig)
|
||||
|
||||
systemCriticalConfig := config.GetSystemCriticalConfig()
|
||||
|
||||
|
@ -75,13 +102,13 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
listenAddr := net.JoinHostPort(workingConfig.SiteHost, strconv.Itoa(workingConfig.Port))
|
||||
listenAddr := net.JoinHostPort(cfg.ListenAddress, strconv.Itoa(cfg.Port))
|
||||
|
||||
router := server.GetRouter()
|
||||
router.GET(path.Join(workingConfig.WebRoot, "/install"), installHandler)
|
||||
router.POST(path.Join(workingConfig.WebRoot, "/install/:page"), installHandler)
|
||||
router.GET(path.Join(cfg.WebRoot, "/install"), installHandler)
|
||||
router.POST(path.Join(cfg.WebRoot, "/install/:page"), installHandler)
|
||||
|
||||
if workingConfig.DocumentRoot == "" {
|
||||
if cfg.DocumentRoot == "" {
|
||||
fatalEv.Msg("-document-root command line argument is required")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@ -97,7 +124,7 @@ func main() {
|
|||
}
|
||||
}()
|
||||
infoEv.Str("listenAddr", listenAddr).Msg("Starting installer server")
|
||||
if workingConfig.UseFastCGI {
|
||||
if cfg.UseFastCGI {
|
||||
listener, err = net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
fatalEv.Err(err).Caller().Msg("Failed listening on address/port")
|
||||
|
@ -161,13 +188,12 @@ func installHandler(writer http.ResponseWriter, req bunrouter.Request) (err erro
|
|||
}()
|
||||
var pageTitle string
|
||||
page := req.Param("page")
|
||||
systemCriticalConfig := config.GetSystemCriticalConfig()
|
||||
data := map[string]any{
|
||||
"page": page,
|
||||
"systemCriticalConfig": systemCriticalConfig,
|
||||
"siteConfig": config.GetSiteConfig(),
|
||||
"nextButton": "Next",
|
||||
"page": page,
|
||||
"config": cfg,
|
||||
"nextButton": "Next",
|
||||
}
|
||||
|
||||
var stopServer bool
|
||||
switch page {
|
||||
case "":
|
||||
|
@ -179,6 +205,7 @@ func installHandler(writer http.ResponseWriter, req bunrouter.Request) (err erro
|
|||
data["nextPage"] = "paths"
|
||||
case "paths":
|
||||
pageTitle = "Paths"
|
||||
data["cfgPaths"] = cfgPaths
|
||||
data["nextPage"] = "database"
|
||||
case "database":
|
||||
var pathFormData pathsForm
|
||||
|
@ -191,12 +218,12 @@ func installHandler(writer http.ResponseWriter, req bunrouter.Request) (err erro
|
|||
httpStatus = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
configDir = pathFormData.ConfigDir
|
||||
systemCriticalConfig.DocumentRoot = pathFormData.DocumentRoot
|
||||
systemCriticalConfig.LogDir = pathFormData.LogDir
|
||||
systemCriticalConfig.TemplateDir = pathFormData.TemplateDir
|
||||
systemCriticalConfig.WebRoot = pathFormData.WebRoot
|
||||
config.SetSystemCriticalConfig(systemCriticalConfig)
|
||||
configPath = pathFormData.ConfigPath
|
||||
cfg.DocumentRoot = pathFormData.DocumentRoot
|
||||
cfg.LogDir = pathFormData.LogDir
|
||||
cfg.TemplateDir = pathFormData.TemplateDir
|
||||
cfg.WebRoot = pathFormData.WebRoot
|
||||
config.SetSystemCriticalConfig(&cfg.SystemCriticalConfig)
|
||||
|
||||
pageTitle = "Database Setup"
|
||||
data["nextPage"] = "dbtest"
|
||||
|
@ -209,33 +236,84 @@ func installHandler(writer http.ResponseWriter, req bunrouter.Request) (err erro
|
|||
errEv.Err(err).Msg("Failed to fill form data")
|
||||
return
|
||||
}
|
||||
var tablesExist bool
|
||||
if tablesExist, err = dbFormData.validate(); err != nil {
|
||||
if currentDBStatus, err = dbFormData.validate(); err != nil {
|
||||
httpStatus = http.StatusBadRequest
|
||||
errEv.Err(err).Msg("Database test failed")
|
||||
return err
|
||||
}
|
||||
data["tablesExist"] = tablesExist
|
||||
if tablesExist {
|
||||
data["testResult"] = fmt.Sprintf(
|
||||
"Database connection was successful but the database appears to contain Gochan tables (found %sdatabase_version). "+
|
||||
"Press Next to continue to use this database or your browser's back button to change the database settings.",
|
||||
dbFormData.DBprefix)
|
||||
warnEv.Str("dbprefix", dbFormData.DBprefix).Str("dbname", dbFormData.DBname).
|
||||
Msg("Database test successful but tables already exist")
|
||||
} else {
|
||||
data["testResult"] = "Database connection was successful. Press Next to continue."
|
||||
|
||||
data["testResult"] = currentDBStatus.String()
|
||||
cfg.DBtype = dbFormData.DBtype
|
||||
cfg.DBhost = dbFormData.DBhost
|
||||
cfg.DBname = dbFormData.DBname
|
||||
cfg.DBusername = dbFormData.DBuser
|
||||
cfg.DBpassword = dbFormData.DBpass
|
||||
cfg.DBprefix = dbFormData.DBprefix
|
||||
config.SetSystemCriticalConfig(&cfg.SystemCriticalConfig)
|
||||
|
||||
data["nextPage"] = "staff"
|
||||
case "staff":
|
||||
pageTitle = "Create Administrator Account"
|
||||
|
||||
if adminUser != nil {
|
||||
data["nextButton"] = "Next"
|
||||
data["alreadyCreated"] = true
|
||||
break
|
||||
}
|
||||
systemCriticalConfig.DBtype = dbFormData.DBtype
|
||||
systemCriticalConfig.DBhost = dbFormData.DBhost
|
||||
systemCriticalConfig.DBname = dbFormData.DBname
|
||||
systemCriticalConfig.DBusername = dbFormData.DBuser
|
||||
systemCriticalConfig.DBpassword = dbFormData.DBpass
|
||||
systemCriticalConfig.DBprefix = dbFormData.DBprefix
|
||||
config.SetSystemCriticalConfig(systemCriticalConfig)
|
||||
data["nextPage"] = "install"
|
||||
case "stop":
|
||||
|
||||
// staff not created yet, show new admin form
|
||||
if currentDBStatus == dbStatusUnknown {
|
||||
httpStatus = http.StatusBadRequest
|
||||
errEv.Msg("Database status is unknown, cannot proceed with provisioning")
|
||||
return errors.New("database status is unknown, cannot proceed with provisioning")
|
||||
}
|
||||
|
||||
err := gcsql.CheckAndInitializeDatabase(cfg.DBtype, false)
|
||||
if err != nil {
|
||||
errEv.Err(err).Msg("Failed to initialize database")
|
||||
httpStatus = http.StatusInternalServerError
|
||||
return err
|
||||
}
|
||||
|
||||
data["nextPage"] = "pre-save"
|
||||
case "pre-save":
|
||||
pageTitle = "Configuration Confirmation"
|
||||
|
||||
if configPath == "" {
|
||||
httpStatus = http.StatusBadRequest
|
||||
errEv.Msg("Configuration path is not set")
|
||||
return errors.New("configuration path is not set")
|
||||
}
|
||||
|
||||
var jsonBuf bytes.Buffer
|
||||
encoder := json.NewEncoder(&jsonBuf)
|
||||
encoder.SetIndent("", " ")
|
||||
if err = encoder.Encode(cfg); err != nil {
|
||||
httpStatus = http.StatusInternalServerError
|
||||
errEv.Err(err).Msg("Failed to encode configuration to JSON")
|
||||
return err
|
||||
}
|
||||
data["configJSON"] = jsonBuf.String()
|
||||
data["configPath"] = configPath
|
||||
data["nextButton"] = "Save"
|
||||
data["nextPage"] = "save"
|
||||
case "save":
|
||||
pageTitle = "Save Configuration"
|
||||
if configPath == "" {
|
||||
httpStatus = http.StatusBadRequest
|
||||
errEv.Msg("Configuration path is not set")
|
||||
return errors.New("configuration path is not set")
|
||||
}
|
||||
|
||||
if err = config.WriteConfig(configPath); err != nil {
|
||||
httpStatus = http.StatusInternalServerError
|
||||
errEv.Err(err).Msg("Failed to write configuration")
|
||||
return err
|
||||
}
|
||||
infoEv.Str("configPath", configPath).Msg("Configuration written successfully")
|
||||
|
||||
stopServer = true
|
||||
data["nextPage"] = "done"
|
||||
default:
|
||||
httpStatus = http.StatusNotFound
|
||||
pageTitle = "Page Not Found"
|
||||
|
|
9
cmd/gochan-installer/paths_darwin.go
Normal file
9
cmd/gochan-installer/paths_darwin.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
//go:build darwin
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/gochan-org/gochan/pkg/config"
|
||||
|
||||
var (
|
||||
cfgPaths = config.StandardConfigSearchPaths
|
||||
)
|
20
cmd/gochan-installer/paths_unix.go
Normal file
20
cmd/gochan-installer/paths_unix.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
//go:build !darwin && !windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
)
|
||||
|
||||
var (
|
||||
cfgPaths = slices.DeleteFunc(config.StandardConfigSearchPaths, func(s string) bool {
|
||||
return s == "/opt/homebrew/etc/gochan/gochan.json"
|
||||
}) // Exclude Homebrew path on non-macOS systems
|
||||
|
||||
)
|
||||
|
||||
func init() {
|
||||
slices.Reverse(cfgPaths) // /etc/gochan/gochan.json should be first on *nix systems
|
||||
}
|
7
cmd/gochan-installer/paths_windows.go
Normal file
7
cmd/gochan-installer/paths_windows.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
//go:build windows
|
||||
|
||||
package main
|
||||
|
||||
var (
|
||||
cfgPaths []string = nil
|
||||
)
|
|
@ -85,7 +85,7 @@ func (m *Pre2021Migrator) renameTablesForInPlace() error {
|
|||
}
|
||||
}
|
||||
|
||||
if err = gcsql.CheckAndInitializeDatabase(m.config.DBtype); err != nil {
|
||||
if err = gcsql.CheckAndInitializeDatabase(m.config.DBtype, true); err != nil {
|
||||
errEv.Caller().Err(err).Msg("Error checking and initializing database")
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ func setupMigrationTest(t *testing.T, outDir string, migrateInPlace bool) *Pre20
|
|||
t.FailNow()
|
||||
}
|
||||
if !migrateInPlace {
|
||||
if !assert.NoError(t, gcsql.CheckAndInitializeDatabase("sqlite3")) {
|
||||
if !assert.NoError(t, gcsql.CheckAndInitializeDatabase("sqlite3", true)) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ func main() {
|
|||
if err != nil {
|
||||
fatalEv.Err(err).Caller().Msg("Failed to connect to the database")
|
||||
}
|
||||
if err = gcsql.CheckAndInitializeDatabase(sqlCfg.DBtype); err != nil {
|
||||
if err = gcsql.CheckAndInitializeDatabase(sqlCfg.DBtype, true); err != nil {
|
||||
fatalEv.Err(err).Caller().Msg("Unable to initialize the database")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ func initDB(fatalEv *zerolog.Event, commandLine ...bool) {
|
|||
Str("DBhost", systemCritical.DBhost).
|
||||
Msg("Connected to database")
|
||||
|
||||
if err := gcsql.CheckAndInitializeDatabase(systemCritical.DBtype); err != nil {
|
||||
if err := gcsql.CheckAndInitializeDatabase(systemCritical.DBtype, true); err != nil {
|
||||
cleanup()
|
||||
if len(commandLine) > 0 && commandLine[0] {
|
||||
fmt.Fprintln(os.Stderr, "Failed to initialize the database:", err)
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
max-width: 80%;
|
||||
padding: 1em 0;
|
||||
|
||||
textarea.license {
|
||||
.center-margin-50 {
|
||||
margin: 0 auto;
|
||||
width: 50%;
|
||||
height: 16em;
|
||||
margin: 1em auto;
|
||||
display: block;
|
||||
|
||||
height: 20em;
|
||||
display:block;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
|
@ -19,4 +18,8 @@
|
|||
table {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
input.path, select.path {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
|
@ -691,10 +691,10 @@ img#banpage-image {
|
|||
max-width: 80%;
|
||||
padding: 1em 0;
|
||||
}
|
||||
.install-container textarea.license {
|
||||
.install-container .center-margin-50 {
|
||||
margin: 0 auto;
|
||||
width: 50%;
|
||||
height: 16em;
|
||||
margin: 1em auto;
|
||||
height: 20em;
|
||||
display: block;
|
||||
}
|
||||
.install-container .buttons {
|
||||
|
@ -705,6 +705,9 @@ img#banpage-image {
|
|||
.install-container table {
|
||||
margin: 0 auto;
|
||||
}
|
||||
.install-container input.path, .install-container select.path {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.increase-line-height header, .increase-line-height .post, .increase-line-height .reply {
|
||||
line-height: 1.5;
|
||||
|
|
|
@ -873,7 +873,16 @@ func GetDefaultConfig() *GochanConfig {
|
|||
return defaultGochanConfig
|
||||
}
|
||||
|
||||
func WriteConfig() error {
|
||||
func WriteConfig(path ...string) error {
|
||||
if cfg == nil {
|
||||
return errors.New("configuration not loaded")
|
||||
}
|
||||
if len(path) > 0 {
|
||||
cfg.jsonLocation = path[0]
|
||||
}
|
||||
if cfg.jsonLocation == "" {
|
||||
return errors.New("configuration file path not set")
|
||||
}
|
||||
return cfg.Write()
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ const (
|
|||
var (
|
||||
uid int
|
||||
gid int
|
||||
standardConfigSearchPaths = []string{"gochan.json", "/usr/local/etc/gochan/gochan.json", "/opt/homebrew/etc/gochan/gochan.json", "/etc/gochan/gochan.json"}
|
||||
StandardConfigSearchPaths = []string{"gochan.json", "/usr/local/etc/gochan/gochan.json", "/opt/homebrew/etc/gochan/gochan.json", "/etc/gochan/gochan.json"}
|
||||
|
||||
initialSetupStatus InitialSetupStatus = InitialSetupStatusUnknown
|
||||
)
|
||||
|
@ -78,7 +78,7 @@ func GetGochanJSONPath() string {
|
|||
if jsonPath != "" {
|
||||
return jsonPath
|
||||
}
|
||||
return gcutil.FindResource(standardConfigSearchPaths...)
|
||||
return gcutil.FindResource(StandardConfigSearchPaths...)
|
||||
}
|
||||
|
||||
// GetUser returns the IDs of the user and group gochan should be acting as
|
||||
|
@ -142,7 +142,7 @@ func loadConfig() (err error) {
|
|||
}
|
||||
return
|
||||
}
|
||||
cfgPath = gcutil.FindResource(standardConfigSearchPaths...)
|
||||
cfgPath = gcutil.FindResource(StandardConfigSearchPaths...)
|
||||
if cfgPath == "" {
|
||||
return errors.New("gochan.json not found")
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ func GetCompleteDatabaseVersion() (dbVersion, dbFlag int, err error) {
|
|||
}
|
||||
|
||||
// CheckAndInitializeDatabase checks the validity of the database and initialises it if it is empty
|
||||
func CheckAndInitializeDatabase(dbType string) (err error) {
|
||||
func CheckAndInitializeDatabase(dbType string, withAdmin bool) (err error) {
|
||||
dbVersion, versionFlag, err := GetCompleteDatabaseVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -102,7 +102,7 @@ func CheckAndInitializeDatabase(dbType string) (err error) {
|
|||
case DBModernButBehind:
|
||||
err = ErrDeprecatedDB
|
||||
case DBClean:
|
||||
err = buildNewDatabase(dbType)
|
||||
err = buildNewDatabase(dbType, withAdmin)
|
||||
case DBUpToDate:
|
||||
err = nil
|
||||
case DBCorrupted:
|
||||
|
@ -116,14 +116,17 @@ func CheckAndInitializeDatabase(dbType string) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
func buildNewDatabase(dbType string) error {
|
||||
func buildNewDatabase(dbType string, withAdmin bool) error {
|
||||
var err error
|
||||
if err = initDB("initdb_" + dbType + ".sql"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = createDefaultAdminIfNoStaff(); err != nil {
|
||||
return fmt.Errorf("failed creating default admin account: %w", err)
|
||||
if withAdmin {
|
||||
if err = createDefaultAdminIfNoStaff(); err != nil {
|
||||
return fmt.Errorf("failed creating default admin account: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = createDefaultBoardIfNoneExist(); err != nil {
|
||||
return fmt.Errorf("failed creating default board if non already exists: %w", err)
|
||||
}
|
||||
|
|
|
@ -177,7 +177,7 @@ func setupAndProvisionMockDB(t *testing.T, mock sqlmock.Sqlmock, dbType string,
|
|||
return err
|
||||
}
|
||||
|
||||
if err = buildNewDatabase(dbType); err != nil {
|
||||
if err = buildNewDatabase(dbType, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -42,10 +42,10 @@ CREATE TABLE DBPREFIXboards(
|
|||
redirect_to_thread BOOL NOT NULL,
|
||||
require_file BOOL NOT NULL,
|
||||
enable_catalog BOOL NOT NULL,
|
||||
CONSTRAINT boards_section_id_fk
|
||||
CONSTRAINT DBPREFIXboards_section_id_fk
|
||||
FOREIGN KEY(section_id) REFERENCES DBPREFIXsections(id),
|
||||
CONSTRAINT boards_dir_unique UNIQUE(dir),
|
||||
CONSTRAINT boards_uri_unique UNIQUE(uri)
|
||||
CONSTRAINT DBPREFIXboards_dir_unique UNIQUE(dir),
|
||||
CONSTRAINT DBPREFIXboards_uri_unique UNIQUE(uri)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXthreads(
|
||||
|
@ -59,7 +59,7 @@ CREATE TABLE DBPREFIXthreads(
|
|||
last_bump TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
is_deleted BOOL NOT NULL DEFAULT FALSE,
|
||||
CONSTRAINT threads_board_id_fk
|
||||
CONSTRAINT DBPREFIXthreads_board_id_fk
|
||||
FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -85,7 +85,7 @@ CREATE TABLE DBPREFIXposts(
|
|||
banned_message TEXT,
|
||||
flag VARCHAR(45) NOT NULL DEFAULT '',
|
||||
country VARCHAR(80) NOT NULL DEFAULT '',
|
||||
CONSTRAINT posts_thread_id_fk
|
||||
CONSTRAINT DBPREFIXposts_thread_id_fk
|
||||
FOREIGN KEY(thread_id) REFERENCES DBPREFIXthreads(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -104,9 +104,9 @@ CREATE TABLE DBPREFIXfiles(
|
|||
thumbnail_height INT NOT NULL,
|
||||
width INT NOT NULL,
|
||||
height INT NOT NULL,
|
||||
CONSTRAINT files_post_id_fk
|
||||
CONSTRAINT DBPREFIXfiles_post_id_fk
|
||||
FOREIGN KEY(post_id) REFERENCES DBPREFIXposts(id) ON DELETE CASCADE,
|
||||
CONSTRAINT files_post_id_file_order_unique UNIQUE(post_id, file_order)
|
||||
CONSTRAINT DBPREFIXfiles_post_id_file_order_unique UNIQUE(post_id, file_order)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXstaff(
|
||||
|
@ -117,7 +117,7 @@ CREATE TABLE DBPREFIXstaff(
|
|||
added_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
last_login TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
is_active BOOL NOT NULL DEFAULT TRUE,
|
||||
CONSTRAINT staff_username_unique UNIQUE(username)
|
||||
CONSTRAINT DBPREFIXstaff_username_unique UNIQUE(username)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXsessions(
|
||||
|
@ -125,16 +125,16 @@ CREATE TABLE DBPREFIXsessions(
|
|||
staff_id {fk to serial} NOT NULL,
|
||||
expires TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
data VARCHAR(45) NOT NULL,
|
||||
CONSTRAINT sessions_staff_id_fk
|
||||
CONSTRAINT DBPREFIXsessions_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXboard_staff(
|
||||
board_id {fk to serial} NOT NULL,
|
||||
staff_id {fk to serial} NOT NULL,
|
||||
CONSTRAINT board_staff_board_id_fk
|
||||
CONSTRAINT DBPREFIXboard_staff_board_id_fk
|
||||
FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE,
|
||||
CONSTRAINT board_staff_staff_id_fk
|
||||
CONSTRAINT DBPREFIXboard_staff_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) ON DELETE CASCADE,
|
||||
CONSTRAINT board_staff_pk PRIMARY KEY (board_id,staff_id)
|
||||
);
|
||||
|
@ -145,7 +145,7 @@ CREATE TABLE DBPREFIXannouncements(
|
|||
subject VARCHAR(45) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT announcements_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id)
|
||||
CONSTRAINT DBPREFIXannouncements_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXip_ban(
|
||||
|
@ -165,11 +165,11 @@ CREATE TABLE DBPREFIXip_ban(
|
|||
staff_note VARCHAR(255) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
can_appeal BOOL NOT NULL,
|
||||
CONSTRAINT ip_ban_board_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_board_id_fk
|
||||
FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE,
|
||||
CONSTRAINT ip_ban_staff_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT ip_ban_banned_for_post_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_banned_for_post_id_fk
|
||||
FOREIGN KEY(banned_for_post_id) REFERENCES DBPREFIXposts(id)
|
||||
ON DELETE SET NULL
|
||||
);
|
||||
|
@ -187,9 +187,9 @@ CREATE TABLE DBPREFIXip_ban_audit(
|
|||
message TEXT NOT NULL,
|
||||
can_appeal BOOL NOT NULL,
|
||||
PRIMARY KEY(ip_ban_id, timestamp),
|
||||
CONSTRAINT ip_ban_audit_ip_ban_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_audit_ip_ban_id_fk
|
||||
FOREIGN KEY(ip_ban_id) REFERENCES DBPREFIXip_ban(id) ON DELETE CASCADE,
|
||||
CONSTRAINT ip_ban_audit_staff_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_audit_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id)
|
||||
);
|
||||
|
||||
|
@ -200,9 +200,9 @@ CREATE TABLE DBPREFIXip_ban_appeals(
|
|||
appeal_text TEXT NOT NULL,
|
||||
staff_response TEXT,
|
||||
is_denied BOOL NOT NULL,
|
||||
CONSTRAINT ip_ban_appeals_staff_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_appeals_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT ip_ban_appeals_ip_ban_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_appeals_ip_ban_id_fk
|
||||
FOREIGN KEY(ip_ban_id) REFERENCES DBPREFIXip_ban(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -214,9 +214,9 @@ CREATE TABLE DBPREFIXip_ban_appeals_audit(
|
|||
staff_response TEXT,
|
||||
is_denied BOOL NOT NULL,
|
||||
PRIMARY KEY(appeal_id, timestamp),
|
||||
CONSTRAINT ip_ban_appeals_audit_staff_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_appeals_audit_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT ip_ban_appeals_audit_appeal_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_appeals_audit_appeal_id_fk
|
||||
FOREIGN KEY(appeal_id) REFERENCES DBPREFIXip_ban_appeals(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
@ -228,9 +228,9 @@ CREATE TABLE DBPREFIXreports(
|
|||
ip {inet} NOT NULL,
|
||||
reason TEXT NOT NULL,
|
||||
is_cleared BOOL NOT NULL,
|
||||
CONSTRAINT reports_handled_by_staff_id_fk
|
||||
CONSTRAINT DBPREFIXreports_handled_by_staff_id_fk
|
||||
FOREIGN KEY(handled_by_staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT reports_post_id_fk
|
||||
CONSTRAINT DBPREFIXreports_post_id_fk
|
||||
FOREIGN KEY(post_id) REFERENCES DBPREFIXposts(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -239,9 +239,9 @@ CREATE TABLE DBPREFIXreports_audit(
|
|||
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
handled_by_staff_id {fk to serial},
|
||||
is_cleared BOOL NOT NULL,
|
||||
CONSTRAINT reports_audit_handled_by_staff_id_fk
|
||||
CONSTRAINT DBPREFIXreports_audit_handled_by_staff_id_fk
|
||||
FOREIGN KEY(handled_by_staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT reports_audit_report_id_fk
|
||||
CONSTRAINT DBPREFIXreports_audit_report_id_fk
|
||||
FOREIGN KEY(report_id) REFERENCES DBPREFIXreports(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -254,7 +254,7 @@ CREATE TABLE DBPREFIXfilters(
|
|||
match_detail TEXT NOT NULL,
|
||||
handle_if_any BOOL NOT NULL DEFAULT FALSE,
|
||||
is_active BOOL NOT NULL,
|
||||
CONSTRAINT filters_staff_id_fk
|
||||
CONSTRAINT DBPREFIXfilters_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id)
|
||||
ON DELETE SET NULL
|
||||
);
|
||||
|
@ -263,10 +263,10 @@ CREATE TABLE DBPREFIXfilter_boards(
|
|||
id {serial pk},
|
||||
filter_id {fk to serial} NOT NULL,
|
||||
board_id {fk to serial} NOT NULL,
|
||||
CONSTRAINT filter_boards_filter_id_fk
|
||||
CONSTRAINT DBPREFIXfilter_boards_filter_id_fk
|
||||
FOREIGN KEY(filter_id) REFERENCES DBPREFIXfilters(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT filter_boards_board_id_fk
|
||||
CONSTRAINT DBPREFIXfilter_boards_board_id_fk
|
||||
FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
@ -277,10 +277,11 @@ CREATE TABLE DBPREFIXfilter_conditions(
|
|||
match_mode SMALLINT NOT NULL,
|
||||
search VARCHAR(75) NOT NULL,
|
||||
field VARCHAR(75) NOT NULL,
|
||||
CONSTRAINT filter_conditions_filter_id_fk
|
||||
CONSTRAINT DBPREFIXfilter_conditions_filter_id_fk
|
||||
FOREIGN KEY(filter_id) REFERENCES DBPREFIXfilters(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT filter_conditions_search_check CHECK (search <> '' OR match_mode = 3)
|
||||
CONSTRAINT DBPREFIXfilter_conditions_search_check
|
||||
CHECK (search <> '' OR match_mode = 3)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXfilter_hits(
|
||||
|
@ -288,7 +289,7 @@ CREATE TABLE DBPREFIXfilter_hits(
|
|||
filter_id {fk to serial} NOT NULL,
|
||||
post_data TEXT NOT NULL,
|
||||
match_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT filter_hits_filter_id_fk
|
||||
CONSTRAINT DBPREFIXfilter_hits_filter_id_fk
|
||||
FOREIGN KEY(filter_id) REFERENCES DBPREFIXfilters(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
|
|
@ -35,10 +35,10 @@ CREATE TABLE DBPREFIXboards(
|
|||
redirect_to_thread BOOL NOT NULL,
|
||||
require_file BOOL NOT NULL,
|
||||
enable_catalog BOOL NOT NULL,
|
||||
CONSTRAINT boards_section_id_fk
|
||||
CONSTRAINT DBPREFIXboards_section_id_fk
|
||||
FOREIGN KEY(section_id) REFERENCES DBPREFIXsections(id),
|
||||
CONSTRAINT boards_dir_unique UNIQUE(dir),
|
||||
CONSTRAINT boards_uri_unique UNIQUE(uri)
|
||||
CONSTRAINT DBPREFIXboards_dir_unique UNIQUE(dir),
|
||||
CONSTRAINT DBPREFIXboards_uri_unique UNIQUE(uri)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXthreads(
|
||||
|
@ -52,7 +52,7 @@ CREATE TABLE DBPREFIXthreads(
|
|||
last_bump TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
is_deleted BOOL NOT NULL DEFAULT FALSE,
|
||||
CONSTRAINT threads_board_id_fk
|
||||
CONSTRAINT DBPREFIXthreads_board_id_fk
|
||||
FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -78,7 +78,7 @@ CREATE TABLE DBPREFIXposts(
|
|||
banned_message TEXT,
|
||||
flag VARCHAR(45) NOT NULL DEFAULT '',
|
||||
country VARCHAR(80) NOT NULL DEFAULT '',
|
||||
CONSTRAINT posts_thread_id_fk
|
||||
CONSTRAINT DBPREFIXposts_thread_id_fk
|
||||
FOREIGN KEY(thread_id) REFERENCES DBPREFIXthreads(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -97,9 +97,9 @@ CREATE TABLE DBPREFIXfiles(
|
|||
thumbnail_height INT NOT NULL,
|
||||
width INT NOT NULL,
|
||||
height INT NOT NULL,
|
||||
CONSTRAINT files_post_id_fk
|
||||
CONSTRAINT DBPREFIXfiles_post_id_fk
|
||||
FOREIGN KEY(post_id) REFERENCES DBPREFIXposts(id) ON DELETE CASCADE,
|
||||
CONSTRAINT files_post_id_file_order_unique UNIQUE(post_id, file_order)
|
||||
CONSTRAINT DBPREFIXfiles_post_id_file_order_unique UNIQUE(post_id, file_order)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXstaff(
|
||||
|
@ -110,7 +110,7 @@ CREATE TABLE DBPREFIXstaff(
|
|||
added_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
last_login TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
is_active BOOL NOT NULL DEFAULT TRUE,
|
||||
CONSTRAINT staff_username_unique UNIQUE(username)
|
||||
CONSTRAINT DBPREFIXstaff_username_unique UNIQUE(username)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXsessions(
|
||||
|
@ -118,16 +118,16 @@ CREATE TABLE DBPREFIXsessions(
|
|||
staff_id BIGINT NOT NULL,
|
||||
expires TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
data VARCHAR(45) NOT NULL,
|
||||
CONSTRAINT sessions_staff_id_fk
|
||||
CONSTRAINT DBPREFIXsessions_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXboard_staff(
|
||||
board_id BIGINT NOT NULL,
|
||||
staff_id BIGINT NOT NULL,
|
||||
CONSTRAINT board_staff_board_id_fk
|
||||
CONSTRAINT DBPREFIXboard_staff_board_id_fk
|
||||
FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE,
|
||||
CONSTRAINT board_staff_staff_id_fk
|
||||
CONSTRAINT DBPREFIXboard_staff_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) ON DELETE CASCADE,
|
||||
CONSTRAINT board_staff_pk PRIMARY KEY (board_id,staff_id)
|
||||
);
|
||||
|
@ -138,7 +138,7 @@ CREATE TABLE DBPREFIXannouncements(
|
|||
subject VARCHAR(45) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT announcements_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id)
|
||||
CONSTRAINT DBPREFIXannouncements_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXip_ban(
|
||||
|
@ -158,11 +158,11 @@ CREATE TABLE DBPREFIXip_ban(
|
|||
staff_note VARCHAR(255) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
can_appeal BOOL NOT NULL,
|
||||
CONSTRAINT ip_ban_board_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_board_id_fk
|
||||
FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE,
|
||||
CONSTRAINT ip_ban_staff_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT ip_ban_banned_for_post_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_banned_for_post_id_fk
|
||||
FOREIGN KEY(banned_for_post_id) REFERENCES DBPREFIXposts(id)
|
||||
ON DELETE SET NULL
|
||||
);
|
||||
|
@ -180,9 +180,9 @@ CREATE TABLE DBPREFIXip_ban_audit(
|
|||
message TEXT NOT NULL,
|
||||
can_appeal BOOL NOT NULL,
|
||||
PRIMARY KEY(ip_ban_id, timestamp),
|
||||
CONSTRAINT ip_ban_audit_ip_ban_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_audit_ip_ban_id_fk
|
||||
FOREIGN KEY(ip_ban_id) REFERENCES DBPREFIXip_ban(id) ON DELETE CASCADE,
|
||||
CONSTRAINT ip_ban_audit_staff_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_audit_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id)
|
||||
);
|
||||
|
||||
|
@ -193,9 +193,9 @@ CREATE TABLE DBPREFIXip_ban_appeals(
|
|||
appeal_text TEXT NOT NULL,
|
||||
staff_response TEXT,
|
||||
is_denied BOOL NOT NULL,
|
||||
CONSTRAINT ip_ban_appeals_staff_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_appeals_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT ip_ban_appeals_ip_ban_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_appeals_ip_ban_id_fk
|
||||
FOREIGN KEY(ip_ban_id) REFERENCES DBPREFIXip_ban(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -207,9 +207,9 @@ CREATE TABLE DBPREFIXip_ban_appeals_audit(
|
|||
staff_response TEXT,
|
||||
is_denied BOOL NOT NULL,
|
||||
PRIMARY KEY(appeal_id, timestamp),
|
||||
CONSTRAINT ip_ban_appeals_audit_staff_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_appeals_audit_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT ip_ban_appeals_audit_appeal_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_appeals_audit_appeal_id_fk
|
||||
FOREIGN KEY(appeal_id) REFERENCES DBPREFIXip_ban_appeals(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
@ -221,9 +221,9 @@ CREATE TABLE DBPREFIXreports(
|
|||
ip VARBINARY(16) NOT NULL,
|
||||
reason TEXT NOT NULL,
|
||||
is_cleared BOOL NOT NULL,
|
||||
CONSTRAINT reports_handled_by_staff_id_fk
|
||||
CONSTRAINT DBPREFIXreports_handled_by_staff_id_fk
|
||||
FOREIGN KEY(handled_by_staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT reports_post_id_fk
|
||||
CONSTRAINT DBPREFIXreports_post_id_fk
|
||||
FOREIGN KEY(post_id) REFERENCES DBPREFIXposts(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -232,9 +232,9 @@ CREATE TABLE DBPREFIXreports_audit(
|
|||
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
handled_by_staff_id BIGINT,
|
||||
is_cleared BOOL NOT NULL,
|
||||
CONSTRAINT reports_audit_handled_by_staff_id_fk
|
||||
CONSTRAINT DBPREFIXreports_audit_handled_by_staff_id_fk
|
||||
FOREIGN KEY(handled_by_staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT reports_audit_report_id_fk
|
||||
CONSTRAINT DBPREFIXreports_audit_report_id_fk
|
||||
FOREIGN KEY(report_id) REFERENCES DBPREFIXreports(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -247,7 +247,7 @@ CREATE TABLE DBPREFIXfilters(
|
|||
match_detail TEXT NOT NULL,
|
||||
handle_if_any BOOL NOT NULL DEFAULT FALSE,
|
||||
is_active BOOL NOT NULL,
|
||||
CONSTRAINT filters_staff_id_fk
|
||||
CONSTRAINT DBPREFIXfilters_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id)
|
||||
ON DELETE SET NULL
|
||||
);
|
||||
|
@ -256,10 +256,10 @@ CREATE TABLE DBPREFIXfilter_boards(
|
|||
id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY,
|
||||
filter_id BIGINT NOT NULL,
|
||||
board_id BIGINT NOT NULL,
|
||||
CONSTRAINT filter_boards_filter_id_fk
|
||||
CONSTRAINT DBPREFIXfilter_boards_filter_id_fk
|
||||
FOREIGN KEY(filter_id) REFERENCES DBPREFIXfilters(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT filter_boards_board_id_fk
|
||||
CONSTRAINT DBPREFIXfilter_boards_board_id_fk
|
||||
FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
@ -270,10 +270,11 @@ CREATE TABLE DBPREFIXfilter_conditions(
|
|||
match_mode SMALLINT NOT NULL,
|
||||
search VARCHAR(75) NOT NULL,
|
||||
field VARCHAR(75) NOT NULL,
|
||||
CONSTRAINT filter_conditions_filter_id_fk
|
||||
CONSTRAINT DBPREFIXfilter_conditions_filter_id_fk
|
||||
FOREIGN KEY(filter_id) REFERENCES DBPREFIXfilters(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT filter_conditions_search_check CHECK (search <> '' OR match_mode = 3)
|
||||
CONSTRAINT DBPREFIXfilter_conditions_search_check
|
||||
CHECK (search <> '' OR match_mode = 3)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXfilter_hits(
|
||||
|
@ -281,7 +282,7 @@ CREATE TABLE DBPREFIXfilter_hits(
|
|||
filter_id BIGINT NOT NULL,
|
||||
post_data TEXT NOT NULL,
|
||||
match_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT filter_hits_filter_id_fk
|
||||
CONSTRAINT DBPREFIXfilter_hits_filter_id_fk
|
||||
FOREIGN KEY(filter_id) REFERENCES DBPREFIXfilters(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
|
|
@ -35,10 +35,10 @@ CREATE TABLE DBPREFIXboards(
|
|||
redirect_to_thread BOOL NOT NULL,
|
||||
require_file BOOL NOT NULL,
|
||||
enable_catalog BOOL NOT NULL,
|
||||
CONSTRAINT boards_section_id_fk
|
||||
CONSTRAINT DBPREFIXboards_section_id_fk
|
||||
FOREIGN KEY(section_id) REFERENCES DBPREFIXsections(id),
|
||||
CONSTRAINT boards_dir_unique UNIQUE(dir),
|
||||
CONSTRAINT boards_uri_unique UNIQUE(uri)
|
||||
CONSTRAINT DBPREFIXboards_dir_unique UNIQUE(dir),
|
||||
CONSTRAINT DBPREFIXboards_uri_unique UNIQUE(uri)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXthreads(
|
||||
|
@ -52,7 +52,7 @@ CREATE TABLE DBPREFIXthreads(
|
|||
last_bump TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
is_deleted BOOL NOT NULL DEFAULT FALSE,
|
||||
CONSTRAINT threads_board_id_fk
|
||||
CONSTRAINT DBPREFIXthreads_board_id_fk
|
||||
FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -78,7 +78,7 @@ CREATE TABLE DBPREFIXposts(
|
|||
banned_message TEXT,
|
||||
flag VARCHAR(45) NOT NULL DEFAULT '',
|
||||
country VARCHAR(80) NOT NULL DEFAULT '',
|
||||
CONSTRAINT posts_thread_id_fk
|
||||
CONSTRAINT DBPREFIXposts_thread_id_fk
|
||||
FOREIGN KEY(thread_id) REFERENCES DBPREFIXthreads(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -97,9 +97,9 @@ CREATE TABLE DBPREFIXfiles(
|
|||
thumbnail_height INT NOT NULL,
|
||||
width INT NOT NULL,
|
||||
height INT NOT NULL,
|
||||
CONSTRAINT files_post_id_fk
|
||||
CONSTRAINT DBPREFIXfiles_post_id_fk
|
||||
FOREIGN KEY(post_id) REFERENCES DBPREFIXposts(id) ON DELETE CASCADE,
|
||||
CONSTRAINT files_post_id_file_order_unique UNIQUE(post_id, file_order)
|
||||
CONSTRAINT DBPREFIXfiles_post_id_file_order_unique UNIQUE(post_id, file_order)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXstaff(
|
||||
|
@ -110,7 +110,7 @@ CREATE TABLE DBPREFIXstaff(
|
|||
added_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
last_login TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
is_active BOOL NOT NULL DEFAULT TRUE,
|
||||
CONSTRAINT staff_username_unique UNIQUE(username)
|
||||
CONSTRAINT DBPREFIXstaff_username_unique UNIQUE(username)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXsessions(
|
||||
|
@ -118,16 +118,16 @@ CREATE TABLE DBPREFIXsessions(
|
|||
staff_id BIGINT NOT NULL,
|
||||
expires TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
data VARCHAR(45) NOT NULL,
|
||||
CONSTRAINT sessions_staff_id_fk
|
||||
CONSTRAINT DBPREFIXsessions_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXboard_staff(
|
||||
board_id BIGINT NOT NULL,
|
||||
staff_id BIGINT NOT NULL,
|
||||
CONSTRAINT board_staff_board_id_fk
|
||||
CONSTRAINT DBPREFIXboard_staff_board_id_fk
|
||||
FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE,
|
||||
CONSTRAINT board_staff_staff_id_fk
|
||||
CONSTRAINT DBPREFIXboard_staff_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) ON DELETE CASCADE,
|
||||
CONSTRAINT board_staff_pk PRIMARY KEY (board_id,staff_id)
|
||||
);
|
||||
|
@ -138,7 +138,7 @@ CREATE TABLE DBPREFIXannouncements(
|
|||
subject VARCHAR(45) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT announcements_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id)
|
||||
CONSTRAINT DBPREFIXannouncements_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXip_ban(
|
||||
|
@ -158,11 +158,11 @@ CREATE TABLE DBPREFIXip_ban(
|
|||
staff_note VARCHAR(255) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
can_appeal BOOL NOT NULL,
|
||||
CONSTRAINT ip_ban_board_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_board_id_fk
|
||||
FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE,
|
||||
CONSTRAINT ip_ban_staff_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT ip_ban_banned_for_post_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_banned_for_post_id_fk
|
||||
FOREIGN KEY(banned_for_post_id) REFERENCES DBPREFIXposts(id)
|
||||
ON DELETE SET NULL
|
||||
);
|
||||
|
@ -180,9 +180,9 @@ CREATE TABLE DBPREFIXip_ban_audit(
|
|||
message TEXT NOT NULL,
|
||||
can_appeal BOOL NOT NULL,
|
||||
PRIMARY KEY(ip_ban_id, timestamp),
|
||||
CONSTRAINT ip_ban_audit_ip_ban_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_audit_ip_ban_id_fk
|
||||
FOREIGN KEY(ip_ban_id) REFERENCES DBPREFIXip_ban(id) ON DELETE CASCADE,
|
||||
CONSTRAINT ip_ban_audit_staff_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_audit_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id)
|
||||
);
|
||||
|
||||
|
@ -193,9 +193,9 @@ CREATE TABLE DBPREFIXip_ban_appeals(
|
|||
appeal_text TEXT NOT NULL,
|
||||
staff_response TEXT,
|
||||
is_denied BOOL NOT NULL,
|
||||
CONSTRAINT ip_ban_appeals_staff_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_appeals_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT ip_ban_appeals_ip_ban_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_appeals_ip_ban_id_fk
|
||||
FOREIGN KEY(ip_ban_id) REFERENCES DBPREFIXip_ban(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -207,9 +207,9 @@ CREATE TABLE DBPREFIXip_ban_appeals_audit(
|
|||
staff_response TEXT,
|
||||
is_denied BOOL NOT NULL,
|
||||
PRIMARY KEY(appeal_id, timestamp),
|
||||
CONSTRAINT ip_ban_appeals_audit_staff_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_appeals_audit_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT ip_ban_appeals_audit_appeal_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_appeals_audit_appeal_id_fk
|
||||
FOREIGN KEY(appeal_id) REFERENCES DBPREFIXip_ban_appeals(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
@ -221,9 +221,9 @@ CREATE TABLE DBPREFIXreports(
|
|||
ip INET NOT NULL,
|
||||
reason TEXT NOT NULL,
|
||||
is_cleared BOOL NOT NULL,
|
||||
CONSTRAINT reports_handled_by_staff_id_fk
|
||||
CONSTRAINT DBPREFIXreports_handled_by_staff_id_fk
|
||||
FOREIGN KEY(handled_by_staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT reports_post_id_fk
|
||||
CONSTRAINT DBPREFIXreports_post_id_fk
|
||||
FOREIGN KEY(post_id) REFERENCES DBPREFIXposts(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -232,9 +232,9 @@ CREATE TABLE DBPREFIXreports_audit(
|
|||
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
handled_by_staff_id BIGINT,
|
||||
is_cleared BOOL NOT NULL,
|
||||
CONSTRAINT reports_audit_handled_by_staff_id_fk
|
||||
CONSTRAINT DBPREFIXreports_audit_handled_by_staff_id_fk
|
||||
FOREIGN KEY(handled_by_staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT reports_audit_report_id_fk
|
||||
CONSTRAINT DBPREFIXreports_audit_report_id_fk
|
||||
FOREIGN KEY(report_id) REFERENCES DBPREFIXreports(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -247,7 +247,7 @@ CREATE TABLE DBPREFIXfilters(
|
|||
match_detail TEXT NOT NULL,
|
||||
handle_if_any BOOL NOT NULL DEFAULT FALSE,
|
||||
is_active BOOL NOT NULL,
|
||||
CONSTRAINT filters_staff_id_fk
|
||||
CONSTRAINT DBPREFIXfilters_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id)
|
||||
ON DELETE SET NULL
|
||||
);
|
||||
|
@ -256,10 +256,10 @@ CREATE TABLE DBPREFIXfilter_boards(
|
|||
id BIGSERIAL PRIMARY KEY,
|
||||
filter_id BIGINT NOT NULL,
|
||||
board_id BIGINT NOT NULL,
|
||||
CONSTRAINT filter_boards_filter_id_fk
|
||||
CONSTRAINT DBPREFIXfilter_boards_filter_id_fk
|
||||
FOREIGN KEY(filter_id) REFERENCES DBPREFIXfilters(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT filter_boards_board_id_fk
|
||||
CONSTRAINT DBPREFIXfilter_boards_board_id_fk
|
||||
FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
@ -270,10 +270,11 @@ CREATE TABLE DBPREFIXfilter_conditions(
|
|||
match_mode SMALLINT NOT NULL,
|
||||
search VARCHAR(75) NOT NULL,
|
||||
field VARCHAR(75) NOT NULL,
|
||||
CONSTRAINT filter_conditions_filter_id_fk
|
||||
CONSTRAINT DBPREFIXfilter_conditions_filter_id_fk
|
||||
FOREIGN KEY(filter_id) REFERENCES DBPREFIXfilters(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT filter_conditions_search_check CHECK (search <> '' OR match_mode = 3)
|
||||
CONSTRAINT DBPREFIXfilter_conditions_search_check
|
||||
CHECK (search <> '' OR match_mode = 3)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXfilter_hits(
|
||||
|
@ -281,7 +282,7 @@ CREATE TABLE DBPREFIXfilter_hits(
|
|||
filter_id BIGINT NOT NULL,
|
||||
post_data TEXT NOT NULL,
|
||||
match_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT filter_hits_filter_id_fk
|
||||
CONSTRAINT DBPREFIXfilter_hits_filter_id_fk
|
||||
FOREIGN KEY(filter_id) REFERENCES DBPREFIXfilters(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
|
|
@ -35,10 +35,10 @@ CREATE TABLE DBPREFIXboards(
|
|||
redirect_to_thread BOOL NOT NULL,
|
||||
require_file BOOL NOT NULL,
|
||||
enable_catalog BOOL NOT NULL,
|
||||
CONSTRAINT boards_section_id_fk
|
||||
CONSTRAINT DBPREFIXboards_section_id_fk
|
||||
FOREIGN KEY(section_id) REFERENCES DBPREFIXsections(id),
|
||||
CONSTRAINT boards_dir_unique UNIQUE(dir),
|
||||
CONSTRAINT boards_uri_unique UNIQUE(uri)
|
||||
CONSTRAINT DBPREFIXboards_dir_unique UNIQUE(dir),
|
||||
CONSTRAINT DBPREFIXboards_uri_unique UNIQUE(uri)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXthreads(
|
||||
|
@ -52,7 +52,7 @@ CREATE TABLE DBPREFIXthreads(
|
|||
last_bump TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
is_deleted BOOL NOT NULL DEFAULT FALSE,
|
||||
CONSTRAINT threads_board_id_fk
|
||||
CONSTRAINT DBPREFIXthreads_board_id_fk
|
||||
FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -78,7 +78,7 @@ CREATE TABLE DBPREFIXposts(
|
|||
banned_message TEXT,
|
||||
flag VARCHAR(45) NOT NULL DEFAULT '',
|
||||
country VARCHAR(80) NOT NULL DEFAULT '',
|
||||
CONSTRAINT posts_thread_id_fk
|
||||
CONSTRAINT DBPREFIXposts_thread_id_fk
|
||||
FOREIGN KEY(thread_id) REFERENCES DBPREFIXthreads(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -97,9 +97,9 @@ CREATE TABLE DBPREFIXfiles(
|
|||
thumbnail_height INT NOT NULL,
|
||||
width INT NOT NULL,
|
||||
height INT NOT NULL,
|
||||
CONSTRAINT files_post_id_fk
|
||||
CONSTRAINT DBPREFIXfiles_post_id_fk
|
||||
FOREIGN KEY(post_id) REFERENCES DBPREFIXposts(id) ON DELETE CASCADE,
|
||||
CONSTRAINT files_post_id_file_order_unique UNIQUE(post_id, file_order)
|
||||
CONSTRAINT DBPREFIXfiles_post_id_file_order_unique UNIQUE(post_id, file_order)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXstaff(
|
||||
|
@ -110,7 +110,7 @@ CREATE TABLE DBPREFIXstaff(
|
|||
added_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
last_login TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
is_active BOOL NOT NULL DEFAULT TRUE,
|
||||
CONSTRAINT staff_username_unique UNIQUE(username)
|
||||
CONSTRAINT DBPREFIXstaff_username_unique UNIQUE(username)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXsessions(
|
||||
|
@ -118,16 +118,16 @@ CREATE TABLE DBPREFIXsessions(
|
|||
staff_id BIGINT NOT NULL,
|
||||
expires TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
data VARCHAR(45) NOT NULL,
|
||||
CONSTRAINT sessions_staff_id_fk
|
||||
CONSTRAINT DBPREFIXsessions_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXboard_staff(
|
||||
board_id BIGINT NOT NULL,
|
||||
staff_id BIGINT NOT NULL,
|
||||
CONSTRAINT board_staff_board_id_fk
|
||||
CONSTRAINT DBPREFIXboard_staff_board_id_fk
|
||||
FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE,
|
||||
CONSTRAINT board_staff_staff_id_fk
|
||||
CONSTRAINT DBPREFIXboard_staff_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) ON DELETE CASCADE,
|
||||
CONSTRAINT board_staff_pk PRIMARY KEY (board_id,staff_id)
|
||||
);
|
||||
|
@ -138,7 +138,7 @@ CREATE TABLE DBPREFIXannouncements(
|
|||
subject VARCHAR(45) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT announcements_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id)
|
||||
CONSTRAINT DBPREFIXannouncements_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXip_ban(
|
||||
|
@ -158,11 +158,11 @@ CREATE TABLE DBPREFIXip_ban(
|
|||
staff_note VARCHAR(255) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
can_appeal BOOL NOT NULL,
|
||||
CONSTRAINT ip_ban_board_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_board_id_fk
|
||||
FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE,
|
||||
CONSTRAINT ip_ban_staff_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT ip_ban_banned_for_post_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_banned_for_post_id_fk
|
||||
FOREIGN KEY(banned_for_post_id) REFERENCES DBPREFIXposts(id)
|
||||
ON DELETE SET NULL
|
||||
);
|
||||
|
@ -180,9 +180,9 @@ CREATE TABLE DBPREFIXip_ban_audit(
|
|||
message TEXT NOT NULL,
|
||||
can_appeal BOOL NOT NULL,
|
||||
PRIMARY KEY(ip_ban_id, timestamp),
|
||||
CONSTRAINT ip_ban_audit_ip_ban_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_audit_ip_ban_id_fk
|
||||
FOREIGN KEY(ip_ban_id) REFERENCES DBPREFIXip_ban(id) ON DELETE CASCADE,
|
||||
CONSTRAINT ip_ban_audit_staff_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_audit_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id)
|
||||
);
|
||||
|
||||
|
@ -193,9 +193,9 @@ CREATE TABLE DBPREFIXip_ban_appeals(
|
|||
appeal_text TEXT NOT NULL,
|
||||
staff_response TEXT,
|
||||
is_denied BOOL NOT NULL,
|
||||
CONSTRAINT ip_ban_appeals_staff_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_appeals_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT ip_ban_appeals_ip_ban_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_appeals_ip_ban_id_fk
|
||||
FOREIGN KEY(ip_ban_id) REFERENCES DBPREFIXip_ban(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -207,9 +207,9 @@ CREATE TABLE DBPREFIXip_ban_appeals_audit(
|
|||
staff_response TEXT,
|
||||
is_denied BOOL NOT NULL,
|
||||
PRIMARY KEY(appeal_id, timestamp),
|
||||
CONSTRAINT ip_ban_appeals_audit_staff_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_appeals_audit_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT ip_ban_appeals_audit_appeal_id_fk
|
||||
CONSTRAINT DBPREFIXip_ban_appeals_audit_appeal_id_fk
|
||||
FOREIGN KEY(appeal_id) REFERENCES DBPREFIXip_ban_appeals(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
@ -221,9 +221,9 @@ CREATE TABLE DBPREFIXreports(
|
|||
ip VARCHAR(45) NOT NULL,
|
||||
reason TEXT NOT NULL,
|
||||
is_cleared BOOL NOT NULL,
|
||||
CONSTRAINT reports_handled_by_staff_id_fk
|
||||
CONSTRAINT DBPREFIXreports_handled_by_staff_id_fk
|
||||
FOREIGN KEY(handled_by_staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT reports_post_id_fk
|
||||
CONSTRAINT DBPREFIXreports_post_id_fk
|
||||
FOREIGN KEY(post_id) REFERENCES DBPREFIXposts(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -232,9 +232,9 @@ CREATE TABLE DBPREFIXreports_audit(
|
|||
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
handled_by_staff_id BIGINT,
|
||||
is_cleared BOOL NOT NULL,
|
||||
CONSTRAINT reports_audit_handled_by_staff_id_fk
|
||||
CONSTRAINT DBPREFIXreports_audit_handled_by_staff_id_fk
|
||||
FOREIGN KEY(handled_by_staff_id) REFERENCES DBPREFIXstaff(id),
|
||||
CONSTRAINT reports_audit_report_id_fk
|
||||
CONSTRAINT DBPREFIXreports_audit_report_id_fk
|
||||
FOREIGN KEY(report_id) REFERENCES DBPREFIXreports(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -247,7 +247,7 @@ CREATE TABLE DBPREFIXfilters(
|
|||
match_detail TEXT NOT NULL,
|
||||
handle_if_any BOOL NOT NULL DEFAULT FALSE,
|
||||
is_active BOOL NOT NULL,
|
||||
CONSTRAINT filters_staff_id_fk
|
||||
CONSTRAINT DBPREFIXfilters_staff_id_fk
|
||||
FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id)
|
||||
ON DELETE SET NULL
|
||||
);
|
||||
|
@ -256,10 +256,10 @@ CREATE TABLE DBPREFIXfilter_boards(
|
|||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
filter_id BIGINT NOT NULL,
|
||||
board_id BIGINT NOT NULL,
|
||||
CONSTRAINT filter_boards_filter_id_fk
|
||||
CONSTRAINT DBPREFIXfilter_boards_filter_id_fk
|
||||
FOREIGN KEY(filter_id) REFERENCES DBPREFIXfilters(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT filter_boards_board_id_fk
|
||||
CONSTRAINT DBPREFIXfilter_boards_board_id_fk
|
||||
FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
@ -270,10 +270,11 @@ CREATE TABLE DBPREFIXfilter_conditions(
|
|||
match_mode SMALLINT NOT NULL,
|
||||
search VARCHAR(75) NOT NULL,
|
||||
field VARCHAR(75) NOT NULL,
|
||||
CONSTRAINT filter_conditions_filter_id_fk
|
||||
CONSTRAINT DBPREFIXfilter_conditions_filter_id_fk
|
||||
FOREIGN KEY(filter_id) REFERENCES DBPREFIXfilters(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT filter_conditions_search_check CHECK (search <> '' OR match_mode = 3)
|
||||
CONSTRAINT DBPREFIXfilter_conditions_search_check
|
||||
CHECK (search <> '' OR match_mode = 3)
|
||||
);
|
||||
|
||||
CREATE TABLE DBPREFIXfilter_hits(
|
||||
|
@ -281,7 +282,7 @@ CREATE TABLE DBPREFIXfilter_hits(
|
|||
filter_id BIGINT NOT NULL,
|
||||
post_data TEXT NOT NULL,
|
||||
match_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT filter_hits_filter_id_fk
|
||||
CONSTRAINT DBPREFIXfilter_hits_filter_id_fk
|
||||
FOREIGN KEY(filter_id) REFERENCES DBPREFIXfilters(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
|
|
@ -1,35 +1,44 @@
|
|||
<form class="install-container" method="POST" action="/install/{{.nextPage}}">
|
||||
{{if eq "" .page}}
|
||||
{{- if eq "" .page -}}
|
||||
<p>Welcome to the Gochan installer! This installer will help you configure Gochan, including setting the necessary directories and connecting to the SQL database, in preparation for running a fresh Gochan installation or migrating another imageboard database (if supported) to Gochan.</p>
|
||||
<p class="text-bold">This does not install files like templates, or provision the database. It only creates a configuration file for gochan to use.</p>
|
||||
|
||||
{{else if eq .page "license"}}
|
||||
<p class="text-bold">This does not install files like templates (yet), or provision the database. It only creates a configuration file for gochan to use.</p>
|
||||
{{- else if eq .page "license" -}}
|
||||
<p class="text-center">Gochan is licensed under the BSD 3-Clause License, shown below. By using Gochan, you agree to the terms of this license,</p>
|
||||
<textarea class="license">{{.license}}</textarea>
|
||||
{{else if eq .page "paths"}}
|
||||
<textarea class="center-margin-50">{{.license}}</textarea>
|
||||
{{- else if eq .page "paths" -}}
|
||||
<table>
|
||||
<tr>
|
||||
<th>Output gochan.json Directory</th>
|
||||
<td><input type="text" name="configdir" required/></td>
|
||||
<th>Output gochan.json Path</th>
|
||||
<td>{{with .cfgPaths -}}
|
||||
<select name="configdir" required>
|
||||
<option value="" disabled selected>Select a directory</option>
|
||||
{{range . -}}
|
||||
<option value="{{.}}">{{.}}</option>
|
||||
{{- end}}
|
||||
</select>
|
||||
{{- else -}}
|
||||
<input type="hidden" name="configdir" value="gochan.json" />
|
||||
./gochan.json (current directory)
|
||||
{{- end -}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Templates Directory</th>
|
||||
<td><input type="text" name="templatedir" value="{{.systemCriticalConfig.TemplateDir}}"/></td>
|
||||
<td><input type="text" name="templatedir" class="path" value="{{.config.TemplateDir}}"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Document Root</th>
|
||||
<td><input type="text" name="documentroot" value="{{.systemCriticalConfig.DocumentRoot}}"/></td>
|
||||
<td><input type="text" name="documentroot" class="path" value="{{.config.DocumentRoot}}" required/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Log Directory</th>
|
||||
<td><input type="text" name="logdir"/></td>
|
||||
<td><input type="text" name="logdir" class="path" required/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Web Root</th>
|
||||
<td><input type="text" name="webroot" value="{{.systemCriticalConfig.WebRoot}}"/></td>
|
||||
<td><input type="text" name="webroot" class="path" value="{{.config.WebRoot}}" required/></td>
|
||||
</tr>
|
||||
</table>
|
||||
{{else if eq .page "database"}}
|
||||
{{- else if eq .page "database" -}}
|
||||
<table>
|
||||
<tr>
|
||||
<th>SQL Provider</th>
|
||||
|
@ -60,12 +69,46 @@
|
|||
<th>Database Prefix</th>
|
||||
<td><input type="text" name="dbprefix"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Database Request Timeout (seconds)</th>
|
||||
<td><input type="number" name="timeoutseconds" value="{{.config.DBTimeoutSeconds}}" min="1" required/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Database Max Open Connections</th>
|
||||
<td><input type="number" name="maxopenconns" value="{{.config.DBMaxOpenConnections}}" min="1" required/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Max Idle Database Connections</th>
|
||||
<td><input type="number" name="maxidleconns" value="{{.config.DBMaxIdleConnections}}" min="1" required/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Max DB Connection Lifetime (minutes)</th>
|
||||
<td><input type="number" name="connmaxlifetimemin" value="{{.config.DBConnMaxLifetimeMin}}" min="1" required/></td>
|
||||
</tr>
|
||||
</table>
|
||||
{{else if eq .page "dbtest"}}
|
||||
<p class="text-center">{{.testResult}}</p>
|
||||
{{else}}
|
||||
<p class="text-center">Invalid page</p>
|
||||
{{end}}
|
||||
{{- else if eq .page "dbtest" -}}
|
||||
<p class="text-center">{{.testResult}}</p>
|
||||
{{- else if eq .page "staff" -}}
|
||||
{{- with .alreadyCreated -}}
|
||||
<p class="text-center">Administrator account already created. Click Next to go to miscellaneous settings (flags and </p>
|
||||
{{- else -}}
|
||||
<p class="text-center">Enter a username and password for an administrator staff acount</p>
|
||||
<input type="hidden" name="newstaff" value="yes" />
|
||||
<table>
|
||||
<tr><th>Username</th><td><input type="text" name="username"></td></tr>
|
||||
<tr><th>Password</th><td><input type="password" name="password"></td></tr>
|
||||
<tr><th>Confirm Password</th><td><input type="password" name="confirmpassword"></td></tr>
|
||||
</table>
|
||||
{{- end -}}
|
||||
{{- else if eq .page "pre-save" -}}
|
||||
<p>Initial configuration is complete. In the textbox below, you can see the JSON output that will be saved to {{.configPath}} on the server. After clicking Save, you can then edit it as needed. See config.md for more info</p>
|
||||
<textarea class="center-margin-50" readonly>{{.configJSON}}</textarea>
|
||||
{{- else if eq .page "save" -}}
|
||||
<p class="text-center">Gochan has been configured and provisioned successfully. Click Finish to stop the installer server. From there, you can run gochan, log into the staff account you created <a href="{{webPath `/manage`}}">here</a>, and create new boards as desired. Thank you for using Gochan!</p>
|
||||
<p class="text-center">If you experience any bugs, you can report them <a href="https://github.com/gochan-org/gochan/issues">here</a>.</p>
|
||||
{{- else -}}
|
||||
<p class="text-center">Invalid page</p>
|
||||
{{- end -}}
|
||||
<section class="buttons">
|
||||
<input type="submit" value="{{.nextButton}}" />
|
||||
</section>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue