Continue database creation

dev
Nogard 2025-05-08 23:48:27 +02:00
parent b6104dfdbd
commit 4e9c083b0e
4 changed files with 233 additions and 52 deletions

5
go.mod
View File

@ -7,4 +7,7 @@ require (
github.com/julienschmidt/httprouter v1.3.0
)
require filippo.io/edwards25519 v1.1.0 // indirect
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/dustin/go-humanize v1.0.1
)

2
go.sum
View File

@ -1,5 +1,7 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=

View File

@ -5,8 +5,10 @@ import (
"chromagies/src/profiler"
"fmt"
"net/http"
"runtime"
"time"
"github.com/dustin/go-humanize"
"github.com/julienschmidt/httprouter"
)
@ -40,35 +42,11 @@ func Init(router *httprouter.Router) {
router.GET(apiRootPath+"/debug/profiler", apiHandler(apiDebugProfiler))
router.GET(apiRootPath+"/debug/profiler/:name", apiHandler(apiDebugProfilerName))
/*
router.GET(apiRootPath+"/debug/database/reset", apiHandler(apiDebugDatabaseReset))
// router.HandleFunc(apiRootPath, apiRoot).Methods("GET")
router.GET(apiRootPath+"/debug/memory/get", apiHandler(apiDebugMemoryGet))
router.GET(apiRootPath+"/debug/memory/gc", apiHandler(apiDebugMemoryGC))
routerAuth := router.PathPrefix(apiRootPath + "/auth").Subrouter()
routerPage := router.PathPrefix(apiRootPath + "/page").Subrouter()
routerUser := router.PathPrefix(apiRootPath + "/user").Subrouter()
routerTag := router.PathPrefix(apiRootPath + "/tag").Subrouter()
routerDebug := router.PathPrefix(apiRootPath + "/debug").Subrouter()
routerAuth.HandleFunc("", apiAuth)
routerAuth.HandleFunc("/login", apiAuthLogin)
routerAuth.HandleFunc("/logout", apiAuthLogout)
routerPage.HandleFunc("", apiPage)
routerPage.HandleFunc("/{folder}", apiPageFolder)
routerPage.HandleFunc("/{folder}/{page}", apiPageFolderPage)
routerPage.HandleFunc("/{folder}/{page}/content", apiPageFolderPageContent)
routerUser.HandleFunc("", apiUser)
routerUser.HandleFunc("/{name}", apiUserName)
routerTag.HandleFunc("", apiTag)
routerTag.HandleFunc("/{name}", apiTagName)
routerDebug.HandleFunc("/profiler", apiDebugProfiler)
routerDebug.HandleFunc("/profiler/{name}", apiDebugProfilerName)
*/
}
func apiHandler(h httprouter.Handle) httprouter.Handle {
@ -138,10 +116,9 @@ func apiTagName(w http.ResponseWriter, r *http.Request, params httprouter.Params
fmt.Fprintf(w, "API Tag Name(%s)", params.ByName("page"))
}
// API Tag
// API Debug Profiler
func apiDebugProfiler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
var entries []string
profiler.GetAll(&entries)
@ -149,9 +126,50 @@ func apiDebugProfiler(w http.ResponseWriter, r *http.Request, _ httprouter.Param
var entry string = entries[i]
fmt.Fprintf(w, "API Debug Profiler(%s)\n\n%s\n\n", entry, string(profiler.Get(entry).ToString()))
}
}
func apiDebugProfilerName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
fmt.Fprintf(w, "API Debug Profiler(%s)\n%s", params.ByName("page"), string(profiler.Get(params.ByName("page")).ToString()))
}
// API Debug Database
func apiDebugDatabaseReset(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
setHeaderStream(w)
fmt.Fprintf(w, "Destroying Database...\n")
w.(http.Flusher).Flush()
database.DestroyDatabase()
fmt.Fprintf(w, "Init Database...\n")
w.(http.Flusher).Flush()
database.UpdateDatabaseStructure()
fmt.Fprintf(w, "Done")
}
// API Debug Memory Get
func apiDebugMemoryGet(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Fprintf(w, "Alloc = %s", humanize.IBytes(m.Alloc))
fmt.Fprintf(w, "\tTotalAlloc = %s", humanize.IBytes(m.TotalAlloc))
fmt.Fprintf(w, "\tSys = %s", humanize.IBytes(m.Sys))
fmt.Fprintf(w, "\tNumGC = %v\n", m.NumGC)
}
// API Debug Memory GarbageCollector
func apiDebugMemoryGC(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
runtime.GC()
w.Header().Set("Location", apiRootPath+"/debug/memory/get")
w.WriteHeader(http.StatusSeeOther)
}
// Utils
func setHeaderStream(w http.ResponseWriter) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Expose-Headers", "Content-Type")
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
}

View File

@ -38,6 +38,7 @@ type columsDefinition struct {
Charset any
Collation any
Table string
Primary bool
}
type indexDefinition struct {
@ -62,10 +63,33 @@ type tableDefinition struct {
Columns []columsDefinition
}
type storedRoutineParameterDefinition struct {
Name string
Type string
IsNullable bool
Charset any
Collation any
}
type storedFunctionDefinition struct {
Name string
Parameters []storedRoutineParameterDefinition
Return storedRoutineParameterDefinition
Command string
}
type storedProcedureDefinition struct {
Name string
Parameters []storedRoutineParameterDefinition
Command string
}
type databaseDefinition struct {
Tables []tableDefinition
Indexes []indexDefinition
ForeignKeys []foreignKeyDefinition
StoredProcedures []storedProcedureDefinition
StoredFunctions []storedFunctionDefinition
}
var databaseStructure databaseDefinition = databaseDefinition{
@ -78,6 +102,7 @@ var databaseStructure databaseDefinition = databaseDefinition{
Type: "int(11)",
IsNullable: false,
AutoIncrement: true,
Primary: true,
},
{
Name: "name",
@ -125,6 +150,7 @@ var databaseStructure databaseDefinition = databaseDefinition{
Type: "int(11)",
IsNullable: false,
AutoIncrement: true,
Primary: true,
},
{
Name: "name",
@ -143,6 +169,7 @@ var databaseStructure databaseDefinition = databaseDefinition{
Type: "int(11)",
IsNullable: false,
AutoIncrement: true,
Primary: true,
},
{
Name: "name",
@ -161,6 +188,7 @@ var databaseStructure databaseDefinition = databaseDefinition{
Type: "int(11)",
IsNullable: false,
AutoIncrement: true,
Primary: true,
},
{
Name: "name",
@ -210,13 +238,15 @@ var databaseStructure databaseDefinition = databaseDefinition{
Columns: []columsDefinition{
{
Name: "tag_id",
Type: "int(10)",
Type: "int(11)",
IsNullable: false,
Primary: true,
},
{
Name: "website_id",
Type: "int(10)",
Type: "int(11)",
IsNullable: false,
Primary: true,
},
{
Name: "value",
@ -273,6 +303,7 @@ var databaseStructure databaseDefinition = databaseDefinition{
},
},
*/
// UNIQUE KEYS
{
@ -313,6 +344,48 @@ var databaseStructure databaseDefinition = databaseDefinition{
DeleteRule: "CASCADE",
},
},
StoredProcedures: []storedProcedureDefinition{
{
Name: "ListUsers",
Command: "SELECT id, name, permission_level FROM user;",
},
},
StoredFunctions: []storedFunctionDefinition{
{
Name: "CreateUser",
Parameters: []storedRoutineParameterDefinition{
{
Name: "f_user_name",
Type: "VARCHAR(256)",
Charset: "utf8mb4",
Collation: "utf8mb4_unicode_ci",
},
{
Name: "f_user_password",
Type: "VARCHAR(256)",
Charset: "utf8mb4",
Collation: "utf8mb4_unicode_ci",
},
},
Return: storedRoutineParameterDefinition{
Type: "INT(11)",
},
Command: `
DECLARE f_user_id INT(11);
DECLARE f_salt VARCHAR(32);
DECLARE f_password VARCHAR(256);
DECLARE f_user VARCHAR(256);
SELECT TO_BASE64(RANDOM_BYTES(16)) INTO f_salt;
SELECT SHA2(CONCAT(LOWER(f_user_name), f_user_password, f_salt), 512) INTO f_password;
INSERT INTO user(name, password, password_salt) VALUES (f_user_name, f_password, f_salt);
SELECT LAST_INSERT_ID() INTO f_user_id;
RETURN f_user_id;`,
},
},
}
var dbPath string
@ -333,8 +406,6 @@ func Init() {
profiler.Register(profiler_DATABASE_QUERY, 4000, "µs")
isInit = true
destroyDatabase()
updateDatabaseStructure()
initDatabaseStructure()
}
@ -474,7 +545,6 @@ func executeExec(query string, args ...any) sql.Result {
}
return result
}
func fetchStoredRoutines() {
@ -635,8 +705,6 @@ func fetchForeignKeys() []foreignKeyDefinition {
func initDatabaseStructure() {
fetchStoredRoutines()
fetchIndexes()
fetchForeignKeys()
log.Println("Tables :")
var tables []string = fetchTables()
@ -662,15 +730,15 @@ func initDatabaseStructure() {
}
}
func updateDatabaseStructure() {
func UpdateDatabaseStructure() {
var tablesNeeded []tableDefinition = databaseStructure.Tables
for t := range tablesNeeded {
var table tableDefinition = tablesNeeded[t]
var columnsNeeded []columsDefinition = table.Columns
executeExec(fmt.Sprintf("DROP TABLE IF EXISTS %s;", table.Name))
var query string = fmt.Sprintf("CREATE TABLE %s (\n", table.Name)
var primaryKeys []string = make([]string, 0)
for c := range columnsNeeded {
if c != 0 {
query += ",\n"
@ -678,13 +746,11 @@ func updateDatabaseStructure() {
var column columsDefinition = columnsNeeded[c]
query += fmt.Sprintf("%s %s", column.Name, column.Type)
str, ok := column.Charset.(string)
if ok {
if str, ok := column.Charset.(string); ok {
query += fmt.Sprintf(" CHARACTER SET %s", str)
}
str, ok = column.Collation.(string)
if ok {
if str, ok := column.Collation.(string); ok {
query += fmt.Sprintf(" COLLATE %s", str)
}
@ -692,10 +758,21 @@ func updateDatabaseStructure() {
query += " NOT NULL"
}
str, ok = column.Default.(string)
if ok {
if str, ok := column.Default.(string); ok {
query += fmt.Sprintf(" DEFAULT %s", str)
}
if column.AutoIncrement {
query += " AUTO_INCREMENT"
}
if column.AutoIncrement || column.Primary {
primaryKeys = append(primaryKeys, column.Name)
}
}
if len(primaryKeys) > 0 {
query += fmt.Sprintf(",\nPRIMARY KEY (%s)", strings.Join(primaryKeys, ", "))
}
query += "\n);"
@ -709,10 +786,10 @@ func updateDatabaseStructure() {
var index indexDefinition = indexesNeeded[i]
// ALTER TABLE <table> ADD CONSTRAINT PRIMARY KEY(<columns>);
// ALTER TABLE <table> ADD CONSTRAINT <constraint_name> UNIQUE(<columns>);
var command string = fmt.Sprintf("ALTER TABLE %s ADD CONSTRAINT ", index.Table)
var command string = fmt.Sprintf("ALTER TABLE %s ADD CONSTRAINT", index.Table)
switch index.Type {
case database_ENUM_INDEX_PRIMARY:
command += "PRIMARY KEY("
command += " PRIMARY KEY("
case database_ENUM_INDEX_UNIQUE:
command += fmt.Sprintf(" %s UNIQUE(", index.Name)
}
@ -755,9 +832,74 @@ func updateDatabaseStructure() {
fmt.Println(command)
executeExec(command)
}
var storedFunctions []storedFunctionDefinition = databaseStructure.StoredFunctions
for _, f := range storedFunctions {
// CREATE DEFINER='<user>'@'<host>' FUNCTION <func_name>(<param1>, <param2>, ...) RETURNS <return_type> BEGIN
// [...]
// END
var command string = fmt.Sprintf("CREATE DEFINER=`%s`@`%s` FUNCTION `%s`(", dbConfig.User, dbConfig.Host, f.Name)
for i, fp := range f.Parameters {
if i != 0 {
command += ", "
}
command += fmt.Sprintf("`%s` %s", fp.Name, fp.Type)
if fpc, ok := fp.Charset.(string); ok {
command += fmt.Sprintf(" CHARACTER SET %s", fpc)
}
if fpc, ok := fp.Collation.(string); ok {
command += fmt.Sprintf(" COLLATE %s", fpc)
}
}
command += fmt.Sprintf(") RETURNS %s", f.Return.Type)
if frc, ok := f.Return.Charset.(string); ok {
command += fmt.Sprintf(" CHARACTER SET %s", frc)
}
if frc, ok := f.Return.Collation.(string); ok {
command += fmt.Sprintf(" COLLATE %s", frc)
}
command += fmt.Sprintf(" BEGIN %s END", f.Command)
fmt.Println(command)
executeExec(command)
}
var storedProcedures []storedProcedureDefinition = databaseStructure.StoredProcedures
for _, p := range storedProcedures {
// CREATE DEFINER='<user>'@'<host>' PROCEDURE <proc_name>(IN <param1>, IN <param2>, ...) BEGIN
// [...]
// END
var command string = fmt.Sprintf("CREATE DEFINER=`%s`@`%s` PROCEDURE `%s`(", dbConfig.User, dbConfig.Host, p.Name)
for i, pp := range p.Parameters {
if i != 0 {
command += ", "
}
command += fmt.Sprintf("IN '%s' %s", pp.Name, pp.Type)
if ppc, ok := pp.Charset.(string); ok {
command += fmt.Sprintf(" CHARACTER SET %s", ppc)
}
if ppc, ok := pp.Collation.(string); ok {
command += fmt.Sprintf(" COLLATE %s", ppc)
}
}
command += fmt.Sprintf(") BEGIN %s END", p.Command)
fmt.Println(command)
executeExec(command)
}
}
func destroyDatabase() {
func DestroyDatabase() {
var tables []string = fetchTables()
var foreignKeys []foreignKeyDefinition = fetchForeignKeys()
@ -776,4 +918,20 @@ func destroyDatabase() {
fmt.Println(command)
executeExec(command)
}
fetchStoredRoutines()
for r, t := range storedRoutines {
var command = "DROP"
switch t {
case database_ROUTINE_TYPE_FUNCTION:
command += " FUNCTION "
case database_ROUTINE_TYPE_PROCEDURE:
command += " PROCEDURE "
}
command += r
fmt.Println(command)
executeExec(command)
}
}