Compare commits
7 Commits
69d3d38ab7
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a77a6ee0f | ||
|
|
4bb4b04004 | ||
|
|
fd65631be1 | ||
|
|
07025afefc | ||
|
|
f46ff8333e | ||
|
|
21abe68858 | ||
|
|
1b7c7e9ae3 |
2
Makefile
2
Makefile
@@ -1,7 +1,7 @@
|
||||
-include .env
|
||||
|
||||
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} -world-user=${WORLD_USER} -world-pass=${WORLD_PASS}
|
||||
|
||||
db/migration/new:
|
||||
@echo 'Creating migration files for ${name}...'
|
||||
|
||||
@@ -1,37 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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) {
|
||||
data, err := app.foundryApp.Test()
|
||||
dbConn := app.foundryApp.Transport.DB
|
||||
|
||||
world, err := db.GetWorld(dbConn, "kingmaker")
|
||||
if err != nil {
|
||||
app.slogger.Error("", "error", err)
|
||||
w.Write([]byte(err.Error()))
|
||||
w.Write([]byte("Got error"))
|
||||
return
|
||||
}
|
||||
|
||||
var dbGame db.Game
|
||||
|
||||
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)
|
||||
fmt.Fprintf(w, "Got world with name %s, core_version - %s, next_session - %v", world.ID, world.CoreVersion, world.NextSession)
|
||||
}
|
||||
|
||||
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,
|
||||
"is_available": app.foundryApp.IsAvailable,
|
||||
},
|
||||
"config": app.foundryApp.GetHTTP(),
|
||||
}
|
||||
err := app.writeJSON(w, http.StatusOK, env, nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"flag"
|
||||
"log"
|
||||
"log/slog"
|
||||
@@ -13,7 +12,9 @@ import (
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry"
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/requests"
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport"
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
@@ -47,8 +48,8 @@ type application struct {
|
||||
|
||||
var version = "0.1.0"
|
||||
|
||||
func openDB(cfg config) (*sql.DB, error) {
|
||||
db, err := sql.Open("sqlite3", cfg.db.dsn)
|
||||
func openDB(cfg config) (*sqlx.DB, error) {
|
||||
db, err := sqlx.Open("sqlite3", cfg.db.dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -73,7 +74,7 @@ func openDB(cfg config) (*sql.DB, error) {
|
||||
}
|
||||
|
||||
// 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.env, "env", "development", "Enivornment (development|staging|production)")
|
||||
|
||||
@@ -85,15 +86,29 @@ func (app *application) parseFlags() *requests.FoundryHttpRequest {
|
||||
var mode string
|
||||
flag.StringVar(&mode, "service_mode", "api", "Type of service mode(api|discord|tg)")
|
||||
|
||||
foundryHttpData := requests.FoundryHttpRequest{SessionID: nil}
|
||||
flag.StringVar(&foundryHttpData.Host, "foundry_host", "127.0.0.1", "Address to connect to Foundry")
|
||||
flag.StringVar(&foundryHttpData.Password, "foundry_pass", "", "Password to connect to Foundry")
|
||||
var foundryTransportData transport.FoundryTransportData
|
||||
foundryTransportData.HttpConfig = &requests.FoundryHttpRequest{SessionID: nil}
|
||||
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
|
||||
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 (format: \"test\", \"test1,test2,test3\", \"test1,test2,test3\")")
|
||||
var users string
|
||||
flag.StringVar(&users, "world-user", "", "Usernames to authenticate the world (format: \"test\", \"test1,test2,test3\", \"testForAll\")")
|
||||
var passwords string
|
||||
flag.StringVar(&passwords, "world-pass", "", "Passwords to authenticate the world (format: \"test\", \"test1,test2,test3\", \"testForAll\")")
|
||||
flag.Parse()
|
||||
|
||||
worldsData, err := types.CreateWorldDataSlice(worlds, users, passwords)
|
||||
if err != nil {
|
||||
app.slogger.Warn("Got err on parsing authentication world data", "err", err)
|
||||
return nil
|
||||
}
|
||||
foundryTransportData.Worlds = worldsData
|
||||
|
||||
app.cfg.mode = service_mode(mode)
|
||||
|
||||
logLevel, ok := StringToLogLevel[logLevelStr]
|
||||
@@ -105,20 +120,25 @@ func (app *application) parseFlags() *requests.FoundryHttpRequest {
|
||||
if !ok {
|
||||
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
|
||||
func main() {
|
||||
app := application{foundryApp: &foundry.FoundryApi{}}
|
||||
|
||||
foundryHttpData := app.parseFlags()
|
||||
foundryTransportData := app.parseFlags()
|
||||
if foundryTransportData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
jar, err := cookiejar.New(nil)
|
||||
if err != nil {
|
||||
app.slogger.Error("Error", "text", err.Error())
|
||||
return
|
||||
}
|
||||
foundryHttpData.Jar = jar
|
||||
foundryTransportData.HttpConfig.Jar = jar
|
||||
|
||||
app.slogger.Info("", "dsn", app.cfg.db.dsn)
|
||||
|
||||
@@ -129,8 +149,15 @@ func main() {
|
||||
}
|
||||
defer db.Close()
|
||||
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()
|
||||
|
||||
app.serve()
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
DROP TABLE IF EXISTS options;
|
||||
DROP TABLE IF EXISTS foundry_state;
|
||||
File diff suppressed because it is too large
Load Diff
46
db/migrations/000001_create_setup.down.sql
Normal file
46
db/migrations/000001_create_setup.down.sql
Normal file
@@ -0,0 +1,46 @@
|
||||
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 pack;
|
||||
DROP TABLE IF EXISTS language;
|
||||
DROP TABLE IF EXISTS style;
|
||||
DROP TABLE IF EXISTS media;
|
||||
DROP TABLE IF EXISTS author;
|
||||
DROP TABLE IF EXISTS es_modules;
|
||||
DROP TABLE IF EXISTS scripts;
|
||||
DROP TABLE IF EXISTS tags;
|
||||
DROP TABLE IF EXISTS relationships_systems;
|
||||
DROP TABLE IF EXISTS relationships_requires;
|
||||
DROP TABLE IF EXISTS relationships_recommends;
|
||||
DROP TABLE IF EXISTS relationships_conflicts;
|
||||
DROP TABLE IF EXISTS relationships;
|
||||
DROP TABLE IF EXISTS compatibility;
|
||||
DROP TABLE IF EXISTS world;
|
||||
DROP TABLE IF EXISTS grid;
|
||||
DROP TABLE IF EXISTS system;
|
||||
DROP TABLE IF EXISTS package_warnings_data_error;
|
||||
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;
|
||||
DROP TABLE IF EXISTS news;
|
||||
DROP TABLE IF EXISTS folder_packs;
|
||||
DROP TABLE IF EXISTS folder;
|
||||
DROP TABLE IF EXISTS pack_folder;
|
||||
DROP TABLE IF EXISTS pack_to_index;
|
||||
DROP TABLE IF EXISTS index_;
|
||||
DROP TABLE IF EXISTS ownership;
|
||||
DROP TABLE IF EXISTS relationships_data;
|
||||
DROP TABLE IF EXISTS document_types_data_html;
|
||||
DROP TABLE IF EXISTS document_types_data;
|
||||
DROP TABLE IF EXISTS document_types;
|
||||
DROP TABLE IF EXISTS module;
|
||||
DROP TABLE IF EXISTS setup_language_module;
|
||||
DROP TABLE IF EXISTS setup_language;
|
||||
DROP TABLE IF EXISTS release_;
|
||||
DROP TABLE IF EXISTS setup_options;
|
||||
DROP TABLE IF EXISTS files_storage;
|
||||
DROP TABLE IF EXISTS files;
|
||||
DROP TABLE IF EXISTS featured_content;
|
||||
DROP TABLE IF EXISTS core_update;
|
||||
DROP TABLE IF EXISTS setup;
|
||||
566
db/migrations/000001_create_setup.up.sql
Normal file
566
db/migrations/000001_create_setup.up.sql
Normal file
@@ -0,0 +1,566 @@
|
||||
CREATE TABLE IF NOT EXISTS setup (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
is_admin BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
is_setup BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
|
||||
created_at DATETIME NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS core_update (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
has_update BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
can_update BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
could_reach_website BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
slow_response BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
will_disable_modules BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
version VARCHAR(64) NOT NULL,
|
||||
channel VARCHAR(64) NOT NULL,
|
||||
|
||||
setup_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (setup_id) REFERENCES setup(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS featured_content (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
title VARCHAR(128) NOT NULL,
|
||||
caption VARCHAR(128) NOT NULL,
|
||||
url VARCHAR(128) NOT NULL,
|
||||
image VARCHAR(128) NOT NULL,
|
||||
|
||||
setup_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (setup_id) REFERENCES setup(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS files (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
setup_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (setup_id) REFERENCES setup(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS files_storage (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
storage TEXT NOT NULL,
|
||||
|
||||
files_id INTEGER,
|
||||
FOREIGN KEY (files_id) REFERENCES files(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS setup_options (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
css_theme VARCHAR(128) NOT NULL,
|
||||
data_path VARCHAR(128) NOT NULL,
|
||||
hostname VARCHAR(128) NOT NULL,
|
||||
language VARCHAR(128) NOT NULL,
|
||||
local_hostname VARCHAR(128) NOT NULL,
|
||||
update_channel VARCHAR(128) NOT NULL,
|
||||
port INTEGER NOT NULL,
|
||||
compress_socket BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
compress_static BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
fullscreen BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
hot_reload BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
proxy_ssl BOOLEAN NOT NULL,
|
||||
telemetry BOOLEAN NOT NULL,
|
||||
upnp BOOLEAN NOT NULL,
|
||||
delete_nedb BOOLEAN NOT NULL,
|
||||
no_backups BOOLEAN NOT NULL,
|
||||
|
||||
setup_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (setup_id) REFERENCES setup(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS release_ (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
generation INTEGER NOT NULL,
|
||||
build INTEGER NOT NULL,
|
||||
node_version INTEGER NOT NULL,
|
||||
max_generation INTEGER NOT NULL,
|
||||
max_stable_generation INTEGER NOT NULL,
|
||||
time DATETIME NOT NULL,
|
||||
channel VARCHAR(128) NOT NULL,
|
||||
suffix VARCHAR(128) NOT NULL,
|
||||
|
||||
setup_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (setup_id) REFERENCES setup(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS setup_language (
|
||||
id INTEGER PRIMARY KEY,
|
||||
|
||||
label VARCHAR(128) NOT NULL,
|
||||
|
||||
setup_id INTEGER,
|
||||
FOREIGN KEY (setup_id) REFERENCES setup(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS setup_language_module (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
label VARCHAR(128) NOT NULL,
|
||||
path VARCHAR(128) NOT NULL,
|
||||
|
||||
setup_language_id INTEGER,
|
||||
FOREIGN KEY (setup_language_id) REFERENCES setup_language(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS module (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
title VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(128) NOT NULL,
|
||||
url VARCHAR(128) NOT NULL,
|
||||
license VARCHAR(128) NOT NULL,
|
||||
readme VARCHAR(128) NOT NULL,
|
||||
bugs VARCHAR(128) NOT NULL,
|
||||
changelog VARCHAR(128) NOT NULL,
|
||||
version VARCHAR(128) NOT NULL,
|
||||
manifest VARCHAR(128) NOT NULL,
|
||||
download VARCHAR(128) NOT NULL,
|
||||
socket BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
protected BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
exclusive_ BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
persistent_storage BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
core_translation BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
library BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
locked BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
owned BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
has_storage BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
active BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
availability INTEGER NOT NULL DEFAULT FALSE,
|
||||
|
||||
setup_id INTEGER,
|
||||
FOREIGN KEY (setup_id) REFERENCES setup(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS document_types (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
module_id TEXT UNIQUE,
|
||||
FOREIGN KEY (module_id) REFERENCES module(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS document_types_data (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
type VARCHAR(64) NOT NULL,
|
||||
|
||||
document_types_id INTEGER,
|
||||
FOREIGN KEY (document_types_id) REFERENCES document_types(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS document_types_data_html (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
value TEXT NOT NULL,
|
||||
|
||||
document_types_data_id INTEGER,
|
||||
FOREIGN KEY (document_types_data_id) REFERENCES document_types_data(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS relationships (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
module_id TEXT UNIQUE,
|
||||
FOREIGN KEY (module_id) REFERENCES module(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS relationships_systems (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
key_ TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
manifest TEXT NOT NULL,
|
||||
|
||||
relationships_id INTEGER,
|
||||
FOREIGN KEY (relationships_id) REFERENCES relationships(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS relationships_requires (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
key_ INTEGER NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
manifest TEXT NOT NULL,
|
||||
|
||||
relationships_id INTEGER,
|
||||
FOREIGN KEY (relationships_id) REFERENCES relationships(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS relationships_recommends (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
key_ INTEGER NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
manifest TEXT NOT NULL,
|
||||
|
||||
relationships_id INTEGER,
|
||||
FOREIGN KEY (relationships_id) REFERENCES relationships(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS relationships_conflicts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
key_ INTEGER NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
manifest TEXT NOT NULL,
|
||||
|
||||
relationships_id INTEGER,
|
||||
FOREIGN KEY (relationships_id) REFERENCES relationships(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS compatibility (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
minimum VARCHAR(64) NOT NULL,
|
||||
verified VARCHAR(64) NOT NULL,
|
||||
maximum VARCHAR(64) NOT NULL,
|
||||
|
||||
module_id TEXT UNIQUE,
|
||||
FOREIGN KEY (module_id) REFERENCES module(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE compatibility ADD COLUMN relationships_systems_id TEXT REFERENCES relationships_systems(id) ON DELETE CASCADE;
|
||||
ALTER TABLE compatibility ADD COLUMN relationships_requires_id TEXT REFERENCES relationships_requires(id) ON DELETE CASCADE;
|
||||
ALTER TABLE compatibility ADD COLUMN relationships_recommends_id TEXT REFERENCES relationships_recommends(id) ON DELETE CASCADE;
|
||||
ALTER TABLE compatibility ADD COLUMN relationships_conflicts_id TEXT REFERENCES relationships_conflicts(id) ON DELETE CASCADE;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS scripts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
value TEXT NOT NULL,
|
||||
|
||||
module_id TEXT,
|
||||
FOREIGN KEY (module_id) REFERENCES module(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS es_modules (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
value TEXT NOT NULL,
|
||||
|
||||
module_id TEXT,
|
||||
FOREIGN KEY (module_id) REFERENCES module(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tags (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
value VARCHAR(128) NOT NULL,
|
||||
|
||||
module_id TEXT,
|
||||
FOREIGN KEY (module_id) REFERENCES module(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS author (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
name VARCHAR(128) NOT NULL,
|
||||
url VARCHAR(128) NOT NULL,
|
||||
email VARCHAR(128) NOT NULL,
|
||||
discord VARCHAR(128) NOT NULL,
|
||||
|
||||
module_id TEXT,
|
||||
FOREIGN KEY (module_id) REFERENCES module(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS media (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
type VARCHAR(128) NOT NULL,
|
||||
url VARCHAR(128) NOT NULL,
|
||||
caption VARCHAR(128) NOT NULL,
|
||||
|
||||
module_id TEXT,
|
||||
FOREIGN KEY (module_id) REFERENCES module(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS style (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
src VARCHAR(128) NOT NULL,
|
||||
|
||||
module_id TEXT,
|
||||
FOREIGN KEY (module_id) REFERENCES module(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS language (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
lang VARCHAR(128) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
path VARCHAR(128) NOT NULL,
|
||||
|
||||
module_id TEXT,
|
||||
FOREIGN KEY (module_id) REFERENCES module(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS pack (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
name VARCHAR(128) NOT NULL,
|
||||
label VARCHAR(128) NOT NULL,
|
||||
banner VARCHAR(128) NOT NULL,
|
||||
path VARCHAR(128) NOT NULL,
|
||||
type VARCHAR(128) NOT NULL,
|
||||
system VARCHAR(128) NOT NULL,
|
||||
package_type VARCHAR(128) NOT NULL,
|
||||
package_name VARCHAR(128) NOT NULL,
|
||||
|
||||
module_id TEXT,
|
||||
FOREIGN KEY (module_id) REFERENCES module(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ownership (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
player VARCHAR(128) NOT NULL,
|
||||
trusted VARCHAR(128) NOT NULL,
|
||||
assistant VARCHAR(128) NOT NULL,
|
||||
|
||||
pack_id TEXT,
|
||||
FOREIGN KEY (pack_id) REFERENCES pack(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS index_ (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
folder VARCHAR(128) NOT NULL,
|
||||
img VARCHAR(128) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
type VARCHAR(128) NOT NULL
|
||||
);
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
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 (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
description VARCHAR(128) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
sorting VARCHAR(128) NOT NULL,
|
||||
type VARCHAR(128) NOT NULL,
|
||||
sort INTEGER NOT NULL,
|
||||
|
||||
pack_id TEXT,
|
||||
FOREIGN KEY (pack_id) REFERENCES pack(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS folder (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
name VARCHAR(128) NOT NULL,
|
||||
sorting VARCHAR(128) NOT NULL,
|
||||
color VARCHAR(128) NOT NULL,
|
||||
|
||||
folder_id INTEGER,
|
||||
FOREIGN KEY (folder_id) REFERENCES folder(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE folder ADD COLUMN module_id TEXT REFERENCES module(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_folder_module_id ON folder(module_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS folder_packs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
value VARCHAR(128) NOT NULL,
|
||||
|
||||
folder_id INTEGER,
|
||||
FOREIGN KEY (folder_id) REFERENCES folder(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS news (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
title VARCHAR(64) NOT NULL,
|
||||
caption VARCHAR(64) NOT NULL,
|
||||
url VARCHAR(64) NOT NULL,
|
||||
image VARCHAR(64) NOT NULL,
|
||||
|
||||
setup_id INTEGER,
|
||||
FOREIGN KEY (setup_id) REFERENCES setup(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS package_warnings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
key_ TEXT NOT NULL,
|
||||
|
||||
setup_id INTEGER,
|
||||
FOREIGN KEY (setup_id) REFERENCES setup(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS package_warnings_to_data (
|
||||
package_warnings_id INTEGER,
|
||||
package_warnings_data_id TEXT,
|
||||
|
||||
PRIMARY KEY (package_warnings_id, package_warnings_data_id),
|
||||
FOREIGN KEY (package_warnings_id) REFERENCES package_warnings(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (package_warnings_data_id) REFERENCES package_warnings_data(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS package_warnings_data (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
type VARCHAR(128) NOT NULL,
|
||||
manifest VARCHAR(128) 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 (
|
||||
id INTEGER PRIMARY KEY,
|
||||
|
||||
value VARCHAR(128) NOT NULL,
|
||||
|
||||
package_warnings_data_id TEXT,
|
||||
FOREIGN KEY (package_warnings_data_id) REFERENCES package_warnings_data(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS package_warnings_data_error (
|
||||
id INTEGER PRIMARY KEY,
|
||||
|
||||
value VARCHAR(128) NOT NULL,
|
||||
|
||||
package_warnings_data_id TEXT,
|
||||
FOREIGN KEY (package_warnings_data_id) REFERENCES package_warnings_data(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS system (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
title VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(128) NOT NULL,
|
||||
url VARCHAR(128) NOT NULL,
|
||||
license VARCHAR(128) NOT NULL,
|
||||
bugs VARCHAR(128) NOT NULL,
|
||||
changelog VARCHAR(128) NOT NULL,
|
||||
version VARCHAR(128) NOT NULL,
|
||||
manifest VARCHAR(128) NOT NULL,
|
||||
download VARCHAR(128) NOT NULL,
|
||||
background VARCHAR(128) NOT NULL DEFAULT FALSE,
|
||||
primary_token_attribute VARCHAR(128) NOT NULL DEFAULT FALSE,
|
||||
availability INTEGER NOT NULL DEFAULT FALSE,
|
||||
socket BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
protected BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
exclusive_ BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
persistent_storage BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
locked BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
owned BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
has_storage BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
setup_id INTEGER,
|
||||
FOREIGN KEY (setup_id) REFERENCES setup(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE compatibility ADD COLUMN system_id TEXT REFERENCES system(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_compatibility_system_id ON compatibility(system_id);
|
||||
ALTER TABLE relationships ADD COLUMN system_id TEXT REFERENCES system(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_relationships_system_id ON relationships(system_id);
|
||||
ALTER TABLE document_types ADD COLUMN system_id TEXT REFERENCES system(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_document_types_system_id ON document_types(system_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS grid (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
type INTEGER NOT NULL,
|
||||
size INTEGER NOT NULL,
|
||||
distance INTEGER NOT NULL,
|
||||
diagonals INTEGER NOT NULL,
|
||||
thickness INTEGER NOT NULL,
|
||||
alpha REAL NOT NULL,
|
||||
color VARCHAR(128) NOT NULL,
|
||||
units VARCHAR(128) NOT NULL,
|
||||
style VARCHAR(128) NOT NULL,
|
||||
|
||||
system_id TEXT UNIQUE,
|
||||
FOREIGN KEY (system_id) REFERENCES system(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE es_modules ADD COLUMN system_id TEXT REFERENCES system(id) ON DELETE CASCADE;
|
||||
ALTER TABLE scripts ADD COLUMN system_id TEXT REFERENCES system(id) ON DELETE CASCADE;
|
||||
ALTER TABLE tags ADD COLUMN system_id TEXT REFERENCES system(id) ON DELETE CASCADE;
|
||||
ALTER TABLE author ADD COLUMN system_id TEXT REFERENCES system(id) ON DELETE CASCADE;
|
||||
ALTER TABLE media ADD COLUMN system_id TEXT REFERENCES system(id) ON DELETE CASCADE;
|
||||
ALTER TABLE pack ADD COLUMN system_id TEXT REFERENCES system(id) ON DELETE CASCADE;
|
||||
ALTER TABLE style ADD COLUMN system_id TEXT REFERENCES system(id) ON DELETE CASCADE;
|
||||
ALTER TABLE language ADD COLUMN system_id TEXT REFERENCES system(id) ON DELETE CASCADE;
|
||||
ALTER TABLE folder ADD COLUMN system_id TEXT REFERENCES system(id) ON DELETE CASCADE;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS world (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
title VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(128) NOT NULL,
|
||||
version VARCHAR(128) NOT NULL,
|
||||
system VARCHAR(128) NOT NULL,
|
||||
background VARCHAR(128) NOT NULL,
|
||||
join_theme VARCHAR(128) NOT NULL,
|
||||
core_version VARCHAR(128) NOT NULL,
|
||||
system_version VARCHAR(128) NOT NULL,
|
||||
last_played VARCHAR(128) NOT NULL,
|
||||
playtime INTEGER NOT NULL,
|
||||
availability INTEGER NOT NULL,
|
||||
next_session DATETIME NOT NULL,
|
||||
socket BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
protected BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
exclusive_ BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
persistent_storage BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
locked BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
owned BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
has_storage BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
setup_id INTEGER,
|
||||
FOREIGN KEY (setup_id) REFERENCES setup(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE compatibility ADD COLUMN world_id TEXT REFERENCES world(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_compatibility_world_id ON compatibility(world_id);
|
||||
ALTER TABLE relationships ADD COLUMN world_id TEXT REFERENCES world(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_relationships_world_id ON relationships(world_id);
|
||||
|
||||
ALTER TABLE tags ADD COLUMN world_id TEXT REFERENCES world(id) ON DELETE CASCADE;
|
||||
ALTER TABLE scripts ADD COLUMN world_id TEXT REFERENCES world(id) ON DELETE CASCADE;
|
||||
ALTER TABLE es_modules ADD COLUMN world_id TEXT REFERENCES world(id) ON DELETE CASCADE;
|
||||
ALTER TABLE author ADD COLUMN world_id TEXT REFERENCES world(id) ON DELETE CASCADE;
|
||||
ALTER TABLE media ADD COLUMN world_id TEXT REFERENCES world(id) ON DELETE CASCADE;
|
||||
ALTER TABLE style ADD COLUMN world_id TEXT REFERENCES world(id) ON DELETE CASCADE;
|
||||
ALTER TABLE language ADD COLUMN world_id TEXT REFERENCES world(id) ON DELETE CASCADE;
|
||||
ALTER TABLE pack ADD COLUMN world_id TEXT REFERENCES world(id) ON DELETE CASCADE;
|
||||
ALTER TABLE folder ADD COLUMN world_id TEXT REFERENCES world(id) ON DELETE CASCADE;
|
||||
203
db/migrations/000002_create_game.down.sql
Normal file
203
db/migrations/000002_create_game.down.sql
Normal file
@@ -0,0 +1,203 @@
|
||||
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_pack_pack;
|
||||
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_occludable;
|
||||
DROP TABLE IF EXISTS token_light_darkness;
|
||||
DROP TABLE IF EXISTS token_light_animation;
|
||||
DROP TABLE IF EXISTS token_light;
|
||||
DROP TABLE IF EXISTS token_bar_2;
|
||||
DROP TABLE IF EXISTS token_bar_1;
|
||||
DROP TABLE IF EXISTS token_texture;
|
||||
DROP TABLE IF EXISTS token_sight;
|
||||
DROP TABLE IF EXISTS ring_subject;
|
||||
DROP TABLE IF EXISTS ring_colors;
|
||||
DROP TABLE IF EXISTS ring;
|
||||
DROP TABLE IF EXISTS token;
|
||||
DROP TABLE IF EXISTS actor;
|
||||
DROP TABLE IF EXISTS sound;
|
||||
DROP TABLE IF EXISTS playlist;
|
||||
DROP TABLE IF EXISTS table_result_range;
|
||||
DROP TABLE IF EXISTS table_result;
|
||||
DROP TABLE IF EXISTS table_;
|
||||
DROP TABLE IF EXISTS journal_page_video;
|
||||
DROP TABLE IF EXISTS journal_page_title;
|
||||
DROP TABLE IF EXISTS journal_page_text;
|
||||
DROP TABLE IF EXISTS journal_page;
|
||||
DROP TABLE IF EXISTS journal;
|
||||
DROP TABLE IF EXISTS setting;
|
||||
DROP TABLE IF EXISTS item;
|
||||
DROP TABLE IF EXISTS world_folder;
|
||||
DROP TABLE IF EXISTS macro;
|
||||
DROP TABLE IF EXISTS hotbar;
|
||||
DROP TABLE IF EXISTS user;
|
||||
DROP TABLE IF EXISTS face;
|
||||
DROP TABLE IF EXISTS back;
|
||||
DROP TABLE IF EXISTS card;
|
||||
DROP TABLE IF EXISTS ownership_string;
|
||||
DROP TABLE IF EXISTS card_deck;
|
||||
DROP TABLE IF EXISTS combatant;
|
||||
DROP TABLE IF EXISTS combat_groups;
|
||||
DROP TABLE IF EXISTS combat;
|
||||
DROP TABLE IF EXISTS message_rolls;
|
||||
DROP TABLE IF EXISTS message_whisper;
|
||||
DROP TABLE IF EXISTS speaker;
|
||||
DROP TABLE IF EXISTS stats;
|
||||
DROP TABLE IF EXISTS message;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS pack_new (
|
||||
id VARCHAR(128) PRIMARY KEY,
|
||||
|
||||
name VARCHAR(128) NOT NULL,
|
||||
label VARCHAR(128) NOT NULL,
|
||||
banner VARCHAR(128) NOT NULL,
|
||||
path VARCHAR(128) NOT NULL,
|
||||
type VARCHAR(128) NOT NULL,
|
||||
system VARCHAR(128) NOT NULL,
|
||||
package_type VARCHAR(128) NOT NULL,
|
||||
package_name VARCHAR(128) NOT NULL,
|
||||
|
||||
module_id TEXT,
|
||||
FOREIGN KEY (module_id) REFERENCES module(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;
|
||||
INSERT INTO pack_new (id, name, label, banner, path, type, system, package_type, package_name, module_id, system_id, world_id)
|
||||
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;
|
||||
ALTER TABLE pack_new RENAME TO pack;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS package_warnings_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
key_ TEXT NOT NULL,
|
||||
|
||||
setup_id INTEGER,
|
||||
FOREIGN KEY (setup_id) REFERENCES setup(id) ON DELETE CASCADE
|
||||
);
|
||||
INSERT INTO package_warnings_new (id, key_, setup_id)
|
||||
SELECT id, key_, setup_id FROM package_warnings;
|
||||
DROP TABLE IF EXISTS 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 system_update;
|
||||
DROP INDEX IF EXISTS idx_core_update_game_id;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS core_update_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
has_update BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
can_update BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
could_reach_website BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
slow_response BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
will_disable_modules BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
version VARCHAR(64) NOT NULL,
|
||||
channel VARCHAR(64) NOT NULL,
|
||||
|
||||
setup_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (setup_id) REFERENCES setup(id) ON DELETE CASCADE
|
||||
);
|
||||
INSERT INTO core_update_new (id, has_update, can_update, could_reach_website, slow_response, will_disable_modules, version, channel, setup_id)
|
||||
SELECT id, has_update, can_update, could_reach_website, slow_response, will_disable_modules, version, channel, setup_id FROM core_update;
|
||||
DROP TABLE IF EXISTS core_update;
|
||||
ALTER TABLE core_update_new RENAME TO core_update;
|
||||
|
||||
DROP TABLE IF EXISTS game_to_systems;
|
||||
DROP INDEX IF EXISTS idx_world_game_id;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS world_new (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
title VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(128) NOT NULL,
|
||||
version VARCHAR(128) NOT NULL,
|
||||
system VARCHAR(128) NOT NULL,
|
||||
background VARCHAR(128) NOT NULL,
|
||||
join_theme VARCHAR(128) NOT NULL,
|
||||
core_version VARCHAR(128) NOT NULL,
|
||||
system_version VARCHAR(128) NOT NULL,
|
||||
last_played VARCHAR(128) NOT NULL,
|
||||
playtime INTEGER NOT NULL,
|
||||
availability INTEGER NOT NULL,
|
||||
next_session DATETIME NOT NULL,
|
||||
socket BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
protected BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
exclusive_ BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
persistent_storage BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
locked BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
owned BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
has_storage BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
setup_id INTEGER,
|
||||
FOREIGN KEY (setup_id) REFERENCES setup(id) ON DELETE CASCADE
|
||||
);
|
||||
INSERT INTO world_new (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, created_at, updated_at, setup_id)
|
||||
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, created_at, updated_at, setup_id FROM world;
|
||||
DROP TABLE IF EXISTS world;
|
||||
ALTER TABLE world_new RENAME TO world;
|
||||
|
||||
DROP INDEX IF EXISTS idx_release_game_id;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS release_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
generation INTEGER NOT NULL,
|
||||
build INTEGER NOT NULL,
|
||||
node_version INTEGER NOT NULL,
|
||||
max_generation INTEGER NOT NULL,
|
||||
max_stable_generation INTEGER NOT NULL,
|
||||
time DATETIME NOT NULL,
|
||||
channel VARCHAR(128) NOT NULL,
|
||||
suffix VARCHAR(128) NOT NULL,
|
||||
|
||||
setup_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (setup_id) REFERENCES setup(id) ON DELETE CASCADE
|
||||
);
|
||||
INSERT INTO release_new (id, generation, build, node_version, max_generation, max_stable_generation, time, channel, suffix, setup_id)
|
||||
SELECT id, generation, build, node_version, max_generation, max_stable_generation, time, channel, suffix, setup_id FROM release_;
|
||||
DROP TABLE IF EXISTS release_;
|
||||
ALTER TABLE release_new RENAME TO release_;
|
||||
|
||||
DROP TABLE IF EXISTS game_options;
|
||||
DROP INDEX IF EXISTS idx_files_game_id;
|
||||
|
||||
DROP TABLE IF EXISTS files_new;
|
||||
CREATE TABLE IF NOT EXISTS files_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
setup_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (setup_id) REFERENCES setup(id) ON DELETE CASCADE
|
||||
);
|
||||
INSERT INTO files_new (id, setup_id)
|
||||
SELECT id, setup_id FROM files;
|
||||
DROP TABLE IF EXISTS files;
|
||||
ALTER TABLE files_new RENAME TO files;
|
||||
|
||||
DROP TABLE IF EXISTS addresses;
|
||||
DROP TABLE IF EXISTS game;
|
||||
|
||||
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 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;
|
||||
779
db/migrations/000002_create_game.up.sql
Normal file
779
db/migrations/000002_create_game.up.sql
Normal file
@@ -0,0 +1,779 @@
|
||||
CREATE TABLE IF NOT EXISTS game (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
demo_mode BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
idle_logout BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
paused BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
user_id VARCHAR(128) NOT NULL,
|
||||
|
||||
created_at DATETIME NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS addresses (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
local VARCHAR(128) NOT NULL,
|
||||
remote VARCHAR(128) NOT NULL,
|
||||
remote_is_accessible BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
game_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE files ADD COLUMN game_id INTEGER REFERENCES game(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_files_game_id ON files(game_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS game_options (
|
||||
id INTEGER PRIMARY KEY,
|
||||
|
||||
language VARCHAR(128) NOT NULL,
|
||||
update_channel VARCHAR(128) NOT NULL,
|
||||
port INTEGER NOT NULL,
|
||||
|
||||
game_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE release_ ADD COLUMN game_id INTEGER REFERENCES game(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_release_game_id ON release_(game_id);
|
||||
ALTER TABLE world ADD COLUMN game_id INTEGER REFERENCES game(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_world_game_id ON world(game_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS game_to_systems (
|
||||
game_id INTEGER,
|
||||
system_id TEXT,
|
||||
|
||||
PRIMARY KEY (game_id, system_id),
|
||||
FOREIGN KEY (game_id) REFERENCES game(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
|
||||
AFTER DELETE ON game_to_systems
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
DELETE FROM system
|
||||
WHERE id = OLD.system_id
|
||||
AND NOT EXISTS (SELECT 1 FROM game_to_systems WHERE system_id = OLD.system_id);
|
||||
END;
|
||||
|
||||
ALTER TABLE core_update ADD COLUMN game_id INTEGER REFERENCES game(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_core_update_game_id ON core_update(game_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS system_update (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
has_update BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
version VARCHAR(128) NOT NULL,
|
||||
|
||||
game_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS active_users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
value VARCHAR(128) NOT NULL,
|
||||
|
||||
game_id INTEGER,
|
||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS game_to_module (
|
||||
game_id INTEGER,
|
||||
module_id TEXT,
|
||||
|
||||
PRIMARY KEY (game_id, module_id),
|
||||
FOREIGN KEY (module_id) REFERENCES module(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
|
||||
AFTER DELETE ON game_to_module
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
DELETE FROM module
|
||||
WHERE id = OLD.module_id
|
||||
AND NOT EXISTS (SELECT 1 FROM game_to_module WHERE module_id = OLD.module_id);
|
||||
END;
|
||||
|
||||
ALTER TABLE package_warnings 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 (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
blind BOOLEAN NOT NULL,
|
||||
emote BOOLEAN NOT NULL,
|
||||
style INTEGER NOT NULL,
|
||||
timestamp DATETIME NOT NULL,
|
||||
content VARCHAR(128) NOT NULL,
|
||||
author VARCHAR(128) NOT NULL,
|
||||
type VARCHAR(128) NOT NULL,
|
||||
flavor VARCHAR(128) NOT NULL,
|
||||
sound VARCHAR(128) NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
game_id INTEGER,
|
||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS stats (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
core_version VARCHAR(128) NOT NULL,
|
||||
system_id VARCHAR(128) NOT NULL,
|
||||
system_version VARCHAR(128) NOT NULL,
|
||||
last_modified_by VARCHAR(128) NOT NULL,
|
||||
modified_time DATETIME NOT NULL,
|
||||
|
||||
message_id TEXT UNIQUE,
|
||||
FOREIGN KEY (message_id) REFERENCES message(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS speaker (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
scene VARCHAR(128) NOT NULL,
|
||||
actor VARCHAR(128) NOT NULL,
|
||||
token VARCHAR(128) NOT NULL,
|
||||
alias VARCHAR(128) NOT NULL,
|
||||
|
||||
message_id TEXT UNIQUE,
|
||||
FOREIGN KEY (message_id) REFERENCES message(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS message_whisper (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
value VARCHAR(128) NOT NULL,
|
||||
|
||||
message_id TEXT,
|
||||
FOREIGN KEY (message_id) REFERENCES message(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS message_rolls (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
value VARCHAR(128) NOT NULL,
|
||||
|
||||
message_id TEXT,
|
||||
FOREIGN KEY (message_id) REFERENCES message(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS combat (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
type VARCHAR(128) NOT NULL,
|
||||
scene VARCHAR(128) NOT NULL,
|
||||
round INTEGER NOT NULL,
|
||||
turn INTEGER NOT NULL,
|
||||
sort INTEGER NOT NULL,
|
||||
active INTEGER NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
game_id INTEGER,
|
||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE stats ADD COLUMN combat_id TEXT REFERENCES combat(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_stats_combat_id ON stats(combat_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS combat_groups (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
value VARCHAR(128) NOT NULL,
|
||||
|
||||
combat_id TEXT,
|
||||
FOREIGN KEY (combat_id) REFERENCES combat(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS combatant (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
token_id VARCHAR(128) NOT NULL,
|
||||
scene_id VARCHAR(128) NOT NULL,
|
||||
actor_id VARCHAR(128) NOT NULL,
|
||||
type VARCHAR(128) NOT NULL,
|
||||
img VARCHAR(128) NOT NULL,
|
||||
group_ VARCHAR(128) NOT NULL,
|
||||
initiative INTEGER NOT NULL,
|
||||
hidden BOOLEAN NOT NULL,
|
||||
defeated BOOLEAN NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
combat_id TEXT,
|
||||
FOREIGN KEY (combat_id) REFERENCES combat(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE stats ADD COLUMN combatant_id TEXT REFERENCES combatant(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_stats_combatant_id ON stats(combatant_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS card_deck (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
name VARCHAR(128) NOT NULL,
|
||||
type VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(128) NOT NULL,
|
||||
img VARCHAR(128) NOT NULL,
|
||||
folder VARCHAR(128) NOT NULL,
|
||||
width INTEGER NOT NULL,
|
||||
height INTEGER NOT NULL,
|
||||
rotation INTEGER NOT NULL,
|
||||
sort INTEGER NOT NULL,
|
||||
display_count BOOLEAN NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
game_id INTEGER,
|
||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE stats ADD COLUMN card_deck_id TEXT REFERENCES card_deck(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_stats_card_deck_id ON stats(card_deck_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ownership_string (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
key_ VARCHAR(128) NOT NULL,
|
||||
value INTEGER NOT NULL,
|
||||
|
||||
card_deck_id TEXT,
|
||||
FOREIGN KEY (card_deck_id) REFERENCES card_deck(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS card (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
name VARCHAR(128) NOT NULL,
|
||||
type VARCHAR(128) NOT NULL,
|
||||
suit VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(128) NOT NULL,
|
||||
origin VARCHAR(128) NOT NULL,
|
||||
width INTEGER NOT NULL,
|
||||
height INTEGER NOT NULL,
|
||||
rotation INTEGER NOT NULL,
|
||||
value INTEGER NOT NULL,
|
||||
face INTEGER NOT NULL,
|
||||
sort INTEGER NOT NULL,
|
||||
drawn BOOLEAN NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
card_deck_id TEXT,
|
||||
FOREIGN KEY (card_deck_id) REFERENCES card_deck(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS back (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
name VARCHAR(128) NOT NULL,
|
||||
text VARCHAR(128) NOT NULL,
|
||||
|
||||
card_id TEXT UNIQUE,
|
||||
FOREIGN KEY (card_id) REFERENCES card(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE stats ADD COLUMN card_id TEXT REFERENCES card(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_stats_card_id ON stats(card_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS face (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
name VARCHAR(128) NOT NULL,
|
||||
img VARCHAR(128) NOT NULL,
|
||||
text VARCHAR(128) NOT NULL,
|
||||
|
||||
card_id TEXT,
|
||||
FOREIGN KEY (card_id) REFERENCES card(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
name VARCHAR(128) NOT NULL,
|
||||
avatar VARCHAR(128) NOT NULL,
|
||||
character VARCHAR(128) NOT NULL,
|
||||
color VARCHAR(128) NOT NULL,
|
||||
pronouns VARCHAR(128) NOT NULL,
|
||||
role INTEGER NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
game_id INTEGER,
|
||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE stats ADD COLUMN user_id TEXT REFERENCES user(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_stats_user_id ON stats(user_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS hotbar (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
key_ INTEGER NOT NULL,
|
||||
value VARCHAR(128) NOT NULL,
|
||||
|
||||
user_id TEXT,
|
||||
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS macro (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
command VARCHAR(128) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
type VARCHAR(128) NOT NULL,
|
||||
img VARCHAR(128) NOT NULL,
|
||||
author VARCHAR(128) NOT NULL,
|
||||
scope VARCHAR(128) NOT NULL,
|
||||
folder VARCHAR(128) NOT NULL,
|
||||
sort INTEGER NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
game_id INTEGER,
|
||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE stats ADD COLUMN macro_id TEXT REFERENCES macro(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_stats_macro_id ON stats(macro_id);
|
||||
ALTER TABLE ownership_string ADD COLUMN macro_id TEXT REFERENCES macro(id) ON DELETE CASCADE;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS world_folder (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
name VARCHAR(128) NOT NULL,
|
||||
type VARCHAR(128) NOT NULL,
|
||||
folder VARCHAR(128) NOT NULL,
|
||||
sorting VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(128) NOT NULL,
|
||||
color VARCHAR(128) NOT NULL,
|
||||
sort INTEGER NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
game_id INTEGER,
|
||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE stats ADD COLUMN world_folder_id TEXT REFERENCES world_folder(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_stats_world_folder_id ON stats(world_folder_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS item (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
img VARCHAR(128) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
type VARCHAR(128) NOT NULL,
|
||||
folder VARCHAR(128) NOT NULL,
|
||||
sort INTEGER NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
game_id INTEGER,
|
||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE stats ADD COLUMN item_id TEXT REFERENCES item(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_stats_item_id ON stats(item_id);
|
||||
ALTER TABLE ownership_string ADD COLUMN item_id TEXT REFERENCES item(id) ON DELETE CASCADE;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS setting (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
key_ VARCHAR(128) NOT NULL,
|
||||
value VARCHAR(128) NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
game_id INTEGER,
|
||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE stats ADD COLUMN setting_id TEXT REFERENCES setting(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_stats_setting_id ON stats(setting_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS journal (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
name VARCHAR(128) NOT NULL,
|
||||
sort INTEGER NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
game_id INTEGER,
|
||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS journal_page (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
name VARCHAR(128) NOT NULL,
|
||||
type VARCHAR(128) NOT NULL,
|
||||
src VARCHAR(128) NOT NULL,
|
||||
sort INTEGER NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
journal_id TEXT,
|
||||
FOREIGN KEY (journal_id) REFERENCES journal(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS journal_page_text (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
content VARCHAR(128) NOT NULL,
|
||||
markdown VARCHAR(128) NOT NULL,
|
||||
format INTEGER NOT NULL,
|
||||
|
||||
journal_page_id TEXT,
|
||||
FOREIGN KEY (journal_page_id) REFERENCES journal_page(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS journal_page_title (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
show BOOLEAN NOT NULL,
|
||||
level INTEGER NOT NULL,
|
||||
|
||||
journal_page_id TEXT,
|
||||
FOREIGN KEY (journal_page_id) REFERENCES journal_page(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS journal_page_video (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
controls BOOLEAN NOT NULL,
|
||||
volume REAL NOT NULL,
|
||||
|
||||
journal_page_id TEXT,
|
||||
FOREIGN KEY (journal_page_id) REFERENCES journal_page(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE stats 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;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS table_ (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(128) NOT NULL,
|
||||
formula VARCHAR(128) NOT NULL,
|
||||
img VARCHAR(128) NOT NULL,
|
||||
folder VARCHAR(128) NOT NULL,
|
||||
sort INTEGER NOT NULL,
|
||||
replacement BOOLEAN NOT NULL,
|
||||
display_roll BOOLEAN NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
game_id INTEGER,
|
||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE stats ADD COLUMN table_id TEXT REFERENCES table_(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_stats_table_id ON stats(table_id);
|
||||
ALTER TABLE ownership_string ADD COLUMN table_id TEXT REFERENCES table_(id) ON DELETE CASCADE;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS table_result (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
type VARCHAR(128) NOT NULL,
|
||||
img VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(128) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
weight INTEGER NOT NULL,
|
||||
drawn BOOLEAN NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
table_id TEXT,
|
||||
FOREIGN KEY (table_id) REFERENCES table_(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE stats ADD COLUMN table_result_id TEXT REFERENCES table_result(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_stats_table_result_id ON stats(table_result_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS table_result_range (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
value INTEGER NOT NULL,
|
||||
|
||||
table_result_id TEXT,
|
||||
FOREIGN KEY (table_result_id) REFERENCES table_result(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS playlist (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
name VARCHAR(128) NOT NULL,
|
||||
folder VARCHAR(128) NOT NULL,
|
||||
sorting VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(128) NOT NULL,
|
||||
channel VARCHAR(128) NOT NULL,
|
||||
mode INTEGER NOT NULL,
|
||||
fade INTEGER NOT NULL,
|
||||
seed INTEGER NOT NULL,
|
||||
sort INTEGER NOT NULL,
|
||||
playing BOOLEAN NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
game_id INTEGER,
|
||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE stats ADD COLUMN playlist_id TEXT REFERENCES playlist(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_stats_playlist_id ON stats(playlist_id);
|
||||
ALTER TABLE ownership_string ADD COLUMN playlist_id TEXT REFERENCES playlist(id) ON DELETE CASCADE;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sound (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
name VARCHAR(128) NOT NULL,
|
||||
path VARCHAR(128) NOT NULL,
|
||||
channel VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(128) NOT NULL,
|
||||
fade INTEGER NOT NULL,
|
||||
sort INTEGER NOT NULL,
|
||||
repeat BOOLEAN NOT NULL,
|
||||
playing BOOLEAN NOT NULL,
|
||||
volume REAL NOT NULL,
|
||||
paused_time REAL NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
playlist_id TEXT,
|
||||
FOREIGN KEY (playlist_id) REFERENCES playlist(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS actor (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
img VARCHAR(128) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
type VARCHAR(128) NOT NULL,
|
||||
folder VARCHAR(128) NOT NULL,
|
||||
sort INTEGER NOT NULL,
|
||||
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
game_id INTEGER,
|
||||
FOREIGN KEY (game_id) REFERENCES game(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE stats ADD COLUMN actor_id TEXT REFERENCES actor(id) ON DELETE CASCADE;
|
||||
CREATE UNIQUE INDEX idx_stats_actor_id ON stats(actor_id);
|
||||
ALTER TABLE ownership_string ADD COLUMN actor_id TEXT REFERENCES actor(id) ON DELETE CASCADE;
|
||||
ALTER TABLE item ADD COLUMN actor_id TEXT REFERENCES actor(id) ON DELETE CASCADE;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS token (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
name VARCHAR(128) NOT NULL,
|
||||
actor_link BOOLEAN NOT NULL,
|
||||
append_number BOOLEAN NOT NULL,
|
||||
prepend_adjective BOOLEAN NOT NULL,
|
||||
lock_rotation BOOLEAN NOT NULL,
|
||||
random_img BOOLEAN NOT NULL,
|
||||
display_name INTEGER NOT NULL,
|
||||
display_bars INTEGER NOT NULL,
|
||||
disposition INTEGER NOT NULL,
|
||||
rotation INTEGER NOT NULL,
|
||||
alpha INTEGER NOT NULL,
|
||||
width REAL NOT NULL,
|
||||
height REAL NOT NULL,
|
||||
|
||||
actor_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (actor_id) REFERENCES actor(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ring (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
enabled BOOLEAN NOT NULL,
|
||||
effects INTEGER NOT NULL,
|
||||
|
||||
token_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (token_id) REFERENCES token(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ring_colors (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
ring VARCHAR(128) NOT NULL,
|
||||
background VARCHAR(128) NOT NULL,
|
||||
|
||||
ring_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (ring_id) REFERENCES ring(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ring_subject (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
scale INTEGER NOT NULL,
|
||||
texture VARCHAR(128) NOT NULL,
|
||||
|
||||
ring_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (ring_id) REFERENCES ring(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS token_sight (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
color VARCHAR(128) NOT NULL,
|
||||
vision_mode VARCHAR(128) NOT NULL,
|
||||
range_ INTEGER NOT NULL,
|
||||
angle INTEGER NOT NULL,
|
||||
attenuation REAL NOT NULL,
|
||||
brightness REAL NOT NULL,
|
||||
enabled BOOLEAN NOT NULL,
|
||||
|
||||
token_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (token_id) REFERENCES token(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS token_texture (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
src VARCHAR(128) NOT NULL,
|
||||
fit VARCHAR(128) NOT NULL,
|
||||
tint VARCHAR(128) NOT NULL,
|
||||
scale_x REAL NOT NULL,
|
||||
scale_y REAL NOT NULL,
|
||||
offset_x REAL NOT NULL,
|
||||
offset_y REAL NOT NULL,
|
||||
rotation REAL NOT NULL,
|
||||
anchor_x REAL NOT NULL,
|
||||
anchor_y REAL NOT NULL,
|
||||
alpha_threshold REAL NOT NULL,
|
||||
|
||||
token_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (token_id) REFERENCES token(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS token_bar_1 (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
attribute VARCHAR(128) NOT NULL,
|
||||
|
||||
token_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (token_id) REFERENCES token(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS token_bar_2 (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
attribute VARCHAR(128) NOT NULL,
|
||||
|
||||
token_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (token_id) REFERENCES token(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS token_light (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
color VARCHAR(128) NOT NULL,
|
||||
priority INTEGER NOT NULL,
|
||||
angle INTEGER NOT NULL,
|
||||
negative BOOLEAN NOT NULL,
|
||||
alpha REAL NOT NULL,
|
||||
bright REAL NOT NULL,
|
||||
coloration REAL NOT NULL,
|
||||
dim REAL NOT NULL,
|
||||
attenuation REAL NOT NULL,
|
||||
luminosity REAL NOT NULL,
|
||||
saturation REAL NOT NULL,
|
||||
contrast REAL NOT NULL,
|
||||
shadows REAL NOT NULL,
|
||||
|
||||
token_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (token_id) REFERENCES token(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS token_light_animation (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
speed INTEGER NOT NULL,
|
||||
intensity INTEGER NOT NULL,
|
||||
reverse BOOLEAN NOT NULL,
|
||||
|
||||
token_light_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (token_light_id) REFERENCES token_light(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS token_light_darkness (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
min REAL NOT NULL,
|
||||
max REAL NOT NULL,
|
||||
|
||||
token_light_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (token_light_id) REFERENCES token_light(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS token_occludable (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
radius INTEGER NOT NULL,
|
||||
|
||||
token_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (token_id) REFERENCES token(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS token_turn_maker (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
mode INTEGER NOT NULL,
|
||||
animation VARCHAR(128) NOT NULL,
|
||||
src VARCHAR(128) NOT NULL,
|
||||
disposition BOOLEAN NOT NULL,
|
||||
|
||||
token_id INTEGER UNIQUE,
|
||||
FOREIGN KEY (token_id) REFERENCES token(id) ON DELETE CASCADE
|
||||
);
|
||||
@@ -1,3 +0,0 @@
|
||||
DROP TABLE IF EXISTS modules_languages;
|
||||
DROP TABLE IF EXISTS modules_compatibility;
|
||||
DROP TABLE IF EXISTS modules;
|
||||
@@ -1,32 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS modules (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
state_id INTEGER NOT NULL,
|
||||
text_id VARCHAR(256) NOT NULL,
|
||||
title VARCHAR(256) NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
url VARCHAR(256) NOT NULL,
|
||||
version VARCHAR(64) NOT NULL,
|
||||
availability INTEGER NOT NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT current_timestamp,
|
||||
FOREIGN KEY (state_id) REFERENCES foundry_state(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS modules_compatibility (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
module_id INTEGER UNIQUE,
|
||||
minimum VARCHAR(64) NOT NULL,
|
||||
verified VARCHAR(64) NOT NULL,
|
||||
maximum VARCHAR(64) NOT NULL,
|
||||
|
||||
FOREIGN KEY (module_id) REFERENCES modules(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS modules_languages (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
module_id INTEGER,
|
||||
language VARCHAR(256) NOT NULL,
|
||||
name VARCHAR(256) NOT NULL,
|
||||
path VARCHAR(256) NOT NULL,
|
||||
|
||||
FOREIGN KEY (module_id) REFERENCES modules(id) ON DELETE CASCADE
|
||||
);
|
||||
@@ -1,2 +0,0 @@
|
||||
DROP TABLE IF EXISTS systems_compatibility;
|
||||
DROP TABLE IF EXISTS systems;
|
||||
@@ -1,21 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS systems (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
state_id INTEGER NOT NULL,
|
||||
text_id VARCHAR(256) NOT NULL,
|
||||
title VARCHAR(256) NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
url VARCHAR(256) NOT NULL,
|
||||
download VARCHAR(256) NOT NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT current_timestamp,
|
||||
FOREIGN KEY (state_id) REFERENCES foundry_state(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS systems_compatibility (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
system_id INTEGER UNIQUE,
|
||||
minimum VARCHAR(64) NOT NULL,
|
||||
verified VARCHAR(64) NOT NULL,
|
||||
maximum VARCHAR(64) NOT NULL,
|
||||
|
||||
FOREIGN KEY (system_id) REFERENCES systems(id) ON DELETE CASCADE
|
||||
);
|
||||
@@ -1,2 +0,0 @@
|
||||
DROP TABLE IF EXISTS worlds_compatibility;
|
||||
DROP TABLE IF EXISTS worlds;
|
||||
@@ -1,24 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS worlds (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
state_id INTEGER NOT NULL,
|
||||
text_id VARCHAR(256) NOT NULL,
|
||||
title VARCHAR(256) NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
system VARCHAR(256) NOT NULL,
|
||||
core_version VARCHAR(256) NOT NULL,
|
||||
system_version VARCHAR(256) NOT NULL,
|
||||
playtime INTEGER NOT NULL,
|
||||
next_session DATETIME NOT NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT current_timestamp,
|
||||
FOREIGN KEY (state_id) REFERENCES foundry_state(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS worlds_compatibility (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
world_id INTEGER UNIQUE,
|
||||
minimum VARCHAR(64) NOT NULL,
|
||||
verified VARCHAR(64) NOT NULL,
|
||||
maximum VARCHAR(64) NOT NULL,
|
||||
|
||||
FOREIGN KEY (world_id) REFERENCES worlds(id) ON DELETE CASCADE
|
||||
);
|
||||
@@ -1,3 +0,0 @@
|
||||
DROP TABLE IF EXISTS users;
|
||||
DROP TABLE IF EXISTS users_hotbar;
|
||||
DROP TABLE IF EXISTS users_stats;
|
||||
@@ -1,33 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
state_id INTEGER NOT NULL,
|
||||
name VARCHAR(256) NOT NULL,
|
||||
role INTEGER NOT NULL,
|
||||
character VARCHAR(128) NOT NULL,
|
||||
color VARCHAR(128) NOT NULL,
|
||||
pronouns VARCHAR(128) NOT NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT current_timestamp,
|
||||
FOREIGN KEY (state_id) REFERENCES foundry_state(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users_hotbar (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER,
|
||||
key TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users_stats (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER UNIQUE,
|
||||
core_version TEXT NOT NULL,
|
||||
system_id VARCHAR(64) NOT NULL,
|
||||
system_version VARCHAR(64) NOT NULL,
|
||||
created_time INTEGER NOT NULL,
|
||||
modified_time INTEGER NOT NULL,
|
||||
last_modified_by VARCHAR(256) NOT NULL,
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
1
go.mod
1
go.mod
@@ -54,6 +54,7 @@ require (
|
||||
github.com/in-toto/in-toto-golang v0.9.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf // indirect
|
||||
github.com/jmoiron/sqlx v1.4.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.5.0 // indirect
|
||||
github.com/julienschmidt/httprouter v1.3.0 // indirect
|
||||
github.com/klauspost/compress v1.18.3 // indirect
|
||||
|
||||
6
go.sum
6
go.sum
@@ -1,5 +1,6 @@
|
||||
cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8=
|
||||
cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||
@@ -152,6 +153,7 @@ github.com/go-openapi/swag/yamlutils v0.25.3 h1:LKTJjCn/W1ZfMec0XDL4Vxh8kyAnv1or
|
||||
github.com/go-openapi/swag/yamlutils v0.25.3/go.mod h1:Y7QN6Wc5DOBXK14/xeo1cQlq0EA0wvLoSv13gDQoCao=
|
||||
github.com/go-openapi/validate v0.25.1 h1:sSACUI6Jcnbo5IWqbYHgjibrhhmt3vR6lCzKZnmAgBw=
|
||||
github.com/go-openapi/validate v0.25.1/go.mod h1:RMVyVFYte0gbSTaZ0N4KmTn6u/kClvAFp+mAVfS/DQc=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
|
||||
@@ -195,6 +197,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf h1:FtEj8sfIcaaBfAKrE1Cwb61YDtYq9JxChK1c7AKce7s=
|
||||
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf/go.mod h1:yrqSXGoD/4EKfF26AOGzscPOgTTJcyAwM2rpixWT+t4=
|
||||
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
||||
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
||||
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
|
||||
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
@@ -207,10 +211,12 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.39 h1:sIwSjlJGOaRJjw44/HXaeTblZMjseqr6OOio1tz/+JI=
|
||||
github.com/mattn/go-sqlite3 v1.14.39/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||
|
||||
@@ -6,4 +6,5 @@ var (
|
||||
ErrActionNotFound = errors.New("Action doesn't found")
|
||||
ErrObjTypeNotMatch = errors.New("Provided value type didn't match obj field type")
|
||||
ErrObjNotPointer = errors.New("Passed obj is not pointer")
|
||||
ErrUserChannelIsClosed = errors.New("User channel is closed")
|
||||
)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport"
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
|
||||
)
|
||||
@@ -12,19 +14,7 @@ type WsSessionMsg struct {
|
||||
|
||||
func (msg WsSessionMsg) Action(tr *transport.FoundryTransport, status *types.FoundryStatus) error {
|
||||
if status.IsActive {
|
||||
tr.Logger.Info("Session msg", "userid", msg.UserId)
|
||||
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
|
||||
}
|
||||
// tr.Logger.Warn("World is started. Please, return to setup page to fully initialize database")
|
||||
// go msg.OnActiveWorld(tr)
|
||||
return nil
|
||||
return msg.OnActiveWorld(tr)
|
||||
}
|
||||
|
||||
if tr.IsDbInit {
|
||||
@@ -32,18 +22,44 @@ func (msg WsSessionMsg) Action(tr *transport.FoundryTransport, status *types.Fou
|
||||
}
|
||||
tr.IsDbInit = true
|
||||
|
||||
go tr.FillDBWithFoundryData()
|
||||
return nil
|
||||
return tr.FillDBWithFoundryData()
|
||||
}
|
||||
|
||||
func (msg WsSessionMsg) OnActiveWorld(tr *transport.FoundryTransport) error {
|
||||
wsMsg := types.NewWsMessage("world", tr.CurrWsId)
|
||||
tr.CurrWsId++
|
||||
tr.Logger.Info("Session msg", "userid", msg.UserId)
|
||||
if msg.UserId != "" {
|
||||
return msg.HandleLoggedInUser(tr)
|
||||
}
|
||||
|
||||
answer, err := tr.HandleWebsocketRequest(wsMsg)
|
||||
userId, userPass, err := tr.GetUserIdAndPass()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tr.Logger.Info("World data", "answer", string(answer))
|
||||
|
||||
err = tr.LogInToWorld(userId, userPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tr.Logger.Info("World is started. Succesfully logged into world")
|
||||
close(tr.ReadChan.Reconnect())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg WsSessionMsg) HandleLoggedInUser(tr *transport.FoundryTransport) error {
|
||||
if tr.LoggedInChan == nil {
|
||||
tr.Logger.Info("World had been started before application was started. Run insertion of world data")
|
||||
return tr.InsertGameToDB()
|
||||
}
|
||||
|
||||
select {
|
||||
case <-tr.LoggedInChan:
|
||||
return ErrUserChannelIsClosed
|
||||
default:
|
||||
tr.LoggedInChan <- true
|
||||
}
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
types.CloseChannel(tr.LoggedInChan)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -25,6 +25,5 @@ func (msg WsShutdownMsg) Action(tr *transport.FoundryTransport, status *types.Fo
|
||||
}
|
||||
tr.IsDbInit = true
|
||||
|
||||
go tr.FillDBWithFoundryData()
|
||||
return nil
|
||||
return tr.FillDBWithFoundryData()
|
||||
}
|
||||
|
||||
@@ -8,10 +8,11 @@ import (
|
||||
"time"
|
||||
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/actions"
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/requests"
|
||||
json_models "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/json"
|
||||
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||
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/types"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -24,7 +25,7 @@ var (
|
||||
|
||||
type FoundryApi struct {
|
||||
//TODO: make check of admin's authentication
|
||||
transport *transport.FoundryTransport
|
||||
Transport *transport.FoundryTransport
|
||||
IsAvailable bool
|
||||
|
||||
// Logger *slog.Logger
|
||||
@@ -37,16 +38,16 @@ func NewFoundry() *FoundryApi {
|
||||
}
|
||||
|
||||
func (foundry *FoundryApi) SetTransport(tr *transport.FoundryTransport) {
|
||||
foundry.transport = tr
|
||||
foundry.Transport = tr
|
||||
}
|
||||
|
||||
func (foundry *FoundryApi) ListenAndServeWS() error {
|
||||
var err error
|
||||
|
||||
foundry.transport.ReadChan = *types.InitWsChannels()
|
||||
defer foundry.transport.ReadChan.Close()
|
||||
foundry.Transport.ReadChan = *types.InitWsChannels()
|
||||
defer foundry.Transport.ReadChan.Close()
|
||||
|
||||
foundry.background(foundry.transport.ListenWebSocket)
|
||||
foundry.background(foundry.Transport.ListenWebSocket)
|
||||
|
||||
err = foundry.ServeWebSocket()
|
||||
if err != nil {
|
||||
@@ -57,21 +58,21 @@ func (foundry *FoundryApi) ListenAndServeWS() error {
|
||||
|
||||
func (foundry *FoundryApi) ServeWebSocket() error {
|
||||
var err error
|
||||
wsChannels := &foundry.transport.ReadChan
|
||||
wsChannels := &foundry.Transport.ReadChan
|
||||
|
||||
for {
|
||||
select {
|
||||
case message, ok := <-wsChannels.Msg():
|
||||
if !ok {
|
||||
foundry.transport.Logger.Debug("Read channel is closed", "type", types.WebSocketCode)
|
||||
foundry.transport.Http.SessionID = nil
|
||||
foundry.Transport.Logger.Debug("Read channel is closed", "type", types.WebSocketCode)
|
||||
foundry.Transport.Http.SessionID = nil
|
||||
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 {
|
||||
case types.RespPingCode, types.RespSessionDataCode:
|
||||
err := foundry.transport.SendOnlyCodeRequest(message.Code)
|
||||
err := foundry.Transport.SendOnlyCodeRequest(message.Code)
|
||||
if err != nil {
|
||||
wsChannels.Err() <- &types.FoundryError{Direction: types.WriterCode, Err: err, Type: types.WebSocketCode}
|
||||
continue
|
||||
@@ -84,69 +85,78 @@ func (foundry *FoundryApi) ServeWebSocket() error {
|
||||
continue
|
||||
}
|
||||
|
||||
foundry.transport.Logger.Debug("RespServerChangeCode", "data", data)
|
||||
foundry.Transport.Logger.Debug("RespServerChangeCode", "data", data)
|
||||
if data == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
err = data.Action(foundry.transport, &foundry.Status)
|
||||
if err != nil {
|
||||
wsChannels.Err() <- &types.FoundryError{Direction: types.ReaderCode, Err: err, Type: types.WebSocketCode}
|
||||
continue
|
||||
go func() {
|
||||
errAction := data.Action(foundry.Transport, &foundry.Status)
|
||||
|
||||
if errAction != nil {
|
||||
wsChannels.Err() <- &types.FoundryError{Direction: types.ReaderCode, Err: errAction, Type: types.WebSocketCode}
|
||||
}
|
||||
}()
|
||||
case types.RespDataCode:
|
||||
foundry.transport.ExchangeChan.Msgs[message.Id] = make(chan []byte, 1)
|
||||
foundry.transport.ExchangeChan.Msgs[message.Id] <- []byte(message.MsgJson)
|
||||
go foundry.transport.CloseMsgChannel(message.Id, 5*time.Second)
|
||||
tr := foundry.Transport
|
||||
func() {
|
||||
tr.ChanMutex.Lock()
|
||||
defer tr.ChanMutex.Unlock()
|
||||
|
||||
tr.ExchangeChan.Msgs[message.Id] = make(chan []byte, 1)
|
||||
tr.ExchangeChan.Msgs[message.Id] <- []byte(message.MsgJson)
|
||||
go tr.CloseMsgChannel(tr.ExchangeChan.Msgs[message.Id], message.Id, 5*time.Second)
|
||||
|
||||
}()
|
||||
default:
|
||||
}
|
||||
case err = <-wsChannels.Err():
|
||||
var foundryErr *types.FoundryError
|
||||
if errors.As(err, &foundryErr) {
|
||||
if foundryErr.IsFatal {
|
||||
foundry.transport.CloseWebSocketConn()
|
||||
foundry.transport.Http.SessionID = nil
|
||||
foundry.Transport.CloseWebSocketConn()
|
||||
foundry.Transport.Http.SessionID = nil
|
||||
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():
|
||||
foundry.transport.CloseWebSocketConn()
|
||||
foundry.transport.Http.SessionID = nil
|
||||
foundry.Transport.CloseWebSocketConn()
|
||||
foundry.Transport.Http.SessionID = nil
|
||||
return ListenIsDone
|
||||
case <-wsChannels.Reconnect():
|
||||
foundry.transport.CloseWebSocketConn()
|
||||
foundry.Transport.CloseWebSocketConn()
|
||||
return ReconnectToWebSocket
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
foundry.transport.Logger.Info("Completing foundry background tasks")
|
||||
foundry.Transport.Logger.Info("Completing foundry background tasks")
|
||||
|
||||
foundryClosed := make(chan struct{})
|
||||
go func() {
|
||||
types.CloseChannel(foundry.transport.ReadChan.Done())
|
||||
types.CloseChannel(foundry.Transport.ReadChan.Done())
|
||||
|
||||
foundry.wg.Wait()
|
||||
foundry.transport.ExchangeChan.Close()
|
||||
foundry.transport.ReadChan.Close()
|
||||
foundry.Transport.ExchangeChan.Close()
|
||||
foundry.Transport.ReadChan.Close()
|
||||
|
||||
types.CloseChannel(foundryClosed)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-foundryClosed:
|
||||
foundry.transport.Logger.Info("Stopped foundry server")
|
||||
foundry.Transport.Logger.Info("Stopped foundry server")
|
||||
case <-ctx.Done():
|
||||
return CloseTimeoutExceed
|
||||
}
|
||||
@@ -156,24 +166,24 @@ func (foundry *FoundryApi) Shutdown() error {
|
||||
|
||||
func (foundry *FoundryApi) ConnectToWebSocket() (bool, error) {
|
||||
var err error
|
||||
if !foundry.transport.HasSessionId() {
|
||||
err = foundry.transport.InitSessionId()
|
||||
if !foundry.Transport.HasSessionId() {
|
||||
err = foundry.Transport.InitSessionId()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
err = foundry.transport.ConnectToFoundry()
|
||||
err = foundry.Transport.ConnectToFoundry()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
err = foundry.transport.InitWebSocketConnection()
|
||||
err = foundry.Transport.InitWebSocketConnection()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
status, err := foundry.transport.Http.GetStatus()
|
||||
status, err := foundry.Transport.Http.GetStatus()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -195,21 +205,21 @@ func (foundry *FoundryApi) StartListenFoundry() {
|
||||
ok, err := foundry.ConnectToWebSocket()
|
||||
if err != nil {
|
||||
if errors.Is(err, ReconnectToWebSocket) {
|
||||
if foundry.transport.ReconnectNum >= foundry.transport.ReconnectNumMax {
|
||||
if foundry.Transport.ReconnectNum >= foundry.Transport.ReconnectNumMax {
|
||||
return
|
||||
}
|
||||
time.Sleep(foundry.transport.ReconnectTimeout)
|
||||
foundry.transport.ReconnectNum++
|
||||
foundry.transport.Logger.Info("Reconnecting to WebSocket", "times", foundry.transport.ReconnectNum)
|
||||
time.Sleep(foundry.Transport.ReconnectTimeout)
|
||||
foundry.Transport.ReconnectNum++
|
||||
foundry.Transport.Logger.Info("Reconnecting to WebSocket", "times", foundry.Transport.ReconnectNum)
|
||||
continue
|
||||
}
|
||||
|
||||
foundry.transport.Logger.Error("Error raised", "err", err.Error())
|
||||
foundry.Transport.Logger.Error("Error raised", "err", err.Error())
|
||||
|
||||
if ok {
|
||||
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)
|
||||
timeInterval = min(timeInterval*2, 15*time.Second)
|
||||
@@ -220,11 +230,28 @@ func (foundry *FoundryApi) StartListenFoundry() {
|
||||
})
|
||||
}
|
||||
|
||||
func (foundry *FoundryApi) Test() (*json_models.Game, error) {
|
||||
wsMsg := types.NewWsMessage("world", foundry.transport.CurrWsId)
|
||||
foundry.transport.CurrWsId++
|
||||
func (foundry *FoundryApi) PrepareDB() error {
|
||||
dbConn := foundry.Transport.DB
|
||||
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
@@ -238,11 +265,6 @@ func (foundry *FoundryApi) Test() (*json_models.Game, error) {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// TODO: remove
|
||||
func (foundry *FoundryApi) GetHTTP() *requests.FoundryHttpRequest {
|
||||
return foundry.transport.Http
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ func (f *FoundryApi) background(fn func()) {
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
f.transport.Logger.Error(fmt.Sprintf("%s", err))
|
||||
f.Transport.Logger.Error(fmt.Sprintf("%s", err))
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
92
internal/foundry/models/db/actor.go
Normal file
92
internal/foundry/models/db/actor.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Actor struct {
|
||||
ID string
|
||||
|
||||
Img string
|
||||
Name string
|
||||
Type string
|
||||
Folder string
|
||||
Sort int
|
||||
PrototypeToken *Token
|
||||
Stats Stats
|
||||
Items []*Item
|
||||
Ownership []OwnershipString
|
||||
}
|
||||
|
||||
func (a *Actor) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO actor (game_id, id, img, name, type, folder, sort)
|
||||
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 {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
relId := InsertId[string]{id: a.ID, fieldName: "actor_id"}
|
||||
InsertWithCtxParallel(group, tx, a.PrototypeToken, relId)
|
||||
InsertWithCtxParallel(group, tx, a.Stats, relId)
|
||||
InsertSliceParallel(group, tx, a.Ownership, relId)
|
||||
InsertSliceParallel(group, tx, a.Items, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Actor) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, a.ID, a.Img, a.Name, a.Type, a.Folder, a.Sort}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return a.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Actor) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, a.ID, a.Img, a.Name, a.Type, a.Folder, a.Sort}
|
||||
|
||||
mutex := GetMutex("actor_insert")
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return a.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
52
internal/foundry/models/db/addresses.go
Normal file
52
internal/foundry/models/db/addresses.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Addresses struct {
|
||||
ID uint
|
||||
|
||||
Local string
|
||||
Remote string
|
||||
RemoteIsAccessible bool
|
||||
}
|
||||
|
||||
func (a *Addresses) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO addresses (game_id, local, remote, remote_is_accessible)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id`
|
||||
}
|
||||
|
||||
func (a *Addresses) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, a.Local, a.Remote, a.RemoteIsAccessible}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&a.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Addresses) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, a.Local, a.Remote, a.RemoteIsAccessible}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&a.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
59
internal/foundry/models/db/authors.go
Normal file
59
internal/foundry/models/db/authors.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Author struct {
|
||||
ID uint
|
||||
|
||||
Name string
|
||||
URL string
|
||||
Email string
|
||||
Discord string
|
||||
// SystemAuthorsFlags SystemAuthorsFlags `json:"flags"`
|
||||
}
|
||||
|
||||
func (a *Author) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO author (%s, name, url, email, discord)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (a *Author) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, a.Name, a.URL, a.Email, a.Discord}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&a.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Author) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, a.Name, a.URL, a.Email, a.Discord}
|
||||
|
||||
mutex := GetMutex("author_insert")
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&a.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
263
internal/foundry/models/db/card.go
Normal file
263
internal/foundry/models/db/card.go
Normal file
@@ -0,0 +1,263 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type CardDeck struct {
|
||||
ID string
|
||||
|
||||
Name string
|
||||
Type string
|
||||
Description string
|
||||
Img string
|
||||
Folder string
|
||||
Width int
|
||||
Height int
|
||||
Rotation int
|
||||
Sort int
|
||||
DisplayCount bool
|
||||
Stats Stats
|
||||
Ownership []OwnershipString
|
||||
Cards []*Card
|
||||
}
|
||||
|
||||
func (c *CardDeck) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
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)
|
||||
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 {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
relId := InsertId[string]{id: c.ID, fieldName: "card_deck_id"}
|
||||
InsertWithCtxParallel(group, tx, c.Stats, relId)
|
||||
InsertSliceParallel(group, tx, c.Ownership, relId)
|
||||
InsertSliceParallel(group, tx, c.Cards, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CardDeck) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return c.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CardDeck) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return c.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Card struct {
|
||||
ID string
|
||||
|
||||
Name string
|
||||
Type string
|
||||
Suit string
|
||||
Description string
|
||||
Origin string
|
||||
Width int
|
||||
Height int
|
||||
Rotation int
|
||||
Value int
|
||||
Face int
|
||||
Sort int
|
||||
Drawn bool
|
||||
Back Back
|
||||
Stats Stats
|
||||
Faces []Face
|
||||
}
|
||||
|
||||
func (c *Card) Query(data *InsertId[string]) {
|
||||
data.query = `
|
||||
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)
|
||||
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 {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
relId := InsertId[string]{id: c.ID, fieldName: "card_id"}
|
||||
InsertWithCtxParallel(group, tx, c.Back, relId)
|
||||
InsertWithCtxParallel(group, tx, c.Stats, relId)
|
||||
InsertSliceParallel(group, tx, c.Faces, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Card) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
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.Face, c.Sort, c.Drawn}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return c.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Card) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
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.Face, c.Sort, c.Drawn}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return c.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Face struct {
|
||||
ID uint
|
||||
|
||||
Name string
|
||||
Img string
|
||||
Text string
|
||||
}
|
||||
|
||||
func (f Face) Query(data *InsertId[string]) {
|
||||
data.query = `
|
||||
INSERT INTO face (card_id, name, img, text)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id`
|
||||
}
|
||||
|
||||
func (f Face) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, f.Name, f.Img, f.Text}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&f.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f Face) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, f.Name, f.Img, f.Text}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&f.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Back struct {
|
||||
ID uint
|
||||
|
||||
Name string
|
||||
Text string
|
||||
}
|
||||
|
||||
func (b Back) Query(data *InsertId[string]) {
|
||||
data.query = `
|
||||
INSERT INTO back (card_id, name, text)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING id`
|
||||
}
|
||||
|
||||
func (b Back) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, b.Name, b.Text}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&b.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b Back) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, b.Name, b.Text}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&b.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
163
internal/foundry/models/db/combat.go
Normal file
163
internal/foundry/models/db/combat.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Combat struct {
|
||||
ID string
|
||||
|
||||
Type string
|
||||
Scene string
|
||||
Round int
|
||||
Turn int
|
||||
Sort int
|
||||
Active bool
|
||||
Stats Stats
|
||||
Groups []string
|
||||
Combatants []*Combatant
|
||||
}
|
||||
|
||||
func (c *Combat) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO combat (game_id, id, type, scene, round, turn, sort, active)
|
||||
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 {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
relId := InsertId[string]{id: c.ID, fieldName: "combat_id", tableName: "combat_groups"}
|
||||
InsertWithCtxParallel(group, tx, c.Stats, relId)
|
||||
InsertSimpleSliceParallel(group, tx, c.Groups, &relId)
|
||||
InsertSliceParallel(group, tx, c.Combatants, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Combat) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, c.ID, c.Type, c.Scene, c.Round, c.Turn, c.Sort, c.Active}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return c.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Combat) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, c.ID, c.Type, c.Scene, c.Round, c.Turn, c.Sort, c.Active}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return c.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Combatant struct {
|
||||
ID string
|
||||
|
||||
TokenId string
|
||||
SceneId string
|
||||
ActorId string
|
||||
Type string
|
||||
Img string
|
||||
Group string
|
||||
Initiative int
|
||||
Hidden bool
|
||||
Defeated bool
|
||||
Stats Stats
|
||||
}
|
||||
|
||||
func (c *Combatant) Query(data *InsertId[string]) {
|
||||
data.query = `
|
||||
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, $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 {
|
||||
relId := InsertId[string]{id: c.ID, fieldName: "combatant_id"}
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
InsertWithCtxParallel(group, tx, c.Stats, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Combatant) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, c.ID, c.TokenId, c.SceneId, c.ActorId, c.Type, c.Img, c.Group, c.Initiative, c.Hidden, c.Defeated}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return c.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Combatant) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, c.ID, c.TokenId, c.SceneId, c.ActorId, c.Type, c.Img, c.Group, c.Initiative, c.Hidden, c.Defeated}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return c.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
57
internal/foundry/models/db/compatibility.go
Normal file
57
internal/foundry/models/db/compatibility.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Compatibility struct {
|
||||
ID uint
|
||||
|
||||
Minimum string
|
||||
Verified string
|
||||
Maximum string
|
||||
}
|
||||
|
||||
func (c Compatibility) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO compatibility (%s, minimum, verified, maximum)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (c Compatibility) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, c.Minimum, c.Verified, c.Maximum}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&c.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Compatibility) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, c.Minimum, c.Verified, c.Maximum}
|
||||
|
||||
mutex := GetMutex("compatibility_insert")
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&c.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
124
internal/foundry/models/db/document_types.go
Normal file
124
internal/foundry/models/db/document_types.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type DocumentTypes struct {
|
||||
ID uint
|
||||
|
||||
Data []*DocumentTypeData
|
||||
}
|
||||
|
||||
func (d DocumentTypes) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO document_types (%s)
|
||||
VALUES ($1)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (d DocumentTypes) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&d.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
InsertSliceParallel(group, tx, d.Data, InsertId[uint]{id: d.ID})
|
||||
|
||||
err = group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d DocumentTypes) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id}
|
||||
|
||||
mutex := GetMutex("document_types_insert")
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&d.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
InsertSliceParallel(group, tx, d.Data, InsertId[uint]{id: d.ID})
|
||||
|
||||
err = group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DocumentTypeData struct {
|
||||
ID uint
|
||||
|
||||
Type string
|
||||
HtmlFields []string
|
||||
}
|
||||
|
||||
func (d *DocumentTypeData) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO document_types_data (document_types_id, type)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id`
|
||||
}
|
||||
|
||||
func (d *DocumentTypeData) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, d.Type}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&d.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
relData := &InsertId[uint]{id: d.ID, fieldName: "document_types_data_id", tableName: "document_types_data_html"}
|
||||
InsertSimpleSliceParallel(group, tx, d.HtmlFields, relData)
|
||||
|
||||
return group.Wait()
|
||||
}
|
||||
|
||||
func (d *DocumentTypeData) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, d.Type}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&d.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
relData := &InsertId[uint]{id: d.ID, fieldName: "document_types_data_id", tableName: "document_types_data_html"}
|
||||
InsertSimpleSliceParallel(group, tx, d.HtmlFields, relData)
|
||||
|
||||
return group.Wait()
|
||||
}
|
||||
@@ -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")
|
||||
)
|
||||
113
internal/foundry/models/db/files.go
Normal file
113
internal/foundry/models/db/files.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Files struct {
|
||||
ID uint
|
||||
|
||||
Storages []FilesStorage
|
||||
}
|
||||
|
||||
func (f *Files) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO files (%s)
|
||||
VALUES ($1)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (f *Files) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&f.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
InsertSliceParallel(group, tx, f.Storages, InsertId[uint]{id: f.ID})
|
||||
|
||||
err = group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Files) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&f.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
InsertSliceParallel(group, tx, f.Storages, InsertId[uint]{id: f.ID})
|
||||
|
||||
err = group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type FilesStorage struct {
|
||||
ID uint
|
||||
|
||||
Storage string
|
||||
}
|
||||
|
||||
func (f FilesStorage) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO files_storage (files_id, storage)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id`
|
||||
}
|
||||
|
||||
func (f FilesStorage) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, f.Storage}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&f.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f FilesStorage) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, f.Storage}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&f.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
151
internal/foundry/models/db/folder.go
Normal file
151
internal/foundry/models/db/folder.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Folder struct {
|
||||
ID uint
|
||||
|
||||
Name string
|
||||
Sorting string
|
||||
Color string
|
||||
Packs []string
|
||||
Folders []*Folder
|
||||
}
|
||||
|
||||
func (f *Folder) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO folder (%s, name, sorting, color)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (f *Folder) InsertObjects(tx *sqlx.Tx) error {
|
||||
relId := InsertId[string]{
|
||||
id: strconv.FormatUint(uint64(f.ID), 10),
|
||||
fieldName: "folder_id",
|
||||
tableName: "folder_packs",
|
||||
}
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
InsertSimpleSliceParallel(group, tx, f.Packs, &relId)
|
||||
InsertSliceParallel(group, tx, f.Folders, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Folder) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, f.Name, f.Sorting, f.Color}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&f.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return f.InsertObjects(tx)
|
||||
}
|
||||
|
||||
func (f *Folder) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, f.Name, f.Sorting, f.Color}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&f.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return f.InsertObjects(tx)
|
||||
}
|
||||
|
||||
type WorldFolder struct {
|
||||
ID string
|
||||
|
||||
Name string
|
||||
Type string
|
||||
Folder string
|
||||
Sorting string
|
||||
Description string
|
||||
Color string
|
||||
Sort int
|
||||
Stats Stats
|
||||
}
|
||||
|
||||
func (w *WorldFolder) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO world_folder (game_id, id, name, type, folder, sorting, description, color, sort)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
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 {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
relId := InsertId[string]{id: w.ID, fieldName: "world_folder_id"}
|
||||
InsertWithCtxParallel(group, tx, w.Stats, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WorldFolder) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, w.ID, w.Name, w.Type, w.Folder, w.Sorting, w.Description, w.Color, w.Sort}
|
||||
|
||||
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 *WorldFolder) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, w.ID, w.Name, w.Type, w.Folder, w.Sorting, w.Description, w.Color, w.Sort}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package db
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FoundryDataType string
|
||||
|
||||
@@ -17,3 +20,7 @@ type FoundryData struct {
|
||||
Game *Game
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
type FoundryDataModel struct {
|
||||
DB *sql.DB
|
||||
}
|
||||
@@ -1,306 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
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 *sql.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
|
||||
}
|
||||
157
internal/foundry/models/db/game.go
Normal file
157
internal/foundry/models/db/game.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Game struct {
|
||||
ID uint
|
||||
CreatedAt time.Time
|
||||
|
||||
DemoMode bool
|
||||
IdleLogout bool
|
||||
Paused bool
|
||||
UserID string
|
||||
|
||||
Addresses Addresses
|
||||
Files Files
|
||||
Options GameOptions
|
||||
Release Release
|
||||
World *World
|
||||
System *System
|
||||
CoreUpdate CoreUpdate
|
||||
SystemUpdate SystemUpdate
|
||||
ActiveUsers []string
|
||||
Modules []*Module
|
||||
PackageWarnings []*PackageWarning
|
||||
Packs []*Pack
|
||||
Messages []*Message
|
||||
Combats []*Combat
|
||||
CardDeck []*CardDeck
|
||||
Users []*User
|
||||
Macros []*Macro
|
||||
Folders []*WorldFolder
|
||||
Items []*Item
|
||||
Settings []*Setting
|
||||
Journals []*Journal
|
||||
Tables []*Table
|
||||
Playlists []*Playlist
|
||||
Actors []*Actor
|
||||
// Scenes []Scene
|
||||
}
|
||||
|
||||
func (g *Game) InsertObjects(db *sqlx.DB) error {
|
||||
relData := InsertId[uint]{id: g.ID, fieldName: "game_id"}
|
||||
|
||||
err := func() error {
|
||||
tx := db.MustBegin()
|
||||
defer tx.Rollback()
|
||||
|
||||
groupFunc, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
InsertWithCtxParallel(groupFunc, tx, &g.Addresses, relData)
|
||||
InsertWithCtxParallel(groupFunc, tx, &g.Files, relData)
|
||||
InsertWithCtxParallel(groupFunc, tx, &g.Options, relData)
|
||||
InsertWithCtxParallel(groupFunc, tx, &g.Release, relData)
|
||||
InsertWithCtxParallel(groupFunc, tx, &g.CoreUpdate, relData)
|
||||
InsertWithCtxParallel(groupFunc, tx, &g.SystemUpdate, relData)
|
||||
|
||||
relDataString := InsertId[string]{id: strconv.FormatUint(uint64(g.ID), 10), fieldName: "game_id"}
|
||||
InsertWithCtxParallel(groupFunc, tx, g.World, relDataString)
|
||||
InsertWithCtxParallel(groupFunc, tx, g.System, relDataString)
|
||||
|
||||
InsertSimpleSliceParallel(groupFunc, tx, g.ActiveUsers,
|
||||
&InsertId[uint]{id: g.ID, fieldName: "game_id", tableName: "active_users"})
|
||||
|
||||
InsertSliceParallel(groupFunc, tx, g.Modules, relData)
|
||||
InsertSliceParallel(groupFunc, tx, g.PackageWarnings, relData)
|
||||
InsertSliceParallel(groupFunc, tx, g.Packs, relDataString)
|
||||
InsertSliceParallel(groupFunc, tx, g.Messages, relData)
|
||||
InsertSliceParallel(groupFunc, tx, g.Combats, relData)
|
||||
InsertSliceParallel(groupFunc, tx, g.CardDeck, relData)
|
||||
InsertSliceParallel(groupFunc, tx, g.Users, relData)
|
||||
InsertSliceParallel(groupFunc, tx, g.Macros, relData)
|
||||
InsertSliceParallel(groupFunc, tx, g.Folders, relData)
|
||||
|
||||
errFunc := groupFunc.Wait()
|
||||
if errFunc != nil && !errors.Is(errFunc, sql.ErrNoRows) {
|
||||
return errFunc
|
||||
}
|
||||
return tx.Commit()
|
||||
}()
|
||||
|
||||
err = func() error {
|
||||
tx := db.MustBegin()
|
||||
defer tx.Rollback()
|
||||
groupFunc, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
InsertSliceParallel(groupFunc, tx, g.Settings, relData)
|
||||
InsertSliceParallel(groupFunc, tx, g.Journals, relData)
|
||||
InsertSliceParallel(groupFunc, tx, g.Tables, relData)
|
||||
InsertSliceParallel(groupFunc, tx, g.Playlists, relData)
|
||||
|
||||
errFunc := groupFunc.Wait()
|
||||
if errFunc != nil && !errors.Is(errFunc, sql.ErrNoRows) {
|
||||
return errFunc
|
||||
}
|
||||
return tx.Commit()
|
||||
}()
|
||||
|
||||
err = func() error {
|
||||
tx := db.MustBegin()
|
||||
defer tx.Rollback()
|
||||
groupFunc, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
InsertSliceParallelTimeout(groupFunc, tx, g.Items,
|
||||
InsertId[string]{id: strconv.FormatUint(uint64(g.ID), 10), fieldName: "game_id"},
|
||||
15*time.Second)
|
||||
InsertSliceParallelTimeout(groupFunc, tx, g.Actors, relData, 15*time.Second)
|
||||
|
||||
errFunc := groupFunc.Wait()
|
||||
if errFunc != nil && !errors.Is(errFunc, sql.ErrNoRows) {
|
||||
return errFunc
|
||||
}
|
||||
return tx.Commit()
|
||||
}()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (g *Game) Insert(db *sqlx.DB) error {
|
||||
tx := db.MustBegin()
|
||||
defer tx.Rollback()
|
||||
|
||||
const query = `
|
||||
INSERT INTO game (demo_mode, idle_logout, paused, user_id)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id, created_at`
|
||||
|
||||
args := []any{g.DemoMode, g.IdleLogout, g.Paused, g.UserID}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err := tx.QueryRowxContext(ctx, query, args...).Scan(&g.ID, &g.CreatedAt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = g.InsertObjects(db)
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
61
internal/foundry/models/db/grid.go
Normal file
61
internal/foundry/models/db/grid.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Grid struct {
|
||||
ID uint
|
||||
|
||||
Type int
|
||||
Size int
|
||||
Distance int
|
||||
Diagonals int
|
||||
Thickness int
|
||||
Alpha float64
|
||||
Color string
|
||||
Units string
|
||||
Style string
|
||||
}
|
||||
|
||||
func (g *Grid) Query(data *InsertId[string]) {
|
||||
data.query = `
|
||||
INSERT INTO grid (system_id, type, size, distance, diagonals, thickness,
|
||||
alpha, color, units, style)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
RETURNING id`
|
||||
}
|
||||
|
||||
func (g *Grid) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, g.Type, g.Size, g.Distance, g.Diagonals, g.Thickness,
|
||||
g.Alpha, g.Color, g.Units, g.Style}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&g.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Grid) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, g.Type, g.Size, g.Distance, g.Diagonals, g.Thickness,
|
||||
g.Alpha, g.Color, g.Units, g.Style}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&g.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
92
internal/foundry/models/db/index.go
Normal file
92
internal/foundry/models/db/index.go
Normal file
@@ -0,0 +1,92 @@
|
||||
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}
|
||||
|
||||
mutex := GetMutex("index_insert")
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dataCopy := *data
|
||||
i.ConnectGameQuery(&dataCopy)
|
||||
|
||||
return i.ConnectGameCtx(ctx, tx, &dataCopy)
|
||||
}
|
||||
89
internal/foundry/models/db/item.go
Normal file
89
internal/foundry/models/db/item.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Item struct {
|
||||
ID string
|
||||
|
||||
Img string
|
||||
Name string
|
||||
Type string
|
||||
Folder string
|
||||
Sort int
|
||||
Stats Stats
|
||||
Ownership []OwnershipString
|
||||
}
|
||||
|
||||
func (i *Item) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO item (%[1]s, id, img, name, type, folder, sort)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
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 {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
relId := InsertId[string]{id: i.ID, fieldName: "item_id"}
|
||||
InsertWithCtxParallel(group, tx, i.Stats, relId)
|
||||
InsertSliceParallel(group, tx, i.Ownership, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Item) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, i.ID, i.Img, i.Name, i.Type, i.Folder, i.Sort}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return i.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Item) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, i.ID, i.Img, i.Name, i.Type, i.Folder, i.Sort}
|
||||
|
||||
mutex := GetMutex("item_insert")
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return i.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
81
internal/foundry/models/db/journal.go
Normal file
81
internal/foundry/models/db/journal.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Journal struct {
|
||||
ID string
|
||||
|
||||
Name string
|
||||
Sort int
|
||||
Pages []*JournalPage
|
||||
Ownership []OwnershipString
|
||||
}
|
||||
|
||||
func (j *Journal) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO journal (game_id, id, name, sort)
|
||||
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 {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
relId := InsertId[string]{id: j.ID, fieldName: "journal_id"}
|
||||
InsertSliceParallel(group, tx, j.Pages, relId)
|
||||
InsertSliceParallel(group, tx, j.Ownership, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *Journal) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, j.ID, j.Name, j.Sort}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return j.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *Journal) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, j.ID, j.Name, j.Sort}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return j.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
221
internal/foundry/models/db/journal_page.go
Normal file
221
internal/foundry/models/db/journal_page.go
Normal file
@@ -0,0 +1,221 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type JournalPage struct {
|
||||
ID string
|
||||
|
||||
Name string
|
||||
Type string
|
||||
Src string
|
||||
Sort int
|
||||
Text PageText
|
||||
Title PageTitle
|
||||
Video PageVideo
|
||||
Stats Stats
|
||||
Ownership []OwnershipString
|
||||
}
|
||||
|
||||
func (j *JournalPage) Query(data *InsertId[string]) {
|
||||
data.query = `
|
||||
INSERT INTO journal_page (journal_id, id, name, type, src, sort)
|
||||
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 {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
relId := InsertId[string]{id: j.ID, fieldName: "journal_page_id"}
|
||||
InsertWithCtxParallel(group, tx, j.Text, relId)
|
||||
InsertWithCtxParallel(group, tx, j.Title, relId)
|
||||
InsertWithCtxParallel(group, tx, j.Video, relId)
|
||||
InsertWithCtxParallel(group, tx, j.Stats, relId)
|
||||
|
||||
InsertSliceParallel(group, tx, j.Ownership, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *JournalPage) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, j.ID, j.Name, j.Type, j.Src, j.Sort}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return j.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *JournalPage) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, j.ID, j.Name, j.Type, j.Src, j.Sort}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return j.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PageText struct {
|
||||
ID uint
|
||||
|
||||
Content string
|
||||
Markdown string
|
||||
Format int
|
||||
}
|
||||
|
||||
func (p PageText) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO journal_page_text (%s, content, markdown, format)
|
||||
VALUES ($1, $2, $3, $4)`, data.fieldName)
|
||||
}
|
||||
|
||||
func (p PageText) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, p.Content, p.Markdown, p.Format}
|
||||
|
||||
_, err := tx.Exec(data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p PageText) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, p.Content, p.Markdown, p.Format}
|
||||
|
||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type PageTitle struct {
|
||||
ID uint
|
||||
|
||||
Show bool
|
||||
Level int
|
||||
}
|
||||
|
||||
func (p PageTitle) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO journal_page_title (%s, show, level)
|
||||
VALUES ($1, $2, $3)`, data.fieldName)
|
||||
}
|
||||
|
||||
func (p PageTitle) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, p.Show, p.Level}
|
||||
|
||||
_, err := tx.Exec(data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p PageTitle) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, p.Show, p.Level}
|
||||
|
||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type PageVideo struct {
|
||||
ID uint
|
||||
|
||||
Controls bool
|
||||
Volume float64
|
||||
}
|
||||
|
||||
func (p PageVideo) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO journal_page_video (%s, controls, volume)
|
||||
VALUES ($1, $2, $3)`, data.fieldName)
|
||||
}
|
||||
|
||||
func (p PageVideo) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, p.Controls, p.Volume}
|
||||
|
||||
_, err := tx.Exec(data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p PageVideo) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, p.Controls, p.Volume}
|
||||
|
||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
158
internal/foundry/models/db/language.go
Normal file
158
internal/foundry/models/db/language.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type SetupLanguage struct {
|
||||
ID string
|
||||
|
||||
Label string
|
||||
Modules []SetupLanguageModule
|
||||
}
|
||||
|
||||
func (l *SetupLanguage) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO setup_language (setup_id, label)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id`
|
||||
}
|
||||
|
||||
func (l *SetupLanguage) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, l.Label}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&l.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
InsertSliceParallel(group, tx, l.Modules, InsertId[string]{id: l.ID})
|
||||
|
||||
return group.Wait()
|
||||
}
|
||||
|
||||
func (l *SetupLanguage) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, l.Label}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&l.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
InsertSliceParallel(group, tx, l.Modules, InsertId[string]{id: l.ID})
|
||||
|
||||
err = group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SetupLanguageModule struct {
|
||||
ID string
|
||||
|
||||
Label string
|
||||
Path string
|
||||
}
|
||||
|
||||
func (l SetupLanguageModule) Query(data *InsertId[string]) {
|
||||
data.query = `
|
||||
INSERT INTO setup_language_module (setup_language_id, id, label, path)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id`
|
||||
}
|
||||
|
||||
func (l SetupLanguageModule) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, l.ID, l.Label, l.Path}
|
||||
|
||||
_, err := tx.Exec(data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l SetupLanguageModule) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, l.ID, l.Label, l.Path}
|
||||
|
||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Language struct {
|
||||
ID uint
|
||||
|
||||
Lang string
|
||||
Name string
|
||||
Path string
|
||||
}
|
||||
|
||||
func (l *Language) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO language (%s, lang, name, path)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (l *Language) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, l.Lang, l.Name, l.Path}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&l.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Language) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, l.Lang, l.Name, l.Path}
|
||||
|
||||
mutex := GetMutex("language_insert")
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&l.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
174
internal/foundry/models/db/light.go
Normal file
174
internal/foundry/models/db/light.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Light struct {
|
||||
ID uint
|
||||
|
||||
Color string
|
||||
Priority int
|
||||
Angle int
|
||||
Negative bool
|
||||
Alpha float64
|
||||
Bright float64
|
||||
Coloration float64
|
||||
Dim float64
|
||||
Attenuation float64
|
||||
Luminosity float64
|
||||
Saturation float64
|
||||
Contrast float64
|
||||
Shadows float64
|
||||
LightAnimation LightAnimation
|
||||
LightDarkness LightDarkness
|
||||
}
|
||||
|
||||
func (l *Light) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO token_light (%s, color, priority, angle, negative, alpha, bright, coloration, dim,
|
||||
attenuation, luminosity, saturation, contrast, shadows)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (l *Light) InsertObjects(tx *sqlx.Tx) error {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
relId := InsertId[uint]{id: l.ID, fieldName: "token_light_id"}
|
||||
InsertWithCtxParallel(group, tx, l.LightAnimation, relId)
|
||||
InsertWithCtxParallel(group, tx, l.LightDarkness, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Light) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, l.Color, l.Priority, l.Angle, l.Negative, l.Alpha, l.Bright, l.Coloration, l.Dim,
|
||||
l.Attenuation, l.Luminosity, l.Saturation, l.Contrast, l.Shadows}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&l.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Light) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, l.Color, l.Priority, l.Angle, l.Negative, l.Alpha, l.Bright, l.Coloration, l.Dim,
|
||||
l.Attenuation, l.Luminosity, l.Saturation, l.Contrast, l.Shadows}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&l.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type LightAnimation struct {
|
||||
ID uint
|
||||
|
||||
Speed int
|
||||
Intensity int
|
||||
Reverse bool
|
||||
}
|
||||
|
||||
func (l LightAnimation) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO token_light_animation (%s, speed, intensity, reverse)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (l LightAnimation) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, l.Speed, l.Intensity, l.Reverse}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&l.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l LightAnimation) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, l.Speed, l.Intensity, l.Reverse}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&l.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type LightDarkness struct {
|
||||
ID uint
|
||||
|
||||
Min float64
|
||||
Max float64
|
||||
}
|
||||
|
||||
func (l LightDarkness) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO token_light_darkness (%s, min, max)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (l LightDarkness) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, l.Min, l.Max}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&l.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l LightDarkness) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, l.Min, l.Max}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&l.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
87
internal/foundry/models/db/macro.go
Normal file
87
internal/foundry/models/db/macro.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Macro struct {
|
||||
ID string
|
||||
|
||||
Command string
|
||||
Name string
|
||||
Type string
|
||||
Img string
|
||||
Author string
|
||||
Scope string
|
||||
Folder string
|
||||
Sort int
|
||||
Stats Stats
|
||||
Ownership []OwnershipString
|
||||
}
|
||||
|
||||
func (m *Macro) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO macro (game_id, id, command, name, type, img, author, scope, folder, sort)
|
||||
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 {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
relId := InsertId[string]{id: m.ID, fieldName: "macro_id"}
|
||||
InsertWithCtxParallel(group, tx, m.Stats, relId)
|
||||
InsertSliceParallel(group, tx, m.Ownership, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Macro) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, m.ID, m.Command, m.Name, m.Type, m.Img, m.Author, m.Scope, m.Folder, m.Sort}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return m.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Macro) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, m.ID, m.Command, m.Name, m.Type, m.Img, m.Author, m.Scope, m.Folder, m.Sort}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return m.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
57
internal/foundry/models/db/media.go
Normal file
57
internal/foundry/models/db/media.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Media struct {
|
||||
ID uint
|
||||
|
||||
Type string
|
||||
URL string
|
||||
Caption string
|
||||
}
|
||||
|
||||
func (m *Media) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO media (%s, type, url, caption)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (m *Media) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, m.Type, m.URL, m.Caption}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&m.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Media) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, m.Type, m.URL, m.Caption}
|
||||
|
||||
mutex := GetMutex("media_insert")
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&m.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
139
internal/foundry/models/db/message.go
Normal file
139
internal/foundry/models/db/message.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
ID string
|
||||
|
||||
Blind bool
|
||||
Emote bool
|
||||
Style int
|
||||
Timestamp int64
|
||||
Content string
|
||||
Author string
|
||||
Type string
|
||||
Flavor string
|
||||
Sound string
|
||||
Stats Stats
|
||||
Speaker Speaker
|
||||
Whisper []string
|
||||
Rolls []string
|
||||
}
|
||||
|
||||
func (m *Message) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
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)
|
||||
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 {
|
||||
relId := InsertId[string]{id: m.ID, fieldName: "message_id"}
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
InsertWithCtxParallel(group, tx, m.Stats, relId)
|
||||
InsertWithCtxParallel(group, tx, m.Speaker, relId)
|
||||
|
||||
InsertSimpleSliceParallel(group, tx, m.Whisper, &InsertId[string]{id: m.ID, fieldName: "message_id", tableName: "message_whisper"})
|
||||
InsertSimpleSliceParallel(group, tx, m.Rolls, &InsertId[string]{id: m.ID, fieldName: "message_id", tableName: "message_rolls"})
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Message) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, m.ID, m.Blind, m.Emote, m.Style, m.Timestamp, m.Content, m.Author, m.Type, m.Flavor, m.Sound}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return m.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Message) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, m.ID, m.Blind, m.Emote, m.Style, m.Timestamp, m.Content, m.Author, m.Type, m.Flavor, m.Sound}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return m.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Speaker struct {
|
||||
ID uint
|
||||
|
||||
Scene string
|
||||
Actor string
|
||||
Token string
|
||||
Alias string
|
||||
}
|
||||
|
||||
func (s Speaker) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO speaker (%s, scene, actor, token, alias)
|
||||
VALUES ($1, $2, $3, $4, $5)`, data.fieldName)
|
||||
}
|
||||
|
||||
func (s Speaker) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, s.Scene, s.Actor, s.Token, s.Alias}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&s.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Speaker) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, s.Scene, s.Actor, s.Token, s.Alias}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&s.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package db
|
||||
|
||||
import "database/sql"
|
||||
|
||||
// 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 *sql.DB) *Models {
|
||||
return &Models{
|
||||
FoundryState: FoundryStateModel{DB: db},
|
||||
}
|
||||
}
|
||||
|
||||
// func NewMockModels() Models {
|
||||
// return Models{
|
||||
// Movies: MockMovieModel{},
|
||||
// Users: MockMovieModel{},
|
||||
// }
|
||||
// }
|
||||
@@ -4,287 +4,185 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
"strings"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Module struct {
|
||||
Id int64
|
||||
TextId string
|
||||
ID string
|
||||
|
||||
Title string
|
||||
Description string
|
||||
Url string
|
||||
URL string
|
||||
License string
|
||||
Readme string
|
||||
Bugs string
|
||||
Changelog 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
|
||||
CreatedAt time.Time
|
||||
Languages []Language
|
||||
DocumentTypes DocumentTypes
|
||||
Relationships Relationships
|
||||
Compatibility Compatibility
|
||||
Scripts []string
|
||||
Esmodules []string
|
||||
Tags []string
|
||||
Authors []*Author
|
||||
Media []*Media
|
||||
Styles []*Style
|
||||
Languages []*Language
|
||||
Packs []*Pack
|
||||
PackFolders []*Folder
|
||||
}
|
||||
|
||||
type Language struct {
|
||||
Id int64
|
||||
Lang string
|
||||
Name string
|
||||
Path string
|
||||
func (m *Module) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO module (setup_id, id, title, description, url, license, readme, bugs,
|
||||
changelog, version, manifest, download, socket, protected, exclusive_, persistent_storage,
|
||||
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 (modules Modules) GetById(id int) *Module {
|
||||
return &modules[id]
|
||||
func (m *Module) ConnectGameQuery(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO game_to_module (game_id, module_id)
|
||||
VALUES ($1, $2)`
|
||||
}
|
||||
|
||||
func (m FoundryStateModel) InsertModule(module *Module, stateId int64) error {
|
||||
query := `
|
||||
INSERT INTO modules (state_id, text_id, title, description, url, version, availability)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING id, created_at`
|
||||
func (m *Module) InsertObjects(tx *sqlx.Tx) error {
|
||||
relId := InsertId[string]{id: m.ID, fieldName: "module_id"}
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
args := []any{stateId, module.TextId, module.Title, module.Description, module.Url, module.Version, module.Availability}
|
||||
InsertWithCtxParallel(group, tx, m.DocumentTypes, relId)
|
||||
InsertWithCtxParallel(group, tx, m.Relationships, relId)
|
||||
InsertWithCtxParallel(group, tx, m.Compatibility, relId)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
scriptRelId := &InsertId[string]{id: m.ID, fieldName: "module_id", tableName: "scripts"}
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range module.Languages {
|
||||
err = m.InsertModuleLanguages(&module.Languages[i], module.Id)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return m.InsertModuleCompatibility(&module.Compatibility, module.Id)
|
||||
}
|
||||
InsertSliceParallel(group, tx, m.Authors, relId)
|
||||
InsertSliceParallel(group, tx, m.Media, relId)
|
||||
InsertSliceParallel(group, tx, m.Styles, relId)
|
||||
InsertSliceParallel(group, tx, m.Languages, relId)
|
||||
InsertSliceParallel(group, tx, m.Packs, relId)
|
||||
InsertSliceParallel(group, tx, m.PackFolders, relId)
|
||||
|
||||
func (m FoundryStateModel) InsertModuleCompatibility(compatibility *Compatibility, moduleId int64) error {
|
||||
query := `
|
||||
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 {
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m FoundryStateModel) InsertModuleLanguages(lang *Language, moduleId int64) error {
|
||||
query := `
|
||||
INSERT INTO modules_languages (module_id, language, name, path)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id`
|
||||
func (m *Module) ConnectGame(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{moduleId, lang.Lang, lang.Name, lang.Path}
|
||||
args := []any{data.id, m.ID}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&lang.Id)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
_, err := tx.Exec(data.query, args...)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m FoundryStateModel) GetModules(idState int64) (Modules, error) {
|
||||
if idState < 1 {
|
||||
return nil, ErrorRecordNotFound
|
||||
func (m *Module) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
query := `
|
||||
SELECT id, created_at, text_id, title, description, url, version, availability
|
||||
FROM modules
|
||||
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
|
||||
}
|
||||
var dataId *uint
|
||||
if strings.EqualFold(data.fieldName, "setup_id") {
|
||||
dataId = &data.id
|
||||
}
|
||||
|
||||
modules := make(Modules, 0, 1)
|
||||
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}
|
||||
|
||||
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)
|
||||
res, err := tx.Exec(data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
rowsAffected, err := res.RowsAffected()
|
||||
if rowsAffected != 0 {
|
||||
err = m.InsertObjects(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return ErrorRecordNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
if dataId == nil {
|
||||
dataCopy := *data
|
||||
m.ConnectGameQuery(&dataCopy)
|
||||
err = m.ConnectGame(tx, &dataCopy)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m FoundryStateModel) DeleteModule(idModule int64) error {
|
||||
if idModule < 1 {
|
||||
return ErrorRecordNotFound
|
||||
func (m *Module) ConnectGameCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
query := `
|
||||
DELETE FROM modules
|
||||
WHERE id = $1`
|
||||
args := []any{data.id, m.ID}
|
||||
|
||||
_, 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 {
|
||||
return err
|
||||
}
|
||||
|
||||
rowsAffected, err := res.RowsAffected()
|
||||
if rowsAffected != 0 {
|
||||
err = m.InsertObjects(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if dataId == nil {
|
||||
dataCopy := *data
|
||||
m.ConnectGameQuery(&dataCopy)
|
||||
err = m.ConnectGameCtx(ctx, tx, &dataCopy)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result, err := m.DB.ExecContext(ctx, query, idModule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return ErrorRecordNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
116
internal/foundry/models/db/options.go
Normal file
116
internal/foundry/models/db/options.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type GameOptions struct {
|
||||
ID uint
|
||||
|
||||
Language string
|
||||
UpdateChannel string
|
||||
Port int
|
||||
}
|
||||
|
||||
func (g *GameOptions) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO game_options (game_id, language, update_channel, port)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id`
|
||||
}
|
||||
|
||||
func (g *GameOptions) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, g.Language, g.UpdateChannel, g.Port}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&g.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GameOptions) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, g.Language, g.UpdateChannel, g.Port}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&g.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type SetupOptions struct {
|
||||
ID uint
|
||||
|
||||
CSSTheme string
|
||||
DataPath string
|
||||
Hostname string
|
||||
Language string
|
||||
LocalHostname string
|
||||
UpdateChannel string
|
||||
Port int
|
||||
CompressSocket bool
|
||||
CompressStatic bool
|
||||
Fullscreen bool
|
||||
HotReload bool
|
||||
ProxySSL bool
|
||||
Telemetry bool
|
||||
Upnp bool
|
||||
DeleteNEDB bool
|
||||
NoBackups bool
|
||||
}
|
||||
|
||||
func (s *SetupOptions) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO setup_options (setup_id, css_theme, data_path, hostname, language, local_hostname,
|
||||
update_channel, port, compress_socket, compress_static, fullscreen, hot_reload, proxy_ssl,
|
||||
telemetry, upnp, delete_nedb, no_backups)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
|
||||
RETURNING id`
|
||||
}
|
||||
|
||||
func (s *SetupOptions) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, s.CSSTheme, s.DataPath, s.Hostname, s.Language, s.LocalHostname,
|
||||
s.UpdateChannel, s.Port, s.CompressSocket, s.CompressStatic, s.Fullscreen, s.HotReload,
|
||||
s.ProxySSL, s.Telemetry, s.Upnp, s.DeleteNEDB, s.NoBackups}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&s.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SetupOptions) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, s.CSSTheme, s.DataPath, s.Hostname, s.Language, s.LocalHostname,
|
||||
s.UpdateChannel, s.Port, s.CompressSocket, s.CompressStatic, s.Fullscreen, s.HotReload,
|
||||
s.ProxySSL, s.Telemetry, s.Upnp, s.DeleteNEDB, s.NoBackups}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&s.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
101
internal/foundry/models/db/ownership.go
Normal file
101
internal/foundry/models/db/ownership.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Ownership struct {
|
||||
ID uint
|
||||
|
||||
Player string
|
||||
Trusted string
|
||||
Assistant string
|
||||
}
|
||||
|
||||
func (o Ownership) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO ownership (%s, player, trusted, assistant)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (o Ownership) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, o.Player, o.Trusted, o.Assistant}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&o.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o Ownership) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, o.Player, o.Trusted, o.Assistant}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&o.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type OwnershipString struct {
|
||||
ID uint
|
||||
|
||||
Key string
|
||||
Value int
|
||||
}
|
||||
|
||||
func (o OwnershipString) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO ownership_string (%s, key_, value)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (o OwnershipString) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, o.Key, o.Value}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&o.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o OwnershipString) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, o.Key, o.Value}
|
||||
|
||||
mutex := GetMutex("ownership_string_insert")
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&o.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
211
internal/foundry/models/db/pack.go
Normal file
211
internal/foundry/models/db/pack.go
Normal file
@@ -0,0 +1,211 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Pack struct {
|
||||
ID string
|
||||
|
||||
Name string
|
||||
Label string
|
||||
Banner string
|
||||
Path string
|
||||
Type string
|
||||
System string
|
||||
PackageType string
|
||||
PackageName string
|
||||
Ownership Ownership
|
||||
Index []*Index
|
||||
Folders []*PackFolder
|
||||
}
|
||||
|
||||
func (p *Pack) Query(data *InsertId[string]) {
|
||||
if !strings.EqualFold(data.fieldName, "game_id") {
|
||||
data.query = fmt.Sprintf(`
|
||||
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)
|
||||
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 {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
InsertWithCtxParallel(group, tx, p.Ownership,
|
||||
InsertId[string]{id: p.ID, fieldName: "pack_id"})
|
||||
|
||||
relId := InsertId[string]{id: p.ID, fieldName: "pack_id"}
|
||||
InsertSliceParallel(group, tx, p.Index, relId)
|
||||
InsertSliceParallel(group, tx, p.Folders, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
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 {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
mutex := GetMutex("pack_insert")
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
res, err := tx.Exec(data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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 {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
mutex := GetMutex("pack_insert")
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
res, err := tx.ExecContext(ctx, data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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 {
|
||||
ID string
|
||||
|
||||
Description string
|
||||
Name string
|
||||
Sorting string
|
||||
Type string
|
||||
Sort int
|
||||
}
|
||||
|
||||
func (p *PackFolder) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO pack_folder (%s, id, description, name, sorting, type, sort)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)`, data.fieldName)
|
||||
}
|
||||
|
||||
func (p *PackFolder) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, p.ID, p.Description, p.Name, p.Sorting, p.Type, p.Sort}
|
||||
|
||||
_, err := tx.Exec(data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PackFolder) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, p.ID, p.Description, p.Name, p.Sorting, p.Type, p.Sort}
|
||||
|
||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
173
internal/foundry/models/db/package_warnings.go
Normal file
173
internal/foundry/models/db/package_warnings.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type PackageWarning struct {
|
||||
ID uint
|
||||
|
||||
Key string
|
||||
Value *PackageWarningsData
|
||||
}
|
||||
|
||||
func (p *PackageWarning) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO package_warnings (%s, key_)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (p *PackageWarning) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, p.Key}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
InsertWithCtx(tx, p.Value, InsertId[uint]{id: p.ID})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PackageWarning) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, p.Key}
|
||||
|
||||
mutex := GetMutex("package_warning_insert")
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return InsertWithCtx(tx, p.Value, InsertId[uint]{id: p.ID})
|
||||
}
|
||||
|
||||
type PackageWarningsData struct {
|
||||
ID string
|
||||
|
||||
Type string
|
||||
Manifest string
|
||||
Reinstallable bool
|
||||
Warning []string
|
||||
Error []string
|
||||
}
|
||||
|
||||
func (p *PackageWarningsData) InsertObjects(tx *sqlx.Tx) error {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
warningData := &InsertId[string]{id: p.ID, fieldName: "package_warnings_data_id", tableName: "package_warnings_data_warning"}
|
||||
InsertSimpleSliceParallel(group, tx, p.Warning, warningData)
|
||||
errorData := &InsertId[string]{id: p.ID, fieldName: "package_warnings_data_id", tableName: "package_warnings_data_error"}
|
||||
InsertSimpleSliceParallel(group, tx, p.Warning, errorData)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PackageWarningsData) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO package_warnings_data (id, type, reinstallable, manifest)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT(id) DO NOTHING`
|
||||
}
|
||||
|
||||
func (p *PackageWarningsData) ConnectGameQuery(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO package_warnings_to_data (package_warnings_id, package_warnings_data_id)
|
||||
VALUES ($1, $2)`
|
||||
}
|
||||
|
||||
func (p *PackageWarningsData) ConnectGame(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, p.ID}
|
||||
|
||||
_, err := tx.Exec(data.query, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *PackageWarningsData) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{p.ID, p.Type, p.Reinstallable, p.Manifest}
|
||||
|
||||
res, err := tx.Exec(data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rowsAffected, err := res.RowsAffected()
|
||||
if rowsAffected != 0 {
|
||||
err = p.InsertObjects(tx)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dataCopy := *data
|
||||
p.ConnectGameQuery(&dataCopy)
|
||||
|
||||
return p.ConnectGame(tx, &dataCopy)
|
||||
}
|
||||
|
||||
func (p *PackageWarningsData) ConnectGameCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, p.ID}
|
||||
|
||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *PackageWarningsData) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{p.ID, p.Type, p.Reinstallable, p.Manifest}
|
||||
|
||||
res, err := tx.ExecContext(ctx, data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rowsAffected, err := res.RowsAffected()
|
||||
if rowsAffected != 0 {
|
||||
err = p.InsertObjects(tx)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dataCopy := *data
|
||||
p.ConnectGameQuery(&dataCopy)
|
||||
|
||||
return p.ConnectGameCtx(ctx, tx, &dataCopy)
|
||||
}
|
||||
145
internal/foundry/models/db/playlist.go
Normal file
145
internal/foundry/models/db/playlist.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Playlist struct {
|
||||
ID string
|
||||
|
||||
Name string
|
||||
Folder string
|
||||
Sorting string
|
||||
Description string
|
||||
Channel string
|
||||
Mode int
|
||||
Fade int
|
||||
Seed int
|
||||
Sort int
|
||||
Playing bool
|
||||
Stats Stats
|
||||
Ownership []OwnershipString
|
||||
Sounds []*Sound
|
||||
}
|
||||
|
||||
func (p *Playlist) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
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)
|
||||
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 {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
relId := InsertId[string]{id: p.ID, fieldName: "playlist_id"}
|
||||
InsertWithCtxParallel(group, tx, p.Stats, relId)
|
||||
InsertSliceParallel(group, tx, p.Ownership, relId)
|
||||
InsertSliceParallel(group, tx, p.Sounds, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Playlist) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return p.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Playlist) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return p.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Sound struct {
|
||||
ID string
|
||||
|
||||
Name string
|
||||
Path string
|
||||
Channel string
|
||||
Description string
|
||||
Fade int
|
||||
Sort int
|
||||
Repeat bool
|
||||
Playing bool
|
||||
Volume float64
|
||||
PausedTime float64
|
||||
}
|
||||
|
||||
func (s *Sound) Query(data *InsertId[string]) {
|
||||
data.query = `
|
||||
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)
|
||||
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 {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, s.ID, s.Name, s.Path, s.Channel, s.Description, s.Fade, s.Sort, s.Repeat, s.Playing, s.Volume, s.PausedTime}
|
||||
|
||||
_, err := tx.Exec(data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Sound) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, s.ID, s.Name, s.Path, s.Channel, s.Description, s.Fade, s.Sort, s.Repeat, s.Playing, s.Volume, s.PausedTime}
|
||||
|
||||
_, err := tx.ExecContext(ctx, data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
135
internal/foundry/models/db/relationships.go
Normal file
135
internal/foundry/models/db/relationships.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Relationships struct {
|
||||
ID uint
|
||||
|
||||
Systems []RelationshipsData
|
||||
Requires []RelationshipsData
|
||||
Recommends []RelationshipsData
|
||||
Conflicts []RelationshipsData
|
||||
}
|
||||
|
||||
func (r Relationships) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO relationships (%s)
|
||||
VALUES ($1)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (r Relationships) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
InsertSliceParallel(group, tx, r.Systems, InsertId[uint]{id: r.ID, tableName: "relationships_systems"})
|
||||
InsertSliceParallel(group, tx, r.Requires, InsertId[uint]{id: r.ID, tableName: "relationships_requires"})
|
||||
InsertSliceParallel(group, tx, r.Recommends, InsertId[uint]{id: r.ID, tableName: "relationships_recommends"})
|
||||
InsertSliceParallel(group, tx, r.Conflicts, InsertId[uint]{id: r.ID, tableName: "relationships_conflicts"})
|
||||
|
||||
err = group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r Relationships) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id}
|
||||
|
||||
mutex := GetMutex("relationships_insert")
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
InsertSliceParallel(group, tx, r.Systems, InsertId[uint]{id: r.ID, tableName: "relationships_systems"})
|
||||
InsertSliceParallel(group, tx, r.Requires, InsertId[uint]{id: r.ID, tableName: "relationships_requires"})
|
||||
InsertSliceParallel(group, tx, r.Recommends, InsertId[uint]{id: r.ID, tableName: "relationships_recommends"})
|
||||
InsertSliceParallel(group, tx, r.Conflicts, InsertId[uint]{id: r.ID, tableName: "relationships_conflicts"})
|
||||
|
||||
err = group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RelationshipsData struct {
|
||||
ID uint
|
||||
|
||||
Key string
|
||||
Type string
|
||||
Manifest string
|
||||
Compatibility Compatibility
|
||||
}
|
||||
|
||||
func (r RelationshipsData) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO %s (relationships_id, key_, type, manifest)
|
||||
VALUES ($1, $2, $3, $4)`, data.tableName)
|
||||
}
|
||||
|
||||
func (r RelationshipsData) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, r.Key, r.Type, r.Manifest}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
InsertWithCtxParallel(group, tx, r.Compatibility, InsertId[string]{id: strconv.FormatUint(uint64(r.ID), 10), fieldName: fmt.Sprintf("%s_id", data.tableName)})
|
||||
|
||||
return group.Wait()
|
||||
}
|
||||
|
||||
func (r RelationshipsData) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, r.Key, r.Type, r.Manifest}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
InsertWithCtxParallel(group, tx, r.Compatibility, InsertId[string]{id: strconv.FormatUint(uint64(r.ID), 10), fieldName: fmt.Sprintf("%s_id", data.tableName)})
|
||||
|
||||
return group.Wait()
|
||||
}
|
||||
61
internal/foundry/models/db/release.go
Normal file
61
internal/foundry/models/db/release.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Release struct {
|
||||
ID uint
|
||||
|
||||
Generation int
|
||||
Build int
|
||||
NodeVersion int
|
||||
MaxGeneration int
|
||||
MaxStableGeneration int
|
||||
Time int64
|
||||
Channel string
|
||||
Suffix string
|
||||
}
|
||||
|
||||
func (r *Release) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO release_ (%s, generation, build, node_version, max_generation, max_stable_generation,
|
||||
time, channel, suffix)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (r *Release) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, r.Generation, r.Build, r.NodeVersion, r.MaxGeneration, r.MaxStableGeneration,
|
||||
r.Time, r.Channel, r.Suffix}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Release) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, r.Generation, r.Build, r.NodeVersion, r.MaxGeneration, r.MaxStableGeneration,
|
||||
r.Time, r.Channel, r.Suffix}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
159
internal/foundry/models/db/ring.go
Normal file
159
internal/foundry/models/db/ring.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Ring struct {
|
||||
ID uint
|
||||
|
||||
Enabled bool
|
||||
Effects int
|
||||
RingColors RingColors
|
||||
Subject Subject
|
||||
}
|
||||
|
||||
func (r *Ring) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO ring (%s, enabled, effects)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (r *Ring) InsertObjects(tx *sqlx.Tx) error {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
relId := InsertId[uint]{id: r.ID, fieldName: "ring_id"}
|
||||
InsertWithCtxParallel(group, tx, r.RingColors, relId)
|
||||
InsertWithCtxParallel(group, tx, r.Subject, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Ring) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, r.Enabled, r.Effects}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Ring) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, r.Enabled, r.Effects}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type RingColors struct {
|
||||
ID uint
|
||||
|
||||
Ring string
|
||||
Background string
|
||||
}
|
||||
|
||||
func (r RingColors) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO ring_colors (%s, ring, background)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (r RingColors) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, r.Ring, r.Background}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r RingColors) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, r.Ring, r.Background}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Subject struct {
|
||||
ID uint
|
||||
|
||||
Scale int
|
||||
Texture string
|
||||
}
|
||||
|
||||
func (s Subject) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO ring_colors (%s, scale, texture)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (s Subject) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, s.Scale, s.Texture}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&s.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Subject) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, s.Scale, s.Texture}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&s.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -21,7 +21,7 @@ type Scene struct {
|
||||
Grid SceneGrid
|
||||
TokenVision bool
|
||||
Drawings []SceneDrawing
|
||||
Tokens []Token
|
||||
Tokens []*Token
|
||||
Lights []SceneLight
|
||||
Notes []Note
|
||||
Sounds []ScenesSound
|
||||
@@ -119,7 +119,7 @@ type SceneLight struct {
|
||||
Rotation int
|
||||
Walls bool
|
||||
Vision bool
|
||||
Config Light
|
||||
Config *Light
|
||||
Hidden bool
|
||||
Elevation int
|
||||
}
|
||||
79
internal/foundry/models/db/settings.go
Normal file
79
internal/foundry/models/db/settings.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Setting struct {
|
||||
ID string
|
||||
|
||||
Key string
|
||||
Value string
|
||||
Stats Stats
|
||||
}
|
||||
|
||||
func (s *Setting) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO setting (game_id, id, key_, value)
|
||||
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 {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
relId := InsertId[string]{id: s.ID, fieldName: "setting_id"}
|
||||
InsertWithCtxParallel(group, tx, s.Stats, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Setting) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, s.ID, s.Key, s.Value}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return s.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Setting) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, s.ID, s.Key, s.Value}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return s.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
194
internal/foundry/models/db/setup.go
Normal file
194
internal/foundry/models/db/setup.go
Normal file
@@ -0,0 +1,194 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Setup struct {
|
||||
ID uint
|
||||
CreatedAt time.Time
|
||||
|
||||
IsAdmin bool
|
||||
IsSetup bool
|
||||
CoreUpdate CoreUpdate
|
||||
FeaturedContent FeaturedContent
|
||||
Files Files
|
||||
Options *SetupOptions
|
||||
Release Release
|
||||
Languages []*SetupLanguage
|
||||
Modules []*Module
|
||||
News []*News
|
||||
PackageWarnings []*PackageWarning
|
||||
Systems []*System
|
||||
Worlds []*World
|
||||
}
|
||||
|
||||
func (s *Setup) InsertObjects(tx *sqlx.Tx) error {
|
||||
relData := InsertId[uint]{id: s.ID, fieldName: "setup_id"}
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
InsertWithCtxParallel(group, tx, &s.CoreUpdate, relData)
|
||||
InsertWithCtxParallel(group, tx, &s.FeaturedContent, relData)
|
||||
InsertWithCtxParallel(group, tx, &s.Files, relData)
|
||||
InsertWithCtxParallel(group, tx, s.Options, relData)
|
||||
InsertWithCtxParallel(group, tx, &s.Release, relData)
|
||||
|
||||
InsertSliceParallel(group, tx, s.Languages, relData)
|
||||
InsertSliceParallel(group, tx, s.Modules, relData)
|
||||
InsertSliceParallel(group, tx, s.News, relData)
|
||||
InsertSliceParallel(group, tx, s.PackageWarnings, relData)
|
||||
|
||||
relDataString := InsertId[string]{
|
||||
id: strconv.FormatUint(uint64(s.ID), 10),
|
||||
fieldName: "setup_id",
|
||||
}
|
||||
InsertSliceParallel(group, tx, s.Systems, relDataString)
|
||||
InsertSliceParallel(group, tx, s.Worlds, relDataString)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Setup) Insert(db *sqlx.DB) error {
|
||||
tx := db.MustBegin()
|
||||
defer tx.Rollback()
|
||||
|
||||
const query = `
|
||||
INSERT INTO setup (is_admin, is_setup)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id, created_at`
|
||||
|
||||
args := []any{s.IsAdmin, s.IsSetup}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err := tx.QueryRowxContext(ctx, query, args...).Scan(&s.ID, &s.CreatedAt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.InsertObjects(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
type FeaturedContent struct {
|
||||
ID uint
|
||||
|
||||
Title string
|
||||
Caption string
|
||||
URL string
|
||||
Image string
|
||||
}
|
||||
|
||||
func (f *FeaturedContent) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO featured_content (setup_id, title, caption, url, image)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING id`
|
||||
}
|
||||
|
||||
func (f *FeaturedContent) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, f.Title, f.Caption, f.URL, f.Image}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&f.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FeaturedContent) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, f.Title, f.Caption, f.URL, f.Image}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&f.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type News struct {
|
||||
ID uint
|
||||
|
||||
Title string
|
||||
Caption string
|
||||
URL string
|
||||
Image string
|
||||
}
|
||||
|
||||
func (n *News) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO news (setup_id, title, caption, url, image)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING id`
|
||||
}
|
||||
|
||||
func (n *News) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, n.Title, n.Caption, n.URL, n.Image}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&n.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *News) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, n.Title, n.Caption, n.URL, n.Image}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&n.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
58
internal/foundry/models/db/stats.go
Normal file
58
internal/foundry/models/db/stats.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Stats struct {
|
||||
ID uint
|
||||
|
||||
CoreVersion string
|
||||
SystemID string
|
||||
SystemVersion string
|
||||
LastModifiedBy string
|
||||
ModifiedTime int64
|
||||
}
|
||||
|
||||
func (s Stats) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO stats (%s, core_version, system_id, system_version, last_modified_by, modified_time)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)`, data.fieldName)
|
||||
}
|
||||
|
||||
func (s Stats) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, s.CoreVersion, s.SystemID, s.SystemVersion, s.LastModifiedBy, s.ModifiedTime}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&s.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Stats) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, s.CoreVersion, s.SystemID, s.SystemVersion, s.LastModifiedBy, s.ModifiedTime}
|
||||
|
||||
mutex := GetMutex("stats_insert")
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&s.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
55
internal/foundry/models/db/style.go
Normal file
55
internal/foundry/models/db/style.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Style struct {
|
||||
ID uint
|
||||
|
||||
Src string
|
||||
}
|
||||
|
||||
func (s *Style) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO style (%s, src)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (s *Style) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, s.Src}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&s.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Style) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, s.Src}
|
||||
|
||||
mutex := GetMutex("style_insert")
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&s.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -4,201 +4,186 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
"strings"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type System struct {
|
||||
Id int64
|
||||
TextId string
|
||||
ID string
|
||||
|
||||
Title string
|
||||
Description string
|
||||
Url string
|
||||
URL string
|
||||
License string
|
||||
Bugs string
|
||||
Changelog string
|
||||
Version string
|
||||
Manifest 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
|
||||
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 (systems Systems) GetById(id int) *System {
|
||||
return &systems[id]
|
||||
func (s *System) Query(data *InsertId[string]) {
|
||||
data.query = `
|
||||
INSERT INTO system (setup_id, id, title, description, url, license, bugs, changelog, version, manifest,
|
||||
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 {
|
||||
query := `
|
||||
INSERT INTO systems (state_id, text_id, title, description, url, download)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
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 (s *System) ConnectGameQuery(data *InsertId[string]) {
|
||||
data.query = `
|
||||
INSERT INTO game_to_systems (game_id, system_id)
|
||||
VALUES ($1, $2)`
|
||||
}
|
||||
|
||||
func (m FoundryStateModel) InsertSystemCompatibility(compatibility *Compatibility, systemId int64) error {
|
||||
query := `
|
||||
INSERT INTO systems_compatibility (system_id, minimum, verified, maximum)
|
||||
VALUES ($1, $2, $3, $4)`
|
||||
func (s *System) InsertObjects(tx *sqlx.Tx) error {
|
||||
relId := InsertId[string]{id: s.ID, fieldName: "system_id"}
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
args := []any{systemId, compatibility.Minimum, compatibility.Verified, compatibility.Maximum}
|
||||
InsertWithCtxParallel(group, tx, s.Compatibility, relId)
|
||||
InsertWithCtxParallel(group, tx, s.Relationships, relId)
|
||||
InsertWithCtxParallel(group, tx, s.DocumentTypes, relId)
|
||||
InsertWithCtxParallel(group, tx, s.Grid, relId)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
esModulesRelId := &InsertId[string]{id: s.ID, fieldName: "system_id", tableName: "es_modules"}
|
||||
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)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
InsertSliceParallel(group, tx, s.Authors, relId)
|
||||
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 nil
|
||||
}
|
||||
|
||||
func (m FoundryStateModel) GetSystems(idState int64) (Systems, error) {
|
||||
if idState < 1 {
|
||||
return nil, ErrorRecordNotFound
|
||||
func (s *System) ConnectGame(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
query := `
|
||||
SELECT id, created_at, text_id, title, description, url, download
|
||||
FROM systems
|
||||
WHERE state_id = $1`
|
||||
args := []any{data.id, s.ID}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
systems := make(Systems, 0, 1)
|
||||
|
||||
for rows.Next() {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return systems, nil
|
||||
_, err := tx.Exec(data.query, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m FoundryStateModel) GetSystemCompatibility(idSystem int64) (*Compatibility, error) {
|
||||
if idSystem < 1 {
|
||||
return nil, ErrorRecordNotFound
|
||||
func (s *System) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
var dataId *string
|
||||
if strings.EqualFold(data.fieldName, "setup_id") {
|
||||
dataId = &data.id
|
||||
}
|
||||
|
||||
return &compatibility, 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) 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)
|
||||
res, err := tx.Exec(data.query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
rowsAffected, err := res.RowsAffected()
|
||||
if rowsAffected != 0 {
|
||||
err = s.InsertObjects(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return ErrorRecordNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
if dataId == nil {
|
||||
dataCopy := *data
|
||||
s.ConnectGameQuery(&dataCopy)
|
||||
err = s.ConnectGame(tx, &dataCopy)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m FoundryStateModel) DeleteSystem(idSystem int64) error {
|
||||
if idSystem < 1 {
|
||||
return ErrorRecordNotFound
|
||||
func (s *System) ConnectGameCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
query := `
|
||||
DELETE FROM systems
|
||||
WHERE id = $1`
|
||||
args := []any{data.id, s.ID}
|
||||
|
||||
_, 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 {
|
||||
return err
|
||||
}
|
||||
|
||||
rowsAffected, err := res.RowsAffected()
|
||||
if rowsAffected != 0 {
|
||||
err = s.InsertObjects(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if dataId == nil {
|
||||
dataCopy := *data
|
||||
s.ConnectGameQuery(&dataCopy)
|
||||
err = s.ConnectGameCtx(ctx, tx, &dataCopy)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result, err := m.DB.ExecContext(ctx, query, idSystem)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return ErrorRecordNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
163
internal/foundry/models/db/table.go
Normal file
163
internal/foundry/models/db/table.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Table struct {
|
||||
ID string
|
||||
|
||||
Name string
|
||||
Description string
|
||||
Formula string
|
||||
Img string
|
||||
Folder string
|
||||
Sort int
|
||||
Replacement bool
|
||||
DisplayRoll bool
|
||||
Stats Stats
|
||||
Ownership []OwnershipString
|
||||
Results []*TableResult
|
||||
}
|
||||
|
||||
func (t *Table) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
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)
|
||||
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 {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
relId := InsertId[string]{id: t.ID, fieldName: "table_id"}
|
||||
InsertWithCtxParallel(group, tx, t.Stats, relId)
|
||||
InsertSliceParallel(group, tx, t.Ownership, relId)
|
||||
InsertSliceParallel(group, tx, t.Results, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Table) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, t.ID, t.Name, t.Description, t.Formula, t.Img, t.Folder, t.Sort, t.Replacement, t.DisplayRoll}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return t.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Table) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, t.ID, t.Name, t.Description, t.Formula, t.Img, t.Folder, t.Sort, t.Replacement, t.DisplayRoll}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return t.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TableResult struct {
|
||||
ID string
|
||||
|
||||
Type string
|
||||
Img string
|
||||
Description string
|
||||
Name string
|
||||
Weight int
|
||||
Drawn bool
|
||||
Stats Stats
|
||||
Range []int
|
||||
}
|
||||
|
||||
func (t *TableResult) Query(data *InsertId[string]) {
|
||||
data.query = `
|
||||
INSERT INTO table_result (table_id, id, type, img, description, name, weight, drawn)
|
||||
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 {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
InsertWithCtxParallel(group, tx, t.Stats, InsertId[string]{id: t.ID, fieldName: "table_result_id"})
|
||||
InsertSimpleSlice(tx, t.Range, &InsertId[string]{id: t.ID, fieldName: "table_result_id", tableName: "table_result_range"})
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TableResult) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, t.ID, t.Type, t.Img, t.Description, t.Name, t.Weight, t.Drawn}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return t.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TableResult) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, t.ID, t.Type, t.Img, t.Description, t.Name, t.Weight, t.Drawn}
|
||||
|
||||
var isInserted bool
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isInserted {
|
||||
return t.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
335
internal/foundry/models/db/token.go
Normal file
335
internal/foundry/models/db/token.go
Normal file
@@ -0,0 +1,335 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
ID uint
|
||||
|
||||
Name string
|
||||
ActorLink bool
|
||||
AppendNumber bool
|
||||
PrependAdjective bool
|
||||
LockRotation bool
|
||||
RandomImg bool
|
||||
DisplayName int
|
||||
DisplayBars int
|
||||
Disposition int
|
||||
Rotation int
|
||||
Alpha int
|
||||
Width float64
|
||||
Height float64
|
||||
Ring *Ring
|
||||
Sight *TokenSight
|
||||
Texture *TokenTexture
|
||||
Bar1 TokenBar
|
||||
Bar2 TokenBar
|
||||
Light *Light
|
||||
Occludable TokenOccludable
|
||||
TurnMarker TokenTurnMarker
|
||||
}
|
||||
|
||||
func (t *Token) Query(data *InsertId[string]) {
|
||||
data.query = `
|
||||
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)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
|
||||
RETURNING id`
|
||||
}
|
||||
|
||||
func (t *Token) InsertObjects(tx *sqlx.Tx) error {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
relId := InsertId[uint]{id: t.ID, fieldName: "token_id"}
|
||||
InsertWithCtxParallel(group, tx, t.Ring, relId)
|
||||
InsertWithCtxParallel(group, tx, t.Sight, relId)
|
||||
InsertWithCtxParallel(group, tx, t.Texture, relId)
|
||||
InsertWithCtxParallel(group, tx, t.Bar1, InsertId[uint]{id: t.ID, fieldName: "token_id", tableName: "token_bar_1"})
|
||||
InsertWithCtxParallel(group, tx, t.Bar1, InsertId[uint]{id: t.ID, fieldName: "token_id", tableName: "token_bar_2"})
|
||||
InsertWithCtxParallel(group, tx, t.Light, relId)
|
||||
InsertWithCtxParallel(group, tx, t.Occludable, relId)
|
||||
InsertWithCtxParallel(group, tx, t.TurnMarker, relId)
|
||||
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Token) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, t.Name, t.ActorLink, t.AppendNumber, t.PrependAdjective, t.LockRotation, t.RandomImg,
|
||||
t.DisplayName, t.DisplayBars, t.Disposition, t.Rotation, t.Alpha, t.Width, t.Height}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return t.InsertObjects(tx)
|
||||
}
|
||||
|
||||
func (t *Token) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, t.Name, t.ActorLink, t.AppendNumber, t.PrependAdjective, t.LockRotation, t.RandomImg,
|
||||
t.DisplayName, t.DisplayBars, t.Disposition, t.Rotation, t.Alpha, t.Width, t.Height}
|
||||
|
||||
mutex := GetMutex("token_insert")
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return t.InsertObjects(tx)
|
||||
}
|
||||
|
||||
type TokenTexture struct {
|
||||
ID uint
|
||||
|
||||
Src string
|
||||
Fit string
|
||||
Tint string
|
||||
ScaleX float64
|
||||
ScaleY float64
|
||||
OffsetX float64
|
||||
OffsetY float64
|
||||
Rotation float64
|
||||
AnchorX float64
|
||||
AnchorY float64
|
||||
AlphaThreshold float64
|
||||
}
|
||||
|
||||
func (t *TokenTexture) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO token_texture (%s, src, fit, tint, scale_x, scale_y, offset_x, offset_y, rotation, anchor_x, anchor_y, alpha_threshold)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (t *TokenTexture) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, t.Src, t.Fit, t.Tint, t.ScaleX, t.ScaleY, t.OffsetX, t.OffsetY, t.Rotation, t.AnchorX, t.AnchorY, t.AlphaThreshold}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TokenTexture) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, t.Src, t.Fit, t.Tint, t.ScaleX, t.ScaleY, t.OffsetX, t.OffsetY, t.Rotation, t.AnchorX, t.AnchorY, t.AlphaThreshold}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type TokenSight struct {
|
||||
ID uint
|
||||
|
||||
Color string
|
||||
VisionMode string
|
||||
Range int
|
||||
Angle int
|
||||
Attenuation float64
|
||||
Brightness float64
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
func (t *TokenSight) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO token_sight (%s, color, vision_mode, range_, angle, attenuation, brightness, enabled)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (t *TokenSight) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, t.Color, t.VisionMode, t.Range, t.Angle, t.Attenuation, t.Brightness, t.Enabled}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TokenSight) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, t.Color, t.VisionMode, t.Range, t.Angle, t.Attenuation, t.Brightness, t.Enabled}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type TokenBar struct {
|
||||
ID uint
|
||||
|
||||
Attribute string
|
||||
}
|
||||
|
||||
func (t TokenBar) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO %s (%s, attribute)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id`, data.tableName, data.fieldName)
|
||||
}
|
||||
|
||||
func (t TokenBar) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, t.Attribute}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t TokenBar) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, t.Attribute}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type TokenOccludable struct {
|
||||
ID uint
|
||||
|
||||
Radius int
|
||||
}
|
||||
|
||||
func (t TokenOccludable) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO token_occludable (%s, radius)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (t TokenOccludable) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, t.Radius}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t TokenOccludable) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, t.Radius}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type TokenTurnMarker struct {
|
||||
ID uint
|
||||
|
||||
Mode int
|
||||
Animation string
|
||||
Src string
|
||||
Disposition bool
|
||||
}
|
||||
|
||||
func (t TokenTurnMarker) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO token_turn_maker (%s, mode, animation, src, disposition)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (t TokenTurnMarker) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, t.Mode, t.Animation, t.Src, t.Disposition}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t TokenTurnMarker) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, t.Mode, t.Animation, t.Src, t.Disposition}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
101
internal/foundry/models/db/update.go
Normal file
101
internal/foundry/models/db/update.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type CoreUpdate struct {
|
||||
ID uint
|
||||
|
||||
HasUpdate bool
|
||||
CanUpdate bool
|
||||
CouldReachWebsite bool
|
||||
SlowResponse bool
|
||||
WillDisableModules bool
|
||||
Version string
|
||||
Channel string
|
||||
}
|
||||
|
||||
func (c *CoreUpdate) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO core_update (%s, has_update, can_update, could_reach_website, slow_response, will_disable_modules, version, channel)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (c *CoreUpdate) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, c.HasUpdate, c.CanUpdate, c.CouldReachWebsite, c.SlowResponse, c.WillDisableModules, c.Version, c.Channel}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&c.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CoreUpdate) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, c.HasUpdate, c.CanUpdate, c.CouldReachWebsite, c.SlowResponse, c.WillDisableModules, c.Version, c.Channel}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&c.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type SystemUpdate struct {
|
||||
ID uint
|
||||
|
||||
HasUpdate bool
|
||||
Version string
|
||||
}
|
||||
|
||||
func (s *SystemUpdate) Query(data *InsertId[uint]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO system_update (%s, has_update, version)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING id`, data.fieldName)
|
||||
}
|
||||
|
||||
func (s *SystemUpdate) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, s.HasUpdate, s.Version}
|
||||
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&s.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SystemUpdate) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
args := []any{data.id, s.HasUpdate, s.Version}
|
||||
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&s.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -4,293 +4,125 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Id int64
|
||||
ID string
|
||||
|
||||
Name string
|
||||
Role int
|
||||
Avatar string
|
||||
Character string
|
||||
Color string
|
||||
Pronouns string
|
||||
CreatedAt time.Time
|
||||
Hotbar map[string]string
|
||||
Stats UserStats
|
||||
Role int
|
||||
Stats Stats
|
||||
Hotbar []UserHotbar
|
||||
}
|
||||
|
||||
type UserStats struct {
|
||||
Id int64
|
||||
CoreVersion string
|
||||
SystemId string
|
||||
SystemVersion string
|
||||
CreatedTime int64
|
||||
ModifiedTime int64
|
||||
LastModifiedBy string
|
||||
func (u *User) Query(data *InsertId[uint]) {
|
||||
data.query = `
|
||||
INSERT INTO user (game_id, id, name, avatar, character, color, pronouns, role)
|
||||
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`
|
||||
}
|
||||
|
||||
type Users []User
|
||||
func (u *User) InsertObjects(tx *sqlx.Tx) error {
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
func (users Users) GetById(id int) *User {
|
||||
return &users[id]
|
||||
relId := InsertId[string]{id: u.ID, fieldName: "user_id"}
|
||||
InsertWithCtxParallel(group, 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 {
|
||||
query := `
|
||||
INSERT INTO users (state_id, name, role, character, color, pronouns)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING id, created_at`
|
||||
func (u *User) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
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)
|
||||
defer cancel()
|
||||
|
||||
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&user.Id, &user.CreatedAt)
|
||||
var isInserted bool
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for k, v := range user.Hotbar {
|
||||
err = m.InsertUserHotbar(k, v, user.Id)
|
||||
if isInserted {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return m.InsertUserStats(&user.Stats, user.Id)
|
||||
if isInserted {
|
||||
return u.InsertObjects(tx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m FoundryStateModel) InsertUserHotbar(key string, value string, userId int64) error {
|
||||
query := `
|
||||
INSERT INTO users_hotbar (user_id, key, value)
|
||||
type UserHotbar struct {
|
||||
ID uint
|
||||
|
||||
Key int
|
||||
Value string
|
||||
}
|
||||
|
||||
func (u UserHotbar) Query(data *InsertId[string]) {
|
||||
data.query = `
|
||||
INSERT INTO hotbar (user_id, key_, value)
|
||||
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 {
|
||||
query := `
|
||||
INSERT INTO users_stats (user_id, core_version, system_id, system_version, created_time, modified_time, last_modified_by)
|
||||
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) {
|
||||
if idState < 1 {
|
||||
return nil, ErrorRecordNotFound
|
||||
func (u UserHotbar) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
query := `
|
||||
SELECT id, created_at, name, role, character, color, pronouns
|
||||
FROM users
|
||||
WHERE state_id = $1`
|
||||
args := []any{data.id, u.Key, u.Value}
|
||||
|
||||
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)
|
||||
err := tx.QueryRowx(data.query, args...).Scan(&u.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) DeleteUser(idUser int64) error {
|
||||
if idUser < 1 {
|
||||
return ErrorRecordNotFound
|
||||
func (u UserHotbar) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
query := `
|
||||
DELETE FROM users
|
||||
WHERE id = $1`
|
||||
args := []any{data.id, u.Key, u.Value}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result, err := m.DB.ExecContext(ctx, query, idUser)
|
||||
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&u.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return ErrorRecordNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
248
internal/foundry/models/db/utils.go
Normal file
248
internal/foundry/models/db/utils.go
Normal file
@@ -0,0 +1,248 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/mattn/go-sqlite3"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoQuery = errors.New("Query has not been set")
|
||||
ErrorRecordNotFound = errors.New("Record not found")
|
||||
)
|
||||
|
||||
type AllowedIds interface {
|
||||
~uint | ~string
|
||||
}
|
||||
|
||||
type InsertId[T AllowedIds] struct {
|
||||
id T
|
||||
fieldName string
|
||||
tableName string
|
||||
query string
|
||||
}
|
||||
|
||||
type Insertable[T AllowedIds] interface {
|
||||
Query(data *InsertId[T])
|
||||
Insert(tx *sqlx.Tx, relId *InsertId[T]) error
|
||||
InsertCtx(ctx context.Context, tx *sqlx.Tx, 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(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
data.Query(&relId)
|
||||
return data.InsertCtx(ctx, tx, &relId)
|
||||
}
|
||||
|
||||
func InsertWithCtxParallel[T AllowedIds, I Insertable[T]](g *errgroup.Group, tx *sqlx.Tx, data I, relId InsertId[T]) {
|
||||
g.Go(func() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
data.Query(&relId)
|
||||
err := data.InsertCtx(ctx, tx, &relId)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func InsertSlice[T AllowedIds, I Insertable[T]](tx *sqlx.Tx, data []I, relId InsertId[T]) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if len(data) > 0 {
|
||||
data[0].Query(&relId)
|
||||
}
|
||||
for i := range data {
|
||||
err := data[i].InsertCtx(ctx, tx, &relId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
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]) {
|
||||
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(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err = data[i].InsertCtx(ctx, tx, &relId)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
return wg.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
func InsertSimpleSlice[T AllowedIds, I any](tx *sqlx.Tx, data []I, relId *InsertId[T]) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
INSERT INTO %s (%s, value)
|
||||
VALUES ($1, $2)`, relId.tableName, relId.fieldName)
|
||||
for i := range data {
|
||||
_, err := tx.ExecContext(ctx, query, relId.id, data[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func InsertSimpleSliceParallel[T AllowedIds, I any](g *errgroup.Group, tx *sqlx.Tx, data []I, relId *InsertId[T]) {
|
||||
g.Go(func() error {
|
||||
wg, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
INSERT INTO %s (%s, value)
|
||||
VALUES ($1, $2)`, relId.tableName, relId.fieldName)
|
||||
for i := range data {
|
||||
wg.Go(func() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_, err := tx.ExecContext(ctx, query, relId.id, data[i])
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
return wg.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteSetupAll(db *sqlx.DB) error {
|
||||
const query = `DELETE FROM setup`
|
||||
|
||||
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()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return ErrorRecordNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteGameAll(db *sqlx.DB) error {
|
||||
const query = `DELETE FROM game`
|
||||
|
||||
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()
|
||||
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()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return ErrorRecordNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsErrUniqueConstraint(err error) {
|
||||
if sqliteErr, ok := err.(sqlite3.Error); ok {
|
||||
// SQLITE_CONSTRAINT_UNIQUE (extended code 2067)
|
||||
// or SQLITE_CONSTRAINT (basic code 19)
|
||||
if sqliteErr.ExtendedCode == sqlite3.ErrConstraintUnique ||
|
||||
sqliteErr.Code == sqlite3.ErrConstraint {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var modelInsertLock sync.Map
|
||||
|
||||
func GetMutex(id string) *sync.Mutex {
|
||||
val, _ := modelInsertLock.LoadOrStore(id, &sync.Mutex{})
|
||||
|
||||
return val.(*sync.Mutex)
|
||||
}
|
||||
@@ -4,231 +4,179 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type World struct {
|
||||
Id int64
|
||||
TextId string
|
||||
ID string
|
||||
|
||||
Title string
|
||||
Description string
|
||||
Version string
|
||||
System string
|
||||
Background string
|
||||
JoinTheme string
|
||||
CoreVersion string
|
||||
SystemVersion string
|
||||
LastPlayed string
|
||||
PlayTime int64
|
||||
Playtime int
|
||||
Availability int
|
||||
NextSession time.Time
|
||||
CreatedAt time.Time
|
||||
Socket bool
|
||||
Protected bool
|
||||
Exclusive bool
|
||||
PersistentStorage bool
|
||||
Locked bool
|
||||
Owned bool
|
||||
HasStorage bool
|
||||
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 (worlds Worlds) GetById(id int) *World {
|
||||
return &worlds[id]
|
||||
func (w *World) Query(data *InsertId[string]) {
|
||||
data.query = fmt.Sprintf(`
|
||||
INSERT INTO world (%[1]s, 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)
|
||||
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 {
|
||||
query := `
|
||||
INSERT INTO worlds (state_id, text_id, title, description, system, core_version, system_version, playtime, next_session)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
RETURNING id, created_at`
|
||||
func (w *World) InsertObjects(tx *sqlx.Tx) error {
|
||||
relId := InsertId[string]{id: w.ID, fieldName: "world_id"}
|
||||
group, _ := errgroup.WithContext(context.Background())
|
||||
|
||||
args := []any{stateId, world.TextId, world.Title, world.Description, world.System, world.CoreVersion, world.SystemVersion, world.PlayTime, world.NextSession}
|
||||
InsertWithCtxParallel(group, tx, w.Compatibility, relId)
|
||||
InsertWithCtxParallel(group, tx, w.Relationships, relId)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
esModulesRelId := &InsertId[string]{id: w.ID, fieldName: "world_id", tableName: "es_modules"}
|
||||
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)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return err
|
||||
}
|
||||
return m.InsertWorldCompatibility(&world.Compatibility, world.Id)
|
||||
}
|
||||
InsertSliceParallel(group, tx, w.Authors, relId)
|
||||
InsertSliceParallel(group, tx, w.Media, relId)
|
||||
InsertSliceParallel(group, tx, w.Styles, relId)
|
||||
InsertSliceParallel(group, tx, w.Languages, relId)
|
||||
InsertSliceParallel(group, tx, w.Packs, relId)
|
||||
InsertSliceParallel(group, tx, w.PackFolders, relId)
|
||||
|
||||
func (m FoundryStateModel) InsertWorldCompatibility(compatibility *Compatibility, worldId int64) error {
|
||||
query := `
|
||||
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 {
|
||||
err := group.Wait()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m FoundryStateModel) GetWorlds(idState int64) (Worlds, error) {
|
||||
if idState < 1 {
|
||||
return nil, ErrorRecordNotFound
|
||||
func (w *World) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
|
||||
if data.query == "" {
|
||||
return ErrNoQuery
|
||||
}
|
||||
|
||||
query := `
|
||||
SELECT id, created_at, text_id, title, description, system, core_version, system_version, playtime, next_session
|
||||
FROM worlds
|
||||
WHERE state_id = $1`
|
||||
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.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)
|
||||
defer cancel()
|
||||
|
||||
rows, err := m.DB.QueryContext(ctx, query, idState)
|
||||
var worldNames []string
|
||||
err := db.SelectContext(ctx, &worldNames, query)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, sql.ErrNoRows):
|
||||
return nil, ErrorRecordNotFound
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
return worldNames, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
worlds := make(Worlds, 0, 1)
|
||||
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
|
||||
err := rows.Scan(
|
||||
&world.Id,
|
||||
&world.CreatedAt,
|
||||
&world.TextId,
|
||||
&world.Title,
|
||||
&world.Description,
|
||||
&world.System,
|
||||
&world.CoreVersion,
|
||||
&world.SystemVersion,
|
||||
&world.PlayTime,
|
||||
&world.NextSession,
|
||||
err := db.QueryRowxContext(ctx, query, worldName).Scan(
|
||||
&world.ID, &world.Title, &world.Description, &world.Version, &world.System, &world.Background, &world.JoinTheme,
|
||||
&world.CoreVersion, &world.SystemVersion, &world.LastPlayed, &world.Playtime, &world.Availability, &world.NextSession,
|
||||
&world.Socket, &world.Protected, &world.Exclusive, &world.PersistentStorage, &world.Locked, &world.Owned, &world.HasStorage,
|
||||
)
|
||||
if err != nil {
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, err
|
||||
}
|
||||
compatibility, err := m.GetWorldsCompatibility(world.Id)
|
||||
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
|
||||
return &world, 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
|
||||
|
||||
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 {
|
||||
PrototypeToken Token `json:"prototypeToken"`
|
||||
@@ -18,24 +18,28 @@ type Actor struct {
|
||||
// ActorsEffects []any `json:"effects"`
|
||||
}
|
||||
|
||||
func (a *Actor) ToDB(dest *db.Actor) bool {
|
||||
func (a *Actor) ToDB(dest **db.Actor) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.Img = a.Img
|
||||
dest.Name = a.Name
|
||||
dest.Type = a.Type
|
||||
dest.Folder = a.Folder
|
||||
dest.Sort = a.Sort
|
||||
dest.ID = a.ID
|
||||
actor := &db.Actor{
|
||||
Img: a.Img,
|
||||
Name: a.Name,
|
||||
Type: a.Type,
|
||||
Folder: a.Folder,
|
||||
Sort: a.Sort,
|
||||
ID: a.ID,
|
||||
}
|
||||
|
||||
a.PrototypeToken.ToDB(&dest.PrototypeToken)
|
||||
a.Stats.ToDB(&dest.Stats)
|
||||
a.PrototypeToken.ToDB(&actor.PrototypeToken)
|
||||
a.Stats.ToDB(&actor.Stats)
|
||||
|
||||
OwnershipToDB(&dest.Ownership, a.Ownership)
|
||||
OwnershipToDB(&actor.Ownership, a.Ownership)
|
||||
|
||||
CopySliceToDB(&dest.Items, a.Items)
|
||||
CopySliceToDB(&actor.Items, a.Items)
|
||||
|
||||
*dest = actor
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
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 {
|
||||
Local string `json:"local"`
|
||||
@@ -1,6 +1,6 @@
|
||||
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 {
|
||||
Name string `json:"name"`
|
||||
@@ -10,15 +10,19 @@ type Author struct {
|
||||
// SystemAuthorsFlags SystemAuthorsFlags `json:"flags"`
|
||||
}
|
||||
|
||||
func (a *Author) ToDB(dest *db.Author) bool {
|
||||
func (a *Author) ToDB(dest **db.Author) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.Name = a.Name
|
||||
dest.URL = a.URL
|
||||
dest.Email = a.Email
|
||||
dest.Discord = a.Discord
|
||||
author := &db.Author{
|
||||
Name: a.Name,
|
||||
URL: a.URL,
|
||||
Email: a.Email,
|
||||
Discord: a.Discord,
|
||||
}
|
||||
|
||||
*dest = author
|
||||
|
||||
return true
|
||||
}
|
||||
138
internal/foundry/models/json/card.go
Normal file
138
internal/foundry/models/json/card.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package json
|
||||
|
||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||
|
||||
type CardDeck struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
Img string `json:"img"`
|
||||
Cards []*Card `json:"cards"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
Rotation int `json:"rotation"`
|
||||
DisplayCount bool `json:"displayCount"`
|
||||
Stats Stats `json:"_stats"`
|
||||
Ownership map[string]int `json:"ownership,omitempty"`
|
||||
Folder string `json:"folder"`
|
||||
Sort int `json:"sort"`
|
||||
ID string `json:"_id"`
|
||||
// Flags any `json:"flags"`
|
||||
// Cards0System any `json:"system"`
|
||||
}
|
||||
|
||||
func (c *CardDeck) ToDB(dest **db.CardDeck) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
cardDeck := &db.CardDeck{
|
||||
Name: c.Name,
|
||||
Type: c.Type,
|
||||
Description: c.Description,
|
||||
Img: c.Img,
|
||||
Width: c.Width,
|
||||
Height: c.Height,
|
||||
Rotation: c.Rotation,
|
||||
DisplayCount: c.DisplayCount,
|
||||
Folder: c.Folder,
|
||||
Sort: c.Sort,
|
||||
ID: c.ID,
|
||||
}
|
||||
|
||||
c.Stats.ToDB(&cardDeck.Stats)
|
||||
|
||||
OwnershipToDB(&cardDeck.Ownership, c.Ownership)
|
||||
CopySliceToDB(&cardDeck.Cards, c.Cards)
|
||||
|
||||
*dest = cardDeck
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type Card struct {
|
||||
Name string `json:"name"`
|
||||
Faces []*Face `json:"faces"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
Rotation int `json:"rotation"`
|
||||
Type string `json:"type"`
|
||||
Value int `json:"value"`
|
||||
Suit string `json:"suit"`
|
||||
Description string `json:"description"`
|
||||
Face int `json:"face"`
|
||||
Drawn bool `json:"drawn"`
|
||||
Sort int `json:"sort"`
|
||||
Back Back `json:"back"`
|
||||
Origin string `json:"origin"`
|
||||
ID string `json:"_id"`
|
||||
Stats Stats `json:"_stats"`
|
||||
// Flags any `json:"flags"`
|
||||
// System any `json:"system"`
|
||||
}
|
||||
|
||||
func (c *Card) ToDB(dest **db.Card) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
card := &db.Card{
|
||||
Name: c.Name,
|
||||
Width: c.Width,
|
||||
Height: c.Height,
|
||||
Rotation: c.Rotation,
|
||||
Type: c.Type,
|
||||
Value: c.Value,
|
||||
Suit: c.Suit,
|
||||
Description: c.Description,
|
||||
Face: c.Face,
|
||||
Drawn: c.Drawn,
|
||||
Origin: c.Origin,
|
||||
ID: c.ID,
|
||||
Sort: c.Sort,
|
||||
}
|
||||
|
||||
c.Back.ToDB(&card.Back)
|
||||
c.Stats.ToDB(&card.Stats)
|
||||
|
||||
CopySliceToDB(&card.Faces, c.Faces)
|
||||
|
||||
*dest = card
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type Face struct {
|
||||
Name string `json:"name"`
|
||||
Img string `json:"img"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
func (f *Face) ToDB(dest *db.Face) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.Name = f.Name
|
||||
dest.Img = f.Img
|
||||
dest.Text = f.Text
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type Back struct {
|
||||
Img any `json:"img"`
|
||||
Name string `json:"name"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
func (b *Back) ToDB(dest *db.Back) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.Name = b.Name
|
||||
dest.Text = b.Text
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
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 {
|
||||
Id string `json:"_id"`
|
||||
@@ -17,23 +17,28 @@ type Combat struct {
|
||||
// Flags any `json:"flags"`
|
||||
}
|
||||
|
||||
func (c *Combat) ToDB(dest *db.Combat) bool {
|
||||
func (c *Combat) ToDB(dest **db.Combat) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.ID = c.Id
|
||||
dest.Type = c.Type
|
||||
dest.Scene = c.Scene
|
||||
dest.Active = c.Active
|
||||
dest.Round = c.Round
|
||||
dest.Turn = c.Turn
|
||||
dest.Sort = c.Sort
|
||||
combat := &db.Combat{
|
||||
ID: c.Id,
|
||||
Type: c.Type,
|
||||
Scene: c.Scene,
|
||||
Active: c.Active,
|
||||
Round: c.Round,
|
||||
Turn: c.Turn,
|
||||
Sort: c.Sort,
|
||||
}
|
||||
|
||||
c.Stats.ToDB(&dest.Stats)
|
||||
c.Stats.ToDB(&combat.Stats)
|
||||
|
||||
copy(dest.Groups, c.Groups)
|
||||
CopySliceToDB(&dest.Combatants, c.Combatants)
|
||||
combat.Groups = make([]string, len(c.Groups))
|
||||
copy(combat.Groups, c.Groups)
|
||||
CopySliceToDB(&combat.Combatants, c.Combatants)
|
||||
|
||||
*dest = combat
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -54,22 +59,27 @@ type Combatant struct {
|
||||
// Flags any `json:"flags"`
|
||||
}
|
||||
|
||||
func (c *Combatant) ToDB(dest *db.Combatant) bool {
|
||||
func (c *Combatant) ToDB(dest **db.Combatant) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.TokenId = c.TokenId
|
||||
dest.SceneId = c.SceneId
|
||||
dest.ActorId = c.ActorId
|
||||
dest.Hidden = c.Hidden
|
||||
dest.ID = c.Id
|
||||
dest.Type = c.Type
|
||||
dest.Img = c.Img
|
||||
dest.Initiative = c.Initiative
|
||||
dest.Defeated = c.Defeated
|
||||
dest.Group = c.Group
|
||||
c.Stats.ToDB(&dest.Stats)
|
||||
combatant := &db.Combatant{
|
||||
TokenId: c.TokenId,
|
||||
SceneId: c.SceneId,
|
||||
ActorId: c.ActorId,
|
||||
Hidden: c.Hidden,
|
||||
ID: c.Id,
|
||||
Type: c.Type,
|
||||
Img: c.Img,
|
||||
Initiative: c.Initiative,
|
||||
Defeated: c.Defeated,
|
||||
Group: c.Group,
|
||||
}
|
||||
|
||||
c.Stats.ToDB(&combatant.Stats)
|
||||
|
||||
*dest = combatant
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
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 {
|
||||
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
|
||||
|
||||
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 {
|
||||
Details string `json:"details"`
|
||||
@@ -1,6 +1,6 @@
|
||||
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 {
|
||||
Actor DocumentTypeData `json:"Actor"`
|
||||
@@ -13,13 +13,13 @@ func (d *DocumentTypes) ToDB(dest *db.DocumentTypes) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.Data = make([]db.DocumentTypeData, 2)
|
||||
dest.Data = make([]*db.DocumentTypeData, 2)
|
||||
|
||||
d.Actor.ToDB(&dest.Data[0])
|
||||
dest.Data[0].Type = "Actor"
|
||||
dest.Data[0] = &db.DocumentTypeData{Type: "Actor"}
|
||||
d.Actor.ToDB(dest.Data[0])
|
||||
|
||||
d.Item.ToDB(&dest.Data[1])
|
||||
dest.Data[1].Type = "Item"
|
||||
dest.Data[1] = &db.DocumentTypeData{Type: "Item"}
|
||||
d.Item.ToDB(dest.Data[1])
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
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 {
|
||||
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
|
||||
|
||||
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 {
|
||||
Storages []string `json:"storages"`
|
||||
@@ -12,7 +12,10 @@ func (f *Files) ToDB(dest *db.Files) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
copy(dest.Storages, f.Storages)
|
||||
dest.Storages = make([]db.FilesStorage, len(f.Storages))
|
||||
for i := range f.Storages {
|
||||
dest.Storages[i].Storage = f.Storages[i]
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
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 {
|
||||
Name string `json:"name"`
|
||||
@@ -10,17 +10,22 @@ type Folder struct {
|
||||
Folders []*Folder `json:"folders,omitempty"`
|
||||
}
|
||||
|
||||
func (f *Folder) ToDB(dest *db.Folder) bool {
|
||||
func (f *Folder) ToDB(dest **db.Folder) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.Name = f.Name
|
||||
dest.Sorting = f.Sorting
|
||||
dest.Color = f.Color
|
||||
copy(dest.Packs, f.Packs)
|
||||
folder := &db.Folder{
|
||||
Name: f.Name,
|
||||
Sorting: f.Sorting,
|
||||
Color: f.Color,
|
||||
}
|
||||
|
||||
CopySliceToDB(&dest.Folders, f.Folders)
|
||||
folder.Packs = make([]string, len(f.Packs))
|
||||
copy(folder.Packs, f.Packs)
|
||||
CopySliceToDB(&folder.Folders, f.Folders)
|
||||
|
||||
*dest = folder
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -38,20 +43,25 @@ type WorldFolder struct {
|
||||
// Flags any `json:"flags,omitempty"`
|
||||
}
|
||||
|
||||
func (w *WorldFolder) ToDB(dest *db.WorldFolder) bool {
|
||||
func (w *WorldFolder) ToDB(dest **db.WorldFolder) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.Name = w.Name
|
||||
dest.Type = w.Type
|
||||
dest.ID = w.ID
|
||||
dest.Folder = w.Folder
|
||||
dest.Sorting = w.Sorting
|
||||
dest.Sort = w.Sort
|
||||
w.Stats.ToDB(&dest.Stats)
|
||||
dest.Description = w.Description
|
||||
dest.Color = w.Color
|
||||
worldFolder := &db.WorldFolder{
|
||||
Name: w.Name,
|
||||
Type: w.Type,
|
||||
ID: w.ID,
|
||||
Folder: w.Folder,
|
||||
Sorting: w.Sorting,
|
||||
Sort: w.Sort,
|
||||
Description: w.Description,
|
||||
Color: w.Color,
|
||||
}
|
||||
|
||||
w.Stats.ToDB(&worldFolder.Stats)
|
||||
|
||||
*dest = worldFolder
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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 {
|
||||
@@ -40,6 +41,19 @@ type Game struct {
|
||||
// Template Template `json:"template"`
|
||||
}
|
||||
|
||||
func ParseGame(data []byte) (*Game, error) {
|
||||
var modelGame []Game
|
||||
err := json.Unmarshal(data, &modelGame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(modelGame) > 1 {
|
||||
return nil, ErrorSetupMoreThanOne
|
||||
}
|
||||
return &modelGame[0], nil
|
||||
}
|
||||
|
||||
func (g *Game) ToDB(dest *db.Game) bool {
|
||||
dest.UserID = g.UserID
|
||||
dest.DemoMode = g.DemoMode
|
||||
@@ -55,23 +69,25 @@ func (g *Game) ToDB(dest *db.Game) bool {
|
||||
g.Options.ToDB(&dest.Options)
|
||||
g.CoreUpdate.ToDB(&dest.CoreUpdate)
|
||||
g.SystemUpdate.ToDB(&dest.SystemUpdate)
|
||||
|
||||
dest.ActiveUsers = make([]string, len(g.ActiveUsers))
|
||||
copy(dest.ActiveUsers, g.ActiveUsers)
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
go CopySliceToDBParallel(&wg, &dest.Modules, g.Modules)
|
||||
go CopySliceToDBParallel(&wg, &dest.Packs, g.Packs)
|
||||
go CopySliceToDBParallel(&wg, &dest.Messages, g.Messages)
|
||||
go CopySliceToDBParallel(&wg, &dest.Combats, g.Combats)
|
||||
go CopySliceToDBParallel(&wg, &dest.CardDeck, g.CardDeck)
|
||||
go CopySliceToDBParallel(&wg, &dest.Users, g.Users)
|
||||
go CopySliceToDBParallel(&wg, &dest.Macros, g.Macros)
|
||||
go CopySliceToDBParallel(&wg, &dest.Folders, g.Folders)
|
||||
go CopySliceToDBParallel(&wg, &dest.Items, g.Items)
|
||||
go CopySliceToDBParallel(&wg, &dest.Settings, g.Settings)
|
||||
go CopySliceToDBParallel(&wg, &dest.Journals, g.Journals)
|
||||
go CopySliceToDBParallel(&wg, &dest.Tables, g.Tables)
|
||||
go CopySliceToDBParallel(&wg, &dest.Playlists, g.Playlists)
|
||||
go CopySliceToDBParallel(&wg, &dest.Actors, g.Actors)
|
||||
CopySliceToDBParallel(&wg, &dest.Modules, g.Modules)
|
||||
CopySliceToDBParallel(&wg, &dest.Packs, g.Packs)
|
||||
CopySliceToDBParallel(&wg, &dest.Messages, g.Messages)
|
||||
CopySliceToDBParallel(&wg, &dest.Combats, g.Combats)
|
||||
CopySliceToDBParallel(&wg, &dest.CardDeck, g.CardDeck)
|
||||
CopySliceToDBParallel(&wg, &dest.Users, g.Users)
|
||||
CopySliceToDBParallel(&wg, &dest.Macros, g.Macros)
|
||||
CopySliceToDBParallel(&wg, &dest.Folders, g.Folders)
|
||||
CopySliceToDBParallel(&wg, &dest.Items, g.Items)
|
||||
CopySliceToDBParallel(&wg, &dest.Settings, g.Settings)
|
||||
CopySliceToDBParallel(&wg, &dest.Journals, g.Journals)
|
||||
CopySliceToDBParallel(&wg, &dest.Tables, g.Tables)
|
||||
CopySliceToDBParallel(&wg, &dest.Playlists, g.Playlists)
|
||||
CopySliceToDBParallel(&wg, &dest.Actors, g.Actors)
|
||||
// go CopySliceToDBParallel(&wg, &dest.Scenes, g.Scenes)
|
||||
wg.Wait()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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 int `json:"type"`
|
||||
@@ -14,20 +14,24 @@ type Grid struct {
|
||||
Thickness int `json:"thickness,omitempty"`
|
||||
}
|
||||
|
||||
func (g *Grid) ToDB(dest *db.Grid) bool {
|
||||
func (g *Grid) ToDB(dest **db.Grid) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.Type = g.Type
|
||||
dest.Size = g.Size
|
||||
dest.Color = g.Color
|
||||
dest.Alpha = g.Alpha
|
||||
dest.Distance = g.Distance
|
||||
dest.Units = g.Units
|
||||
dest.Diagonals = g.Diagonals
|
||||
dest.Style = g.Style
|
||||
dest.Thickness = g.Thickness
|
||||
grid := &db.Grid{
|
||||
Type: g.Type,
|
||||
Size: g.Size,
|
||||
Color: g.Color,
|
||||
Alpha: g.Alpha,
|
||||
Distance: g.Distance,
|
||||
Units: g.Units,
|
||||
Diagonals: g.Diagonals,
|
||||
Style: g.Style,
|
||||
Thickness: g.Thickness,
|
||||
}
|
||||
|
||||
*dest = grid
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
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 {
|
||||
Id string `json:"_id"`
|
||||
@@ -10,16 +10,20 @@ type Index struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
func (i *Index) ToDB(dest *db.Index) bool {
|
||||
func (i *Index) ToDB(dest **db.Index) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.ID = i.Id
|
||||
dest.Folder = i.Folder
|
||||
dest.Img = i.Img
|
||||
dest.Name = i.Name
|
||||
dest.Type = i.Type
|
||||
index := &db.Index{
|
||||
ID: i.Id,
|
||||
Folder: i.Folder,
|
||||
Img: i.Img,
|
||||
Name: i.Name,
|
||||
Type: i.Type,
|
||||
}
|
||||
|
||||
*dest = index
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
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 {
|
||||
Img string `json:"img"`
|
||||
@@ -16,19 +16,23 @@ type Item struct {
|
||||
// Effects []any `json:"effects"`
|
||||
}
|
||||
|
||||
func (i *Item) ToDB(dest *db.Item) bool {
|
||||
func (i *Item) ToDB(dest **db.Item) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.Img = i.Img
|
||||
dest.Name = i.Name
|
||||
dest.Type = i.Type
|
||||
dest.Folder = i.Folder
|
||||
dest.ID = i.ID
|
||||
dest.Sort = i.Sort
|
||||
item := &db.Item{
|
||||
Img: i.Img,
|
||||
Name: i.Name,
|
||||
Type: i.Type,
|
||||
Folder: i.Folder,
|
||||
ID: i.ID,
|
||||
Sort: i.Sort,
|
||||
}
|
||||
|
||||
i.Stats.ToDB(&dest.Stats)
|
||||
i.Stats.ToDB(&item.Stats)
|
||||
|
||||
*dest = item
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
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 {
|
||||
Folder any `json:"folder"`
|
||||
@@ -14,17 +14,21 @@ type Journal struct {
|
||||
// Categories []any `json:"categories"`
|
||||
}
|
||||
|
||||
func (j *Journal) ToDB(dest *db.Journal) bool {
|
||||
func (j *Journal) ToDB(dest **db.Journal) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.Name = j.Name
|
||||
dest.Sort = j.Sort
|
||||
dest.ID = j.ID
|
||||
journal := &db.Journal{
|
||||
Name: j.Name,
|
||||
Sort: j.Sort,
|
||||
ID: j.ID,
|
||||
}
|
||||
|
||||
OwnershipToDB(&dest.Ownership, j.Ownership)
|
||||
CopySliceToDB(&dest.Pages, j.Pages)
|
||||
OwnershipToDB(&journal.Ownership, j.Ownership)
|
||||
CopySliceToDB(&journal.Pages, j.Pages)
|
||||
|
||||
*dest = journal
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
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 {
|
||||
Name string `json:"name"`
|
||||
@@ -19,23 +19,27 @@ type JournalPage struct {
|
||||
// Category any `json:"category"`
|
||||
}
|
||||
|
||||
func (j *JournalPage) ToDB(dest *db.JournalPage) bool {
|
||||
func (j *JournalPage) ToDB(dest **db.JournalPage) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.Name = j.Name
|
||||
dest.Type = j.Type
|
||||
dest.ID = j.ID
|
||||
dest.Src = j.Src
|
||||
dest.Sort = j.Sort
|
||||
journalPage := &db.JournalPage{
|
||||
Name: j.Name,
|
||||
Type: j.Type,
|
||||
ID: j.ID,
|
||||
Src: j.Src,
|
||||
Sort: j.Sort,
|
||||
}
|
||||
|
||||
j.Text.ToDB(&dest.Text)
|
||||
j.Title.ToDB(&dest.Title)
|
||||
j.Video.ToDB(&dest.Video)
|
||||
j.Stats.ToDB(&dest.Stats)
|
||||
j.Text.ToDB(&journalPage.Text)
|
||||
j.Title.ToDB(&journalPage.Title)
|
||||
j.Video.ToDB(&journalPage.Video)
|
||||
j.Stats.ToDB(&journalPage.Stats)
|
||||
|
||||
OwnershipToDB(&dest.Ownership, j.Ownership)
|
||||
OwnershipToDB(&journalPage.Ownership, j.Ownership)
|
||||
|
||||
*dest = journalPage
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,13 +1,67 @@
|
||||
package json
|
||||
|
||||
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
|
||||
|
||||
type Language struct {
|
||||
Id string `json:"id"`
|
||||
Label string `json:"label"`
|
||||
Modules []LangModule `json:"modules"`
|
||||
Lang string `json:"lang"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
// SystemLanguagesFlags SystemLanguagesFlags `json:"flags"`
|
||||
}
|
||||
|
||||
type LangModule struct {
|
||||
Id string `json:"id"`
|
||||
func (l *Language) ToDB(dest **db.Language) bool {
|
||||
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"`
|
||||
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
|
||||
|
||||
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 {
|
||||
Alpha float64 `json:"alpha"`
|
||||
@@ -20,27 +20,31 @@ type Light struct {
|
||||
Color string `json:"color"`
|
||||
}
|
||||
|
||||
func (l *Light) ToDB(dest *db.Light) bool {
|
||||
func (l *Light) ToDB(dest **db.Light) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.Alpha = l.Alpha
|
||||
dest.Angle = l.Angle
|
||||
dest.Bright = l.Bright
|
||||
dest.Coloration = l.Coloration
|
||||
dest.Dim = l.Dim
|
||||
dest.Attenuation = l.Attenuation
|
||||
dest.Luminosity = l.Luminosity
|
||||
dest.Saturation = l.Saturation
|
||||
dest.Contrast = l.Contrast
|
||||
dest.Shadows = l.Shadows
|
||||
dest.Negative = l.Negative
|
||||
dest.Priority = l.Priority
|
||||
dest.Color = l.Color
|
||||
light := &db.Light{
|
||||
Alpha: l.Alpha,
|
||||
Angle: l.Angle,
|
||||
Bright: l.Bright,
|
||||
Coloration: l.Coloration,
|
||||
Dim: l.Dim,
|
||||
Attenuation: l.Attenuation,
|
||||
Luminosity: l.Luminosity,
|
||||
Saturation: l.Saturation,
|
||||
Contrast: l.Contrast,
|
||||
Shadows: l.Shadows,
|
||||
Negative: l.Negative,
|
||||
Priority: l.Priority,
|
||||
Color: l.Color,
|
||||
}
|
||||
|
||||
l.LightAnimation.ToDB(&dest.LightAnimation)
|
||||
l.LightDarkness.ToDB(&dest.LightDarkness)
|
||||
l.LightAnimation.ToDB(&light.LightAnimation)
|
||||
l.LightDarkness.ToDB(&light.LightDarkness)
|
||||
|
||||
*dest = light
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
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 {
|
||||
Command string `json:"command"`
|
||||
@@ -17,24 +17,28 @@ type Macro struct {
|
||||
// Flags any `json:"flags,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Macro) ToDB(dest *db.Macro) bool {
|
||||
func (m *Macro) ToDB(dest **db.Macro) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.Command = m.Command
|
||||
dest.Name = m.Name
|
||||
dest.Type = m.Type
|
||||
dest.Img = m.Img
|
||||
dest.ID = m.ID
|
||||
dest.Author = m.Author
|
||||
dest.Scope = m.Scope
|
||||
dest.Folder = m.Folder
|
||||
dest.Sort = m.Sort
|
||||
macro := &db.Macro{
|
||||
Command: m.Command,
|
||||
Name: m.Name,
|
||||
Type: m.Type,
|
||||
Img: m.Img,
|
||||
ID: m.ID,
|
||||
Author: m.Author,
|
||||
Scope: m.Scope,
|
||||
Folder: m.Folder,
|
||||
Sort: m.Sort,
|
||||
}
|
||||
|
||||
m.Stats.ToDB(&dest.Stats)
|
||||
m.Stats.ToDB(¯o.Stats)
|
||||
|
||||
OwnershipToDB(&dest.Ownership, m.Ownership)
|
||||
OwnershipToDB(¯o.Ownership, m.Ownership)
|
||||
|
||||
*dest = macro
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
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 string `json:"type"`
|
||||
@@ -8,14 +8,18 @@ type Media struct {
|
||||
Caption string `json:"caption"`
|
||||
}
|
||||
|
||||
func (m *Media) ToDB(dest *db.Media) bool {
|
||||
func (m *Media) ToDB(dest **db.Media) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.Type = m.Type
|
||||
dest.URL = m.URL
|
||||
dest.Caption = m.Caption
|
||||
media := &db.Media{
|
||||
Type: m.Type,
|
||||
URL: m.URL,
|
||||
Caption: m.Caption,
|
||||
}
|
||||
|
||||
*dest = media
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
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 {
|
||||
Content string `json:"content"`
|
||||
@@ -21,27 +21,33 @@ type Message struct {
|
||||
// Flags any `json:"flags"`
|
||||
}
|
||||
|
||||
func (m *Message) ToDB(dest *db.Message) bool {
|
||||
func (m *Message) ToDB(dest **db.Message) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.Content = m.Content
|
||||
dest.Style = m.Style
|
||||
dest.Author = m.Author
|
||||
dest.ID = m.Id
|
||||
dest.Type = m.Type
|
||||
dest.Timestamp = m.Timestamp
|
||||
dest.Flavor = m.Flavor
|
||||
dest.Blind = m.Blind
|
||||
dest.Sound = m.Sound
|
||||
dest.Emote = m.Emote
|
||||
message := &db.Message{
|
||||
Content: m.Content,
|
||||
Style: m.Style,
|
||||
Author: m.Author,
|
||||
ID: m.Id,
|
||||
Type: m.Type,
|
||||
Timestamp: m.Timestamp,
|
||||
Flavor: m.Flavor,
|
||||
Blind: m.Blind,
|
||||
Sound: m.Sound,
|
||||
Emote: m.Emote,
|
||||
}
|
||||
|
||||
m.Speaker.ToDB(&dest.Speaker)
|
||||
m.Stats.ToDB(&dest.Stats)
|
||||
m.Speaker.ToDB(&message.Speaker)
|
||||
m.Stats.ToDB(&message.Stats)
|
||||
|
||||
copy(dest.Whisper, m.Whisper)
|
||||
copy(dest.Rolls, m.Rolls)
|
||||
message.Whisper = make([]string, len(m.Whisper))
|
||||
copy(message.Whisper, m.Whisper)
|
||||
message.Rolls = make([]string, len(m.Rolls))
|
||||
copy(message.Rolls, m.Rolls)
|
||||
|
||||
*dest = message
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package json
|
||||
import (
|
||||
"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 {
|
||||
@@ -44,50 +44,57 @@ type Module struct {
|
||||
// ModulesFlags ModulesFlags `json:"flags,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Module) ToDB(dest *db.Module) bool {
|
||||
func (m *Module) ToDB(dest **db.Module) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
dest.ID = m.ID
|
||||
dest.Title = m.Title
|
||||
dest.Description = m.Description
|
||||
dest.URL = m.URL
|
||||
dest.License = m.License
|
||||
dest.Readme = m.Readme
|
||||
dest.Bugs = m.Bugs
|
||||
dest.Changelog = m.Changelog
|
||||
dest.Version = m.Version
|
||||
dest.Socket = m.Socket
|
||||
dest.Manifest = m.Manifest
|
||||
dest.Download = m.Download
|
||||
dest.Protected = m.Protected
|
||||
dest.Exclusive = m.Exclusive
|
||||
dest.PersistentStorage = m.PersistentStorage
|
||||
dest.CoreTranslation = m.CoreTranslation
|
||||
dest.Library = m.Library
|
||||
dest.Availability = m.Availability
|
||||
dest.Locked = m.Locked
|
||||
dest.Owned = m.Owned
|
||||
dest.HasStorage = m.HasStorage
|
||||
dest.Active = m.Active
|
||||
module := &db.Module{
|
||||
ID: m.ID,
|
||||
Title: m.Title,
|
||||
Description: m.Description,
|
||||
URL: m.URL,
|
||||
License: m.License,
|
||||
Readme: m.Readme,
|
||||
Bugs: m.Bugs,
|
||||
Changelog: m.Changelog,
|
||||
Version: m.Version,
|
||||
Socket: m.Socket,
|
||||
Manifest: m.Manifest,
|
||||
Download: m.Download,
|
||||
Protected: m.Protected,
|
||||
Exclusive: m.Exclusive,
|
||||
PersistentStorage: m.PersistentStorage,
|
||||
CoreTranslation: m.CoreTranslation,
|
||||
Library: m.Library,
|
||||
Availability: m.Availability,
|
||||
Locked: m.Locked,
|
||||
Owned: m.Owned,
|
||||
HasStorage: m.HasStorage,
|
||||
Active: m.Active,
|
||||
}
|
||||
|
||||
copy(dest.Scripts, m.Scripts)
|
||||
copy(dest.Esmodules, m.Esmodules)
|
||||
copy(dest.Tags, m.Tags)
|
||||
module.Scripts = make([]string, len(m.Scripts))
|
||||
copy(module.Scripts, m.Scripts)
|
||||
module.Esmodules = make([]string, len(m.Esmodules))
|
||||
copy(module.Esmodules, m.Esmodules)
|
||||
module.Tags = make([]string, len(m.Tags))
|
||||
copy(module.Tags, m.Tags)
|
||||
|
||||
m.Compatibility.ToDB(&dest.Compatibility)
|
||||
m.Relationships.ToDB(&dest.Relationships)
|
||||
m.DocumentTypes.ToDB(&dest.DocumentTypes)
|
||||
m.Compatibility.ToDB(&module.Compatibility)
|
||||
m.Relationships.ToDB(&module.Relationships)
|
||||
m.DocumentTypes.ToDB(&module.DocumentTypes)
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
go CopySliceToDBParallel(&wg, &dest.Authors, m.Authors)
|
||||
go CopySliceToDBParallel(&wg, &dest.Media, m.Media)
|
||||
go CopySliceToDBParallel(&wg, &dest.Styles, m.Styles)
|
||||
go CopySliceToDBParallel(&wg, &dest.Languages, m.Languages)
|
||||
go CopySliceToDBParallel(&wg, &dest.Packs, m.Packs)
|
||||
go CopySliceToDBParallel(&wg, &dest.PackFolders, m.PackFolders)
|
||||
CopySliceToDBParallel(&wg, &module.Authors, m.Authors)
|
||||
CopySliceToDBParallel(&wg, &module.Media, m.Media)
|
||||
CopySliceToDBParallel(&wg, &module.Styles, m.Styles)
|
||||
CopySliceToDBParallel(&wg, &module.Languages, m.Languages)
|
||||
CopySliceToDBParallel(&wg, &module.Packs, m.Packs)
|
||||
CopySliceToDBParallel(&wg, &module.PackFolders, m.PackFolders)
|
||||
wg.Wait()
|
||||
|
||||
*dest = module
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user