finish world initialization, set up init on startup and on shutdown, move all models to main directory
This commit is contained in:
2
Makefile
2
Makefile
@@ -1,7 +1,7 @@
|
|||||||
-include .env
|
-include .env
|
||||||
|
|
||||||
api/run:
|
api/run:
|
||||||
go run ./cmd/api -foundry_pass=${FOUNDRY_PASS} -foundry_host=${FOUNDRY_HOST} -port=${LISTEN_PORT} -env=development -log_level=${LOG_LEVEL} -db-dsn=${DB_DSN}
|
go run ./cmd/api -foundry_pass=${FOUNDRY_PASS} -foundry_host=${FOUNDRY_HOST} -port=${LISTEN_PORT} -env=development -log_level=${LOG_LEVEL} -db-dsn=${DB_DSN} -foundry-worlds=${FOUNDRY_WORLDS}
|
||||||
|
|
||||||
db/migration/new:
|
db/migration/new:
|
||||||
@echo 'Creating migration files for ${name}...'
|
@echo 'Creating migration files for ${name}...'
|
||||||
|
|||||||
@@ -1,37 +1,22 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (app *application) ShowText(w http.ResponseWriter, r *http.Request) {
|
func (app *application) ShowText(w http.ResponseWriter, r *http.Request) {
|
||||||
data, err := app.foundryApp.Test()
|
dbConn := app.foundryApp.Transport.DB
|
||||||
|
|
||||||
|
world, err := db.GetWorld(dbConn, "kingmaker")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.slogger.Error("", "error", err)
|
w.Write([]byte("Got error"))
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var dbGame db.Game
|
w.Write([]byte(fmt.Sprintf("Got world with name %s, core_version - %s, next_session - %v", world.ID, world.CoreVersion, world.NextSession)))
|
||||||
|
|
||||||
start := time.Now()
|
|
||||||
data.ToDB(&dbGame)
|
|
||||||
elapsed := time.Since(start)
|
|
||||||
|
|
||||||
app.slogger.Info("Game data successfully parsed", "elapsedtime", elapsed)
|
|
||||||
|
|
||||||
byteData, err := json.Marshal(data)
|
|
||||||
if err != nil {
|
|
||||||
app.slogger.Error("", "error", err)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Write(byteData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *application) WhenNextSession(w http.ResponseWriter, r *http.Request) {
|
func (app *application) WhenNextSession(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ func (app *application) healthcheckHandler(w http.ResponseWriter, r *http.Reques
|
|||||||
"status": app.foundryApp.Status,
|
"status": app.foundryApp.Status,
|
||||||
"is_available": app.foundryApp.IsAvailable,
|
"is_available": app.foundryApp.IsAvailable,
|
||||||
},
|
},
|
||||||
"config": app.foundryApp.GetHTTP(),
|
|
||||||
}
|
}
|
||||||
err := app.writeJSON(w, http.StatusOK, env, nil)
|
err := app.writeJSON(w, http.StatusOK, env, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry"
|
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry"
|
||||||
@@ -73,7 +74,7 @@ func openDB(cfg config) (*sqlx.DB, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: change default values
|
// TODO: change default values
|
||||||
func (app *application) parseFlags() *requests.FoundryHttpRequest {
|
func (app *application) parseFlags() *transport.FoundryTransportData {
|
||||||
flag.StringVar(&app.cfg.port, "port", ":9090", "Service port")
|
flag.StringVar(&app.cfg.port, "port", ":9090", "Service port")
|
||||||
flag.StringVar(&app.cfg.env, "env", "development", "Enivornment (development|staging|production)")
|
flag.StringVar(&app.cfg.env, "env", "development", "Enivornment (development|staging|production)")
|
||||||
|
|
||||||
@@ -85,15 +86,20 @@ func (app *application) parseFlags() *requests.FoundryHttpRequest {
|
|||||||
var mode string
|
var mode string
|
||||||
flag.StringVar(&mode, "service_mode", "api", "Type of service mode(api|discord|tg)")
|
flag.StringVar(&mode, "service_mode", "api", "Type of service mode(api|discord|tg)")
|
||||||
|
|
||||||
foundryHttpData := requests.FoundryHttpRequest{SessionID: nil}
|
var foundryTransportData transport.FoundryTransportData
|
||||||
flag.StringVar(&foundryHttpData.Host, "foundry_host", "127.0.0.1", "Address to connect to Foundry")
|
foundryTransportData.HttpConfig = &requests.FoundryHttpRequest{SessionID: nil}
|
||||||
flag.StringVar(&foundryHttpData.Password, "foundry_pass", "", "Password to connect to Foundry")
|
flag.StringVar(&foundryTransportData.HttpConfig.Host, "foundry_host", "127.0.0.1", "Address to connect to Foundry")
|
||||||
|
flag.StringVar(&foundryTransportData.HttpConfig.Password, "foundry_pass", "", "Password to connect to Foundry")
|
||||||
|
|
||||||
var logLevelStr string
|
var logLevelStr string
|
||||||
flag.StringVar(&logLevelStr, "log_level", "info", "Password to connect to Foundry(debug|info|warn|error)")
|
flag.StringVar(&logLevelStr, "log_level", "info", "Password to connect to Foundry(debug|info|warn|error)")
|
||||||
|
|
||||||
|
var worlds string
|
||||||
|
flag.StringVar(&worlds, "foundry-worlds", "", "Worlds that initialize in db on startup")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
foundryTransportData.Worlds = strings.Split(worlds, ",")
|
||||||
|
|
||||||
app.cfg.mode = service_mode(mode)
|
app.cfg.mode = service_mode(mode)
|
||||||
|
|
||||||
logLevel, ok := StringToLogLevel[logLevelStr]
|
logLevel, ok := StringToLogLevel[logLevelStr]
|
||||||
@@ -105,20 +111,21 @@ func (app *application) parseFlags() *requests.FoundryHttpRequest {
|
|||||||
if !ok {
|
if !ok {
|
||||||
app.slogger.Warn("Wrong log level. Started with log_level=info", "received", logLevelStr)
|
app.slogger.Warn("Wrong log level. Started with log_level=info", "received", logLevelStr)
|
||||||
}
|
}
|
||||||
return &foundryHttpData
|
foundryTransportData.Logger = app.slogger
|
||||||
|
return &foundryTransportData
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Handle DB connection error
|
// TODO: Handle DB connection error
|
||||||
func main() {
|
func main() {
|
||||||
app := application{foundryApp: &foundry.FoundryApi{}}
|
app := application{foundryApp: &foundry.FoundryApi{}}
|
||||||
|
|
||||||
foundryHttpData := app.parseFlags()
|
foundryTransportData := app.parseFlags()
|
||||||
jar, err := cookiejar.New(nil)
|
jar, err := cookiejar.New(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.slogger.Error("Error", "text", err.Error())
|
app.slogger.Error("Error", "text", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
foundryHttpData.Jar = jar
|
foundryTransportData.HttpConfig.Jar = jar
|
||||||
|
|
||||||
app.slogger.Info("", "dsn", app.cfg.db.dsn)
|
app.slogger.Info("", "dsn", app.cfg.db.dsn)
|
||||||
|
|
||||||
@@ -129,8 +136,15 @@ func main() {
|
|||||||
}
|
}
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
app.slogger.Info("Database connection pool established")
|
app.slogger.Info("Database connection pool established")
|
||||||
|
foundryTransportData.DbConn = db
|
||||||
|
|
||||||
|
app.foundryApp.SetTransport(transport.NewFoundryTransport(foundryTransportData))
|
||||||
|
err = app.foundryApp.PrepareDB()
|
||||||
|
if err != nil {
|
||||||
|
app.slogger.Error("Error on preparing database", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
app.foundryApp.SetTransport(transport.NewFoundryTransport(db, app.slogger, foundryHttpData))
|
|
||||||
go app.foundryApp.StartListenFoundry()
|
go app.foundryApp.StartListenFoundry()
|
||||||
|
|
||||||
app.serve()
|
app.serve()
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
DROP TRIGGER IF EXISTS tr_delete_package_warnings_to_data_package_warnings_data;
|
||||||
|
DROP TRIGGER IF EXISTS tr_delete_pack_to_index_index;
|
||||||
DROP TABLE IF EXISTS folder;
|
DROP TABLE IF EXISTS folder;
|
||||||
DROP TABLE IF EXISTS pack;
|
DROP TABLE IF EXISTS pack;
|
||||||
DROP TABLE IF EXISTS language;
|
DROP TABLE IF EXISTS language;
|
||||||
@@ -18,13 +20,14 @@ DROP TABLE IF EXISTS grid;
|
|||||||
DROP TABLE IF EXISTS system;
|
DROP TABLE IF EXISTS system;
|
||||||
DROP TABLE IF EXISTS package_warnings_data_error;
|
DROP TABLE IF EXISTS package_warnings_data_error;
|
||||||
DROP TABLE IF EXISTS package_warnings_data_warning;
|
DROP TABLE IF EXISTS package_warnings_data_warning;
|
||||||
|
DROP TABLE IF EXISTS package_warnings_to_data;
|
||||||
DROP TABLE IF EXISTS package_warnings_data;
|
DROP TABLE IF EXISTS package_warnings_data;
|
||||||
DROP TRIGGER IF EXISTS tr_delete_package_warnings_to_data_package_warnings_data;
|
|
||||||
DROP TABLE IF EXISTS package_warnings;
|
DROP TABLE IF EXISTS package_warnings;
|
||||||
DROP TABLE IF EXISTS news;
|
DROP TABLE IF EXISTS news;
|
||||||
DROP TABLE IF EXISTS folder_packs;
|
DROP TABLE IF EXISTS folder_packs;
|
||||||
DROP TABLE IF EXISTS folder;
|
DROP TABLE IF EXISTS folder;
|
||||||
DROP TABLE IF EXISTS pack_folder;
|
DROP TABLE IF EXISTS pack_folder;
|
||||||
|
DROP TABLE IF EXISTS pack_to_index;
|
||||||
DROP TABLE IF EXISTS index_;
|
DROP TABLE IF EXISTS index_;
|
||||||
DROP TABLE IF EXISTS ownership;
|
DROP TABLE IF EXISTS ownership;
|
||||||
DROP TABLE IF EXISTS relationships_data;
|
DROP TABLE IF EXISTS relationships_data;
|
||||||
|
|||||||
@@ -301,9 +301,8 @@ CREATE TABLE IF NOT EXISTS language (
|
|||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS pack (
|
CREATE TABLE IF NOT EXISTS pack (
|
||||||
id INTEGER PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
|
|
||||||
key_ VARCHAR(128) NOT NULL,
|
|
||||||
name VARCHAR(128) NOT NULL,
|
name VARCHAR(128) NOT NULL,
|
||||||
label VARCHAR(128) NOT NULL,
|
label VARCHAR(128) NOT NULL,
|
||||||
banner VARCHAR(128) NOT NULL,
|
banner VARCHAR(128) NOT NULL,
|
||||||
@@ -324,7 +323,7 @@ CREATE TABLE IF NOT EXISTS ownership (
|
|||||||
trusted VARCHAR(128) NOT NULL,
|
trusted VARCHAR(128) NOT NULL,
|
||||||
assistant VARCHAR(128) NOT NULL,
|
assistant VARCHAR(128) NOT NULL,
|
||||||
|
|
||||||
pack_id INTEGER UNIQUE,
|
pack_id TEXT,
|
||||||
FOREIGN KEY (pack_id) REFERENCES pack(id) ON DELETE CASCADE
|
FOREIGN KEY (pack_id) REFERENCES pack(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -334,12 +333,29 @@ CREATE TABLE IF NOT EXISTS index_ (
|
|||||||
folder VARCHAR(128) NOT NULL,
|
folder VARCHAR(128) NOT NULL,
|
||||||
img VARCHAR(128) NOT NULL,
|
img VARCHAR(128) NOT NULL,
|
||||||
name VARCHAR(128) NOT NULL,
|
name VARCHAR(128) NOT NULL,
|
||||||
type VARCHAR(128) NOT NULL,
|
type VARCHAR(128) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
pack_id INTEGER,
|
CREATE TABLE IF NOT EXISTS pack_to_index (
|
||||||
|
pack_id TEXT,
|
||||||
|
index_id TEXT,
|
||||||
|
|
||||||
|
PRIMARY KEY (pack_id, index_id),
|
||||||
|
FOREIGN KEY (index_id) REFERENCES index_(id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (pack_id) REFERENCES pack(id) ON DELETE CASCADE
|
FOREIGN KEY (pack_id) REFERENCES pack(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_pack_to_index_index_id ON pack_to_index(index_id);
|
||||||
|
|
||||||
|
CREATE TRIGGER tr_delete_pack_to_index_index
|
||||||
|
AFTER DELETE ON pack_to_index
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
DELETE FROM index_
|
||||||
|
WHERE id = OLD.index_id
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM pack_to_index WHERE index_id = OLD.index_id);
|
||||||
|
END;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS pack_folder (
|
CREATE TABLE IF NOT EXISTS pack_folder (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
|
|
||||||
@@ -349,7 +365,7 @@ CREATE TABLE IF NOT EXISTS pack_folder (
|
|||||||
type VARCHAR(128) NOT NULL,
|
type VARCHAR(128) NOT NULL,
|
||||||
sort INTEGER NOT NULL,
|
sort INTEGER NOT NULL,
|
||||||
|
|
||||||
pack_id INTEGER,
|
pack_id TEXT,
|
||||||
FOREIGN KEY (pack_id) REFERENCES pack(id) ON DELETE CASCADE
|
FOREIGN KEY (pack_id) REFERENCES pack(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -406,15 +422,6 @@ CREATE TABLE IF NOT EXISTS package_warnings_to_data (
|
|||||||
FOREIGN KEY (package_warnings_data_id) REFERENCES package_warnings_data(id) ON DELETE CASCADE
|
FOREIGN KEY (package_warnings_data_id) REFERENCES package_warnings_data(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TRIGGER tr_delete_package_warnings_to_data_package_warnings_data
|
|
||||||
AFTER DELETE ON package_warnings_to_data
|
|
||||||
FOR EACH ROW
|
|
||||||
BEGIN
|
|
||||||
DELETE FROM package_warnings_data
|
|
||||||
WHERE id = OLD.id
|
|
||||||
AND NOT EXISTS (SELECT 1 FROM package_warnings_to_data WHERE package_warnings_data_id = OLD.package_warnings_data_id);
|
|
||||||
END;
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS package_warnings_data (
|
CREATE TABLE IF NOT EXISTS package_warnings_data (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
|
|
||||||
@@ -423,6 +430,17 @@ CREATE TABLE IF NOT EXISTS package_warnings_data (
|
|||||||
reinstallable BOOLEAN NOT NULL
|
reinstallable BOOLEAN NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_package_warnings_to_data_package_warnings_data_id ON package_warnings_to_data(package_warnings_data_id);
|
||||||
|
|
||||||
|
CREATE TRIGGER tr_delete_package_warnings_to_data_package_warnings_data
|
||||||
|
AFTER DELETE ON package_warnings_to_data
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
DELETE FROM package_warnings_data
|
||||||
|
WHERE id = OLD.package_warnings_data_id
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM package_warnings_to_data WHERE package_warnings_data_id = OLD.package_warnings_data_id);
|
||||||
|
END;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS package_warnings_data_warning (
|
CREATE TABLE IF NOT EXISTS package_warnings_data_warning (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
DROP TRIGGER IF EXISTS tr_delete_package_warnings_to_data_package_warnings_data;
|
DROP TRIGGER IF EXISTS tr_delete_package_warnings_to_data_package_warnings_data;
|
||||||
DROP TRIGGER IF EXISTS tr_delete_game_to_systems_system;
|
DROP TRIGGER IF EXISTS tr_delete_game_to_systems_system;
|
||||||
|
DROP TRIGGER IF EXISTS tr_delete_game_to_pack_pack;
|
||||||
DROP TRIGGER IF EXISTS tr_delete_game_to_module_module;
|
DROP TRIGGER IF EXISTS tr_delete_game_to_module_module;
|
||||||
|
DROP TRIGGER IF EXISTS tr_delete_pack_to_index_index;
|
||||||
DROP TABLE IF EXISTS token_turn_maker;
|
DROP TABLE IF EXISTS token_turn_maker;
|
||||||
DROP TABLE IF EXISTS token_occludable;
|
DROP TABLE IF EXISTS token_occludable;
|
||||||
DROP TABLE IF EXISTS token_light_darkness;
|
DROP TABLE IF EXISTS token_light_darkness;
|
||||||
@@ -46,9 +48,8 @@ DROP TABLE IF EXISTS stats;
|
|||||||
DROP TABLE IF EXISTS message;
|
DROP TABLE IF EXISTS message;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS pack_new (
|
CREATE TABLE IF NOT EXISTS pack_new (
|
||||||
id INTEGER PRIMARY KEY,
|
id VARCHAR(128) PRIMARY KEY,
|
||||||
|
|
||||||
key_ VARCHAR(128) NOT NULL,
|
|
||||||
name VARCHAR(128) NOT NULL,
|
name VARCHAR(128) NOT NULL,
|
||||||
label VARCHAR(128) NOT NULL,
|
label VARCHAR(128) NOT NULL,
|
||||||
banner VARCHAR(128) NOT NULL,
|
banner VARCHAR(128) NOT NULL,
|
||||||
@@ -63,8 +64,8 @@ CREATE TABLE IF NOT EXISTS pack_new (
|
|||||||
);
|
);
|
||||||
ALTER TABLE pack_new ADD COLUMN system_id TEXT REFERENCES system(id) ON DELETE CASCADE;
|
ALTER TABLE pack_new ADD COLUMN system_id TEXT REFERENCES system(id) ON DELETE CASCADE;
|
||||||
ALTER TABLE pack_new ADD COLUMN world_id TEXT REFERENCES world(id) ON DELETE CASCADE;
|
ALTER TABLE pack_new ADD COLUMN world_id TEXT REFERENCES world(id) ON DELETE CASCADE;
|
||||||
INSERT INTO pack_new (id, key_, name, label, banner, path, type, system, package_type, package_name, module_id, system_id, world_id)
|
INSERT INTO pack_new (id, name, label, banner, path, type, system, package_type, package_name, module_id, system_id, world_id)
|
||||||
SELECT id, key_, name, label, banner, path, type, system, package_type, package_name, module_id, system_id, world_id FROM pack;
|
SELECT id, name, label, banner, path, type, system, package_type, package_name, module_id, system_id, world_id FROM pack;
|
||||||
DROP TABLE IF EXISTS pack;
|
DROP TABLE IF EXISTS pack;
|
||||||
ALTER TABLE pack_new RENAME TO pack;
|
ALTER TABLE pack_new RENAME TO pack;
|
||||||
|
|
||||||
@@ -81,6 +82,8 @@ SELECT id, key_, setup_id FROM package_warnings;
|
|||||||
DROP TABLE IF EXISTS package_warnings;
|
DROP TABLE IF EXISTS package_warnings;
|
||||||
ALTER TABLE package_warnings_new RENAME TO package_warnings;
|
ALTER TABLE package_warnings_new RENAME TO package_warnings;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS game_to_pack;
|
||||||
|
DROP TABLE IF EXISTS game_to_module;
|
||||||
DROP TABLE IF EXISTS active_users;
|
DROP TABLE IF EXISTS active_users;
|
||||||
DROP TABLE IF EXISTS system_update;
|
DROP TABLE IF EXISTS system_update;
|
||||||
DROP INDEX IF EXISTS idx_core_update_game_id;
|
DROP INDEX IF EXISTS idx_core_update_game_id;
|
||||||
@@ -186,6 +189,15 @@ AFTER DELETE ON package_warnings_to_data
|
|||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
BEGIN
|
BEGIN
|
||||||
DELETE FROM package_warnings_data
|
DELETE FROM package_warnings_data
|
||||||
WHERE id = OLD.id
|
WHERE id = OLD.package_warnings_data_id
|
||||||
AND NOT EXISTS (SELECT 1 FROM package_warnings_to_data WHERE package_warnings_data_id = OLD.package_warnings_data_id);
|
AND NOT EXISTS (SELECT 1 FROM package_warnings_to_data WHERE package_warnings_data_id = OLD.package_warnings_data_id);
|
||||||
END;
|
END;
|
||||||
|
|
||||||
|
CREATE TRIGGER tr_delete_pack_to_index_index
|
||||||
|
AFTER DELETE ON pack_to_index
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
DELETE FROM index_
|
||||||
|
WHERE id = OLD.index_id
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM pack_to_index WHERE index_id = OLD.index_id);
|
||||||
|
END;
|
||||||
@@ -48,12 +48,14 @@ CREATE TABLE IF NOT EXISTS game_to_systems (
|
|||||||
FOREIGN KEY (system_id) REFERENCES system(id) ON DELETE CASCADE
|
FOREIGN KEY (system_id) REFERENCES system(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_game_to_systems_system_id ON game_to_systems(system_id);
|
||||||
|
|
||||||
CREATE TRIGGER tr_delete_game_to_systems_system
|
CREATE TRIGGER tr_delete_game_to_systems_system
|
||||||
AFTER DELETE ON game_to_systems
|
AFTER DELETE ON game_to_systems
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
BEGIN
|
BEGIN
|
||||||
DELETE FROM system
|
DELETE FROM system
|
||||||
WHERE id = OLD.id
|
WHERE id = OLD.system_id
|
||||||
AND NOT EXISTS (SELECT 1 FROM game_to_systems WHERE system_id = OLD.system_id);
|
AND NOT EXISTS (SELECT 1 FROM game_to_systems WHERE system_id = OLD.system_id);
|
||||||
END;
|
END;
|
||||||
|
|
||||||
@@ -88,17 +90,38 @@ CREATE TABLE IF NOT EXISTS game_to_module (
|
|||||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_game_to_module_module_id ON game_to_module(module_id);
|
||||||
|
|
||||||
CREATE TRIGGER tr_delete_game_to_module_module
|
CREATE TRIGGER tr_delete_game_to_module_module
|
||||||
AFTER DELETE ON game_to_module
|
AFTER DELETE ON game_to_module
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
BEGIN
|
BEGIN
|
||||||
DELETE FROM module
|
DELETE FROM module
|
||||||
WHERE id = OLD.id
|
WHERE id = OLD.module_id
|
||||||
AND NOT EXISTS (SELECT 1 FROM game_to_module WHERE module_id = OLD.module_id);
|
AND NOT EXISTS (SELECT 1 FROM game_to_module WHERE module_id = OLD.module_id);
|
||||||
END;
|
END;
|
||||||
|
|
||||||
ALTER TABLE package_warnings ADD COLUMN game_id INTEGER REFERENCES game(id) ON DELETE CASCADE;
|
ALTER TABLE package_warnings ADD COLUMN game_id INTEGER REFERENCES game(id) ON DELETE CASCADE;
|
||||||
ALTER TABLE pack ADD COLUMN game_id INTEGER REFERENCES game(id) ON DELETE CASCADE;
|
|
||||||
|
CREATE TABLE IF NOT EXISTS game_to_pack (
|
||||||
|
game_id INTEGER,
|
||||||
|
pack_id TEXT,
|
||||||
|
|
||||||
|
PRIMARY KEY (game_id, pack_id),
|
||||||
|
FOREIGN KEY (pack_id) REFERENCES pack(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_game_to_pack_pack_id ON game_to_pack(pack_id);
|
||||||
|
|
||||||
|
CREATE TRIGGER tr_delete_game_to_pack_pack
|
||||||
|
AFTER DELETE ON game_to_pack
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
DELETE FROM pack
|
||||||
|
WHERE id = OLD.pack_id
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM game_to_pack WHERE pack_id = OLD.pack_id);
|
||||||
|
END;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS message (
|
CREATE TABLE IF NOT EXISTS message (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
@@ -113,6 +136,9 @@ CREATE TABLE IF NOT EXISTS message (
|
|||||||
flavor VARCHAR(128) NOT NULL,
|
flavor VARCHAR(128) NOT NULL,
|
||||||
sound VARCHAR(128) NOT NULL,
|
sound VARCHAR(128) NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
game_id INTEGER,
|
game_id INTEGER,
|
||||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -170,6 +196,9 @@ CREATE TABLE IF NOT EXISTS combat (
|
|||||||
sort INTEGER NOT NULL,
|
sort INTEGER NOT NULL,
|
||||||
active INTEGER NOT NULL,
|
active INTEGER NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
game_id INTEGER,
|
game_id INTEGER,
|
||||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -199,6 +228,9 @@ CREATE TABLE IF NOT EXISTS combatant (
|
|||||||
hidden BOOLEAN NOT NULL,
|
hidden BOOLEAN NOT NULL,
|
||||||
defeated BOOLEAN NOT NULL,
|
defeated BOOLEAN NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
combat_id TEXT,
|
combat_id TEXT,
|
||||||
FOREIGN KEY (combat_id) REFERENCES combat(id) ON DELETE CASCADE
|
FOREIGN KEY (combat_id) REFERENCES combat(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -220,6 +252,9 @@ CREATE TABLE IF NOT EXISTS card_deck (
|
|||||||
sort INTEGER NOT NULL,
|
sort INTEGER NOT NULL,
|
||||||
display_count BOOLEAN NOT NULL,
|
display_count BOOLEAN NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
game_id INTEGER,
|
game_id INTEGER,
|
||||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -253,6 +288,9 @@ CREATE TABLE IF NOT EXISTS card (
|
|||||||
sort INTEGER NOT NULL,
|
sort INTEGER NOT NULL,
|
||||||
drawn BOOLEAN NOT NULL,
|
drawn BOOLEAN NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
card_deck_id TEXT,
|
card_deck_id TEXT,
|
||||||
FOREIGN KEY (card_deck_id) REFERENCES card_deck(id) ON DELETE CASCADE
|
FOREIGN KEY (card_deck_id) REFERENCES card_deck(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -291,6 +329,9 @@ CREATE TABLE IF NOT EXISTS user (
|
|||||||
pronouns VARCHAR(128) NOT NULL,
|
pronouns VARCHAR(128) NOT NULL,
|
||||||
role INTEGER NOT NULL,
|
role INTEGER NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
game_id INTEGER,
|
game_id INTEGER,
|
||||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -320,6 +361,9 @@ CREATE TABLE IF NOT EXISTS macro (
|
|||||||
folder VARCHAR(128) NOT NULL,
|
folder VARCHAR(128) NOT NULL,
|
||||||
sort INTEGER NOT NULL,
|
sort INTEGER NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
game_id INTEGER,
|
game_id INTEGER,
|
||||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -339,6 +383,9 @@ CREATE TABLE IF NOT EXISTS world_folder (
|
|||||||
color VARCHAR(128) NOT NULL,
|
color VARCHAR(128) NOT NULL,
|
||||||
sort INTEGER NOT NULL,
|
sort INTEGER NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
game_id INTEGER,
|
game_id INTEGER,
|
||||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -355,6 +402,9 @@ CREATE TABLE IF NOT EXISTS item (
|
|||||||
folder VARCHAR(128) NOT NULL,
|
folder VARCHAR(128) NOT NULL,
|
||||||
sort INTEGER NOT NULL,
|
sort INTEGER NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
game_id INTEGER,
|
game_id INTEGER,
|
||||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -369,6 +419,9 @@ CREATE TABLE IF NOT EXISTS setting (
|
|||||||
key_ VARCHAR(128) NOT NULL,
|
key_ VARCHAR(128) NOT NULL,
|
||||||
value VARCHAR(128) NOT NULL,
|
value VARCHAR(128) NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
game_id INTEGER,
|
game_id INTEGER,
|
||||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -382,6 +435,9 @@ CREATE TABLE IF NOT EXISTS journal (
|
|||||||
name VARCHAR(128) NOT NULL,
|
name VARCHAR(128) NOT NULL,
|
||||||
sort INTEGER NOT NULL,
|
sort INTEGER NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
game_id INTEGER,
|
game_id INTEGER,
|
||||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -394,6 +450,9 @@ CREATE TABLE IF NOT EXISTS journal_page (
|
|||||||
src VARCHAR(128) NOT NULL,
|
src VARCHAR(128) NOT NULL,
|
||||||
sort INTEGER NOT NULL,
|
sort INTEGER NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
journal_id TEXT,
|
journal_id TEXT,
|
||||||
FOREIGN KEY (journal_id) REFERENCES journal(id) ON DELETE CASCADE
|
FOREIGN KEY (journal_id) REFERENCES journal(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -430,7 +489,6 @@ CREATE TABLE IF NOT EXISTS journal_page_video (
|
|||||||
);
|
);
|
||||||
|
|
||||||
ALTER TABLE stats ADD COLUMN journal_page_id TEXT REFERENCES journal_page(id) ON DELETE CASCADE;
|
ALTER TABLE stats ADD COLUMN journal_page_id TEXT REFERENCES journal_page(id) ON DELETE CASCADE;
|
||||||
CREATE UNIQUE INDEX idx_stats_journal_page_id ON stats(journal_page_id);
|
|
||||||
ALTER TABLE ownership_string ADD COLUMN journal_page_id TEXT REFERENCES journal_page(id) ON DELETE CASCADE;
|
ALTER TABLE ownership_string ADD COLUMN journal_page_id TEXT REFERENCES journal_page(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
ALTER TABLE ownership_string ADD COLUMN journal_id TEXT REFERENCES journal(id) ON DELETE CASCADE;
|
ALTER TABLE ownership_string ADD COLUMN journal_id TEXT REFERENCES journal(id) ON DELETE CASCADE;
|
||||||
@@ -447,6 +505,9 @@ CREATE TABLE IF NOT EXISTS table_ (
|
|||||||
replacement BOOLEAN NOT NULL,
|
replacement BOOLEAN NOT NULL,
|
||||||
display_roll BOOLEAN NOT NULL,
|
display_roll BOOLEAN NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
game_id INTEGER,
|
game_id INTEGER,
|
||||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -465,6 +526,9 @@ CREATE TABLE IF NOT EXISTS table_result (
|
|||||||
weight INTEGER NOT NULL,
|
weight INTEGER NOT NULL,
|
||||||
drawn BOOLEAN NOT NULL,
|
drawn BOOLEAN NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
table_id TEXT,
|
table_id TEXT,
|
||||||
FOREIGN KEY (table_id) REFERENCES table_(id) ON DELETE CASCADE
|
FOREIGN KEY (table_id) REFERENCES table_(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -495,6 +559,9 @@ CREATE TABLE IF NOT EXISTS playlist (
|
|||||||
sort INTEGER NOT NULL,
|
sort INTEGER NOT NULL,
|
||||||
playing BOOLEAN NOT NULL,
|
playing BOOLEAN NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
game_id INTEGER,
|
game_id INTEGER,
|
||||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -517,6 +584,9 @@ CREATE TABLE IF NOT EXISTS sound (
|
|||||||
volume REAL NOT NULL,
|
volume REAL NOT NULL,
|
||||||
paused_time REAL NOT NULL,
|
paused_time REAL NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
playlist_id TEXT,
|
playlist_id TEXT,
|
||||||
FOREIGN KEY (playlist_id) REFERENCES playlist(id) ON DELETE CASCADE
|
FOREIGN KEY (playlist_id) REFERENCES playlist(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -530,6 +600,9 @@ CREATE TABLE IF NOT EXISTS actor (
|
|||||||
folder VARCHAR(128) NOT NULL,
|
folder VARCHAR(128) NOT NULL,
|
||||||
sort INTEGER NOT NULL,
|
sort INTEGER NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT DEFAULT (datetime('now')),
|
||||||
|
|
||||||
game_id INTEGER,
|
game_id INTEGER,
|
||||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package actions
|
package actions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
"time"
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/json"
|
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport"
|
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport"
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
|
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
|
||||||
)
|
)
|
||||||
@@ -14,29 +14,7 @@ type WsSessionMsg struct {
|
|||||||
|
|
||||||
func (msg WsSessionMsg) Action(tr *transport.FoundryTransport, status *types.FoundryStatus) error {
|
func (msg WsSessionMsg) Action(tr *transport.FoundryTransport, status *types.FoundryStatus) error {
|
||||||
if status.IsActive {
|
if status.IsActive {
|
||||||
tr.Logger.Info("Session msg", "userid", msg.UserId)
|
return msg.OnActiveWorld(tr)
|
||||||
if msg.UserId == "" {
|
|
||||||
err := tr.LogInToWorld()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tr.Logger.Info("World is started. Succesfully logged into world")
|
|
||||||
close(tr.ReadChan.Reconnect())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// select {
|
|
||||||
// case <-tr.LoggedInChan:
|
|
||||||
// return ErrUserChannelIsClosed
|
|
||||||
// default:
|
|
||||||
// tr.LoggedInChan <- true
|
|
||||||
// }
|
|
||||||
|
|
||||||
// time.Sleep(3 * time.Second)
|
|
||||||
// types.CloseChannel(tr.LoggedInChan)
|
|
||||||
|
|
||||||
go msg.OnActiveWorld(tr)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if tr.IsDbInit {
|
if tr.IsDbInit {
|
||||||
@@ -49,27 +27,36 @@ func (msg WsSessionMsg) Action(tr *transport.FoundryTransport, status *types.Fou
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (msg WsSessionMsg) OnActiveWorld(tr *transport.FoundryTransport) error {
|
func (msg WsSessionMsg) OnActiveWorld(tr *transport.FoundryTransport) error {
|
||||||
msgJson, err := tr.GetJsonData("world")
|
tr.Logger.Info("Session msg", "userid", msg.UserId)
|
||||||
|
if msg.UserId != "" {
|
||||||
|
return msg.HandleLoggedInUser(tr)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := tr.LogInToWorld()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tr.Logger.Info("Game data received")
|
tr.Logger.Info("World is started. Succesfully logged into world")
|
||||||
|
close(tr.ReadChan.Reconnect())
|
||||||
gameJson, err := json.ParseGame(msgJson)
|
return nil
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
func (msg WsSessionMsg) HandleLoggedInUser(tr *transport.FoundryTransport) error {
|
||||||
|
if tr.LoggedInChan == nil {
|
||||||
var foundryStateDb db.Game
|
tr.Logger.Info("World had been started before application was started. Run insertion of world data")
|
||||||
gameJson.ToDB(&foundryStateDb)
|
go tr.InsertGameToDB()
|
||||||
|
return nil
|
||||||
err = foundryStateDb.Insert(tr.DB)
|
}
|
||||||
if err != nil {
|
|
||||||
tr.ReadChan.Err() <- &types.FoundryError{Direction: types.WriterCode, Err: err, Type: types.DbCode}
|
select {
|
||||||
}
|
case <-tr.LoggedInChan:
|
||||||
|
return ErrUserChannelIsClosed
|
||||||
tr.Logger.Info("Game data succesfully inserted to DB")
|
default:
|
||||||
|
tr.LoggedInChan <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
types.CloseChannel(tr.LoggedInChan)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/actions"
|
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/actions"
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/requests"
|
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
json_models "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/json"
|
json_models "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/json"
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport"
|
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport"
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
|
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -24,7 +25,7 @@ var (
|
|||||||
|
|
||||||
type FoundryApi struct {
|
type FoundryApi struct {
|
||||||
//TODO: make check of admin's authentication
|
//TODO: make check of admin's authentication
|
||||||
transport *transport.FoundryTransport
|
Transport *transport.FoundryTransport
|
||||||
IsAvailable bool
|
IsAvailable bool
|
||||||
|
|
||||||
// Logger *slog.Logger
|
// Logger *slog.Logger
|
||||||
@@ -37,16 +38,16 @@ func NewFoundry() *FoundryApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (foundry *FoundryApi) SetTransport(tr *transport.FoundryTransport) {
|
func (foundry *FoundryApi) SetTransport(tr *transport.FoundryTransport) {
|
||||||
foundry.transport = tr
|
foundry.Transport = tr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (foundry *FoundryApi) ListenAndServeWS() error {
|
func (foundry *FoundryApi) ListenAndServeWS() error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
foundry.transport.ReadChan = *types.InitWsChannels()
|
foundry.Transport.ReadChan = *types.InitWsChannels()
|
||||||
defer foundry.transport.ReadChan.Close()
|
defer foundry.Transport.ReadChan.Close()
|
||||||
|
|
||||||
foundry.background(foundry.transport.ListenWebSocket)
|
foundry.background(foundry.Transport.ListenWebSocket)
|
||||||
|
|
||||||
err = foundry.ServeWebSocket()
|
err = foundry.ServeWebSocket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -57,21 +58,21 @@ func (foundry *FoundryApi) ListenAndServeWS() error {
|
|||||||
|
|
||||||
func (foundry *FoundryApi) ServeWebSocket() error {
|
func (foundry *FoundryApi) ServeWebSocket() error {
|
||||||
var err error
|
var err error
|
||||||
wsChannels := &foundry.transport.ReadChan
|
wsChannels := &foundry.Transport.ReadChan
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case message, ok := <-wsChannels.Msg():
|
case message, ok := <-wsChannels.Msg():
|
||||||
if !ok {
|
if !ok {
|
||||||
foundry.transport.Logger.Debug("Read channel is closed", "type", types.WebSocketCode)
|
foundry.Transport.Logger.Debug("Read channel is closed", "type", types.WebSocketCode)
|
||||||
foundry.transport.Http.SessionID = nil
|
foundry.Transport.Http.SessionID = nil
|
||||||
return ChannelIsClosed
|
return ChannelIsClosed
|
||||||
}
|
}
|
||||||
foundry.transport.Logger.Debug("Data has been received\n", "msg", message.Code, "type", types.WebSocketCode) //, "msg", message.MsgJson)
|
foundry.Transport.Logger.Debug("Data has been received\n", "msg", message.Code, "type", types.WebSocketCode) //, "msg", message.MsgJson)
|
||||||
|
|
||||||
switch message.Code {
|
switch message.Code {
|
||||||
case types.RespPingCode, types.RespSessionDataCode:
|
case types.RespPingCode, types.RespSessionDataCode:
|
||||||
err := foundry.transport.SendOnlyCodeRequest(message.Code)
|
err := foundry.Transport.SendOnlyCodeRequest(message.Code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wsChannels.Err() <- &types.FoundryError{Direction: types.WriterCode, Err: err, Type: types.WebSocketCode}
|
wsChannels.Err() <- &types.FoundryError{Direction: types.WriterCode, Err: err, Type: types.WebSocketCode}
|
||||||
continue
|
continue
|
||||||
@@ -84,69 +85,69 @@ func (foundry *FoundryApi) ServeWebSocket() error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
foundry.transport.Logger.Debug("RespServerChangeCode", "data", data)
|
foundry.Transport.Logger.Debug("RespServerChangeCode", "data", data)
|
||||||
if data == nil {
|
if data == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err = data.Action(foundry.transport, &foundry.Status)
|
err = data.Action(foundry.Transport, &foundry.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wsChannels.Err() <- &types.FoundryError{Direction: types.ReaderCode, Err: err, Type: types.WebSocketCode}
|
wsChannels.Err() <- &types.FoundryError{Direction: types.ReaderCode, Err: err, Type: types.WebSocketCode}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
case types.RespDataCode:
|
case types.RespDataCode:
|
||||||
foundry.transport.ExchangeChan.Msgs[message.Id] = make(chan []byte, 1)
|
foundry.Transport.ExchangeChan.Msgs[message.Id] = make(chan []byte, 1)
|
||||||
foundry.transport.ExchangeChan.Msgs[message.Id] <- []byte(message.MsgJson)
|
foundry.Transport.ExchangeChan.Msgs[message.Id] <- []byte(message.MsgJson)
|
||||||
go foundry.transport.CloseMsgChannel(message.Id, 5*time.Second)
|
go foundry.Transport.CloseMsgChannel(message.Id, 5*time.Second)
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
case err = <-wsChannels.Err():
|
case err = <-wsChannels.Err():
|
||||||
var foundryErr *types.FoundryError
|
var foundryErr *types.FoundryError
|
||||||
if errors.As(err, &foundryErr) {
|
if errors.As(err, &foundryErr) {
|
||||||
if foundryErr.IsFatal {
|
if foundryErr.IsFatal {
|
||||||
foundry.transport.CloseWebSocketConn()
|
foundry.Transport.CloseWebSocketConn()
|
||||||
foundry.transport.Http.SessionID = nil
|
foundry.Transport.Http.SessionID = nil
|
||||||
return foundryErr
|
return foundryErr
|
||||||
}
|
}
|
||||||
foundry.transport.Logger.Warn("Got error when listening or served", "err", err.Error(), "type", foundryErr.Type, "direction", foundryErr.Direction)
|
foundry.Transport.Logger.Warn("Got error when listening or served", "err", err.Error(), "type", foundryErr.Type, "direction", foundryErr.Direction)
|
||||||
}
|
}
|
||||||
case <-wsChannels.Done():
|
case <-wsChannels.Done():
|
||||||
foundry.transport.CloseWebSocketConn()
|
foundry.Transport.CloseWebSocketConn()
|
||||||
foundry.transport.Http.SessionID = nil
|
foundry.Transport.Http.SessionID = nil
|
||||||
return ListenIsDone
|
return ListenIsDone
|
||||||
case <-wsChannels.Reconnect():
|
case <-wsChannels.Reconnect():
|
||||||
foundry.transport.CloseWebSocketConn()
|
foundry.Transport.CloseWebSocketConn()
|
||||||
return ReconnectToWebSocket
|
return ReconnectToWebSocket
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (foundry *FoundryApi) HandleWSRequest(msgType string) ([]byte, error) {
|
func (foundry *FoundryApi) HandleWSRequest(msgType string) ([]byte, error) {
|
||||||
msg := types.NewWsMessageByPage(msgType, foundry.transport.CurrWsId)
|
msg := types.NewWsMessageByPage(msgType, foundry.Transport.CurrWsId)
|
||||||
|
|
||||||
return foundry.transport.HandleWebsocketRequest(msg)
|
return foundry.Transport.HandleWebsocketRequest(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (foundry *FoundryApi) Shutdown() error {
|
func (foundry *FoundryApi) Shutdown() error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
foundry.transport.Logger.Info("Completing foundry background tasks")
|
foundry.Transport.Logger.Info("Completing foundry background tasks")
|
||||||
|
|
||||||
foundryClosed := make(chan struct{})
|
foundryClosed := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
types.CloseChannel(foundry.transport.ReadChan.Done())
|
types.CloseChannel(foundry.Transport.ReadChan.Done())
|
||||||
|
|
||||||
foundry.wg.Wait()
|
foundry.wg.Wait()
|
||||||
foundry.transport.ExchangeChan.Close()
|
foundry.Transport.ExchangeChan.Close()
|
||||||
foundry.transport.ReadChan.Close()
|
foundry.Transport.ReadChan.Close()
|
||||||
|
|
||||||
types.CloseChannel(foundryClosed)
|
types.CloseChannel(foundryClosed)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-foundryClosed:
|
case <-foundryClosed:
|
||||||
foundry.transport.Logger.Info("Stopped foundry server")
|
foundry.Transport.Logger.Info("Stopped foundry server")
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return CloseTimeoutExceed
|
return CloseTimeoutExceed
|
||||||
}
|
}
|
||||||
@@ -156,24 +157,24 @@ func (foundry *FoundryApi) Shutdown() error {
|
|||||||
|
|
||||||
func (foundry *FoundryApi) ConnectToWebSocket() (bool, error) {
|
func (foundry *FoundryApi) ConnectToWebSocket() (bool, error) {
|
||||||
var err error
|
var err error
|
||||||
if !foundry.transport.HasSessionId() {
|
if !foundry.Transport.HasSessionId() {
|
||||||
err = foundry.transport.InitSessionId()
|
err = foundry.Transport.InitSessionId()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = foundry.transport.ConnectToFoundry()
|
err = foundry.Transport.ConnectToFoundry()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = foundry.transport.InitWebSocketConnection()
|
err = foundry.Transport.InitWebSocketConnection()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
status, err := foundry.transport.Http.GetStatus()
|
status, err := foundry.Transport.Http.GetStatus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -195,21 +196,21 @@ func (foundry *FoundryApi) StartListenFoundry() {
|
|||||||
ok, err := foundry.ConnectToWebSocket()
|
ok, err := foundry.ConnectToWebSocket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, ReconnectToWebSocket) {
|
if errors.Is(err, ReconnectToWebSocket) {
|
||||||
if foundry.transport.ReconnectNum >= foundry.transport.ReconnectNumMax {
|
if foundry.Transport.ReconnectNum >= foundry.Transport.ReconnectNumMax {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
time.Sleep(foundry.transport.ReconnectTimeout)
|
time.Sleep(foundry.Transport.ReconnectTimeout)
|
||||||
foundry.transport.ReconnectNum++
|
foundry.Transport.ReconnectNum++
|
||||||
foundry.transport.Logger.Info("Reconnecting to WebSocket", "times", foundry.transport.ReconnectNum)
|
foundry.Transport.Logger.Info("Reconnecting to WebSocket", "times", foundry.Transport.ReconnectNum)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
foundry.transport.Logger.Error("Error raised", "err", err.Error())
|
foundry.Transport.Logger.Error("Error raised", "err", err.Error())
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
timeInterval = 1 * time.Second
|
timeInterval = 1 * time.Second
|
||||||
}
|
}
|
||||||
foundry.transport.Logger.Info("Trying to reconnect", "timer", timeInterval.String())
|
foundry.Transport.Logger.Info("Trying to reconnect", "timer", timeInterval.String())
|
||||||
|
|
||||||
time.Sleep(timeInterval)
|
time.Sleep(timeInterval)
|
||||||
timeInterval = min(timeInterval*2, 15*time.Second)
|
timeInterval = min(timeInterval*2, 15*time.Second)
|
||||||
@@ -220,11 +221,28 @@ func (foundry *FoundryApi) StartListenFoundry() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (foundry *FoundryApi) Test() (*json_models.Game, error) {
|
func (foundry *FoundryApi) PrepareDB() error {
|
||||||
wsMsg := types.NewWsMessage("world", foundry.transport.CurrWsId)
|
dbConn := foundry.Transport.DB
|
||||||
foundry.transport.CurrWsId++
|
|
||||||
|
|
||||||
answer, err := foundry.transport.HandleWebsocketRequest(wsMsg)
|
group, _ := errgroup.WithContext(context.Background())
|
||||||
|
|
||||||
|
group.Go(func() error { return db.DeleteSetupAll(dbConn) })
|
||||||
|
group.Go(func() error { return db.DeleteGameAll(dbConn) })
|
||||||
|
group.Go(func() error { return db.DeleteSeqAll(dbConn) })
|
||||||
|
|
||||||
|
err := group.Wait()
|
||||||
|
if err != nil && !errors.Is(err, db.ErrorRecordNotFound) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (foundry *FoundryApi) Test() (*json_models.Game, error) {
|
||||||
|
wsMsg := types.NewWsMessage("world", foundry.Transport.CurrWsId)
|
||||||
|
foundry.Transport.CurrWsId++
|
||||||
|
|
||||||
|
answer, err := foundry.Transport.HandleWebsocketRequest(wsMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -238,11 +256,6 @@ func (foundry *FoundryApi) Test() (*json_models.Game, error) {
|
|||||||
}
|
}
|
||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
|
|
||||||
foundry.transport.Logger.Info("World data successfully received and parsed", "parseTime", elapsed)
|
foundry.Transport.Logger.Info("World data successfully received and parsed", "parseTime", elapsed)
|
||||||
return &game[0], nil
|
return &game[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove
|
|
||||||
func (foundry *FoundryApi) GetHTTP() *requests.FoundryHttpRequest {
|
|
||||||
return foundry.transport.Http
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ func (f *FoundryApi) background(fn func()) {
|
|||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
f.transport.Logger.Error(fmt.Sprintf("%s", err))
|
f.Transport.Logger.Error(fmt.Sprintf("%s", err))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@@ -25,9 +24,13 @@ type Actor struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Actor) Query(data *InsertId[uint]) {
|
func (a *Actor) Query(data *InsertId[uint]) {
|
||||||
data.query = fmt.Sprintf(`
|
data.query = `
|
||||||
INSERT INTO actor (%s, id, img, name, type, folder, sort)
|
INSERT INTO actor (game_id, id, img, name, type, folder, sort)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7)`, data.fieldName)
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
game_id = EXCLUDED.game_id,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
RETURNING (created_at == updated_at) AS is_inserted`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Actor) InsertObjects(tx *sqlx.Tx) error {
|
func (a *Actor) InsertObjects(tx *sqlx.Tx) error {
|
||||||
@@ -53,13 +56,17 @@ func (a *Actor) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
|||||||
|
|
||||||
args := []any{data.id, a.ID, a.Img, a.Name, a.Type, a.Folder, a.Sort}
|
args := []any{data.id, a.ID, a.Img, a.Name, a.Type, a.Folder, a.Sort}
|
||||||
|
|
||||||
_, err := tx.Exec(data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return a.InsertObjects(tx)
|
return a.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Actor) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
func (a *Actor) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||||
if data.query == "" {
|
if data.query == "" {
|
||||||
@@ -68,10 +75,14 @@ func (a *Actor) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]
|
|||||||
|
|
||||||
args := []any{data.id, a.ID, a.Img, a.Name, a.Type, a.Folder, a.Sort}
|
args := []any{data.id, a.ID, a.Img, a.Name, a.Type, a.Folder, a.Sort}
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return a.InsertObjects(tx)
|
return a.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -30,7 +30,11 @@ type CardDeck struct {
|
|||||||
func (c *CardDeck) Query(data *InsertId[uint]) {
|
func (c *CardDeck) Query(data *InsertId[uint]) {
|
||||||
data.query = `
|
data.query = `
|
||||||
INSERT INTO card_deck (game_id, id, name, type, description, img, folder, width, height, rotation, sort, display_count)
|
INSERT INTO card_deck (game_id, id, name, type, description, img, folder, width, height, rotation, sort, display_count)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
game_id = EXCLUDED.game_id,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
RETURNING (created_at == updated_at) AS is_inserted`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CardDeck) InsertObjects(tx *sqlx.Tx) error {
|
func (c *CardDeck) InsertObjects(tx *sqlx.Tx) error {
|
||||||
@@ -55,11 +59,15 @@ func (c *CardDeck) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
|||||||
|
|
||||||
args := []any{data.id, c.ID, c.Name, c.Type, c.Description, c.Img, c.Folder, c.Width, c.Height, c.Rotation, c.Sort, c.DisplayCount}
|
args := []any{data.id, c.ID, c.Name, c.Type, c.Description, c.Img, c.Folder, c.Width, c.Height, c.Rotation, c.Sort, c.DisplayCount}
|
||||||
|
|
||||||
_, err := tx.Exec(data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
|
return c.InsertObjects(tx)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,11 +78,15 @@ func (c *CardDeck) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[ui
|
|||||||
|
|
||||||
args := []any{data.id, c.ID, c.Name, c.Type, c.Description, c.Img, c.Folder, c.Width, c.Height, c.Rotation, c.Sort, c.DisplayCount}
|
args := []any{data.id, c.ID, c.Name, c.Type, c.Description, c.Img, c.Folder, c.Width, c.Height, c.Rotation, c.Sort, c.DisplayCount}
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
|
return c.InsertObjects(tx)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +113,11 @@ type Card struct {
|
|||||||
func (c *Card) Query(data *InsertId[string]) {
|
func (c *Card) Query(data *InsertId[string]) {
|
||||||
data.query = `
|
data.query = `
|
||||||
INSERT INTO card (card_deck_id, id, name, type, suit, description, origin, width, height, rotation, value, face, sort, drawn)
|
INSERT INTO card (card_deck_id, id, name, type, suit, description, origin, width, height, rotation, value, face, sort, drawn)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)`
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
card_deck_id = EXCLUDED.card_deck_id,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
RETURNING (created_at == updated_at) AS is_inserted`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Card) InsertObjects(tx *sqlx.Tx) error {
|
func (c *Card) InsertObjects(tx *sqlx.Tx) error {
|
||||||
@@ -124,13 +140,17 @@ func (c *Card) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
|||||||
return ErrNoQuery
|
return ErrNoQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []any{data.id, c.ID, c.Name, c.Type, c.Suit, c.Description, c.Origin, c.Width, c.Height, c.Rotation, c.Value, c.Sort, c.Drawn}
|
args := []any{data.id, c.ID, c.Name, c.Type, c.Suit, c.Description, c.Origin, c.Width, c.Height, c.Rotation, c.Value, c.Face, c.Sort, c.Drawn}
|
||||||
|
|
||||||
_, err := tx.Exec(data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
|
return c.InsertObjects(tx)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,13 +159,17 @@ func (c *Card) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string
|
|||||||
return ErrNoQuery
|
return ErrNoQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []any{data.id, c.ID, c.Name, c.Type, c.Suit, c.Description, c.Origin, c.Width, c.Height, c.Rotation, c.Value, c.Sort, c.Drawn}
|
args := []any{data.id, c.ID, c.Name, c.Type, c.Suit, c.Description, c.Origin, c.Width, c.Height, c.Rotation, c.Value, c.Face, c.Sort, c.Drawn}
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
|
return c.InsertObjects(tx)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +26,11 @@ type Combat struct {
|
|||||||
func (c *Combat) Query(data *InsertId[uint]) {
|
func (c *Combat) Query(data *InsertId[uint]) {
|
||||||
data.query = `
|
data.query = `
|
||||||
INSERT INTO combat (game_id, id, type, scene, round, turn, sort, active)
|
INSERT INTO combat (game_id, id, type, scene, round, turn, sort, active)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
game_id = EXCLUDED.game_id,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
RETURNING (created_at == updated_at) AS is_inserted`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Combat) InsertObjects(tx *sqlx.Tx) error {
|
func (c *Combat) InsertObjects(tx *sqlx.Tx) error {
|
||||||
@@ -51,11 +55,15 @@ func (c *Combat) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
|||||||
|
|
||||||
args := []any{data.id, c.ID, c.Type, c.Scene, c.Round, c.Turn, c.Sort, c.Active}
|
args := []any{data.id, c.ID, c.Type, c.Scene, c.Round, c.Turn, c.Sort, c.Active}
|
||||||
|
|
||||||
_, err := tx.Exec(data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
|
return c.InsertObjects(tx)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,11 +74,15 @@ func (c *Combat) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint
|
|||||||
|
|
||||||
args := []any{data.id, c.ID, c.Type, c.Scene, c.Round, c.Turn, c.Sort, c.Active}
|
args := []any{data.id, c.ID, c.Type, c.Scene, c.Round, c.Turn, c.Sort, c.Active}
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
|
return c.InsertObjects(tx)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,12 +103,16 @@ type Combatant struct {
|
|||||||
|
|
||||||
func (c *Combatant) Query(data *InsertId[string]) {
|
func (c *Combatant) Query(data *InsertId[string]) {
|
||||||
data.query = `
|
data.query = `
|
||||||
INSERT INTO combatant (combat_id, id, token_id, scene_id, actor_id, img, group_, initiative, hidden, defeated)
|
INSERT INTO combatant (combat_id, id, token_id, scene_id, actor_id, type, img, group_, initiative, hidden, defeated)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
combat_id = EXCLUDED.combat_id,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
RETURNING (created_at == updated_at) AS is_inserted`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Combatant) InsertObjects(tx *sqlx.Tx) error {
|
func (c *Combatant) InsertObjects(tx *sqlx.Tx) error {
|
||||||
relId := InsertId[string]{id: c.ID, fieldName: "combat_id"}
|
relId := InsertId[string]{id: c.ID, fieldName: "combatant_id"}
|
||||||
group, ctx := errgroup.WithContext(context.Background())
|
group, ctx := errgroup.WithContext(context.Background())
|
||||||
|
|
||||||
InsertWithCtxParallel(group, ctx, tx, c.Stats, relId)
|
InsertWithCtxParallel(group, ctx, tx, c.Stats, relId)
|
||||||
@@ -113,13 +129,17 @@ func (c *Combatant) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
|||||||
return ErrNoQuery
|
return ErrNoQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []any{data.id, c.ID, c.TokenId, c.SceneId, c.ActorId, c.Img, c.Group, c.Initiative, c.Hidden, c.Defeated}
|
args := []any{data.id, c.ID, c.TokenId, c.SceneId, c.ActorId, c.Type, c.Img, c.Group, c.Initiative, c.Hidden, c.Defeated}
|
||||||
|
|
||||||
_, err := tx.Exec(data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
|
return c.InsertObjects(tx)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,12 +148,16 @@ func (c *Combatant) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[s
|
|||||||
return ErrNoQuery
|
return ErrNoQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []any{data.id, c.ID, c.TokenId, c.SceneId, c.ActorId, c.Img, c.Group, c.Initiative, c.Hidden, c.Defeated}
|
args := []any{data.id, c.ID, c.TokenId, c.SceneId, c.ActorId, c.Type, c.Img, c.Group, c.Initiative, c.Hidden, c.Defeated}
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
|
return c.InsertObjects(tx)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package db
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrorSetupMoreThanOne = errors.New("Passed more than one setupData")
|
|
||||||
ErrorSetupNotFound = errors.New("Not found setup data from your request")
|
|
||||||
ErrorRecordNotFound = errors.New("Record not found")
|
|
||||||
)
|
|
||||||
@@ -90,10 +90,13 @@ type WorldFolder struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *WorldFolder) Query(data *InsertId[uint]) {
|
func (w *WorldFolder) Query(data *InsertId[uint]) {
|
||||||
data.query = fmt.Sprintf(`
|
data.query = `
|
||||||
INSERT INTO world_folder (%s, id, name, type, folder, sorting, description, color, sort)
|
INSERT INTO world_folder (game_id, id, name, type, folder, sorting, description, color, sort)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||||
RETURNING id`, data.fieldName)
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
game_id = EXCLUDED.game_id,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
RETURNING (created_at == updated_at) AS is_inserted`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WorldFolder) InsertObjects(tx *sqlx.Tx) error {
|
func (w *WorldFolder) InsertObjects(tx *sqlx.Tx) error {
|
||||||
@@ -116,13 +119,17 @@ func (w *WorldFolder) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
|||||||
|
|
||||||
args := []any{data.id, w.ID, w.Name, w.Type, w.Folder, w.Sorting, w.Description, w.Color, w.Sort}
|
args := []any{data.id, w.ID, w.Name, w.Type, w.Folder, w.Sorting, w.Description, w.Color, w.Sort}
|
||||||
|
|
||||||
_, err := tx.Exec(data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return w.InsertObjects(tx)
|
return w.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (w *WorldFolder) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
func (w *WorldFolder) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||||
if data.query == "" {
|
if data.query == "" {
|
||||||
@@ -131,10 +138,14 @@ func (w *WorldFolder) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId
|
|||||||
|
|
||||||
args := []any{data.id, w.ID, w.Name, w.Type, w.Folder, w.Sorting, w.Description, w.Color, w.Sort}
|
args := []any{data.id, w.ID, w.Name, w.Type, w.Folder, w.Sorting, w.Description, w.Color, w.Sort}
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return w.InsertObjects(tx)
|
return w.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,308 +0,0 @@
|
|||||||
package db
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StateType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
AuthState = StateType(0)
|
|
||||||
SetupState = StateType(1)
|
|
||||||
JoinState = StateType(2)
|
|
||||||
PlayersState = StateType(3)
|
|
||||||
UpdateState = StateType(4)
|
|
||||||
LicenseState = StateType(5)
|
|
||||||
)
|
|
||||||
|
|
||||||
type FoundryState struct {
|
|
||||||
Id int64
|
|
||||||
IsAdmin bool
|
|
||||||
IsSetup bool
|
|
||||||
Type StateType
|
|
||||||
CreatedAt time.Time
|
|
||||||
Options Options
|
|
||||||
Modules Modules
|
|
||||||
Systems Systems
|
|
||||||
Worlds Worlds
|
|
||||||
Users Users
|
|
||||||
}
|
|
||||||
|
|
||||||
type Compatibility struct {
|
|
||||||
Id int64
|
|
||||||
Minimum string
|
|
||||||
Verified string
|
|
||||||
Maximum string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Options struct {
|
|
||||||
Id int64
|
|
||||||
Language string
|
|
||||||
}
|
|
||||||
|
|
||||||
type FoundryStateModel struct {
|
|
||||||
DB *sqlx.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) Insert(state *FoundryState) error {
|
|
||||||
query := `
|
|
||||||
INSERT INTO foundry_state (is_admin, is_setup, state_type)
|
|
||||||
VALUES ($1, $2, $3)
|
|
||||||
RETURNING id, created_at`
|
|
||||||
|
|
||||||
args := []any{state.IsAdmin, state.IsSetup, state.Type}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&state.Id, &state.CreatedAt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = m.InsertOptions(&state.Options, state.Id)
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range state.Modules {
|
|
||||||
err = m.InsertModule(&state.Modules[i], state.Id)
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range state.Systems {
|
|
||||||
err = m.InsertSystem(&state.Systems[i], state.Id)
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range state.Worlds {
|
|
||||||
err = m.InsertWorld(&state.Worlds[i], state.Id)
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range state.Users {
|
|
||||||
err = m.InsertUser(&state.Users[i], state.Id)
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) InsertOptions(options *Options, stateId int64) error {
|
|
||||||
query := `
|
|
||||||
INSERT INTO options (state_id, lang)
|
|
||||||
VALUES ($1, $2)
|
|
||||||
RETURNING id`
|
|
||||||
|
|
||||||
args := []any{stateId, options.Language}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
return m.DB.QueryRowContext(ctx, query, args...).Scan(&options.Id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) Get(id int64) (*FoundryState, error) {
|
|
||||||
if id < 1 {
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
|
||||||
SELECT id, created_at, is_admin, is_setup, state_type
|
|
||||||
FROM foundry_state
|
|
||||||
WHERE id = $1`
|
|
||||||
|
|
||||||
var state FoundryState
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
err := m.DB.QueryRowContext(ctx, query, id).Scan(
|
|
||||||
&state.Id,
|
|
||||||
&state.CreatedAt,
|
|
||||||
&state.IsAdmin,
|
|
||||||
&state.IsSetup,
|
|
||||||
&state.Type,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state.Modules, err = m.GetModules(state.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
state.Systems, err = m.GetSystems(state.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
state.Worlds, err = m.GetWorlds(state.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
state.Users, err = m.GetUsers(state.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
options, err := m.GetOptions(state.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
state.Options = *options
|
|
||||||
|
|
||||||
return &state, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) GetOptions(idState int64) (*Options, error) {
|
|
||||||
if idState < 1 {
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
|
||||||
SELECT id, lang
|
|
||||||
FROM options
|
|
||||||
WHERE state_id = $1`
|
|
||||||
|
|
||||||
var options Options
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
err := m.DB.QueryRowContext(ctx, query, idState).Scan(
|
|
||||||
&options.Id,
|
|
||||||
&options.Language,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &options, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) GetIdByType(stateType StateType) (int64, error) {
|
|
||||||
if stateType < 0 {
|
|
||||||
return -1, ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
|
||||||
SELECT id
|
|
||||||
FROM foundry_state
|
|
||||||
WHERE state_type = $1`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
var id int64
|
|
||||||
|
|
||||||
err := m.DB.QueryRowContext(ctx, query, stateType).Scan(&id)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
|
||||||
return -1, ErrorRecordNotFound
|
|
||||||
default:
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return id, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) Delete(id int64) error {
|
|
||||||
if id < 1 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
|
||||||
DELETE FROM foundry_state
|
|
||||||
WHERE id = $1`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
result, err := m.DB.ExecContext(ctx, query, id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
rowsAffected, err := result.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rowsAffected == 0 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) DeleteAll() error {
|
|
||||||
query := `
|
|
||||||
DELETE FROM foundry_state`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
result, err := m.DB.ExecContext(ctx, query)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
rowsAffected, err := result.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rowsAffected == 0 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) DeleteAllSeq() error {
|
|
||||||
query := `
|
|
||||||
DELETE FROM sqlite_sequence`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
result, err := m.DB.ExecContext(ctx, query)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
rowsAffected, err := result.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rowsAffected == 0 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -68,24 +68,31 @@ func (g *Game) InsertObjects(tx *sqlx.Tx) error {
|
|||||||
InsertSliceParallel(group, tx, g.Modules, relData)
|
InsertSliceParallel(group, tx, g.Modules, relData)
|
||||||
InsertSliceParallel(group, tx, g.PackageWarnings, relData)
|
InsertSliceParallel(group, tx, g.PackageWarnings, relData)
|
||||||
InsertSliceParallel(group, tx, g.Packs, relDataString)
|
InsertSliceParallel(group, tx, g.Packs, relDataString)
|
||||||
// InsertSliceParallel(group, tx, g.Messages, relData)
|
InsertSliceParallel(group, tx, g.Messages, relData)
|
||||||
// InsertSliceParallel(group, tx, g.Combats, relData)
|
InsertSliceParallel(group, tx, g.Combats, relData)
|
||||||
// InsertSliceParallel(group, tx, g.CardDeck, relData)
|
InsertSliceParallel(group, tx, g.CardDeck, relData)
|
||||||
// InsertSliceParallel(group, tx, g.Users, relData)
|
InsertSliceParallel(group, tx, g.Users, relData)
|
||||||
// InsertSliceParallel(group, tx, g.Macros, relData)
|
InsertSliceParallel(group, tx, g.Macros, relData)
|
||||||
// InsertSliceParallel(group, tx, g.Folders, relData)
|
InsertSliceParallel(group, tx, g.Folders, relData)
|
||||||
// InsertSliceParallel(group, tx, g.Items,
|
InsertSliceParallel(group, tx, g.Settings, relData)
|
||||||
// InsertId[string]{id: strconv.FormatUint(uint64(g.ID), 10), fieldName: "game_id"})
|
InsertSliceParallel(group, tx, g.Journals, relData)
|
||||||
// InsertSliceParallel(group, tx, g.Settings, relData)
|
InsertSliceParallel(group, tx, g.Tables, relData)
|
||||||
// InsertSliceParallel(group, tx, g.Journals, relData)
|
InsertSliceParallel(group, tx, g.Playlists, relData)
|
||||||
// InsertSliceParallel(group, tx, g.Tables, relData)
|
|
||||||
// InsertSliceParallel(group, tx, g.Playlists, relData)
|
|
||||||
// InsertSliceParallel(group, tx, g.Actors, relData)
|
|
||||||
|
|
||||||
err := group.Wait()
|
err := group.Wait()
|
||||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InsertSliceParallelTimeout(group, tx, g.Items,
|
||||||
|
InsertId[string]{id: strconv.FormatUint(uint64(g.ID), 10), fieldName: "game_id"},
|
||||||
|
15*time.Second)
|
||||||
|
InsertSliceParallelTimeout(group, tx, g.Actors, relData, 15*time.Second)
|
||||||
|
|
||||||
|
err = group.Wait()
|
||||||
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
88
internal/foundry/models/db/index.go
Normal file
88
internal/foundry/models/db/index.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Index struct {
|
||||||
|
ID string
|
||||||
|
|
||||||
|
Folder string
|
||||||
|
Img string
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Index) Query(data *InsertId[string]) {
|
||||||
|
data.query = `
|
||||||
|
INSERT INTO index_ (id, folder, img, name, type)
|
||||||
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
|
ON CONFLICT(id) DO NOTHING`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Index) ConnectGameQuery(data *InsertId[string]) {
|
||||||
|
data.query = `
|
||||||
|
INSERT INTO pack_to_index (pack_id, index_id)
|
||||||
|
VALUES ($1, $2)
|
||||||
|
ON CONFLICT(pack_id, index_id) DO NOTHING`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Index) ConnectGame(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
|
if data.query == "" {
|
||||||
|
return ErrNoQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []any{data.id, i.ID}
|
||||||
|
|
||||||
|
_, err := tx.Exec(data.query, args...)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Index) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
|
if data.query == "" {
|
||||||
|
return ErrNoQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []any{i.ID, i.Folder, i.Img, i.Name, i.Type}
|
||||||
|
|
||||||
|
_, err := tx.Exec(data.query, args...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dataCopy := *data
|
||||||
|
i.ConnectGameQuery(&dataCopy)
|
||||||
|
|
||||||
|
return i.ConnectGame(tx, &dataCopy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Index) ConnectGameCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
|
if data.query == "" {
|
||||||
|
return ErrNoQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []any{data.id, i.ID}
|
||||||
|
|
||||||
|
_, err := tx.ExecContext(ctx, data.query, args...)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Index) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
|
if data.query == "" {
|
||||||
|
return ErrNoQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []any{i.ID, i.Folder, i.Img, i.Name, i.Type}
|
||||||
|
|
||||||
|
_, err := tx.ExecContext(ctx, data.query, args...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dataCopy := *data
|
||||||
|
i.ConnectGameQuery(&dataCopy)
|
||||||
|
|
||||||
|
return i.ConnectGameCtx(ctx, tx, &dataCopy)
|
||||||
|
}
|
||||||
@@ -24,9 +24,12 @@ type Item struct {
|
|||||||
|
|
||||||
func (i *Item) Query(data *InsertId[string]) {
|
func (i *Item) Query(data *InsertId[string]) {
|
||||||
data.query = fmt.Sprintf(`
|
data.query = fmt.Sprintf(`
|
||||||
INSERT INTO world_folder (%s, id, img, name, type, folder, sort)
|
INSERT INTO item (%[1]s, id, img, name, type, folder, sort)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
RETURNING id`, data.fieldName)
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
%[1]s = EXCLUDED.%[1]s,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
RETURNING (created_at == updated_at) AS is_inserted`, data.fieldName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Item) InsertObjects(tx *sqlx.Tx) error {
|
func (i *Item) InsertObjects(tx *sqlx.Tx) error {
|
||||||
@@ -50,13 +53,17 @@ func (i *Item) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
|||||||
|
|
||||||
args := []any{data.id, i.ID, i.Img, i.Name, i.Type, i.Folder, i.Sort}
|
args := []any{data.id, i.ID, i.Img, i.Name, i.Type, i.Folder, i.Sort}
|
||||||
|
|
||||||
_, err := tx.Exec(data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return i.InsertObjects(tx)
|
return i.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Item) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
func (i *Item) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
if data.query == "" {
|
if data.query == "" {
|
||||||
@@ -65,10 +72,14 @@ func (i *Item) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string
|
|||||||
|
|
||||||
args := []any{data.id, i.ID, i.Img, i.Name, i.Type, i.Folder, i.Sort}
|
args := []any{data.id, i.ID, i.Img, i.Name, i.Type, i.Folder, i.Sort}
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return i.InsertObjects(tx)
|
return i.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@@ -20,9 +19,13 @@ type Journal struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (j *Journal) Query(data *InsertId[uint]) {
|
func (j *Journal) Query(data *InsertId[uint]) {
|
||||||
data.query = fmt.Sprintf(`
|
data.query = `
|
||||||
INSERT INTO journal (%s, id, name, sort)
|
INSERT INTO journal (game_id, id, name, sort)
|
||||||
VALUES ($1, $2, $3, $4)`, data.fieldName)
|
VALUES ($1, $2, $3, $4)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
game_id = EXCLUDED.game_id,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
RETURNING (created_at == updated_at) AS is_inserted`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *Journal) InsertObjects(tx *sqlx.Tx) error {
|
func (j *Journal) InsertObjects(tx *sqlx.Tx) error {
|
||||||
@@ -46,13 +49,17 @@ func (j *Journal) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
|||||||
|
|
||||||
args := []any{data.id, j.ID, j.Name, j.Sort}
|
args := []any{data.id, j.ID, j.Name, j.Sort}
|
||||||
|
|
||||||
_, err := tx.Exec(data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return j.InsertObjects(tx)
|
return j.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (j *Journal) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
func (j *Journal) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||||
if data.query == "" {
|
if data.query == "" {
|
||||||
@@ -61,10 +68,14 @@ func (j *Journal) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uin
|
|||||||
|
|
||||||
args := []any{data.id, j.ID, j.Name, j.Sort}
|
args := []any{data.id, j.ID, j.Name, j.Sort}
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return j.InsertObjects(tx)
|
return j.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -25,9 +25,13 @@ type JournalPage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (j *JournalPage) Query(data *InsertId[string]) {
|
func (j *JournalPage) Query(data *InsertId[string]) {
|
||||||
data.query = fmt.Sprintf(`
|
data.query = `
|
||||||
INSERT INTO journal_page (%s, id, name, type, src, sort)
|
INSERT INTO journal_page (journal_id, id, name, type, src, sort)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)`, data.fieldName)
|
VALUES ($1, $2, $3, $4, $5, $6)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
journal_id = EXCLUDED.journal_id,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
RETURNING (created_at == updated_at) AS is_inserted`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *JournalPage) InsertObjects(tx *sqlx.Tx) error {
|
func (j *JournalPage) InsertObjects(tx *sqlx.Tx) error {
|
||||||
@@ -55,13 +59,17 @@ func (j *JournalPage) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
|||||||
|
|
||||||
args := []any{data.id, j.ID, j.Name, j.Type, j.Src, j.Sort}
|
args := []any{data.id, j.ID, j.Name, j.Type, j.Src, j.Sort}
|
||||||
|
|
||||||
_, err := tx.Exec(data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return j.InsertObjects(tx)
|
return j.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (j *JournalPage) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
func (j *JournalPage) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
if data.query == "" {
|
if data.query == "" {
|
||||||
@@ -70,13 +78,17 @@ func (j *JournalPage) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId
|
|||||||
|
|
||||||
args := []any{data.id, j.ID, j.Name, j.Type, j.Src, j.Sort}
|
args := []any{data.id, j.ID, j.Name, j.Type, j.Src, j.Sort}
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return j.InsertObjects(tx)
|
return j.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type PageText struct {
|
type PageText struct {
|
||||||
ID uint
|
ID uint
|
||||||
@@ -27,7 +27,11 @@ type Macro struct {
|
|||||||
func (m *Macro) Query(data *InsertId[uint]) {
|
func (m *Macro) Query(data *InsertId[uint]) {
|
||||||
data.query = `
|
data.query = `
|
||||||
INSERT INTO macro (game_id, id, command, name, type, img, author, scope, folder, sort)
|
INSERT INTO macro (game_id, id, command, name, type, img, author, scope, folder, sort)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
game_id = EXCLUDED.game_id,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
RETURNING (created_at == updated_at) AS is_inserted`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Macro) InsertObjects(tx *sqlx.Tx) error {
|
func (m *Macro) InsertObjects(tx *sqlx.Tx) error {
|
||||||
@@ -51,11 +55,15 @@ func (m *Macro) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
|||||||
|
|
||||||
args := []any{data.id, m.ID, m.Command, m.Name, m.Type, m.Img, m.Author, m.Scope, m.Folder, m.Sort}
|
args := []any{data.id, m.ID, m.Command, m.Name, m.Type, m.Img, m.Author, m.Scope, m.Folder, m.Sort}
|
||||||
|
|
||||||
_, err := tx.Exec(data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
|
return m.InsertObjects(tx)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,10 +74,14 @@ func (m *Macro) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]
|
|||||||
|
|
||||||
args := []any{data.id, m.ID, m.Command, m.Name, m.Type, m.Img, m.Author, m.Scope, m.Folder, m.Sort}
|
args := []any{data.id, m.ID, m.Command, m.Name, m.Type, m.Img, m.Author, m.Scope, m.Folder, m.Sort}
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
|
return m.InsertObjects(tx)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -30,8 +30,12 @@ type Message struct {
|
|||||||
|
|
||||||
func (m *Message) Query(data *InsertId[uint]) {
|
func (m *Message) Query(data *InsertId[uint]) {
|
||||||
data.query = `
|
data.query = `
|
||||||
INSERT INTO addresses (game_id, id, blind, emote, style, timestamp, content, author, type, flavor, sound)
|
INSERT INTO message (game_id, id, blind, emote, style, timestamp, content, author, type, flavor, sound)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
game_id = EXCLUDED.game_id,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
RETURNING (created_at == updated_at) AS is_inserted`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) InsertObjects(tx *sqlx.Tx) error {
|
func (m *Message) InsertObjects(tx *sqlx.Tx) error {
|
||||||
@@ -58,11 +62,15 @@ func (m *Message) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
|||||||
|
|
||||||
args := []any{data.id, m.ID, m.Blind, m.Emote, m.Style, m.Timestamp, m.Content, m.Author, m.Type, m.Flavor, m.Sound}
|
args := []any{data.id, m.ID, m.Blind, m.Emote, m.Style, m.Timestamp, m.Content, m.Author, m.Type, m.Flavor, m.Sound}
|
||||||
|
|
||||||
_, err := tx.Exec(data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
|
return m.InsertObjects(tx)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,11 +81,15 @@ func (m *Message) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uin
|
|||||||
|
|
||||||
args := []any{data.id, m.ID, m.Blind, m.Emote, m.Style, m.Timestamp, m.Content, m.Author, m.Type, m.Flavor, m.Sound}
|
args := []any{data.id, m.ID, m.Blind, m.Emote, m.Style, m.Timestamp, m.Content, m.Author, m.Type, m.Flavor, m.Sound}
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
|
return m.InsertObjects(tx)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +105,7 @@ type Speaker struct {
|
|||||||
func (s Speaker) Query(data *InsertId[string]) {
|
func (s Speaker) Query(data *InsertId[string]) {
|
||||||
data.query = fmt.Sprintf(`
|
data.query = fmt.Sprintf(`
|
||||||
INSERT INTO speaker (%s, scene, actor, token, alias)
|
INSERT INTO speaker (%s, scene, actor, token, alias)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)`, data.fieldName)
|
VALUES ($1, $2, $3, $4, $5)`, data.fieldName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Speaker) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
func (s Speaker) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package db
|
|
||||||
|
|
||||||
import "github.com/jmoiron/sqlx"
|
|
||||||
|
|
||||||
// var (
|
|
||||||
// ErrRecordNotFound = errors.New("record not found")
|
|
||||||
// ErrEditConflict = errors.New("edit conflict")
|
|
||||||
// )
|
|
||||||
|
|
||||||
type Models struct {
|
|
||||||
FoundryState FoundryStateModel
|
|
||||||
}
|
|
||||||
|
|
||||||
// type Models struct {
|
|
||||||
// Movies interface {
|
|
||||||
// Insert(movie *Movie) error
|
|
||||||
// Get(id int64) (*Movie, error)
|
|
||||||
// Update(movie *Movie) error
|
|
||||||
// Delete(id int64) error
|
|
||||||
// GetAll(title string, genres []string, filter Filters) ([]*Movie, Metadata, error)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
func NewModels(db *sqlx.DB) *Models {
|
|
||||||
return &Models{
|
|
||||||
FoundryState: FoundryStateModel{DB: db},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// func NewMockModels() Models {
|
|
||||||
// return Models{
|
|
||||||
// Movies: MockMovieModel{},
|
|
||||||
// Users: MockMovieModel{},
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@@ -4,287 +4,185 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Module struct {
|
type Module struct {
|
||||||
Id int64
|
ID string
|
||||||
TextId string
|
|
||||||
Title string
|
Title string
|
||||||
Description string
|
Description string
|
||||||
Url string
|
URL string
|
||||||
|
License string
|
||||||
|
Readme string
|
||||||
|
Bugs string
|
||||||
|
Changelog string
|
||||||
Version string
|
Version string
|
||||||
|
Download string
|
||||||
|
Manifest string
|
||||||
|
Socket bool
|
||||||
|
Protected bool
|
||||||
|
Exclusive bool
|
||||||
|
PersistentStorage bool
|
||||||
|
CoreTranslation bool
|
||||||
|
Library bool
|
||||||
|
Locked bool
|
||||||
|
Owned bool
|
||||||
|
HasStorage bool
|
||||||
|
Active bool
|
||||||
Availability int
|
Availability int
|
||||||
CreatedAt time.Time
|
DocumentTypes DocumentTypes
|
||||||
Languages []Language
|
Relationships Relationships
|
||||||
Compatibility Compatibility
|
Compatibility Compatibility
|
||||||
|
Scripts []string
|
||||||
|
Esmodules []string
|
||||||
|
Tags []string
|
||||||
|
Authors []*Author
|
||||||
|
Media []*Media
|
||||||
|
Styles []*Style
|
||||||
|
Languages []*Language
|
||||||
|
Packs []*Pack
|
||||||
|
PackFolders []*Folder
|
||||||
}
|
}
|
||||||
|
|
||||||
type Language struct {
|
func (m *Module) Query(data *InsertId[uint]) {
|
||||||
Id int64
|
data.query = `
|
||||||
Lang string
|
INSERT INTO module (setup_id, id, title, description, url, license, readme, bugs,
|
||||||
Name string
|
changelog, version, manifest, download, socket, protected, exclusive_, persistent_storage,
|
||||||
Path string
|
core_translation, library, locked, owned, has_storage, active, availability)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17,
|
||||||
|
$18, $19, $20, $21, $22, $23)
|
||||||
|
ON CONFLICT(id) DO NOTHING`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Modules []Module
|
func (m *Module) ConnectGameQuery(data *InsertId[uint]) {
|
||||||
|
data.query = `
|
||||||
func (modules Modules) GetById(id int) *Module {
|
INSERT INTO game_to_module (game_id, module_id)
|
||||||
return &modules[id]
|
VALUES ($1, $2)`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m FoundryStateModel) InsertModule(module *Module, stateId int64) error {
|
func (m *Module) InsertObjects(tx *sqlx.Tx) error {
|
||||||
query := `
|
relId := InsertId[string]{id: m.ID, fieldName: "module_id"}
|
||||||
INSERT INTO modules (state_id, text_id, title, description, url, version, availability)
|
group, ctx := errgroup.WithContext(context.Background())
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
||||||
RETURNING id, created_at`
|
|
||||||
|
|
||||||
args := []any{stateId, module.TextId, module.Title, module.Description, module.Url, module.Version, module.Availability}
|
InsertWithCtxParallel(group, ctx, tx, m.DocumentTypes, relId)
|
||||||
|
InsertWithCtxParallel(group, ctx, tx, m.Relationships, relId)
|
||||||
|
InsertWithCtxParallel(group, ctx, tx, m.Compatibility, relId)
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
scriptRelId := &InsertId[string]{id: m.ID, fieldName: "module_id", tableName: "scripts"}
|
||||||
defer cancel()
|
InsertSimpleSliceParallel(group, tx, m.Scripts, scriptRelId)
|
||||||
|
esModulesRelId := &InsertId[string]{id: m.ID, fieldName: "module_id", tableName: "es_modules"}
|
||||||
|
InsertSimpleSliceParallel(group, tx, m.Esmodules, esModulesRelId)
|
||||||
|
tagsRelId := &InsertId[string]{id: m.ID, fieldName: "module_id", tableName: "tags"}
|
||||||
|
InsertSimpleSliceParallel(group, tx, m.Tags, tagsRelId)
|
||||||
|
|
||||||
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&module.Id, &module.CreatedAt)
|
InsertSliceParallel(group, tx, m.Authors, relId)
|
||||||
if err != nil {
|
InsertSliceParallel(group, tx, m.Media, relId)
|
||||||
return err
|
InsertSliceParallel(group, tx, m.Styles, relId)
|
||||||
}
|
InsertSliceParallel(group, tx, m.Languages, relId)
|
||||||
for i := range module.Languages {
|
InsertSliceParallel(group, tx, m.Packs, relId)
|
||||||
err = m.InsertModuleLanguages(&module.Languages[i], module.Id)
|
InsertSliceParallel(group, tx, m.PackFolders, relId)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return m.InsertModuleCompatibility(&module.Compatibility, module.Id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) InsertModuleCompatibility(compatibility *Compatibility, moduleId int64) error {
|
err := group.Wait()
|
||||||
query := `
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
INSERT INTO modules_compatibility (module_id, minimum, verified, maximum)
|
|
||||||
VALUES ($1, $2, $3, $4)
|
|
||||||
RETURNING id`
|
|
||||||
|
|
||||||
args := []any{moduleId, compatibility.Minimum, compatibility.Verified, compatibility.Maximum}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&compatibility.Id)
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m FoundryStateModel) InsertModuleLanguages(lang *Language, moduleId int64) error {
|
func (m *Module) ConnectGame(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||||
query := `
|
if data.query == "" {
|
||||||
INSERT INTO modules_languages (module_id, language, name, path)
|
return ErrNoQuery
|
||||||
VALUES ($1, $2, $3, $4)
|
}
|
||||||
RETURNING id`
|
|
||||||
|
|
||||||
args := []any{moduleId, lang.Lang, lang.Name, lang.Path}
|
args := []any{data.id, m.ID}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
_, err := tx.Exec(data.query, args...)
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&lang.Id)
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
func (m *Module) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||||
|
if data.query == "" {
|
||||||
|
return ErrNoQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m FoundryStateModel) GetModules(idState int64) (Modules, error) {
|
var dataId *uint
|
||||||
if idState < 1 {
|
if strings.EqualFold(data.fieldName, "setup_id") {
|
||||||
return nil, ErrorRecordNotFound
|
dataId = &data.id
|
||||||
}
|
}
|
||||||
|
|
||||||
query := `
|
args := []any{dataId, m.ID, m.Title, m.Description, m.URL, m.License, m.Readme, m.Bugs,
|
||||||
SELECT id, created_at, text_id, title, description, url, version, availability
|
m.Changelog, m.Version, m.Manifest, m.Download, m.Socket, m.Protected, m.Exclusive, m.PersistentStorage,
|
||||||
FROM modules
|
m.CoreTranslation, m.Library, m.Locked, m.Owned, m.HasStorage, m.Active, m.Availability}
|
||||||
WHERE state_id = $1`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
res, err := tx.Exec(data.query, args...)
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
rows, err := m.DB.QueryContext(ctx, query, idState)
|
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
modules := make(Modules, 0, 1)
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
var module Module
|
|
||||||
err := rows.Scan(
|
|
||||||
&module.Id,
|
|
||||||
&module.CreatedAt,
|
|
||||||
&module.TextId,
|
|
||||||
&module.Title,
|
|
||||||
&module.Description,
|
|
||||||
&module.Url,
|
|
||||||
&module.Version,
|
|
||||||
&module.Availability,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
module.Languages, err = m.GetModuleLanguages(module.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
compatibility, err := m.GetModuleCompatibility(module.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
module.Compatibility = *compatibility
|
|
||||||
modules = append(modules, module)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return modules, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) GetModuleCompatibility(idModule int64) (*Compatibility, error) {
|
|
||||||
if idModule < 1 {
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
|
||||||
SELECT id, minimum, verified, maximum
|
|
||||||
FROM modules_compatibility
|
|
||||||
WHERE module_id = $1`
|
|
||||||
|
|
||||||
var compatibility Compatibility
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
err := m.DB.QueryRowContext(ctx, query, idModule).Scan(
|
|
||||||
&compatibility.Id,
|
|
||||||
&compatibility.Minimum,
|
|
||||||
&compatibility.Verified,
|
|
||||||
&compatibility.Maximum,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &compatibility, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) GetModuleLanguages(idModule int64) ([]Language, error) {
|
|
||||||
if idModule < 1 {
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
|
||||||
SELECT id, language, name, path
|
|
||||||
FROM modules_languages
|
|
||||||
WHERE module_id = $1`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
rows, err := m.DB.QueryContext(ctx, query, idModule)
|
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
languages := make([]Language, 0, 1)
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
var lang Language
|
|
||||||
err := rows.Scan(
|
|
||||||
&lang.Id,
|
|
||||||
&lang.Lang,
|
|
||||||
&lang.Name,
|
|
||||||
&lang.Path,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
languages = append(languages, lang)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return languages, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) DeleteModules(idState int64) error {
|
|
||||||
if idState < 1 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
|
||||||
DELETE FROM modules
|
|
||||||
WHERE state_id = $1`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
result, err := m.DB.ExecContext(ctx, query, idState)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
rowsAffected, err := result.RowsAffected()
|
rowsAffected, err := res.RowsAffected()
|
||||||
|
if rowsAffected != 0 {
|
||||||
|
err = m.InsertObjects(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rowsAffected == 0 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if dataId == nil {
|
||||||
|
dataCopy := *data
|
||||||
|
m.ConnectGameQuery(&dataCopy)
|
||||||
|
err = m.ConnectGame(tx, &dataCopy)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m FoundryStateModel) DeleteModule(idModule int64) error {
|
return err
|
||||||
if idModule < 1 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
}
|
||||||
|
|
||||||
query := `
|
func (m *Module) ConnectGameCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||||
DELETE FROM modules
|
if data.query == "" {
|
||||||
WHERE id = $1`
|
return ErrNoQuery
|
||||||
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
args := []any{data.id, m.ID}
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
result, err := m.DB.ExecContext(ctx, query, idModule)
|
_, err := tx.ExecContext(ctx, data.query, args...)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Module) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||||
|
if data.query == "" {
|
||||||
|
return ErrNoQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataId *uint
|
||||||
|
if strings.EqualFold(data.fieldName, "setup_id") {
|
||||||
|
dataId = &data.id
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []any{dataId, m.ID, m.Title, m.Description, m.URL, m.License, m.Readme, m.Bugs,
|
||||||
|
m.Changelog, m.Version, m.Manifest, m.Download, m.Socket, m.Protected, m.Exclusive, m.PersistentStorage,
|
||||||
|
m.CoreTranslation, m.Library, m.Locked, m.Owned, m.HasStorage, m.Active, m.Availability}
|
||||||
|
|
||||||
|
res, err := tx.ExecContext(ctx, data.query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
rowsAffected, err := result.RowsAffected()
|
rowsAffected, err := res.RowsAffected()
|
||||||
|
if rowsAffected != 0 {
|
||||||
|
err = m.InsertObjects(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rowsAffected == 0 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if dataId == nil {
|
||||||
|
dataCopy := *data
|
||||||
|
m.ConnectGameQuery(&dataCopy)
|
||||||
|
err = m.ConnectGameCtx(ctx, tx, &dataCopy)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@@ -27,9 +28,23 @@ type Pack struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pack) Query(data *InsertId[string]) {
|
func (p *Pack) Query(data *InsertId[string]) {
|
||||||
|
if !strings.EqualFold(data.fieldName, "game_id") {
|
||||||
data.query = fmt.Sprintf(`
|
data.query = fmt.Sprintf(`
|
||||||
INSERT INTO pack (%s, id, name, label, banner, path, type, system, package_type, package_name)
|
INSERT INTO pack (%s, id, name, label, banner, path, type, system, package_type, package_name)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`, data.fieldName)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||||
|
ON CONFLICT(id) DO NOTHING`, data.fieldName)
|
||||||
|
} else {
|
||||||
|
data.query = `
|
||||||
|
INSERT INTO pack (module_id, id, name, label, banner, path, type, system, package_type, package_name)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||||
|
ON CONFLICT(id) DO NOTHING`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pack) ConnectGameQuery(data *InsertId[string]) {
|
||||||
|
data.query = `
|
||||||
|
INSERT INTO game_to_pack (game_id, pack_id)
|
||||||
|
VALUES ($1, $2)`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pack) InsertObjects(tx *sqlx.Tx) error {
|
func (p *Pack) InsertObjects(tx *sqlx.Tx) error {
|
||||||
@@ -49,20 +64,61 @@ func (p *Pack) InsertObjects(tx *sqlx.Tx) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Pack) ConnectGame(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
|
if data.query == "" {
|
||||||
|
return ErrNoQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []any{data.id, p.ID}
|
||||||
|
|
||||||
|
_, err := tx.Exec(data.query, args...)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Pack) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
func (p *Pack) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
if data.query == "" {
|
if data.query == "" {
|
||||||
return ErrNoQuery
|
return ErrNoQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []any{data.id, p.ID, p.Name, p.Label, p.Banner, p.Path, p.Type,
|
var dataId *string
|
||||||
|
if !strings.EqualFold(data.fieldName, "game_id") {
|
||||||
|
dataId = &data.id
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []any{dataId, p.ID, p.Name, p.Label, p.Banner, p.Path, p.Type,
|
||||||
p.System, p.PackageType, p.PackageName}
|
p.System, p.PackageType, p.PackageName}
|
||||||
|
|
||||||
_, err := tx.Exec(data.query, args...)
|
res, err := tx.Exec(data.query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.InsertObjects(tx)
|
rowsAffected, err := res.RowsAffected()
|
||||||
|
if rowsAffected != 0 {
|
||||||
|
err = p.InsertObjects(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dataId == nil {
|
||||||
|
dataCopy := *data
|
||||||
|
p.ConnectGameQuery(&dataCopy)
|
||||||
|
err = p.ConnectGame(tx, &dataCopy)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pack) ConnectGameCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
|
if data.query == "" {
|
||||||
|
return ErrNoQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []any{data.id, p.ID}
|
||||||
|
|
||||||
|
_, err := tx.ExecContext(ctx, data.query, args...)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pack) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
func (p *Pack) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
@@ -70,15 +126,34 @@ func (p *Pack) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string
|
|||||||
return ErrNoQuery
|
return ErrNoQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []any{data.id, p.ID, p.Name, p.Label, p.Banner, p.Path, p.Type,
|
var dataId *string
|
||||||
|
if !strings.EqualFold(data.fieldName, "game_id") {
|
||||||
|
dataId = &data.id
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []any{dataId, p.ID, p.Name, p.Label, p.Banner, p.Path, p.Type,
|
||||||
p.System, p.PackageType, p.PackageName}
|
p.System, p.PackageType, p.PackageName}
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
res, err := tx.ExecContext(ctx, data.query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.InsertObjects(tx)
|
rowsAffected, err := res.RowsAffected()
|
||||||
|
if rowsAffected != 0 {
|
||||||
|
err = p.InsertObjects(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dataId == nil {
|
||||||
|
dataCopy := *data
|
||||||
|
p.ConnectGameQuery(&dataCopy)
|
||||||
|
err = p.ConnectGameCtx(ctx, tx, &dataCopy)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
type PackFolder struct {
|
type PackFolder struct {
|
||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@@ -29,9 +28,13 @@ type Playlist struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Playlist) Query(data *InsertId[uint]) {
|
func (p *Playlist) Query(data *InsertId[uint]) {
|
||||||
data.query = fmt.Sprintf(`
|
data.query = `
|
||||||
INSERT INTO playlist (%s, id, name, folder, sorting, description, channel, mode, fade, seed, sort, playing)
|
INSERT INTO playlist (game_id, id, name, folder, sorting, description, channel, mode, fade, seed, sort, playing)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`, data.fieldName)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
game_id = EXCLUDED.game_id,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
RETURNING (created_at == updated_at) AS is_inserted`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Playlist) InsertObjects(tx *sqlx.Tx) error {
|
func (p *Playlist) InsertObjects(tx *sqlx.Tx) error {
|
||||||
@@ -56,13 +59,17 @@ func (p *Playlist) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
|||||||
|
|
||||||
args := []any{data.id, p.ID, p.Name, p.Folder, p.Sorting, p.Description, p.Channel, p.Mode, p.Fade, p.Seed, p.Sort, p.Playing}
|
args := []any{data.id, p.ID, p.Name, p.Folder, p.Sorting, p.Description, p.Channel, p.Mode, p.Fade, p.Seed, p.Sort, p.Playing}
|
||||||
|
|
||||||
_, err := tx.Exec(data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return p.InsertObjects(tx)
|
return p.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Playlist) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
func (p *Playlist) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||||
if data.query == "" {
|
if data.query == "" {
|
||||||
@@ -71,13 +78,17 @@ func (p *Playlist) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[ui
|
|||||||
|
|
||||||
args := []any{data.id, p.ID, p.Name, p.Folder, p.Sorting, p.Description, p.Channel, p.Mode, p.Fade, p.Seed, p.Sort, p.Playing}
|
args := []any{data.id, p.ID, p.Name, p.Folder, p.Sorting, p.Description, p.Channel, p.Mode, p.Fade, p.Seed, p.Sort, p.Playing}
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return p.InsertObjects(tx)
|
return p.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type Sound struct {
|
type Sound struct {
|
||||||
ID string
|
ID string
|
||||||
@@ -95,9 +106,12 @@ type Sound struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sound) Query(data *InsertId[string]) {
|
func (s *Sound) Query(data *InsertId[string]) {
|
||||||
data.query = fmt.Sprintf(`
|
data.query = `
|
||||||
INSERT INTO Sound (%s, id, name, path, channel, description, fade, sort, repeat, playing, volume, paused_time)
|
INSERT INTO sound (playlist_id, id, name, path, channel, description, fade, sort, repeat, playing, volume, paused_time)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`, data.fieldName)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
playlist_id = EXCLUDED.playlist_id,
|
||||||
|
updated_at = datetime('now')`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sound) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
func (s *Sound) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@@ -19,9 +18,13 @@ type Setting struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Setting) Query(data *InsertId[uint]) {
|
func (s *Setting) Query(data *InsertId[uint]) {
|
||||||
data.query = fmt.Sprintf(`
|
data.query = `
|
||||||
INSERT INTO setting (%s, id, key_, value)
|
INSERT INTO setting (game_id, id, key_, value)
|
||||||
VALUES ($1, $2, $3, $4)`, data.fieldName)
|
VALUES ($1, $2, $3, $4)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
game_id = EXCLUDED.game_id,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
RETURNING (created_at == updated_at) AS is_inserted`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Setting) InsertObjects(tx *sqlx.Tx) error {
|
func (s *Setting) InsertObjects(tx *sqlx.Tx) error {
|
||||||
@@ -44,13 +47,17 @@ func (s *Setting) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
|||||||
|
|
||||||
args := []any{data.id, s.ID, s.Key, s.Value}
|
args := []any{data.id, s.ID, s.Key, s.Value}
|
||||||
|
|
||||||
_, err := tx.Exec(data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return s.InsertObjects(tx)
|
return s.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Setting) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
func (s *Setting) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||||
if data.query == "" {
|
if data.query == "" {
|
||||||
@@ -59,10 +66,14 @@ func (s *Setting) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uin
|
|||||||
|
|
||||||
args := []any{data.id, s.ID, s.Key, s.Value}
|
args := []any{data.id, s.ID, s.Key, s.Value}
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return s.InsertObjects(tx)
|
return s.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -177,3 +177,18 @@ func (n *News) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint])
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HasSetup(db *sqlx.DB) (bool, error) {
|
||||||
|
const query = `
|
||||||
|
SELECT EXISTS(SELECT 1 FROM setup)`
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
var exists bool
|
||||||
|
err := db.GetContext(ctx, &exists, query)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return exists, nil
|
||||||
|
}
|
||||||
@@ -4,201 +4,186 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
type System struct {
|
type System struct {
|
||||||
Id int64
|
ID string
|
||||||
TextId string
|
|
||||||
Title string
|
Title string
|
||||||
Description string
|
Description string
|
||||||
Url string
|
URL string
|
||||||
|
License string
|
||||||
|
Bugs string
|
||||||
|
Changelog string
|
||||||
|
Version string
|
||||||
|
Manifest string
|
||||||
Download string
|
Download string
|
||||||
CreatedAt time.Time
|
Background string
|
||||||
|
PrimaryTokenAttribute string
|
||||||
|
Availability int
|
||||||
|
Socket bool
|
||||||
|
Protected bool
|
||||||
|
Exclusive bool
|
||||||
|
PersistentStorage bool
|
||||||
|
Locked bool
|
||||||
|
Owned bool
|
||||||
|
HasStorage bool
|
||||||
Compatibility Compatibility
|
Compatibility Compatibility
|
||||||
|
Relationships Relationships
|
||||||
|
DocumentTypes DocumentTypes
|
||||||
|
Grid *Grid
|
||||||
|
Esmodules []string
|
||||||
|
Scripts []string
|
||||||
|
Tags []string
|
||||||
|
Authors []*Author
|
||||||
|
Media []*Media
|
||||||
|
Packs []*Pack
|
||||||
|
Styles []*Style
|
||||||
|
Languages []*Language
|
||||||
|
PackFolders []*Folder
|
||||||
}
|
}
|
||||||
|
|
||||||
type Systems []System
|
func (s *System) Query(data *InsertId[string]) {
|
||||||
|
data.query = `
|
||||||
func (systems Systems) GetById(id int) *System {
|
INSERT INTO system (setup_id, id, title, description, url, license, bugs, changelog, version, manifest,
|
||||||
return &systems[id]
|
download, background, primary_token_attribute, availability, socket, protected, exclusive_,
|
||||||
|
persistent_storage, locked, owned, has_storage)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)
|
||||||
|
ON CONFLICT(id) DO NOTHING`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m FoundryStateModel) InsertSystem(system *System, stateId int64) error {
|
func (s *System) ConnectGameQuery(data *InsertId[string]) {
|
||||||
query := `
|
data.query = `
|
||||||
INSERT INTO systems (state_id, text_id, title, description, url, download)
|
INSERT INTO game_to_systems (game_id, system_id)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)
|
VALUES ($1, $2)`
|
||||||
RETURNING id, created_at`
|
|
||||||
|
|
||||||
args := []any{stateId, system.TextId, system.Title, system.Description, system.Url, system.Download}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&system.Id, &system.CreatedAt)
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return m.InsertSystemCompatibility(&system.Compatibility, system.Id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m FoundryStateModel) InsertSystemCompatibility(compatibility *Compatibility, systemId int64) error {
|
func (s *System) InsertObjects(tx *sqlx.Tx) error {
|
||||||
query := `
|
relId := InsertId[string]{id: s.ID, fieldName: "system_id"}
|
||||||
INSERT INTO systems_compatibility (system_id, minimum, verified, maximum)
|
group, ctx := errgroup.WithContext(context.Background())
|
||||||
VALUES ($1, $2, $3, $4)`
|
|
||||||
|
|
||||||
args := []any{systemId, compatibility.Minimum, compatibility.Verified, compatibility.Maximum}
|
InsertWithCtxParallel(group, ctx, tx, s.Compatibility, relId)
|
||||||
|
InsertWithCtxParallel(group, ctx, tx, s.Relationships, relId)
|
||||||
|
InsertWithCtxParallel(group, ctx, tx, s.DocumentTypes, relId)
|
||||||
|
InsertWithCtxParallel(group, ctx, tx, s.Grid, relId)
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
esModulesRelId := &InsertId[string]{id: s.ID, fieldName: "system_id", tableName: "es_modules"}
|
||||||
defer cancel()
|
InsertSimpleSliceParallel(group, tx, s.Esmodules, esModulesRelId)
|
||||||
|
scriptRelId := &InsertId[string]{id: s.ID, fieldName: "system_id", tableName: "scripts"}
|
||||||
|
InsertSimpleSliceParallel(group, tx, s.Scripts, scriptRelId)
|
||||||
|
tagsRelId := &InsertId[string]{id: s.ID, fieldName: "system_id", tableName: "tags"}
|
||||||
|
InsertSimpleSliceParallel(group, tx, s.Tags, tagsRelId)
|
||||||
|
|
||||||
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&compatibility.Id)
|
InsertSliceParallel(group, tx, s.Authors, relId)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
InsertSliceParallel(group, tx, s.Media, relId)
|
||||||
|
InsertSliceParallel(group, tx, s.Styles, relId)
|
||||||
|
InsertSliceParallel(group, tx, s.Languages, relId)
|
||||||
|
InsertSliceParallel(group, tx, s.Packs, relId)
|
||||||
|
InsertSliceParallel(group, tx, s.PackFolders, relId)
|
||||||
|
|
||||||
|
err := group.Wait()
|
||||||
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m FoundryStateModel) GetSystems(idState int64) (Systems, error) {
|
func (s *System) ConnectGame(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
if idState < 1 {
|
if data.query == "" {
|
||||||
return nil, ErrorRecordNotFound
|
return ErrNoQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
query := `
|
args := []any{data.id, s.ID}
|
||||||
SELECT id, created_at, text_id, title, description, url, download
|
|
||||||
FROM systems
|
|
||||||
WHERE state_id = $1`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
_, err := tx.Exec(data.query, args...)
|
||||||
defer cancel()
|
return err
|
||||||
|
|
||||||
rows, err := m.DB.QueryContext(ctx, query, idState)
|
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
systems := make(Systems, 0, 1)
|
func (s *System) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
|
if data.query == "" {
|
||||||
for rows.Next() {
|
return ErrNoQuery
|
||||||
var system System
|
|
||||||
err := rows.Scan(
|
|
||||||
&system.Id,
|
|
||||||
&system.CreatedAt,
|
|
||||||
&system.TextId,
|
|
||||||
&system.Title,
|
|
||||||
&system.Description,
|
|
||||||
&system.Url,
|
|
||||||
&system.Download,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
compatibility, err := m.GetModuleCompatibility(system.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
system.Compatibility = *compatibility
|
|
||||||
systems = append(systems, system)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := rows.Err(); err != nil {
|
var dataId *string
|
||||||
return nil, err
|
if strings.EqualFold(data.fieldName, "setup_id") {
|
||||||
|
dataId = &data.id
|
||||||
}
|
}
|
||||||
|
|
||||||
return systems, nil
|
args := []any{dataId, s.ID, s.Title, s.Description, s.URL, s.License, s.Bugs,
|
||||||
}
|
s.Changelog, s.Version, s.Manifest, s.Download, s.Background,
|
||||||
|
s.PrimaryTokenAttribute, s.Availability, s.Socket, s.Protected, s.Exclusive,
|
||||||
|
s.PersistentStorage, s.Locked, s.Owned, s.HasStorage}
|
||||||
|
|
||||||
func (m FoundryStateModel) GetSystemCompatibility(idSystem int64) (*Compatibility, error) {
|
res, err := tx.Exec(data.query, args...)
|
||||||
if idSystem < 1 {
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
|
||||||
SELECT id, minimum, verified, maximum
|
|
||||||
FROM systems_compatibility
|
|
||||||
WHERE system_id = $1`
|
|
||||||
|
|
||||||
var compatibility Compatibility
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
err := m.DB.QueryRowContext(ctx, query, idSystem).Scan(
|
|
||||||
&compatibility.Id,
|
|
||||||
&compatibility.Minimum,
|
|
||||||
&compatibility.Verified,
|
|
||||||
&compatibility.Maximum,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &compatibility, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) DeleteSystems(idState int64) error {
|
|
||||||
if idState < 1 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
|
||||||
DELETE FROM systems
|
|
||||||
WHERE state_id = $1`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
result, err := m.DB.ExecContext(ctx, query, idState)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
rowsAffected, err := result.RowsAffected()
|
rowsAffected, err := res.RowsAffected()
|
||||||
|
if rowsAffected != 0 {
|
||||||
|
err = s.InsertObjects(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rowsAffected == 0 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if dataId == nil {
|
||||||
|
dataCopy := *data
|
||||||
|
s.ConnectGameQuery(&dataCopy)
|
||||||
|
err = s.ConnectGame(tx, &dataCopy)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m FoundryStateModel) DeleteSystem(idSystem int64) error {
|
return err
|
||||||
if idSystem < 1 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
}
|
||||||
|
|
||||||
query := `
|
func (s *System) ConnectGameCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
DELETE FROM systems
|
if data.query == "" {
|
||||||
WHERE id = $1`
|
return ErrNoQuery
|
||||||
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
args := []any{data.id, s.ID}
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
result, err := m.DB.ExecContext(ctx, query, idSystem)
|
_, err := tx.ExecContext(ctx, data.query, args...)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *System) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
|
if data.query == "" {
|
||||||
|
return ErrNoQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataId *string
|
||||||
|
if strings.EqualFold(data.fieldName, "setup_id") {
|
||||||
|
dataId = &data.id
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []any{dataId, s.ID, s.Title, s.Description, s.URL, s.License, s.Bugs,
|
||||||
|
s.Changelog, s.Version, s.Manifest, s.Download, s.Background,
|
||||||
|
s.PrimaryTokenAttribute, s.Availability, s.Socket, s.Protected, s.Exclusive,
|
||||||
|
s.PersistentStorage, s.Locked, s.Owned, s.HasStorage}
|
||||||
|
|
||||||
|
res, err := tx.ExecContext(ctx, data.query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
rowsAffected, err := result.RowsAffected()
|
rowsAffected, err := res.RowsAffected()
|
||||||
|
if rowsAffected != 0 {
|
||||||
|
err = s.InsertObjects(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rowsAffected == 0 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if dataId == nil {
|
||||||
|
dataCopy := *data
|
||||||
|
s.ConnectGameQuery(&dataCopy)
|
||||||
|
err = s.ConnectGameCtx(ctx, tx, &dataCopy)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@@ -27,9 +26,13 @@ type Table struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Table) Query(data *InsertId[uint]) {
|
func (t *Table) Query(data *InsertId[uint]) {
|
||||||
data.query = fmt.Sprintf(`
|
data.query = `
|
||||||
INSERT INTO table_ (%s, id, name, description, formula, img, folder, sort, replacement, display_roll)
|
INSERT INTO table_ (game_id, id, name, description, formula, img, folder, sort, replacement, display_roll)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`, data.fieldName)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
game_id = EXCLUDED.game_id,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
RETURNING (created_at == updated_at) AS is_inserted`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Table) InsertObjects(tx *sqlx.Tx) error {
|
func (t *Table) InsertObjects(tx *sqlx.Tx) error {
|
||||||
@@ -54,13 +57,17 @@ func (t *Table) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
|||||||
|
|
||||||
args := []any{data.id, t.ID, t.Name, t.Description, t.Formula, t.Img, t.Folder, t.Sort, t.Replacement, t.DisplayRoll}
|
args := []any{data.id, t.ID, t.Name, t.Description, t.Formula, t.Img, t.Folder, t.Sort, t.Replacement, t.DisplayRoll}
|
||||||
|
|
||||||
_, err := tx.Exec(data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return t.InsertObjects(tx)
|
return t.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Table) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
func (t *Table) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||||
if data.query == "" {
|
if data.query == "" {
|
||||||
@@ -69,13 +76,17 @@ func (t *Table) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]
|
|||||||
|
|
||||||
args := []any{data.id, t.ID, t.Name, t.Description, t.Formula, t.Img, t.Folder, t.Sort, t.Replacement, t.DisplayRoll}
|
args := []any{data.id, t.ID, t.Name, t.Description, t.Formula, t.Img, t.Folder, t.Sort, t.Replacement, t.DisplayRoll}
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return t.InsertObjects(tx)
|
return t.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type TableResult struct {
|
type TableResult struct {
|
||||||
ID string
|
ID string
|
||||||
@@ -91,9 +102,13 @@ type TableResult struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *TableResult) Query(data *InsertId[string]) {
|
func (t *TableResult) Query(data *InsertId[string]) {
|
||||||
data.query = fmt.Sprintf(`
|
data.query = `
|
||||||
INSERT INTO table_result (%s, id, type, img, description, name, weight, drawn)
|
INSERT INTO table_result (table_id, id, type, img, description, name, weight, drawn)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, data.fieldName)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
table_id = EXCLUDED.table_id,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
RETURNING (created_at == updated_at) AS is_inserted`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TableResult) InsertObjects(tx *sqlx.Tx) error {
|
func (t *TableResult) InsertObjects(tx *sqlx.Tx) error {
|
||||||
@@ -116,13 +131,17 @@ func (t *TableResult) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
|||||||
|
|
||||||
args := []any{data.id, t.ID, t.Type, t.Img, t.Description, t.Name, t.Weight, t.Drawn}
|
args := []any{data.id, t.ID, t.Type, t.Img, t.Description, t.Name, t.Weight, t.Drawn}
|
||||||
|
|
||||||
_, err := tx.Exec(data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return t.InsertObjects(tx)
|
return t.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t *TableResult) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
func (t *TableResult) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
if data.query == "" {
|
if data.query == "" {
|
||||||
@@ -131,10 +150,14 @@ func (t *TableResult) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId
|
|||||||
|
|
||||||
args := []any{data.id, t.ID, t.Type, t.Img, t.Description, t.Name, t.Weight, t.Drawn}
|
args := []any{data.id, t.ID, t.Type, t.Img, t.Description, t.Name, t.Weight, t.Drawn}
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
return t.InsertObjects(tx)
|
return t.InsertObjects(tx)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -37,11 +37,11 @@ type Token struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Token) Query(data *InsertId[string]) {
|
func (t *Token) Query(data *InsertId[string]) {
|
||||||
data.query = fmt.Sprintf(`
|
data.query = `
|
||||||
INSERT INTO token (%s, name, actor_link_ append_number, prepend_adjective, lock_rotation, random_img, display_name,
|
INSERT INTO token (actor_id, name, actor_link, append_number, prepend_adjective, lock_rotation, random_img, display_name,
|
||||||
display_bars, disposition, rotation, alpha, width, height)
|
display_bars, disposition, rotation, alpha, width, height)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
|
||||||
RETURNING id`, data.fieldName)
|
RETURNING id`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Token) InsertObjects(tx *sqlx.Tx) error {
|
func (t *Token) InsertObjects(tx *sqlx.Tx) error {
|
||||||
@@ -4,293 +4,125 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Id int64
|
ID string
|
||||||
|
|
||||||
Name string
|
Name string
|
||||||
Role int
|
Avatar string
|
||||||
Character string
|
Character string
|
||||||
Color string
|
Color string
|
||||||
Pronouns string
|
Pronouns string
|
||||||
CreatedAt time.Time
|
Role int
|
||||||
Hotbar map[string]string
|
Stats Stats
|
||||||
Stats UserStats
|
Hotbar []UserHotbar
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserStats struct {
|
func (u *User) Query(data *InsertId[uint]) {
|
||||||
Id int64
|
data.query = `
|
||||||
CoreVersion string
|
INSERT INTO user (game_id, id, name, avatar, character, color, pronouns, role)
|
||||||
SystemId string
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||||
SystemVersion string
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
CreatedTime int64
|
game_id = EXCLUDED.game_id,
|
||||||
ModifiedTime int64
|
updated_at = datetime('now')
|
||||||
LastModifiedBy string
|
RETURNING (created_at == updated_at) AS is_inserted`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Users []User
|
func (u *User) InsertObjects(tx *sqlx.Tx) error {
|
||||||
|
group, ctx := errgroup.WithContext(context.Background())
|
||||||
|
|
||||||
func (users Users) GetById(id int) *User {
|
relId := InsertId[string]{id: u.ID, fieldName: "user_id"}
|
||||||
return &users[id]
|
InsertWithCtxParallel(group, ctx, tx, u.Stats, relId)
|
||||||
|
InsertSliceParallel(group, tx, u.Hotbar, relId)
|
||||||
|
|
||||||
|
err := group.Wait()
|
||||||
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m FoundryStateModel) InsertUser(user *User, stateId int64) error {
|
func (u *User) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||||
query := `
|
if data.query == "" {
|
||||||
INSERT INTO users (state_id, name, role, character, color, pronouns)
|
return ErrNoQuery
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)
|
}
|
||||||
RETURNING id, created_at`
|
|
||||||
|
|
||||||
args := []any{stateId, user.Name, user.Role, user.Character, user.Color, user.Pronouns}
|
args := []any{data.id, u.ID, u.Name, u.Avatar, u.Character, u.Color, u.Pronouns, u.Role}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
var isInserted bool
|
||||||
defer cancel()
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
|
|
||||||
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&user.Id, &user.CreatedAt)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range user.Hotbar {
|
if isInserted {
|
||||||
err = m.InsertUserHotbar(k, v, user.Id)
|
return u.InsertObjects(tx)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||||
|
if data.query == "" {
|
||||||
|
return ErrNoQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []any{data.id, u.ID, u.Name, u.Avatar, u.Character, u.Color, u.Pronouns, u.Role}
|
||||||
|
|
||||||
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
|
return u.InsertObjects(tx)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.InsertUserStats(&user.Stats, user.Id)
|
type UserHotbar struct {
|
||||||
|
ID uint
|
||||||
|
|
||||||
|
Key int
|
||||||
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m FoundryStateModel) InsertUserHotbar(key string, value string, userId int64) error {
|
func (u UserHotbar) Query(data *InsertId[string]) {
|
||||||
query := `
|
data.query = `
|
||||||
INSERT INTO users_hotbar (user_id, key, value)
|
INSERT INTO hotbar (user_id, key_, value)
|
||||||
VALUES ($1, $2, $3)`
|
VALUES ($1, $2, $3)`
|
||||||
|
|
||||||
args := []any{userId, key, value}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
_, err := m.DB.ExecContext(ctx, query, args...)
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m FoundryStateModel) InsertUserStats(stats *UserStats, userId int64) error {
|
func (u UserHotbar) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
query := `
|
if data.query == "" {
|
||||||
INSERT INTO users_stats (user_id, core_version, system_id, system_version, created_time, modified_time, last_modified_by)
|
return ErrNoQuery
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
||||||
RETURNING id`
|
|
||||||
|
|
||||||
args := []any{userId, stats.CoreVersion, stats.SystemId, stats.SystemVersion, stats.CreatedTime, stats.ModifiedTime, stats.LastModifiedBy}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
return m.DB.QueryRowContext(ctx, query, args...).Scan(&stats.Id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m FoundryStateModel) GetUsers(idState int64) (Users, error) {
|
args := []any{data.id, u.Key, u.Value}
|
||||||
if idState < 1 {
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
err := tx.QueryRowx(data.query, args...).Scan(&u.ID)
|
||||||
SELECT id, created_at, name, role, character, color, pronouns
|
|
||||||
FROM users
|
|
||||||
WHERE state_id = $1`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
rows, err := m.DB.QueryContext(ctx, query, idState)
|
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
users := make(Users, 0, 1)
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
var user User
|
|
||||||
err := rows.Scan(
|
|
||||||
&user.Id,
|
|
||||||
&user.CreatedAt,
|
|
||||||
&user.Name,
|
|
||||||
&user.Role,
|
|
||||||
&user.Character,
|
|
||||||
&user.Color,
|
|
||||||
&user.Pronouns,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
user.Hotbar, err = m.GetUserHotbar(user.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
userState, err := m.GetUserStats(user.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
user.Stats = *userState
|
|
||||||
users = append(users, user)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return users, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) GetUserHotbar(idUser int64) (map[string]string, error) {
|
|
||||||
if idUser < 1 {
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
|
||||||
SELECT key, value
|
|
||||||
FROM users_hotbar
|
|
||||||
WHERE user_id = $1`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
rows, err := m.DB.QueryContext(ctx, query, idUser)
|
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hotbar := make(map[string]string)
|
|
||||||
for rows.Next() {
|
|
||||||
var key string
|
|
||||||
var val string
|
|
||||||
|
|
||||||
err := rows.Scan(
|
|
||||||
&key,
|
|
||||||
&val,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
hotbar[key] = val
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hotbar, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) GetUserStats(idSystem int64) (*UserStats, error) {
|
|
||||||
if idSystem < 1 {
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
|
||||||
SELECT id, core_version, system_id, system_version, created_time, modified_time, last_modified_by
|
|
||||||
FROM users_stats
|
|
||||||
WHERE user_id = $1`
|
|
||||||
|
|
||||||
var userStats UserStats
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
err := m.DB.QueryRowContext(ctx, query, idSystem).Scan(
|
|
||||||
&userStats.Id,
|
|
||||||
&userStats.CoreVersion,
|
|
||||||
&userStats.SystemId,
|
|
||||||
&userStats.SystemVersion,
|
|
||||||
&userStats.CreatedTime,
|
|
||||||
&userStats.ModifiedTime,
|
|
||||||
&userStats.LastModifiedBy,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &userStats, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) DeleteUsers(idState int64) error {
|
|
||||||
if idState < 1 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
|
||||||
DELETE FROM users
|
|
||||||
WHERE state_id = $1`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
result, err := m.DB.ExecContext(ctx, query, idState)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
rowsAffected, err := result.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rowsAffected == 0 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m FoundryStateModel) DeleteUser(idUser int64) error {
|
func (u UserHotbar) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
if idUser < 1 {
|
if data.query == "" {
|
||||||
return ErrorRecordNotFound
|
return ErrNoQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
query := `
|
args := []any{data.id, u.Key, u.Value}
|
||||||
DELETE FROM users
|
|
||||||
WHERE id = $1`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&u.ID)
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
result, err := m.DB.ExecContext(ctx, query, idUser)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
rowsAffected, err := result.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rowsAffected == 0 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ type Insertable[T AllowedIds] interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func InsertWithCtx[T AllowedIds, I Insertable[T]](tx *sqlx.Tx, data I, relId InsertId[T]) error {
|
func InsertWithCtx[T AllowedIds, I Insertable[T]](tx *sqlx.Tx, data I, relId InsertId[T]) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
data.Query(&relId)
|
data.Query(&relId)
|
||||||
@@ -43,7 +43,7 @@ func InsertWithCtx[T AllowedIds, I Insertable[T]](tx *sqlx.Tx, data I, relId Ins
|
|||||||
|
|
||||||
func InsertWithCtxParallel[T AllowedIds, I Insertable[T]](g *errgroup.Group, ctx context.Context, tx *sqlx.Tx, data I, relId InsertId[T]) {
|
func InsertWithCtxParallel[T AllowedIds, I Insertable[T]](g *errgroup.Group, ctx context.Context, tx *sqlx.Tx, data I, relId InsertId[T]) {
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
data.Query(&relId)
|
data.Query(&relId)
|
||||||
@@ -53,7 +53,7 @@ func InsertWithCtxParallel[T AllowedIds, I Insertable[T]](g *errgroup.Group, ctx
|
|||||||
}
|
}
|
||||||
|
|
||||||
func InsertSlice[T AllowedIds, I Insertable[T]](tx *sqlx.Tx, data []I, relId InsertId[T]) error {
|
func InsertSlice[T AllowedIds, I Insertable[T]](tx *sqlx.Tx, data []I, relId InsertId[T]) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if len(data) > 0 {
|
if len(data) > 0 {
|
||||||
@@ -68,6 +68,28 @@ func InsertSlice[T AllowedIds, I Insertable[T]](tx *sqlx.Tx, data []I, relId Ins
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func InsertSliceParallelTimeout[T AllowedIds, I Insertable[T]](g *errgroup.Group, tx *sqlx.Tx, data []I, relId InsertId[T], timeout time.Duration) {
|
||||||
|
g.Go(func() error {
|
||||||
|
wg, _ := errgroup.WithContext(context.Background())
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if len(data) > 0 {
|
||||||
|
data[0].Query(&relId)
|
||||||
|
}
|
||||||
|
for i := range data {
|
||||||
|
wg.Go(func() error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
err = data[i].InsertCtx(ctx, tx, &relId)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return wg.Wait()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func InsertSliceParallel[T AllowedIds, I Insertable[T]](g *errgroup.Group, tx *sqlx.Tx, data []I, relId InsertId[T]) {
|
func InsertSliceParallel[T AllowedIds, I Insertable[T]](g *errgroup.Group, tx *sqlx.Tx, data []I, relId InsertId[T]) {
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
wg, _ := errgroup.WithContext(context.Background())
|
wg, _ := errgroup.WithContext(context.Background())
|
||||||
@@ -78,7 +100,7 @@ func InsertSliceParallel[T AllowedIds, I Insertable[T]](g *errgroup.Group, tx *s
|
|||||||
}
|
}
|
||||||
for i := range data {
|
for i := range data {
|
||||||
wg.Go(func() error {
|
wg.Go(func() error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
err = data[i].InsertCtx(ctx, tx, &relId)
|
err = data[i].InsertCtx(ctx, tx, &relId)
|
||||||
@@ -91,7 +113,7 @@ func InsertSliceParallel[T AllowedIds, I Insertable[T]](g *errgroup.Group, tx *s
|
|||||||
}
|
}
|
||||||
|
|
||||||
func InsertSimpleSlice[T AllowedIds, I any](tx *sqlx.Tx, data []I, relId *InsertId[T]) error {
|
func InsertSimpleSlice[T AllowedIds, I any](tx *sqlx.Tx, data []I, relId *InsertId[T]) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
query := fmt.Sprintf(`
|
query := fmt.Sprintf(`
|
||||||
@@ -116,7 +138,7 @@ func InsertSimpleSliceParallel[T AllowedIds, I any](g *errgroup.Group, tx *sqlx.
|
|||||||
VALUES ($1, $2)`, relId.tableName, relId.fieldName)
|
VALUES ($1, $2)`, relId.tableName, relId.fieldName)
|
||||||
for i := range data {
|
for i := range data {
|
||||||
wg.Go(func() error {
|
wg.Go(func() error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
_, err := tx.ExecContext(ctx, query, relId.id, data[i])
|
_, err := tx.ExecContext(ctx, query, relId.id, data[i])
|
||||||
@@ -128,17 +150,20 @@ func InsertSimpleSliceParallel[T AllowedIds, I any](g *errgroup.Group, tx *sqlx.
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteAll(db *sqlx.DB) error {
|
func DeleteSetupAll(db *sqlx.DB) error {
|
||||||
query := `
|
const query = `DELETE FROM setup`
|
||||||
DELETE FROM setup`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
result, err := db.ExecContext(ctx, query)
|
tx := db.MustBegin()
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
result, err := tx.ExecContext(ctx, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
tx.Commit()
|
||||||
|
|
||||||
rowsAffected, err := result.RowsAffected()
|
rowsAffected, err := result.RowsAffected()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -151,17 +176,46 @@ func DeleteAll(db *sqlx.DB) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteAllSeq(db *sqlx.DB) error {
|
func DeleteGameAll(db *sqlx.DB) error {
|
||||||
query := `
|
const query = `DELETE FROM game`
|
||||||
DELETE FROM sqlite_sequence`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
result, err := db.ExecContext(ctx, query)
|
tx := db.MustBegin()
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
result, err := tx.ExecContext(ctx, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
tx.Commit()
|
||||||
|
|
||||||
|
rowsAffected, err := result.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rowsAffected == 0 {
|
||||||
|
return ErrorRecordNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteSeqAll(db *sqlx.DB) error {
|
||||||
|
const query = `DELETE FROM sqlite_sequence`
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
tx := db.MustBegin()
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
result, err := tx.ExecContext(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tx.Commit()
|
||||||
|
|
||||||
rowsAffected, err := result.RowsAffected()
|
rowsAffected, err := result.RowsAffected()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -180,7 +234,6 @@ func IsErrUniqueConstraint(err error) {
|
|||||||
// or SQLITE_CONSTRAINT (basic code 19)
|
// or SQLITE_CONSTRAINT (basic code 19)
|
||||||
if sqliteErr.ExtendedCode == sqlite3.ErrConstraintUnique ||
|
if sqliteErr.ExtendedCode == sqlite3.ErrConstraintUnique ||
|
||||||
sqliteErr.Code == sqlite3.ErrConstraint {
|
sqliteErr.Code == sqlite3.ErrConstraint {
|
||||||
fmt.Println("Record already exists")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,231 +4,179 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
type World struct {
|
type World struct {
|
||||||
Id int64
|
ID string
|
||||||
TextId string
|
|
||||||
Title string
|
Title string
|
||||||
Description string
|
Description string
|
||||||
|
Version string
|
||||||
System string
|
System string
|
||||||
|
Background string
|
||||||
|
JoinTheme string
|
||||||
CoreVersion string
|
CoreVersion string
|
||||||
SystemVersion string
|
SystemVersion string
|
||||||
LastPlayed string
|
LastPlayed string
|
||||||
PlayTime int64
|
Playtime int
|
||||||
|
Availability int
|
||||||
NextSession time.Time
|
NextSession time.Time
|
||||||
CreatedAt time.Time
|
Socket bool
|
||||||
|
Protected bool
|
||||||
|
Exclusive bool
|
||||||
|
PersistentStorage bool
|
||||||
|
Locked bool
|
||||||
|
Owned bool
|
||||||
|
HasStorage bool
|
||||||
Compatibility Compatibility
|
Compatibility Compatibility
|
||||||
|
Relationships Relationships
|
||||||
|
Tags []string
|
||||||
|
Scripts []string
|
||||||
|
Esmodules []string
|
||||||
|
Authors []*Author
|
||||||
|
Media []*Media
|
||||||
|
Styles []*Style
|
||||||
|
Languages []*Language
|
||||||
|
Packs []*Pack
|
||||||
|
PackFolders []*Folder
|
||||||
}
|
}
|
||||||
|
|
||||||
type Worlds []World
|
func (w *World) Query(data *InsertId[string]) {
|
||||||
|
data.query = fmt.Sprintf(`
|
||||||
func (worlds Worlds) GetById(id int) *World {
|
INSERT INTO world (%[1]s, id, title, description, version, system, background, join_theme,
|
||||||
return &worlds[id]
|
core_version, system_version, last_played, playtime, availability, next_session, socket,
|
||||||
|
protected, exclusive_, persistent_storage, locked, owned, has_storage)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
%[1]s = EXCLUDED.%[1]s,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
RETURNING (created_at == updated_at) AS is_inserted`,
|
||||||
|
data.fieldName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m FoundryStateModel) InsertWorld(world *World, stateId int64) error {
|
func (w *World) InsertObjects(tx *sqlx.Tx) error {
|
||||||
query := `
|
relId := InsertId[string]{id: w.ID, fieldName: "world_id"}
|
||||||
INSERT INTO worlds (state_id, text_id, title, description, system, core_version, system_version, playtime, next_session)
|
group, ctx := errgroup.WithContext(context.Background())
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
||||||
RETURNING id, created_at`
|
|
||||||
|
|
||||||
args := []any{stateId, world.TextId, world.Title, world.Description, world.System, world.CoreVersion, world.SystemVersion, world.PlayTime, world.NextSession}
|
InsertWithCtxParallel(group, ctx, tx, w.Compatibility, relId)
|
||||||
|
InsertWithCtxParallel(group, ctx, tx, w.Relationships, relId)
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
esModulesRelId := &InsertId[string]{id: w.ID, fieldName: "world_id", tableName: "es_modules"}
|
||||||
defer cancel()
|
InsertSimpleSliceParallel(group, tx, w.Esmodules, esModulesRelId)
|
||||||
|
scriptRelId := &InsertId[string]{id: w.ID, fieldName: "world_id", tableName: "scripts"}
|
||||||
|
InsertSimpleSliceParallel(group, tx, w.Scripts, scriptRelId)
|
||||||
|
tagsRelId := &InsertId[string]{id: w.ID, fieldName: "world_id", tableName: "tags"}
|
||||||
|
InsertSimpleSliceParallel(group, tx, w.Tags, tagsRelId)
|
||||||
|
|
||||||
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&world.Id, &world.CreatedAt)
|
InsertSliceParallel(group, tx, w.Authors, relId)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
InsertSliceParallel(group, tx, w.Media, relId)
|
||||||
return err
|
InsertSliceParallel(group, tx, w.Styles, relId)
|
||||||
}
|
InsertSliceParallel(group, tx, w.Languages, relId)
|
||||||
return m.InsertWorldCompatibility(&world.Compatibility, world.Id)
|
InsertSliceParallel(group, tx, w.Packs, relId)
|
||||||
}
|
InsertSliceParallel(group, tx, w.PackFolders, relId)
|
||||||
|
|
||||||
func (m FoundryStateModel) InsertWorldCompatibility(compatibility *Compatibility, worldId int64) error {
|
err := group.Wait()
|
||||||
query := `
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
INSERT INTO worlds_compatibility (world_id, minimum, verified, maximum)
|
|
||||||
VALUES ($1, $2, $3, $4)
|
|
||||||
RETURNING id`
|
|
||||||
|
|
||||||
args := []any{worldId, compatibility.Minimum, compatibility.Verified, compatibility.Maximum}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&compatibility.Id)
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m FoundryStateModel) GetWorlds(idState int64) (Worlds, error) {
|
func (w *World) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
if idState < 1 {
|
if data.query == "" {
|
||||||
return nil, ErrorRecordNotFound
|
return ErrNoQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
query := `
|
args := []any{data.id, w.ID, w.Title, w.Description, w.Version, w.System, w.Background, w.JoinTheme,
|
||||||
SELECT id, created_at, text_id, title, description, system, core_version, system_version, playtime, next_session
|
w.CoreVersion, w.SystemVersion, w.LastPlayed, w.Playtime, w.Availability, w.NextSession, w.Socket,
|
||||||
FROM worlds
|
w.Protected, w.Exclusive, w.PersistentStorage, w.Locked, w.Owned, w.HasStorage}
|
||||||
WHERE state_id = $1`
|
|
||||||
|
var isInserted bool
|
||||||
|
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
|
return w.InsertObjects(tx)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *World) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||||
|
if data.query == "" {
|
||||||
|
return ErrNoQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []any{data.id, w.ID, w.Title, w.Description, w.Version, w.System, w.Background, w.JoinTheme,
|
||||||
|
w.CoreVersion, w.SystemVersion, w.LastPlayed, w.Playtime, w.Availability, w.NextSession, w.Socket,
|
||||||
|
w.Protected, w.Exclusive, w.PersistentStorage, w.Locked, w.Owned, w.HasStorage}
|
||||||
|
|
||||||
|
var isInserted bool
|
||||||
|
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if isInserted {
|
||||||
|
return w.InsertObjects(tx)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetNotStartedWorlds(db *sqlx.DB) ([]string, error) {
|
||||||
|
const query = `
|
||||||
|
SELECT id FROM world WHERE game_id IS NULL`
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
rows, err := m.DB.QueryContext(ctx, query, idState)
|
var worldNames []string
|
||||||
|
err := db.SelectContext(ctx, &worldNames, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch {
|
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return worldNames, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
worlds := make(Worlds, 0, 1)
|
func IsWorldInserted(db *sqlx.DB, worldName string) (bool, error) {
|
||||||
|
const query = `
|
||||||
|
SELECT EXISTS(SELECT 1 FROM world WHERE id = $1 AND game_id IS NOT NULL)`
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
var exist bool
|
||||||
|
err := db.GetContext(ctx, &exist, query, worldName)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return exist, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWorld(db *sqlx.DB, worldName string) (*World, error) {
|
||||||
|
const query = `
|
||||||
|
SELECT id, title, description, version, system, background, join_theme, core_version, system_version,
|
||||||
|
last_played, playtime, availability, next_session, socket, protected, exclusive_, persistent_storage,
|
||||||
|
locked, owned, has_storage
|
||||||
|
FROM world WHERE id = $1`
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
var world World
|
var world World
|
||||||
err := rows.Scan(
|
err := db.QueryRowxContext(ctx, query, worldName).Scan(
|
||||||
&world.Id,
|
&world.ID, &world.Title, &world.Description, &world.Version, &world.System, &world.Background, &world.JoinTheme,
|
||||||
&world.CreatedAt,
|
&world.CoreVersion, &world.SystemVersion, &world.LastPlayed, &world.Playtime, &world.Availability, &world.NextSession,
|
||||||
&world.TextId,
|
&world.Socket, &world.Protected, &world.Exclusive, &world.PersistentStorage, &world.Locked, &world.Owned, &world.HasStorage,
|
||||||
&world.Title,
|
|
||||||
&world.Description,
|
|
||||||
&world.System,
|
|
||||||
&world.CoreVersion,
|
|
||||||
&world.SystemVersion,
|
|
||||||
&world.PlayTime,
|
|
||||||
&world.NextSession,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
compatibility, err := m.GetWorldsCompatibility(world.Id)
|
return &world, nil
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
world.Compatibility = *compatibility
|
|
||||||
worlds = append(worlds, world)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return worlds, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) GetWorldsCompatibility(idWorld int64) (*Compatibility, error) {
|
|
||||||
if idWorld < 1 {
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
|
||||||
SELECT id, minimum, verified, maximum
|
|
||||||
FROM worlds_compatibility
|
|
||||||
WHERE world_id = $1`
|
|
||||||
|
|
||||||
var compatibility Compatibility
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
err := m.DB.QueryRowContext(ctx, query, idWorld).Scan(
|
|
||||||
&compatibility.Id,
|
|
||||||
&compatibility.Minimum,
|
|
||||||
&compatibility.Verified,
|
|
||||||
&compatibility.Maximum,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, sql.ErrNoRows):
|
|
||||||
return nil, ErrorRecordNotFound
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &compatibility, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) DeleteWorlds(idState int64) error {
|
|
||||||
if idState < 1 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
|
||||||
DELETE FROM worlds
|
|
||||||
WHERE state_id = $1`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
result, err := m.DB.ExecContext(ctx, query, idState)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
rowsAffected, err := result.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rowsAffected == 0 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m FoundryStateModel) DeleteWorld(idWorld int64) error {
|
|
||||||
if idWorld < 1 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
|
||||||
DELETE FROM worlds
|
|
||||||
WHERE id = $1`
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
result, err := m.DB.ExecContext(ctx, query, idWorld)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
rowsAffected, err := result.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rowsAffected == 0 {
|
|
||||||
return ErrorRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// func (worlds Worlds) GetSessionTime(worldName string) (*time.Time, error) {
|
|
||||||
// if worldName == "" && len(worlds) != 1 {
|
|
||||||
// return nil, ErrorSetupNotFound
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if worldName == "" {
|
|
||||||
// return &worlds.GetById(0).NextSession, nil
|
|
||||||
// } else {
|
|
||||||
// for i := range worlds {
|
|
||||||
// if worlds[i].Id == worldName {
|
|
||||||
// return &worlds.GetById(0).NextSession, nil
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return nil, ErrorSetupNotFound
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (world World) GetSessionTime() *time.Time {
|
|
||||||
// return &world.NextSession
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Actor struct {
|
type Actor struct {
|
||||||
PrototypeToken Token `json:"prototypeToken"`
|
PrototypeToken Token `json:"prototypeToken"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Addresses struct {
|
type Addresses struct {
|
||||||
Local string `json:"local"`
|
Local string `json:"local"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Author struct {
|
type Author struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type CardDeck struct {
|
type CardDeck struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Combat struct {
|
type Combat struct {
|
||||||
Id string `json:"_id"`
|
Id string `json:"_id"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Compatibility struct {
|
type Compatibility struct {
|
||||||
Minimum string `json:"minimum,omitempty"`
|
Minimum string `json:"minimum,omitempty"`
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
package json
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FoundryState struct {
|
|
||||||
IsAdmin bool `json:"isAdmin,omitempty"`
|
|
||||||
IsSetup bool `json:"isSetup,omitempty"`
|
|
||||||
Languages []Language `json:"languages,omitempty"`
|
|
||||||
Modules []DataTemplate `json:"modules"`
|
|
||||||
Release Release `json:"release"`
|
|
||||||
Systems []DataTemplate `json:"systems,omitempty"`
|
|
||||||
Worlds []DataTemplate `json:"worlds,omitempty"`
|
|
||||||
World *DataTemplate `json:"world,omitempty"`
|
|
||||||
Users Users `json:"users,omitempty"`
|
|
||||||
Options Options `json:"options,omitempty"`
|
|
||||||
|
|
||||||
//coreUpdate struct{}
|
|
||||||
//featuredContent struct{}
|
|
||||||
//files struct{}
|
|
||||||
//news struct{}
|
|
||||||
|
|
||||||
//packageWarnings struct{} think about it
|
|
||||||
}
|
|
||||||
|
|
||||||
type Options struct {
|
|
||||||
Language string `json:"language,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (state FoundryState) GetRelease() Release {
|
|
||||||
return state.Release
|
|
||||||
}
|
|
||||||
|
|
||||||
func (state FoundryState) GetFoundryStateDB(stateType db.StateType) *db.FoundryState {
|
|
||||||
dbFoundryState := db.FoundryState{
|
|
||||||
IsAdmin: state.IsAdmin,
|
|
||||||
IsSetup: state.IsSetup,
|
|
||||||
Type: stateType,
|
|
||||||
Options: db.Options{Language: state.Options.Language},
|
|
||||||
}
|
|
||||||
|
|
||||||
dbFoundryState.Modules = state.GetModules()
|
|
||||||
dbFoundryState.Systems = state.GetSystems()
|
|
||||||
dbFoundryState.Worlds = state.GetWorlds()
|
|
||||||
dbFoundryState.Users = state.GetUsers()
|
|
||||||
|
|
||||||
return &dbFoundryState
|
|
||||||
}
|
|
||||||
|
|
||||||
func (state *FoundryState) GetModules() db.Modules {
|
|
||||||
modulesCopy := make([]db.Module, 0, 8)
|
|
||||||
for i := range state.Modules {
|
|
||||||
module := &(state.Modules[i])
|
|
||||||
moduleCopy := db.Module{
|
|
||||||
TextId: module.Id,
|
|
||||||
Title: module.Title,
|
|
||||||
Description: module.Description,
|
|
||||||
Compatibility: db.Compatibility{
|
|
||||||
Minimum: module.Compatibility.Minimum,
|
|
||||||
Maximum: module.Compatibility.Maximum,
|
|
||||||
},
|
|
||||||
Url: module.Url,
|
|
||||||
Version: module.CoreVersion,
|
|
||||||
|
|
||||||
Availability: module.Availability,
|
|
||||||
}
|
|
||||||
for j := range module.Languages {
|
|
||||||
lang := db.Language{
|
|
||||||
Lang: module.Languages[j].Lang,
|
|
||||||
Name: module.Languages[j].Name,
|
|
||||||
Path: module.Languages[j].Path,
|
|
||||||
}
|
|
||||||
moduleCopy.Languages = append(moduleCopy.Languages, lang)
|
|
||||||
}
|
|
||||||
modulesCopy = append(modulesCopy, moduleCopy)
|
|
||||||
}
|
|
||||||
return modulesCopy
|
|
||||||
}
|
|
||||||
|
|
||||||
func (state *FoundryState) GetSystems() db.Systems {
|
|
||||||
systemsCopy := make([]db.System, 0, 8)
|
|
||||||
for i := range state.Systems {
|
|
||||||
system := &(state.Systems[i])
|
|
||||||
systemCopy := db.System{
|
|
||||||
TextId: system.Id,
|
|
||||||
Title: system.Title,
|
|
||||||
Description: system.Description,
|
|
||||||
Url: system.Url,
|
|
||||||
Compatibility: db.Compatibility{
|
|
||||||
Minimum: system.Compatibility.Minimum,
|
|
||||||
Maximum: system.Compatibility.Maximum,
|
|
||||||
},
|
|
||||||
Download: system.Download,
|
|
||||||
}
|
|
||||||
systemsCopy = append(systemsCopy, systemCopy)
|
|
||||||
}
|
|
||||||
return systemsCopy
|
|
||||||
}
|
|
||||||
|
|
||||||
func (state *FoundryState) GetWorld() *db.World {
|
|
||||||
return &db.World{
|
|
||||||
TextId: state.World.Id,
|
|
||||||
Title: state.World.Title,
|
|
||||||
Description: state.World.Description,
|
|
||||||
Compatibility: db.Compatibility{
|
|
||||||
Minimum: state.World.Compatibility.Minimum,
|
|
||||||
Maximum: state.World.Compatibility.Maximum,
|
|
||||||
},
|
|
||||||
System: state.World.System,
|
|
||||||
CoreVersion: state.World.CoreVersion,
|
|
||||||
SystemVersion: state.World.SystemVersion,
|
|
||||||
LastPlayed: state.World.LastPlayed,
|
|
||||||
PlayTime: state.World.PlayTime,
|
|
||||||
NextSession: state.World.NextSession,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (state *FoundryState) GetWorlds() db.Worlds {
|
|
||||||
worldsCopy := make([]db.World, 0, 8)
|
|
||||||
for i := range state.Worlds {
|
|
||||||
world := &(state.Worlds[i])
|
|
||||||
worldCopy := db.World{
|
|
||||||
TextId: world.Id,
|
|
||||||
Title: world.Title,
|
|
||||||
Description: world.Description,
|
|
||||||
Compatibility: db.Compatibility{
|
|
||||||
Minimum: world.Compatibility.Minimum,
|
|
||||||
Maximum: world.Compatibility.Maximum,
|
|
||||||
},
|
|
||||||
System: world.System,
|
|
||||||
CoreVersion: world.CoreVersion,
|
|
||||||
SystemVersion: world.SystemVersion,
|
|
||||||
LastPlayed: world.LastPlayed,
|
|
||||||
PlayTime: world.PlayTime,
|
|
||||||
NextSession: world.NextSession,
|
|
||||||
}
|
|
||||||
worldsCopy = append(worldsCopy, worldCopy)
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.World != nil {
|
|
||||||
worldsCopy = append(worldsCopy, *state.GetWorld())
|
|
||||||
}
|
|
||||||
return worldsCopy
|
|
||||||
}
|
|
||||||
|
|
||||||
func (state *FoundryState) GetUsers() db.Users {
|
|
||||||
usersCopy := make(db.Users, 0, 8)
|
|
||||||
for i := range state.Users {
|
|
||||||
user := &(state.Users[i])
|
|
||||||
userCopy := db.User{
|
|
||||||
Name: user.Name,
|
|
||||||
Role: user.Role,
|
|
||||||
Character: user.Character,
|
|
||||||
Color: user.Color,
|
|
||||||
Pronouns: user.Pronouns,
|
|
||||||
Hotbar: user.Hotbar,
|
|
||||||
Stats: db.UserStats{
|
|
||||||
CoreVersion: user.Stats.CoreVersion,
|
|
||||||
SystemId: user.Stats.SystemId,
|
|
||||||
SystemVersion: user.Stats.SystemVersion,
|
|
||||||
CreatedTime: user.Stats.CreatedTime,
|
|
||||||
ModifiedTime: user.Stats.ModifiedTime,
|
|
||||||
LastModifiedBy: user.Stats.LastModifiedBy,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
usersCopy = append(usersCopy, userCopy)
|
|
||||||
}
|
|
||||||
return usersCopy
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseSetupModel(data []byte) (*FoundryState, error) {
|
|
||||||
var modelSetup []FoundryState
|
|
||||||
err := json.Unmarshal(data, &modelSetup)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(modelSetup) > 1 {
|
|
||||||
return nil, ErrorSetupMoreThanOne
|
|
||||||
}
|
|
||||||
return &modelSetup[0], nil
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type DetailsLanguages struct {
|
type DetailsLanguages struct {
|
||||||
Details string `json:"details"`
|
Details string `json:"details"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type DocumentTypes struct {
|
type DocumentTypes struct {
|
||||||
Actor DocumentTypeData `json:"Actor"`
|
Actor DocumentTypeData `json:"Actor"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Environment struct {
|
type Environment struct {
|
||||||
GlobalLight EnvironmentGlobalLight `json:"globalLight"`
|
GlobalLight EnvironmentGlobalLight `json:"globalLight"`
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package json
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrorSetupMoreThanOne = errors.New("Passed more than one setupData")
|
|
||||||
ErrorSetupNotFound = errors.New("Not found setup data from your request")
|
|
||||||
)
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Files struct {
|
type Files struct {
|
||||||
Storages []string `json:"storages"`
|
Storages []string `json:"storages"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Folder struct {
|
type Folder struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Game struct {
|
type Game struct {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Grid struct {
|
type Grid struct {
|
||||||
Type int `json:"type"`
|
Type int `json:"type"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Index struct {
|
type Index struct {
|
||||||
Id string `json:"_id"`
|
Id string `json:"_id"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Item struct {
|
type Item struct {
|
||||||
Img string `json:"img"`
|
Img string `json:"img"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Journal struct {
|
type Journal struct {
|
||||||
Folder any `json:"folder"`
|
Folder any `json:"folder"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type JournalPage struct {
|
type JournalPage struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -1,13 +1,67 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Language struct {
|
type Language struct {
|
||||||
Id string `json:"id"`
|
Lang string `json:"lang"`
|
||||||
Label string `json:"label"`
|
Name string `json:"name"`
|
||||||
Modules []LangModule `json:"modules"`
|
Path string `json:"path"`
|
||||||
|
// SystemLanguagesFlags SystemLanguagesFlags `json:"flags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LangModule struct {
|
func (l *Language) ToDB(dest **db.Language) bool {
|
||||||
Id string `json:"id"`
|
if dest == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
lang := &db.Language{
|
||||||
|
Lang: l.Lang,
|
||||||
|
Name: l.Name,
|
||||||
|
Path: l.Path,
|
||||||
|
}
|
||||||
|
|
||||||
|
*dest = lang
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetupLanguage struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
Modules []*SetupLanguageModule `json:"modules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SetupLanguage) ToDB(dest **db.SetupLanguage) bool {
|
||||||
|
if dest == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
lang := &db.SetupLanguage{
|
||||||
|
ID: s.ID,
|
||||||
|
Label: s.Label,
|
||||||
|
}
|
||||||
|
|
||||||
|
CopySliceToDB(&lang.Modules, s.Modules)
|
||||||
|
|
||||||
|
*dest = lang
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetupLanguageModule struct {
|
||||||
|
ID string `json:"id"`
|
||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SetupLanguageModule) ToDB(dest *db.SetupLanguageModule) bool {
|
||||||
|
if dest == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
dest.ID = s.ID
|
||||||
|
dest.Label = s.Label
|
||||||
|
dest.Path = s.Path
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Light struct {
|
type Light struct {
|
||||||
Alpha float64 `json:"alpha"`
|
Alpha float64 `json:"alpha"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Macro struct {
|
type Macro struct {
|
||||||
Command string `json:"command"`
|
Command string `json:"command"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Media struct {
|
type Media struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
@@ -3,7 +3,7 @@ package json
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Module struct {
|
type Module struct {
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package modules
|
|
||||||
|
|
||||||
type DiceStats struct {
|
|
||||||
PlayerRollData DiceStatsRollData `json:"player_roll_data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DiceStatsRollData struct {
|
|
||||||
PlayerDice []DicePlayerDice `json:"PLAYER_DICE"`
|
|
||||||
Username string `json:"USERNAME"`
|
|
||||||
Userid string `json:"USERID"`
|
|
||||||
Gm bool `json:"GM"`
|
|
||||||
PlayerRollInfo DiceRollInfo `json:"PLAYER_ROLL_INFO"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DicePlayerDice struct {
|
|
||||||
Type string `json:"TYPE"`
|
|
||||||
Max int `json:"MAX"`
|
|
||||||
TotalRolls int `json:"TOTAL_ROLLS"`
|
|
||||||
Rolls []int `json:"ROLLS"`
|
|
||||||
BlindRolls []int `json:"BLIND_ROLLS"`
|
|
||||||
StreakSize int `json:"STREAK_SIZE"`
|
|
||||||
StreakInit int `json:"STREAK_INIT"`
|
|
||||||
StreakIsBlind bool `json:"STREAK_ISBLIND"`
|
|
||||||
LongestStreak int `json:"LONGEST_STREAK"`
|
|
||||||
LongestStreakInit int `json:"LONGEST_STREAK_INIT"`
|
|
||||||
Mean int `json:"MEAN"`
|
|
||||||
Median int `json:"MEDIAN"`
|
|
||||||
Mode int `json:"MODE"`
|
|
||||||
Means []int `json:"MEANS"`
|
|
||||||
Medians []int `json:"MEDIANS"`
|
|
||||||
Modes []int `json:"MODES"`
|
|
||||||
RollCounters []int `json:"ROLL_COUNTERS"`
|
|
||||||
AtkRolls []int `json:"ATK_ROLLS"`
|
|
||||||
DmgRolls []int `json:"DMG_ROLLS"`
|
|
||||||
SavesRolls []int `json:"SAVES_ROLLS"`
|
|
||||||
SkillsRolls []int `json:"SKILLS_ROLLS"`
|
|
||||||
AbilityRolls []int `json:"ABILITY_ROLLS"`
|
|
||||||
UnknownRolls []int `json:"UNKNOWN_ROLLS"`
|
|
||||||
PerceptionRolls []int `json:"PERCEPTION_ROLLS"`
|
|
||||||
InitiativeRolls []int `json:"INITIATIVE_ROLLS"`
|
|
||||||
AtkRollsBlind []int `json:"ATK_ROLLS_BLIND"`
|
|
||||||
DmgRollsBlind []int `json:"DMG_ROLLS_BLIND"`
|
|
||||||
SavesRollsBlind []int `json:"SAVES_ROLLS_BLIND"`
|
|
||||||
SkillsRollsBlind []int `json:"SKILLS_ROLLS_BLIND"`
|
|
||||||
AbilityRollsBlind []int `json:"ABILITY_ROLLS_BLIND"`
|
|
||||||
UnknownRollsBlind []int `json:"UNKNOWN_ROLLS_BLIND"`
|
|
||||||
PerceptionRollsBlind []int `json:"PERCEPTION_ROLLS_BLIND"`
|
|
||||||
InitiativeRollsBlind []int `json:"INITIATIVE_ROLLS_BLIND"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DiceRollInfo struct {
|
|
||||||
IsRollInfoTracked bool `json:"IS_ROLL_INFO_TRACKED"`
|
|
||||||
AtkOutcomeTracker []int `json:"ATK_OUTCOME_TRACKER"`
|
|
||||||
NumUntargetedAtks int `json:"NUM_UNTARGETED_ATKS"`
|
|
||||||
TotalAttacks int `json:"TOTAL_ATTACKS"`
|
|
||||||
SaveOutcomeTracker []int `json:"SAVE_OUTCOME_TRACKER"`
|
|
||||||
NumUntargetedSaves int `json:"NUM_UNTARGETED_SAVES"`
|
|
||||||
TotalSaves int `json:"TOTAL_SAVES"`
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package modules
|
|
||||||
|
|
||||||
type Pf2eModule struct {
|
|
||||||
settings Pf2eSettings
|
|
||||||
}
|
|
||||||
|
|
||||||
type Pf2eSettings struct {
|
|
||||||
showEffectPanel bool
|
|
||||||
showCheckDialogs bool
|
|
||||||
showDamageDialogs bool
|
|
||||||
monochromeDarkvision bool
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Note struct {
|
type Note struct {
|
||||||
EntryID string `json:"entryId"`
|
EntryID string `json:"entryId"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type GameOptions struct {
|
type GameOptions struct {
|
||||||
Language string `json:"language"`
|
Language string `json:"language"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Ownership struct {
|
type Ownership struct {
|
||||||
Player string `json:"PLAYER,omitempty"`
|
Player string `json:"PLAYER,omitempty"`
|
||||||
@@ -3,7 +3,7 @@ package json
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Pack struct {
|
type Pack struct {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type PackageWarningsData struct {
|
type PackageWarningsData struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Playlist struct {
|
type Playlist struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -3,7 +3,7 @@ package json
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Relationships struct {
|
type Relationships struct {
|
||||||
@@ -1,11 +1,32 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Release struct {
|
type Release struct {
|
||||||
Generation int `json:"generation"`
|
Generation int `json:"generation"`
|
||||||
Channel string `json:"channel"`
|
Channel string `json:"channel"`
|
||||||
Suffix string `json:"suffix"`
|
Suffix string `json:"suffix"`
|
||||||
Build int `json:"build"`
|
Build int `json:"build"`
|
||||||
Node_version int `json:"node_version"`
|
NodeVersion int `json:"node_version"`
|
||||||
|
MaxGeneration int `json:"maxGeneration"`
|
||||||
|
MaxStableGeneration int `json:"maxStableGeneration"`
|
||||||
Time int64 `json:"time"`
|
Time int64 `json:"time"`
|
||||||
flags struct{} `json:"-"`
|
// Flags Flags `json:"flags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Release) ToDB(dest *db.Release) bool {
|
||||||
|
if dest == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
dest.Generation = r.Generation
|
||||||
|
dest.Channel = r.Channel
|
||||||
|
dest.Suffix = r.Suffix
|
||||||
|
dest.Build = r.Build
|
||||||
|
dest.NodeVersion = r.NodeVersion
|
||||||
|
dest.MaxGeneration = r.MaxGeneration
|
||||||
|
dest.MaxStableGeneration = r.MaxStableGeneration
|
||||||
|
dest.Time = r.Time
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Ring struct {
|
type Ring struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
@@ -3,7 +3,7 @@ package json
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Scene struct {
|
type Scene struct {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
|
|
||||||
type Setting struct {
|
type Setting struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
|
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Setup struct {
|
type Setup struct {
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user