mirror of
https://github.com/akyaiy/GoSally-mvp.git
synced 2026-01-03 08:32:24 +00:00
Compare commits
38 Commits
auth-serve
...
6cc24a1e7f
| Author | SHA1 | Date | |
|---|---|---|---|
| 6cc24a1e7f | |||
| ea41c435dd | |||
| d24e1a94ae | |||
| 846dc06601 | |||
| 740fbbff78 | |||
| 40be3c8d09 | |||
| 9c140abc6d | |||
| e90233aec4 | |||
| df1ef57769 | |||
| 4c840c40bb | |||
| 57f35e8f33 | |||
| f0c591f325 | |||
| 36ee320c45 | |||
| ee6fd205d5 | |||
| bed0471cc4 | |||
| e3812a18a6 | |||
| b7d939d5d7 | |||
| c737e80b8f | |||
| 5783a756c3 | |||
| ba47ee4219 | |||
| 5d49e0afc7 | |||
| 76fed578ff | |||
| 975c52b58e | |||
| 4e75d48f1d | |||
| 65af07fffa | |||
| 1252634420 | |||
| 4a58845211 | |||
| b0701632e6 | |||
| 9277aa9f1a | |||
| 19654e1eca | |||
| d4306a0d89 | |||
| 73095a69e0 | |||
| 0f82ce941b | |||
|
|
0ec8493ab4 | ||
|
|
24eef9eee0 | ||
|
|
a6c9e5102f | ||
| f3c4b9e9b1 | |||
|
|
81359c036c |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -10,4 +10,8 @@ com/_config.lua
|
||||
|
||||
.vscode
|
||||
Taskfile.yml
|
||||
config.yaml
|
||||
config.yaml
|
||||
|
||||
# Garbage
|
||||
com/_*
|
||||
com/test.lua
|
||||
23
Makefile
23
Makefile
@@ -4,7 +4,10 @@ GOPATH := $(shell go env GOPATH)
|
||||
export CONFIG_PATH := ./config.yaml
|
||||
export NODE_PATH := $(shell pwd)
|
||||
|
||||
LDFLAGS := -X 'github.com/akyaiy/GoSally-mvp/internal/engine/config.NodeVersion=v0.0.1-dev'
|
||||
NODE_VERSION := v0.0.1-dev
|
||||
SV1_VERSION := v0.0.1-dev
|
||||
|
||||
LDFLAGS := -X 'github.com/akyaiy/GoSally-mvp/src/internal/engine/config.NodeVersion=$(NODE_VERSION)' -X 'github.com/akyaiy/GoSally-mvp/src/internal/server/sv1.SV1Version=$(SV1_VERSION)'
|
||||
CGO_CFLAGS := -I/usr/local/include
|
||||
CGO_LDFLAGS := -L/usr/local/lib -llua5.1 -lm -ldl
|
||||
.PHONY: all build run runq test fmt vet lint check clean
|
||||
@@ -29,15 +32,15 @@ build:
|
||||
@echo "Building..."
|
||||
@# @echo "CGO_CFLAGS is: '$(CGO_CFLAGS)'"
|
||||
@# @echo "CGO_LDFLAGS is: '$(CGO_LDFLAGS)'"
|
||||
@# CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)"
|
||||
@go build -trimpath -ldflags "-w -s $(LDFLAGS)" -o $(BIN_DIR)/$(APP_NAME) ./
|
||||
@if ! command -v upx >/dev/null 2>&1; then \
|
||||
echo "upx not found, skipping compression."; \
|
||||
elif upx -t $(BIN_DIR)/$(APP_NAME) >/dev/null 2>&1; then \
|
||||
echo "$(BIN_DIR)/$(APP_NAME) already compressed, skipping."; \
|
||||
else \
|
||||
upx $(BIN_DIR)/$(APP_NAME) >/dev/null 2>&1 || true; \
|
||||
fi
|
||||
@# CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)"
|
||||
cd src && go build -trimpath -ldflags "-w -s $(LDFLAGS)" -o ../$(BIN_DIR)/$(APP_NAME) ./
|
||||
# @if ! command -v upx >/dev/null 2>&1; then \
|
||||
# echo "upx not found, skipping compression."; \
|
||||
# elif upx -t $(BIN_DIR)/$(APP_NAME) >/dev/null 2>&1; then \
|
||||
# echo "$(BIN_DIR)/$(APP_NAME) already compressed, skipping."; \
|
||||
# else \
|
||||
# upx $(BIN_DIR)/$(APP_NAME) >/dev/null 2>&1 || true; \
|
||||
# fi
|
||||
|
||||
run: build
|
||||
@echo "Running!"
|
||||
|
||||
91
README.md
91
README.md
@@ -1,51 +1,66 @@
|
||||
# Go Sally MVP (Minimum/Minimal Viable Product)
|
||||
|
||||
### What is this?
|
||||
System that allows you to build your own infrastructure based on identical nodes and various scripts written using built-in Lua 5.1, shebang scripts (scripts that start with the `#!` symbols), compiled binaries.
|
||||
> [!NOTE]
|
||||
> If you see "💡" in the text, it means the information below is about plans for the future of the project.
|
||||
|
||||
### Features
|
||||
Go Sally is not viable at the moment, but it already has the ability to run embedded scripts, log slog events to stdout, handle RPC like requests, and independent automatic update from the repository (my pride, to be honest).
|
||||
- **Decentralized nodes**<details>this means that *multiple GS[^1] nodes can be located on a single machine*, provided no attempt is made to disrupt, sabotage, or bypass the built-in protection mechanism against running a node under the same identifier as one already running in the system. Identification plays a role in node communication. 💡 In the future, we plan to create tools for conveniently building distributed systems using node identification.</details>
|
||||
- **RPC request processing**<details>the GS operates *using HTTP/https and the JSONRPC2.0 protocol.* Unlike gRPC, jsonrpc is extremely simple, allows for easy sending of requests from the browser, and does not require any additional code compilation.</details>
|
||||
- **Lua script-based methods**<details>*The gopher-lua library is used, providing full support for Lua 5.1.* scripts implement libraries for interacting with sessions (receiving parameters and sending responses), hashing, logging, and more. This allows you to quickly write business logic on the fly without touching the lower layers of abstraction, which also eliminates unnecessary compilation and the risk of breaking the codebase.
|
||||
Example of the "echo" method:
|
||||
```lua
|
||||
local session = require("internal.session")
|
||||
-- import the internal library for interacting with sessions
|
||||
|
||||
### Example of use
|
||||
The basic directory tree looks something like this
|
||||
```
|
||||
.
|
||||
├── bin
|
||||
│ └── node Node core binary file
|
||||
├── com
|
||||
│ ├── echo.lua
|
||||
│ ├── _globals.lua Declaring global variables and functions for all internal scripts (also required for luarc to work correctly)
|
||||
│ └── _prepare.lua Script that is executed before each script launch
|
||||
└── config.yaml
|
||||
session.response.send(session.request.params.get())
|
||||
-- send everything passed in the parameters in response.
|
||||
```
|
||||
</details>
|
||||
- **Relatively flexible configuration** <details>
|
||||
you can configure the server port, address, name, node settings, and more. 💡 More settings are planned in the future.</details>
|
||||
- ***And more in the future***
|
||||
|
||||
3 directories, 5 files
|
||||
> [!IMPORTANT]
|
||||
> This is the beginning of the project's development, and some aspects of it may be unstable, unfinished, and the text about it may be overly ambitious. It's just a matter of time.
|
||||
|
||||
```
|
||||
Launch by command
|
||||
```bash
|
||||
./bin/node run
|
||||
```
|
||||
or for structured logs
|
||||
```bash
|
||||
./bin/node run | jq
|
||||
```
|
||||
## Why?
|
||||
The project was originally conceived as a tool for building infrastructure using relatively *small nodes with limited functionality*. 💡 In the future, we plan to create a *web interface for interacting with nodes, administration, and configuration*. The concept is simple: suppose we have a node that manages Bind9. It has all the necessary methods for interacting with the service: creating new zones, viewing zone status, changing configuration, and server operation status. All of this works only through manual configuration, with the exception of larger solutions like Webmin and the BIND DNS Server module. The big problem is that while we only needed web configuration for Bind9, we have to pull in a massive amount of software just to implement one module. What if the service is hosted on a low-power Raspberry Pi? That's where GS nodes come in. By default, GS nodes communicate only through API calls, so 💡 in the future, we plan to create a dedicated, also programmable, web node that will provide convenient access to node management.
|
||||
|
||||
Example of GET request to server
|
||||
```bash
|
||||
curl -s http://localhost:8080/api/v1/com/echo?msg=Hello
|
||||
```
|
||||
Then the response from the server
|
||||
There's an obvious advantage here: transparency. The project is *completely open source and aims to support community-driven node functionality*. 💡 In the future, we plan to create a "store" similar to Docker Hub, which will contain scripts for configuring bind9, openvpn, and even custom projects.
|
||||
|
||||
## API
|
||||
As mentioned earlier, *the server handles [jsonrpc2.0](https://www.jsonrpc.org/specification) requests*
|
||||
```json
|
||||
{
|
||||
"ResponsibleAgentUUID": "4593a87000bbe088f4e79c477e9c90d3",
|
||||
"RequestedCommand": "echo",
|
||||
"Response": {
|
||||
"answer": "Hello",
|
||||
"status": "ok"
|
||||
}
|
||||
"jsonrpc": "2.0",
|
||||
"context-version": "v1",
|
||||
"method": "test",
|
||||
"params": [
|
||||
"Hi!!"
|
||||
],
|
||||
"id": 1
|
||||
}
|
||||
|
||||
```
|
||||
This is a typical example of a request using the jsonrpc2.0 protocol.
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": [
|
||||
"Hi!!"
|
||||
],
|
||||
"data": {
|
||||
"responsible-node": "2ad6ebeaf579a7c52801fb6c9dd1b83d",
|
||||
"salt": "e7a81115-01c1-45b1-9618-0eae0ff26451",
|
||||
"checksum-md5": "cd8bec6a365d1b8ee90773567cb3ad0a"
|
||||
}
|
||||
}
|
||||
```
|
||||
In the result field, we see the echo method's response. Those familiar with the jsonrpc2.0 specification will notice that the data structure here is unclear. This is my extension, which has three functions:
|
||||
| Field | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `responsible-node` | string | ID of the node that executed the task |
|
||||
| `salt` | string | Random value for each request — can be used to check that the response is unique |
|
||||
| `checksum-md5` | string | MD5 hash of the result field — can be used to avoid processing identical results separately |
|
||||
|
||||
### How to install
|
||||
**You don't need it now, but you can figure it out with the Makefile**
|
||||
[^1]: Go Sally
|
||||
@@ -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
|
||||
68
com/Unit/Create.lua
Normal file
68
com/Unit/Create.lua
Normal file
@@ -0,0 +1,68 @@
|
||||
-- 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)
|
||||
|
||||
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 _, err = ctx:wait()
|
||||
if err ~= nil then
|
||||
close_db()
|
||||
if tostring(err):match("UNIQUE constraint failed") then
|
||||
session.response.send_error(errors.UNIT_EXISTS.code, errors.UNIT_EXISTS.message)
|
||||
else
|
||||
log.error("Insert confirmation failed: "..tostring(err))
|
||||
session.response.send_error()
|
||||
end
|
||||
end
|
||||
|
||||
close_db()
|
||||
session.response.send({unit_id = unitID})
|
||||
77
com/Unit/Delete.lua
Normal file
77
com/Unit/Delete.lua
Normal 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()
|
||||
55
com/Unit/Get.lua
Normal file
55
com/Unit/Get.lua
Normal 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)
|
||||
102
com/Unit/Update.lua
Normal file
102
com/Unit/Update.lua
Normal file
@@ -0,0 +1,102 @@
|
||||
-- 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 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
|
||||
local crypt = require("internal.crypt.bcrypt")
|
||||
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()
|
||||
23
com/Unit/_common.lua
Normal file
23
com/Unit/_common.lua
Normal 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
30
com/Unit/_errors.lua
Normal 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
|
||||
@@ -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()
|
||||
@@ -1,119 +0,0 @@
|
||||
-- com/DeleteUnit.lua
|
||||
|
||||
---@diagnostic disable: redefined-local
|
||||
local db = require("internal.database.sqlite").connect("db/user-database.db", {log = true})
|
||||
local log = require("internal.log")
|
||||
local session = require("internal.session")
|
||||
local crypt = require("internal.crypt.bcrypt")
|
||||
local jwt = require("internal.crypt.jwt")
|
||||
local sha256 = require("internal.crypt.sha256")
|
||||
|
||||
local params = session.request.params.get()
|
||||
local token = session.request.headers.get("authorization")
|
||||
|
||||
local function close_db()
|
||||
if db then
|
||||
db:close()
|
||||
db = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function error_response(message, code, data)
|
||||
session.response.error = {
|
||||
code = code or nil,
|
||||
message = message,
|
||||
data = data or nil
|
||||
}
|
||||
close_db()
|
||||
end
|
||||
|
||||
if not token or type(token) ~= "string" then
|
||||
return error_response("Access denied")
|
||||
end
|
||||
|
||||
local prefix = "Bearer "
|
||||
if token:sub(1, #prefix) ~= prefix then
|
||||
return error_response("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
|
||||
session.response.error = {
|
||||
message = err
|
||||
}
|
||||
return
|
||||
end
|
||||
|
||||
-- if data.session_uuid ~= session.id then
|
||||
-- return error_response("Access denied")
|
||||
-- end
|
||||
|
||||
-- if data.key ~= sha256.sum(session.request.address .. session.id .. session.request.headers.get("user-agent", "noagent")) then
|
||||
-- return error_response("Access denied")
|
||||
-- end
|
||||
|
||||
if not params then
|
||||
return error_response("no params provided")
|
||||
end
|
||||
|
||||
if not (params.username and params.email and params.password) then
|
||||
return error_response("no username/email/password provided")
|
||||
end
|
||||
|
||||
local existing, err = db:query(
|
||||
"SELECT password FROM users WHERE email = ? AND username = ? AND deleted = 0 LIMIT 1",
|
||||
{
|
||||
params.email,
|
||||
params.username
|
||||
}
|
||||
)
|
||||
|
||||
if err ~= nil then
|
||||
log.error("Password fetch failed: " .. tostring(err))
|
||||
return error_response("Database query failed: " .. tostring(err))
|
||||
end
|
||||
|
||||
if not existing or #existing == 0 then
|
||||
return error_response("Unit not found")
|
||||
end
|
||||
|
||||
local hashed_password = existing[1].password
|
||||
|
||||
local ok = crypt.compare(hashed_password, params.password)
|
||||
if not ok then
|
||||
log.warn("Wrong password attempt for: " .. params.username)
|
||||
return error_response("Invalid password")
|
||||
end
|
||||
|
||||
local ctx, err = db:exec(
|
||||
[[
|
||||
UPDATE users
|
||||
SET deleted = 1,
|
||||
deleted_at = CURRENT_TIMESTAMP
|
||||
WHERE email = ? AND username = ? AND deleted = 0
|
||||
]],
|
||||
{ params.email, params.username }
|
||||
)
|
||||
|
||||
if err ~= nil then
|
||||
log.error("Soft delete failed: " .. tostring(err))
|
||||
return error_response("Soft delete failed: " .. tostring(err))
|
||||
end
|
||||
|
||||
local res, err = ctx:wait()
|
||||
if err ~= nil then
|
||||
log.error("Soft delete confirmation failed: " .. tostring(err))
|
||||
return error_response("Soft delete confirmation failed: " .. tostring(err))
|
||||
end
|
||||
|
||||
session.response.result = {
|
||||
rows_affected = res,
|
||||
message = "Unit soft-deleted successfully"
|
||||
}
|
||||
|
||||
log.info("user " .. params.username .. " soft-deleted successfully")
|
||||
|
||||
close_db()
|
||||
@@ -1,76 +0,0 @@
|
||||
-- com/GetAccess
|
||||
|
||||
---@diagnostic disable: redefined-local
|
||||
local db = require("internal.database.sqlite").connect("db/user-database.db", {log = true})
|
||||
local log = require("internal.log")
|
||||
local session = require("internal.session")
|
||||
local crypt = require("internal.crypt.bcrypt")
|
||||
local jwt = require("internal.crypt.jwt")
|
||||
local sha256 = require("internal.crypt.sha256")
|
||||
|
||||
local params = session.request.params.get()
|
||||
local secret = require("_config").token()
|
||||
|
||||
local function close_db()
|
||||
if db then
|
||||
db:close()
|
||||
db = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function error_response(message, code, data)
|
||||
session.response.error = {
|
||||
code = code or nil,
|
||||
message = message,
|
||||
data = data or nil
|
||||
}
|
||||
close_db()
|
||||
end
|
||||
|
||||
if not params then
|
||||
return error_response("No params provided")
|
||||
end
|
||||
|
||||
if not (params.username and params.email and params.password) then
|
||||
return error_response("Missing username, email or password")
|
||||
end
|
||||
|
||||
local unit, err = db:query(
|
||||
"SELECT id, username, email, password, created_at FROM users WHERE email = ? AND username = ? AND deleted = 0 LIMIT 1",
|
||||
{
|
||||
params.email,
|
||||
params.username
|
||||
}
|
||||
)
|
||||
|
||||
if err then
|
||||
log.error("DB query error: " .. tostring(err))
|
||||
return error_response("Database query failed")
|
||||
end
|
||||
|
||||
if not unit or #unit == 0 then
|
||||
return error_response("Unit not found")
|
||||
end
|
||||
|
||||
unit = unit[1]
|
||||
|
||||
local ok = crypt.compare(unit.password, params.password)
|
||||
if not ok then
|
||||
log.warn("Login failed: wrong password for " .. params.username)
|
||||
return error_response("Invalid password")
|
||||
end
|
||||
|
||||
local token = jwt.encode({
|
||||
secret = secret,
|
||||
payload = { session_uuid = session.id,
|
||||
admin_user = params.username,
|
||||
key = sha256.sum(session.request.address .. session.id .. session.request.headers.get("user-agent", "noagent"))
|
||||
},
|
||||
expires_in = 3600
|
||||
})
|
||||
|
||||
session.response.result = {
|
||||
access_token = token
|
||||
}
|
||||
|
||||
close_db()
|
||||
@@ -1,109 +0,0 @@
|
||||
-- com/PutNewUnit.lua
|
||||
|
||||
---@diagnostic disable: redefined-local
|
||||
local db = require("internal.database.sqlite").connect("db/user-database.db", {log = true})
|
||||
local log = require("internal.log")
|
||||
local session = require("internal.session")
|
||||
local crypt = require("internal.crypt.bcrypt")
|
||||
local jwt = require("internal.crypt.jwt")
|
||||
local sha256 = require("internal.crypt.sha256")
|
||||
|
||||
local params = session.request.params.get()
|
||||
local token = session.request.headers.get("authorization")
|
||||
|
||||
local function close_db()
|
||||
if db then
|
||||
db:close()
|
||||
db = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function error_response(message, code, data)
|
||||
session.response.error = {
|
||||
code = code or nil,
|
||||
message = message,
|
||||
data = data or nil
|
||||
}
|
||||
close_db()
|
||||
end
|
||||
|
||||
if not token or type(token) ~= "string" then
|
||||
return error_response("Access denied")
|
||||
end
|
||||
|
||||
local prefix = "Bearer "
|
||||
if token:sub(1, #prefix) ~= prefix then
|
||||
return error_response("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
|
||||
session.response.error = {
|
||||
message = err
|
||||
}
|
||||
return
|
||||
end
|
||||
|
||||
if data.session_uuid ~= session.id then
|
||||
return error_response("Access denied")
|
||||
end
|
||||
|
||||
if data.key ~= sha256.sum(session.request.address .. session.id .. session.request.headers.get("user-agent", "noagent")) then
|
||||
return error_response("Access denied")
|
||||
end
|
||||
|
||||
if not params then
|
||||
return error_response("no params provided")
|
||||
end
|
||||
|
||||
if not (params.username and params.email and params.password) then
|
||||
return error_response("no username/email/password provided")
|
||||
end
|
||||
|
||||
local hashPass = crypt.generate(params.password, crypt.DefaultCost)
|
||||
|
||||
local existing, err = db:query("SELECT 1 FROM users WHERE deleted = 0 AND (email = ? OR username = ?) LIMIT 1", {
|
||||
params.email,
|
||||
params.username
|
||||
})
|
||||
|
||||
if err ~= nil then
|
||||
log.error("Email check failed: "..tostring(err))
|
||||
return error_response("Database check failed: "..tostring(err))
|
||||
end
|
||||
|
||||
if existing and #existing > 0 then
|
||||
return error_response("Unit already exists")
|
||||
end
|
||||
|
||||
local ctx, err = db:exec(
|
||||
"INSERT INTO users (username, email, password, first_name, last_name, phone_number) VALUES (?, ?, ?, ?, ?, ?)",
|
||||
{
|
||||
params.username,
|
||||
params.email,
|
||||
hashPass,
|
||||
params.first_name or "",
|
||||
params.last_name or "",
|
||||
params.phone_number or ""
|
||||
}
|
||||
)
|
||||
if err ~= nil then
|
||||
log.error("Insert failed: "..tostring(err))
|
||||
return error_response("Insert failed: "..tostring(err))
|
||||
end
|
||||
|
||||
local res, err = ctx:wait()
|
||||
if err ~= nil then
|
||||
log.error("Insert confirmation failed: "..tostring(err))
|
||||
return error_response("Insert confirmation failed: "..tostring(err))
|
||||
end
|
||||
|
||||
session.response.result = {
|
||||
rows_affected = res,
|
||||
message = "Unit created successfully"
|
||||
}
|
||||
|
||||
close_db()
|
||||
@@ -1,66 +0,0 @@
|
||||
---@diagnostic disable: redefined-local
|
||||
local db = require("internal.database-sqlite").connect("db/test.db", {log = true})
|
||||
local log = require("internal.log")
|
||||
local session = require("internal.session")
|
||||
|
||||
if not (session.request.params.name and session.request.params.email) then
|
||||
session.response.error = {
|
||||
code = -32602,
|
||||
message = "Name and email are required"
|
||||
}
|
||||
return
|
||||
end
|
||||
|
||||
local existing, err = db:query("SELECT 1 FROM users WHERE email = ? LIMIT 1", {
|
||||
session.request.params.email
|
||||
})
|
||||
if err ~= nil then
|
||||
session.response.error = {
|
||||
code = -32603,
|
||||
message = "Database check failed: "..tostring(err)
|
||||
}
|
||||
log.error("Email check failed: "..tostring(err))
|
||||
return
|
||||
end
|
||||
|
||||
if existing and #existing > 0 then
|
||||
session.response.error = {
|
||||
code = -32604,
|
||||
message = "Email already exists"
|
||||
}
|
||||
return
|
||||
end
|
||||
|
||||
local ctx, err = db:exec(
|
||||
"INSERT INTO users (name, email) VALUES (?, ?)",
|
||||
{
|
||||
session.request.params.name,
|
||||
session.request.params.email
|
||||
}
|
||||
)
|
||||
if err ~= nil then
|
||||
session.response.error = {
|
||||
code = -32605,
|
||||
message = "Insert failed: "..tostring(err)
|
||||
}
|
||||
log.error("Insert failed: "..tostring(err))
|
||||
return
|
||||
end
|
||||
|
||||
local res, err = ctx:wait()
|
||||
if err ~= nil then
|
||||
session.response.error = {
|
||||
code = -32606,
|
||||
message = "Insert confirmation failed: "..tostring(err)
|
||||
}
|
||||
log.error("Insert confirmation failed: "..tostring(err))
|
||||
return
|
||||
end
|
||||
|
||||
session.response.result = {
|
||||
success = true,
|
||||
rows_affected = res,
|
||||
message = "User created successfully"
|
||||
}
|
||||
|
||||
db:close()
|
||||
@@ -1,29 +0,0 @@
|
||||
local session = require("internal.session")
|
||||
local net = require("internal.net")
|
||||
|
||||
local reqAddr
|
||||
local logReq = true
|
||||
|
||||
if session.request.params and session.request.params.url then
|
||||
reqAddr = session.request.params.url
|
||||
else
|
||||
session.response.error = {
|
||||
code = -32602,
|
||||
message = "no url provided"
|
||||
}
|
||||
return
|
||||
end
|
||||
|
||||
local resp = net.http.get_request(logReq, reqAddr)
|
||||
if resp then
|
||||
session.response.result.answer = {
|
||||
status = resp.status,
|
||||
body = resp.body
|
||||
}
|
||||
return
|
||||
end
|
||||
|
||||
session.response.error = {
|
||||
data = "error while requesting"
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
local session = require("internal.session")
|
||||
local net = require("internal.net")
|
||||
local log = require("internal.log")
|
||||
|
||||
local reqAddr
|
||||
local logReq = true
|
||||
local payload
|
||||
|
||||
log.debug(session.request.params)
|
||||
|
||||
if not (session.request.params and session.request.params.url) then
|
||||
session.response.error = {
|
||||
code = -32602,
|
||||
message = "no url or payload provided"
|
||||
}
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
|
||||
reqAddr = session.request.params.url
|
||||
payload = session.request.params.payload
|
||||
|
||||
local resp = net.http.post_request(logReq, reqAddr, "application/json", payload)
|
||||
if resp then
|
||||
session.response.result.answer = {
|
||||
status = resp.status,
|
||||
body = resp.body
|
||||
}
|
||||
return
|
||||
end
|
||||
|
||||
session.response.error = {
|
||||
data = "error while requesting"
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
node:
|
||||
mode: dev
|
||||
name: "My gosally node"
|
||||
show_config: true
|
||||
com_dir: "%path%/com"
|
||||
|
||||
http_server:
|
||||
address: "0.0.0.0"
|
||||
port: "8080"
|
||||
session_ttl: 5s
|
||||
timeout: 3s
|
||||
idle_timeout: 30s
|
||||
|
||||
tls:
|
||||
enabled: true
|
||||
cert_file: "%path%/cert/fullchain.pem"
|
||||
key_file: "%path%/cert/privkey.pem"
|
||||
|
||||
updates:
|
||||
enabled: false
|
||||
check-interval: 1h
|
||||
repository_url: "https://repo.serve.lv/raw/go-sally"
|
||||
|
||||
log:
|
||||
json_format: false
|
||||
level: "debug"
|
||||
|
||||
disable_warnings:
|
||||
- --WNonStdTmpDir
|
||||
- --WUndefLogLevel
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/hooks"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/colors"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/corestate"
|
||||
"github.com/akyaiy/GoSally-mvp/src/hooks"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/colors"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/corestate"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/akyaiy/GoSally-mvp/hooks"
|
||||
"github.com/akyaiy/GoSally-mvp/src/hooks"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -4,7 +4,8 @@ import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/config"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/config"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/server/sv1"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -13,7 +14,8 @@ var verCmd = &cobra.Command{
|
||||
Aliases: []string{"ver", "v"},
|
||||
Short: "Return node version",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("GoSally node: %s\n", config.NodeVersion)
|
||||
fmt.Printf("Go Sally version: %s\n", config.NodeVersion)
|
||||
fmt.Printf("sv1 version: %s\n", sv1.SV1Version)
|
||||
fmt.Printf("Go version: %s\n", runtime.Version())
|
||||
fmt.Printf("Go OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
|
||||
},
|
||||
@@ -1,4 +1,4 @@
|
||||
module github.com/akyaiy/GoSally-mvp
|
||||
module github.com/akyaiy/GoSally-mvp/src
|
||||
|
||||
go 1.24.4
|
||||
|
||||
@@ -13,8 +13,6 @@ github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE=
|
||||
github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
@@ -18,13 +18,13 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/colors"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/corestate"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/run_manager"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/utils"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/app"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/config"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/logs"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/colors"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/corestate"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/run_manager"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/utils"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/app"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/config"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/logs"
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
@@ -11,17 +11,17 @@ import (
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/colors"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/corestate"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/run_manager"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/update"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/utils"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/app"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/config"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/logs"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/server/gateway"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/server/session"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/server/sv1"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/corestate"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/colors"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/run_manager"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/update"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/utils"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/app"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/config"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/logs"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/server/gateway"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/server/session"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/server/sv1"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/cors"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
)
|
||||
|
||||
var NodeApp = app.New()
|
||||
var AllowedCmdPattern = `^[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*$`
|
||||
|
||||
func Run(cmd *cobra.Command, args []string) {
|
||||
NodeApp.InitialHooks(
|
||||
@@ -60,7 +61,7 @@ func RunHook(ctx context.Context, cs *corestate.CoreState, x *app.AppX) error {
|
||||
serverv1 := sv1.InitV1Server(&sv1.HandlerV1InitStruct{
|
||||
X: x,
|
||||
CS: cs,
|
||||
AllowedCmd: regexp.MustCompile(`^[a-zA-Z0-9]+(>[a-zA-Z0-9]+)*$`),
|
||||
AllowedCmd: regexp.MustCompile(AllowedCmdPattern),
|
||||
Ver: "v1",
|
||||
})
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/utils"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/config"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/utils"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/config"
|
||||
)
|
||||
|
||||
// GetNodeUUID outputs the correct uuid from the file at the path specified in the arguments.
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/utils"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/utils"
|
||||
)
|
||||
|
||||
type RunManagerContract interface {
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/run_manager"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/utils"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/app"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/config"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/run_manager"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/utils"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/app"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/config"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/config"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/config"
|
||||
)
|
||||
|
||||
func NewUUIDRaw(length int) ([]byte, error) {
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/corestate"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/config"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/corestate"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/config"
|
||||
)
|
||||
|
||||
type AppContract interface {
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/colors"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/colors"
|
||||
)
|
||||
|
||||
func (c *Compositor) Print(v any) {
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/config"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/config"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
@@ -3,9 +3,9 @@ package lua
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/corestate"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/app"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/server/rpc"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/corestate"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/app"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/server/rpc"
|
||||
)
|
||||
|
||||
type LuaEngineDeps struct {
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/corestate"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/app"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/server/rpc"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/server/session"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/corestate"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/app"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/server/rpc"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/server/session"
|
||||
)
|
||||
|
||||
// serversApiVer is a type alias for string, used to represent API version strings in the GeneralServer.
|
||||
@@ -3,9 +3,9 @@ package gateway
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/corestate"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/app"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/server/session"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/corestate"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/app"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/server/session"
|
||||
)
|
||||
|
||||
// GeneralServerInit structure only for initialization general server.
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/utils"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/server/rpc"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/utils"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/server/rpc"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/corestate"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/corestate"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -37,7 +37,9 @@ func NewError(code int, message string, data any, id *json.RawMessage) *RPCRespo
|
||||
Error = map[string]any{
|
||||
"code": code,
|
||||
"message": message,
|
||||
"data": data,
|
||||
}
|
||||
if data != nil {
|
||||
Error["data"] = data
|
||||
}
|
||||
|
||||
return &RPCResponse{
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/server/rpc"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/server/rpc"
|
||||
)
|
||||
|
||||
func (h *HandlerV1) Handle(_ context.Context, sid string, r *http.Request, req *rpc.RPCRequest) *rpc.RPCResponse {
|
||||
@@ -17,8 +17,8 @@ import (
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/colors"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/server/rpc"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/colors"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/server/rpc"
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
@@ -186,11 +186,6 @@ func (h *HandlerV1) handleLUA(sid string, r *http.Request, req *rpc.RPCRequest,
|
||||
L.SetField(scriptDataTable, "result", resTable)
|
||||
L.SetField(outTable, "send", L.NewFunction(func(L *lua.LState) int {
|
||||
res := L.Get(1)
|
||||
if res == lua.LNil {
|
||||
__exit = 0
|
||||
L.RaiseError("__successfull")
|
||||
return 0
|
||||
}
|
||||
|
||||
resFTable := scriptDataTable.RawGetString("result")
|
||||
if resPTable, ok := res.(*lua.LTable); ok {
|
||||
@@ -542,7 +537,7 @@ func (h *HandlerV1) handleLUA(sid string, r *http.Request, req *rpc.RPCRequest,
|
||||
llog.Debug("import module crypt.sha256", slog.String("script", path))
|
||||
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))
|
||||
var dataStr = fmt.Sprint(data)
|
||||
|
||||
@@ -6,15 +6,17 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/server/rpc"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/server/rpc"
|
||||
)
|
||||
|
||||
var RPCMethodSeparator = "."
|
||||
|
||||
func (h *HandlerV1) resolveMethodPath(method string) (string, error) {
|
||||
if !h.allowedCmd.MatchString(method) {
|
||||
return "", errors.New(rpc.ErrInvalidMethodFormatS)
|
||||
}
|
||||
|
||||
parts := strings.Split(method, ">")
|
||||
parts := strings.Split(method, RPCMethodSeparator)
|
||||
relPath := filepath.Join(parts...) + ".lua"
|
||||
fullPath := filepath.Join(*h.x.Config.Conf.Node.ComDir, relPath)
|
||||
|
||||
@@ -5,10 +5,12 @@ package sv1
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/akyaiy/GoSally-mvp/internal/core/corestate"
|
||||
"github.com/akyaiy/GoSally-mvp/internal/engine/app"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/core/corestate"
|
||||
"github.com/akyaiy/GoSally-mvp/src/internal/engine/app"
|
||||
)
|
||||
|
||||
var SV1Version = "null"
|
||||
|
||||
// HandlerV1InitStruct structure is only for initialization
|
||||
type HandlerV1InitStruct struct {
|
||||
Ver string
|
||||
@@ -2,7 +2,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/akyaiy/GoSally-mvp/cmd"
|
||||
"github.com/akyaiy/GoSally-mvp/src/cmd"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user