Compare commits

21 Commits

Author SHA1 Message Date
c737e80b8f add password changing support 2025-10-10 20:02:17 +03:00
5783a756c3 add method update 2025-10-10 19:27:04 +03:00
ba47ee4219 move error messages to variables 2025-10-10 19:26:57 +03:00
5d49e0afc7 add _errors.lua 2025-10-10 19:26:35 +03:00
76fed578ff create fully functional get method 2025-10-09 20:26:25 +03:00
975c52b58e delete unused modules 2025-10-09 20:03:44 +03:00
4e75d48f1d small fixes 2025-10-09 20:00:48 +03:00
65af07fffa add delete method 2025-10-09 20:00:40 +03:00
1252634420 Change method separator to . and move separator symbol and regexp template to global variables 2025-10-09 19:56:41 +03:00
4a58845211 deledet some files 2025-10-05 19:10:53 +03:00
b0701632e6 add common function to Unit layer 2025-10-05 19:10:45 +03:00
9277aa9f1a add some files to ginignore 2025-10-05 19:10:31 +03:00
19654e1eca Ad some CRUD methods to manage units table 2025-10-05 19:09:58 +03:00
d4306a0d89 rename internal.sha256.sum to hash 2025-10-05 19:09:00 +03:00
73095a69e0 Merge branch 'main' of https://github.com/akyaiy/GoSally-mvp 2025-09-12 19:19:08 +03:00
0f82ce941b in 2025-09-12 19:18:22 +03:00
Aleksey
0ec8493ab4 Merge pull request #3 from akyaiy/auth-server
Auth server
2025-09-12 19:16:32 +03:00
Aleksey
24eef9eee0 Merge pull request #2 from akyaiy/dev
Dev
2025-08-04 15:15:00 +03:00
Aleksey
a6c9e5102f Merge branch 'main' into dev 2025-08-04 15:13:55 +03:00
f3c4b9e9b1 update config example 2025-07-30 12:02:12 +03:00
Aleksey
81359c036c Merge pull request #1 from akyaiy/dev
Dev
2025-07-29 16:51:18 +03:00
19 changed files with 434 additions and 150 deletions

6
.gitignore vendored
View File

@@ -10,4 +10,8 @@ com/_config.lua
.vscode .vscode
Taskfile.yml Taskfile.yml
config.yaml config.yaml
# Garbage
com/_Access/GetMasterAccess.lua
com/_Zones/GetZoneInfo.lua

View File

@@ -1,77 +0,0 @@
local session = require("internal.session")
local log = require("internal.log")
local jwt = require("internal.crypt.jwt")
local bc = require("internal.crypt.bcrypt")
local db = require("internal.database.sqlite").connect("db/root.db", {log = true})
local sha256 = require("internal.crypt.sha256")
log.info("Someone at "..session.request.address.." trying to get master access")
local function close_db()
if db then
db:close()
db = nil
end
end
local params = session.request.params.get()
local function check_missing(arr, p)
local is_missing = {}
local ok = true
for _, key in ipairs(arr) do
if p[key] == nil then
table.insert(is_missing, key)
ok = false
end
end
return ok, is_missing
end
local ok, mp = check_missing({"master_secret", "master_name", "my_key"}, params)
if not ok then
close_db()
session.response.send_error(-32602, "Missing params", mp)
end
if type(params.master_secret) ~= "string" then
close_db()
session.response.send_error(-32050, "Access denied")
end
if type(params.master_name) ~= "string" then
close_db()
session.response.send_error(-32050, "Access denied")
end
local master, err = db:query_row("SELECT * FROM master_units WHERE master_name = ?", {params.master_name})
if not master then
log.event("DB query failed:", err)
close_db()
session.response.send_error(-32050, "Access denied")
end
local ok = bc.compare(master.master_secret, params.master_secret)
if not ok then
log.warn("Login failed: wrong password")
close_db()
session.response.send_error(-32050, "Access denied")
end
local token = jwt.encode({
secret = require("_config").token(),
payload = {
session_uuid = session.id,
master_id = master.id,
key = sha256.sum(params.my_key)
},
expires_in = 3600
})
close_db()
session.response.send({
token = token
})
-- G7HgOgl72o7t7u7r

88
com/Unit/Create.lua Normal file
View File

@@ -0,0 +1,88 @@
-- File com/Unit/Create.lua
--
-- Created at 2025-05-10 18:23
--
-- Updated at -
-- Description:
--- Creates a record in the unit.db database without
--- requiring additional permissions. Requires username,
--- password (hashing occurs at the server level), and email fields.
local log = require("internal.log")
local db = require("internal.database.sqlite").connect("db/unit.db", {log = true})
local session = require("internal.session")
local crypt = require("internal.crypt.bcrypt")
local sha256 = require("internal.crypt.sha256")
local common = require("com/Unit/_common")
local errors = require("com/Unit/_errors")
-- Preparing for first db query
local function close_db()
if db then
log.debug("Closing DB connection")
db:close()
db = nil
end
end
local params = session.request.params.get()
local ok, mp = common.CheckMissingElement({"username", "password", "email"}, params)
if not ok then
close_db()
session.response.send_error(errors.MISSING_PARAMS.code, errors.MISSING_PARAMS.message, mp)
end
local hashPass = crypt.generate(params.password, crypt.DefaultCost)
local unitID = string.sub(sha256.hash(session.__seed), 1, 16)
-- First db query: check if username or email already exists among active users
local existing, err = db:query([[
SELECT 1
FROM units
WHERE (email = ? OR username = ?)
AND entry_status != 'deleted'
AND deleted_at IS NULL
LIMIT 1
]], {
params.email,
params.username
})
if err ~= nil then
log.error("Email check failed: "..tostring(err))
close_db()
session.response.send_error()
end
if existing and #existing > 0 then
close_db()
session.response.send_error(errors.UNIT_EXISTS.code, errors.UNIT_EXISTS.message)
end
-- Second db query: insert new unit
local ctx, err = db:exec(
"INSERT INTO units (user_id, username, email, password) VALUES (?, ?, ?, ?)",
{
unitID,
params.username,
params.email,
hashPass,
}
)
if err ~= nil then
log.error("Insert failed: "..tostring(err))
close_db()
session.response.send_error(errors.DB_INSERT_FAILED.code, errors.DB_INSERT_FAILED.message)
end
local res, err = ctx:wait()
if err ~= nil then
log.error("Insert confirmation failed: "..tostring(err))
close_db()
session.response.send_error(errors.DB_INSERT_FAILED.code, errors.DB_INSERT_FAILED.message)
end
close_db()
session.response.send({message = "Unit created successfully", unit_id = unitID})

77
com/Unit/Delete.lua Normal file
View File

@@ -0,0 +1,77 @@
-- File com/Unit/Delete.lua
--
-- Created at 2025-05-10 19:18
--
-- Updated at -
local log = require("internal.log")
local db = require("internal.database.sqlite").connect("db/unit.db", {log = true})
local session = require("internal.session")
local common = require("com/Unit/_common")
local errors = require("com/Unit/_errors")
-- Preparing for first db query
local function close_db()
if db then
log.debug("Closing DB connection")
db:close()
db = nil
end
end
local params = session.request.params.get()
local ok, mp = common.CheckMissingElement({"user_id"}, params)
if not ok then
close_db()
session.response.send_error(errors.MISSING_PARAMS.code, errors.MISSING_PARAMS.message, mp)
end
local existing, err = db:query([[
SELECT 1
FROM units
WHERE user_id = ?
AND entry_status != 'deleted'
AND deleted_at IS NULL
LIMIT 1
]], {
params.user_id
})
if err ~= nil then
log.error("Email check failed: "..tostring(err))
close_db()
session.response.send_error()
end
if existing and #existing == 0 then
close_db()
session.response.send_error(errors.UNIT_NOT_FOUND.code, errors.UNIT_NOT_FOUND.message)
end
local ctx, err = db:exec(
[[
UPDATE units
SET entry_status = 'deleted',
deleted_at = CURRENT_TIMESTAMP
WHERE user_id = ? AND deleted_at is NULL
]],
{ params.user_id }
)
if err ~= nil then
log.error("Soft delete failed: " .. tostring(err))
close_db()
session.response.send_error(errors.DB_DELETE_FAILED.code, errors.DB_DELETE_FAILED.message)
end
local res, err = ctx:wait()
if err ~= nil then
log.error("Soft delete confirmation failed: " .. tostring(err))
close_db()
session.response.send_error(errors.DB_DELETE_FAILED.code, errors.DB_DELETE_FAILED.message)
end
close_db()
session.response.send({message = "Unit deleted successfully", unit_id = params.user_id})

55
com/Unit/Get.lua Normal file
View File

@@ -0,0 +1,55 @@
-- File com/Unit/Get.lua
--
-- Created at 2025-09-25 20:04
--
-- Updated at -
local log = require("internal.log")
local db = require("internal.database.sqlite").connect("db/unit.db", {log = true})
local session = require("internal.session")
local common = require("com/Unit/_common")
local errors = require("com/Unit/_errors")
-- Preparing for first db query
local function close_db()
if db then
log.debug("Closing DB connection")
db:close()
db = nil
end
end
local params = session.request.params.get()
local ok, mp = common.CheckMissingElement({"by", "value"}, params)
if not ok then
close_db()
session.response.send_error(errors.MISSING_PARAMS.code, errors.MISSING_PARAMS.message, mp)
end
if not (params.by == "email" or params.by == "username" or params.by == "user_id") then
close_db()
session.response.send_error(errors.INVALID_BY_PARAM.code, errors.INVALID_BY_PARAM.message)
end
local unit, err = db:query_row(
"SELECT user_id, username, email, created_at, updated_at, deleted_at, entry_status FROM units WHERE "..params.by.." = ? AND deleted_at IS NULL LIMIT 1",
{
params.value
}
)
if err then
close_db()
log.error("DB query error: " .. tostring(err))
session.response.send_error()
end
if not unit then
close_db()
session.response.send_error(errors.UNIT_NOT_FOUND.code, errors.UNIT_NOT_FOUND.message)
end
close_db()
session.response.send(unit)

106
com/Unit/Update.lua Normal file
View File

@@ -0,0 +1,106 @@
-- File com/Unit/Update.lua
--
-- Created at 2025-10-10
--
local log = require("internal.log")
local db = require("internal.database.sqlite").connect("db/unit.db", { log = true })
local session = require("internal.session")
local crypt = require("internal.crypt.bcrypt")
local common = require("com/Unit/_common")
local errors = require("com/Unit/_errors")
local function close_db()
if db then
log.debug("Closing DB connection")
db:close()
db = nil
end
end
local params = session.request.params.get()
local ok, mp = common.CheckMissingElement({"user_id", "fields"}, params)
if not ok then
close_db()
session.response.send_error(errors.MISSING_PARAMS.code, errors.MISSING_PARAMS.message, mp)
end
if type(params.fields) ~= "table" or next(params.fields) == nil then
close_db()
session.response.send_error(errors.INVALID_FIELD_TYPE.code, errors.INVALID_FIELD_TYPE.message)
end
local allowed = {
username = true,
email = true,
password = true,
entry_status = true
}
local exists = db:query_row(
"SELECT 1 FROM units WHERE user_id = ? AND deleted_at IS NULL LIMIT 1",
{ params.user_id }
)
if not exists then
close_db()
session.response.send_error(errors.UNIT_NOT_FOUND.code, errors.UNIT_NOT_FOUND.message)
end
local set_clauses = {}
local values = {}
for k, v in pairs(params.fields) do
if allowed[k] then
if k == "password" then
v = crypt.generate(v, crypt.DefaultCost)
end
table.insert(set_clauses, k .. " = ?")
table.insert(values, v)
else
log.warn("Ignoring unsupported field: " .. k)
end
end
if #set_clauses == 0 then
close_db()
session.response.send_error(errors.NO_VALID_FIELDS.code, errors.NO_VALID_FIELDS.message)
end
table.insert(set_clauses, "updated_at = CURRENT_TIMESTAMP")
local query = "UPDATE units SET " .. table.concat(set_clauses, ", ")
.. " WHERE user_id = ? AND deleted_at IS NULL"
table.insert(values, params.user_id)
local ctx, err = db:exec(query, values)
if not ctx then
close_db()
if tostring(err):match("UNIQUE constraint failed") then
session.response.send_error(errors.UNIQUE_CONSTRAINT.code, errors.UNIQUE_CONSTRAINT.message)
else
session.response.send_error()
end
end
local _, err = ctx:wait()
if err ~= nil then
close_db()
if tostring(err):match("UNIQUE constraint failed") then
session.response.send_error(errors.UNIQUE_CONSTRAINT.code, errors.UNIQUE_CONSTRAINT.message)
else
log.error("Insert confirmation failed: "..tostring(err))
session.response.send_error()
end
end
close_db()
session.response.send({
message = "User updated successfully",
fields_updated = #set_clauses - 1, -- excluding updated_at
fields = params.fields
})

23
com/Unit/_common.lua Normal file
View File

@@ -0,0 +1,23 @@
-- File com/Unit/_common.lua
--
-- Created at 2025-05-10 18:23
--
-- Updated at -
-- Description:
--- Common functions for Unit module
local common = {}
function common.CheckMissingElement(arr, cmp)
local is_missing = {}
local ok = true
for _, key in ipairs(arr) do
if cmp[key] == nil then
table.insert(is_missing, key)
ok = false
end
end
return ok, is_missing
end
return common

30
com/Unit/_errors.lua Normal file
View File

@@ -0,0 +1,30 @@
-- File com/Unit/_errors.lua
--
-- Created at 2025-10-10
-- Description:
--- Centralized error definitions for Unit operations
--- to keep API responses consistent and clean.
local errors = {
-- Common validation
MISSING_PARAMS = { code = -32602, message = "Missing params" },
INVALID_FIELD_TYPE = { code = -32602, message = "'fields' must be a non-empty table" },
INVALID_BY_PARAM = { code = -32602, message = "Invalid 'by' param" },
NO_VALID_FIELDS = { code = -32604, message = "No valid fields to update" },
-- Existence / duplication
UNIT_NOT_FOUND = { code = -32102, message = "Unit is not exists" },
UNIT_EXISTS = { code = -32101, message = "Unit is already exists" },
-- Database & constraint
UNIQUE_CONSTRAINT = { code = -32602, message = "Unique constraint failed" },
DB_QUERY_FAILED = { code = -32001, message = "Database query failed" },
DB_EXEC_FAILED = { code = -32002, message = "Database execution failed" },
DB_INSERT_FAILED = { code = -32003, message = "Failed to create unit" },
DB_DELETE_FAILED = { code = -32004, message = "Failed to delete unit" },
-- Generic fallback
UNKNOWN = { code = -32099, message = "Unexpected internal error" },
}
return errors

View File

@@ -1,69 +0,0 @@
local session = require("internal.session")
local log = require("internal.log")
local jwt = require("internal.crypt.jwt")
local bc = require("internal.crypt.bcrypt")
local sha256 = require("internal.crypt.sha256")
local dbdriver = require("internal.database.sqlite")
local db_root = dbdriver.connect("db/root.db", {log = true})
local db_zone = nil
local function close_db()
if db_root then
db_root:close()
db_root = nil
end
if db_zone then
db_zone:close()
db_zone = nil
end
end
local token = session.request.headers.get("authorization")
if not token or type(token) ~= "string" then
close_db()
session.response.send_error(-32050, "Access denied")
end
local prefix = "Bearer "
if token:sub(1, #prefix) ~= prefix then
close_db()
session.response.send_error(-32052, "Invalid Authorization scheme")
end
local access_token = token:sub(#prefix + 1)
local err, data = jwt.decode(access_token, { secret = require("_config").token() })
if err or not data then
close_db()
session.response.send_error(-32053, "Cannod parse JWT", {err})
end
if data.master_id then
end
local params = session.request.params.get()
local function check_missing(arr, p)
local is_missing = {}
local ok = true
for _, key in ipairs(arr) do
if p[key] == nil then
table.insert(is_missing, key)
ok = false
end
end
return ok, is_missing
end
local ok, mp = check_missing({"zone_name"}, params)
if not ok then
close_db()
session.response.send_error(-32602, "Missing params", mp)
end
close_db()

44
com/test.lua Normal file
View File

@@ -0,0 +1,44 @@
local sha256 = require("internal.crypt.sha256")
local log = require("internal.log")
local session = require("internal.session")
-- local secret = require("_config").token()
-- local token = jwt.encode({
-- secret = secret,
-- payload = { session_uuid = session.id },
-- expires_in = 3600
-- })
-- local err, data = jwt.decode(token, { secret = secret })
-- if not err then
-- session.response.result = {
-- token = token
-- }
-- return
-- end
-- session.response.error = {
-- message = "not sigma"
-- }
-- local array = session.request.params.get("array", "oops")
-- function s()
-- session.throw_error("dqdqwdqwdqiwhodiwqohdq", 10)
-- end
-- s()
-- session.response.__script_data.result = {
-- data = {
-- sewf = 1
-- },
-- 2
-- }
session.response.set_error()
--session.response.send_error({1})
-- session.response.set()
-- session.response.__script_data.result = {
-- status = "ok"
-- }
session.response.set(1)
log.event("popi")

BIN
db/backup/root.db Normal file

Binary file not shown.

BIN
db/backup/zones/zone0.db Normal file

Binary file not shown.

BIN
db/root.db Normal file

Binary file not shown.

BIN
db/user-database.db Normal file

Binary file not shown.

0
db/zones-registry.db Normal file
View File

BIN
db/zones/zone0.db Normal file

Binary file not shown.

View File

@@ -29,6 +29,7 @@ import (
) )
var NodeApp = app.New() var NodeApp = app.New()
var AllowedCmdPattern = `^[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*$`
func Run(cmd *cobra.Command, args []string) { func Run(cmd *cobra.Command, args []string) {
NodeApp.InitialHooks( NodeApp.InitialHooks(
@@ -60,7 +61,7 @@ func RunHook(ctx context.Context, cs *corestate.CoreState, x *app.AppX) error {
serverv1 := sv1.InitV1Server(&sv1.HandlerV1InitStruct{ serverv1 := sv1.InitV1Server(&sv1.HandlerV1InitStruct{
X: x, X: x,
CS: cs, CS: cs,
AllowedCmd: regexp.MustCompile(`^[a-zA-Z0-9]+(>[a-zA-Z0-9]+)*$`), AllowedCmd: regexp.MustCompile(AllowedCmdPattern),
Ver: "v1", Ver: "v1",
}) })

View File

@@ -542,7 +542,7 @@ func (h *HandlerV1) handleLUA(sid string, r *http.Request, req *rpc.RPCRequest,
llog.Debug("import module crypt.sha256", slog.String("script", path)) llog.Debug("import module crypt.sha256", slog.String("script", path))
sha265mod := L.NewTable() sha265mod := L.NewTable()
L.SetField(sha265mod, "sum", L.NewFunction(func(l *lua.LState) int { L.SetField(sha265mod, "hash", L.NewFunction(func(l *lua.LState) int {
data := ConvertLuaTypesToGolang(L.Get(1)) data := ConvertLuaTypesToGolang(L.Get(1))
var dataStr = fmt.Sprint(data) var dataStr = fmt.Sprint(data)

View File

@@ -9,12 +9,14 @@ import (
"github.com/akyaiy/GoSally-mvp/internal/server/rpc" "github.com/akyaiy/GoSally-mvp/internal/server/rpc"
) )
var RPCMethodSeparator = "."
func (h *HandlerV1) resolveMethodPath(method string) (string, error) { func (h *HandlerV1) resolveMethodPath(method string) (string, error) {
if !h.allowedCmd.MatchString(method) { if !h.allowedCmd.MatchString(method) {
return "", errors.New(rpc.ErrInvalidMethodFormatS) return "", errors.New(rpc.ErrInvalidMethodFormatS)
} }
parts := strings.Split(method, ">") parts := strings.Split(method, RPCMethodSeparator)
relPath := filepath.Join(parts...) + ".lua" relPath := filepath.Join(parts...) + ".lua"
fullPath := filepath.Join(*h.x.Config.Conf.Node.ComDir, relPath) fullPath := filepath.Join(*h.x.Config.Conf.Node.ComDir, relPath)