Continue database creation
parent
b6104dfdbd
commit
4e9c083b0e
5
go.mod
5
go.mod
|
@ -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
2
go.sum
|
@ -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=
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue