1
0
Fork 0
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:
Eggbertx 2025-06-27 17:01:57 -07:00
parent ccad76ff28
commit d5f489ef22
20 changed files with 482 additions and 283 deletions

View file

@ -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
}

View file

@ -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"

View file

@ -0,0 +1,9 @@
//go:build darwin
package main
import "github.com/gochan-org/gochan/pkg/config"
var (
cfgPaths = config.StandardConfigSearchPaths
)

View 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
}

View file

@ -0,0 +1,7 @@
//go:build windows
package main
var (
cfgPaths []string = nil
)

View file

@ -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
}

View file

@ -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()
}
}

View file

@ -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")
}
}

View file

@ -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)

View file

@ -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%;
}
}

View file

@ -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;

View file

@ -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()
}

View file

@ -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")
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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
);

View file

@ -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
);

View file

@ -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
);

View file

@ -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
);

View file

@ -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>