Compare commits

24 Commits

Author SHA1 Message Date
d4413c433f fmt 2025-11-02 11:04:12 +02:00
d9a4bb7871 fix fmt 2025-11-02 10:17:49 +02:00
8e017af3ed change sv1 version 2025-11-01 10:22:14 +02:00
ef6023330d 2025-10-22 15:05:12 +03:00
5474b22fc8 Update README.md 2025-10-12 13:11:55 +03:00
6cd678d9f1 Update README.md 2025-10-12 13:10:10 +03:00
856d3b418c Update README.md 2025-10-12 13:06:37 +03:00
5734ca7a67 update readme.md 2025-10-12 12:56:30 +03:00
608c5aed4a Update README.md 2025-10-12 12:46:07 +03:00
Aleksey
d4d04115f3 Update README.md 2025-10-12 11:10:21 +03:00
Aleksey
4b916f4fc9 Update README.md 2025-10-12 10:46:41 +03:00
54cc496c39 fix noname node message 2025-10-12 01:57:52 +03:00
f7b0014a37 fix bug 2025-10-12 01:56:11 +03:00
54eb5eec6a add wiki to gitignore 2025-10-11 23:20:14 +03:00
6cc24a1e7f update readme 2025-10-11 21:07:51 +03:00
ea41c435dd update readme 2025-10-11 21:04:59 +03:00
d24e1a94ae 2025-10-11 21:01:17 +03:00
846dc06601 update makefile 2025-10-11 16:31:35 +03:00
740fbbff78 fix version message 2025-10-11 16:25:17 +03:00
40be3c8d09 add sv1 version 2025-10-11 16:24:58 +03:00
9c140abc6d make a table 2025-10-11 12:49:53 +03:00
e90233aec4 complete part of readme 2025-10-11 12:44:48 +03:00
df1ef57769 fix bugs 2025-10-10 22:58:31 +03:00
4c840c40bb some changes 2025-10-10 22:54:29 +03:00
35 changed files with 386 additions and 217 deletions

2
.gitignore vendored
View File

@@ -12,6 +12,8 @@ com/_config.lua
Taskfile.yml
config.yaml
wiki
# Garbage
com/_*
com/test.lua

View File

@@ -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,8 +32,8 @@ 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) ./
@# 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 \
@@ -52,30 +55,22 @@ pure-run:
exec ./$(BIN_DIR)/$(APP_NAME)
test:
@go test ./... | grep -v '^?' || true
@cd src && go test ./... | grep -v '^?' || true
fmt:
@go fmt ./internal/./...
@go fmt ./cmd/./...
@go fmt ./hooks/./...
@$(GOPATH)/bin/goimports -w ./internal/
@$(GOPATH)/bin/goimports -w ./cmd/
@$(GOPATH)/bin/goimports -w ./hooks/
@cd src && go fmt .
@cd src && $(GOPATH)/bin/goimports -w .
vet:
@go vet ./...
lint:
@$(GOPATH)/bin/golangci-lint run
@cd src && go vet ./...
check: fmt vet lint test
licenses:
lint:
@cd src && $(GOPATH)/bin/golangci-lint run ./...
@$(GOPATH)/bin/go-licenses save ./... --save_path=third_party/licenses --force
@echo "Licenses have been exported to third_party/licenses"
clean:
@rm -rf bin
licenses:
@cd src && $(GOPATH)/bin/go-licenses save ./... --save_path=../third_party/licenses --force
@echo "Licenses have been exported to third_party/licenses"
help:
@echo "Available commands: $$(cat Makefile | grep -E '^[a-zA-Z_-]+:.*?' | grep -v -- '-setup:' | sed 's/:.*//g' | sort | uniq | tr '\n' ' ')"

147
README.md
View File

@@ -1,51 +1,132 @@
# Go Sally MVP (Minimum/Minimal Viable Product)
[![Status](https://img.shields.io/badge/status-MVP-orange.svg)]()
[![Go Version](https://img.shields.io/badge/Go-1.24.6-informational)](https://go.dev/)
[![Lua Version](https://img.shields.io/badge/Lua-5.1-informational)](https://www.lua.org/manual/5.1/)
[![Go Reference](https://pkg.go.dev/badge/github.com/akyaiy/GoSally-mvp.svg)](https://pkg.go.dev/github.com/akyaiy/GoSally-mvp)
[![License](https://img.shields.io/badge/license-BSD--3--Clause-blue)](LICENSE)
### 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.
[![Last Commit](https://img.shields.io/github/last-commit/akyaiy/GoSally-mvp)]()
[![Commits per month](https://img.shields.io/github/commit-activity/m/akyaiy/GoSally-mvp)]()
[![Docs](https://img.shields.io/badge/docs-wiki-blue)](https://github.com/akyaiy/GoSally-mvp/wiki)
### 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).
### 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
> ⚡ **What, Why, Why Care?**
3 directories, 5 files
> **What:** Go Sally is a lightweight decentralized node system with Lua scripting and JSON-RPC2.0.
```
Launch by command
> **Why:** Large admin tools are too heavy, and Raspberry Pi and small servers require a lightweight, modular architecture.
> **Why Care:** Create, automate, and expand your infrastructure quickly, without unnecessary software or dependencies.
## Navigation
* [Core features](#core-features)
* [Quick start](#quick-start)
* [Test it](#test-it)
* [Concept](#concept)
* [API](#api)
* [License](#license)
* [Wiki →](https://github.com/akyaiy/GoSally-mvp/wiki)
> [!NOTE]
> If you see "💡" in the text, it means the information below is about plans for the future of the project.
## Core features
- **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.
**Why Care?** Multiple nodes on one machine allow testing, experimentation, and scaling small infrastructures without extra hardware or complex setup.</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. **Why Care?** Easy-to-use API means you can control nodes from anywhere, including lightweight web clients, without compiling extra code.</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
session.response.send(session.request.params.get())
-- send everything passed in the parameters in response.
```
**Why Care?** You can extend node behavior dynamically, write custom logic fast, and iterate without recompiling — perfect for experiments or automation.
</details>
- **Relatively flexible configuration** <details>
you can configure the server port, address, name, node settings, and more. 💡 More settings are planned in the future. **Why Care?** Configure nodes for any environment, from Raspberry Pi to VPS, without touching the source code. obviously :)</details>
- ***And more in the future***
> [!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.
## Quick start
```bash
git clone https://github.com/akyaiy/GoSally-mvp.git && \
cd GoSally-mvp && \
make build && \
echo -e "node:\n com_dir: \"%path%/com\"" > config.yaml && \
mkdir -p com && \
echo -e 'local session = require("internal.session")\n\nsession.response.send(session.request.params.get())' > com/echo.lua && \
./bin/node run
```
or for structured logs
```bash
./bin/node run | jq
```
Example of GET request to server
If you have problems, make sure you have all [dependencies](https://github.com/akyaiy/GoSally-mvp/wiki/Getting-started#installing-dependencies) installed, otherwise [file an issue report](https://github.com/akyaiy/GoSally-mvp/issues)
### Test it
```bash
curl -s http://localhost:8080/api/v1/com/echo?msg=Hello
curl -X POST http://localhost:8080/com \
-d '{"jsonrpc":"2.0","context-version": "v1","method":"echo","params":["Hi!!"],"id":1}'
```
Then the response from the server
Expected response:
```json
{
"ResponsibleAgentUUID": "4593a87000bbe088f4e79c477e9c90d3",
"RequestedCommand": "echo",
"Response": {
"answer": "Hello",
"status": "ok"
"jsonrpc": "2.0",
"id": 1,
"result": [
"Hi!!"
],
"data": {
"responsible-node": "a0e1c440473ffd4d87e32cff2717f5b3",
"salt": "f26df732-a3be-4400-8e71-b8dc3ba705fc",
"checksum-md5": "cd8bec6a365d1b8ee90773567cb3ad0a"
}
}
```
### How to install
**You don't need it now, but you can figure it out with the Makefile**
## Concept
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.
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
{
"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 |
## License
Distributed under the BSD 3-Clause License. See [`LICENSE`](./LICENSE) for more information.
[^1]: Go Sally

22
com/Access/_common.lua Normal file
View File

@@ -0,0 +1,22 @@
-- File com/Access/_common.lua
--
-- Created at 2025-21-10
--
-- 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/Access/_errors.lua Normal file
View File

@@ -0,0 +1,30 @@
-- File com/Access/_errors.lua
--
-- Created at 2025-21-10
-- Description:
--- Centralized error definitions for Access 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,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

View File

@@ -1,4 +1,4 @@
// The cmd package is the main package where all the main hooks and methods are called.
// The cmd package is the main package where all the main hooks and methods are called.
// GoSally uses spf13/cobra to organize all the calls.
package cmd
@@ -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"
)
@@ -24,7 +24,7 @@ scripts in a given directory. For more information, visit: https://gosally.oblat
},
}
// Execute prepares global log, loads cmdline args
// Execute prepares global log, loads cmdline args
// and executes rootCmd.Execute()
func Execute() {
log.SetOutput(os.Stdout)

View File

@@ -1,7 +1,7 @@
package cmd
import (
"github.com/akyaiy/GoSally-mvp/hooks"
"github.com/akyaiy/GoSally-mvp/src/hooks"
"github.com/spf13/cobra"
)

View File

@@ -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)
},

View File

@@ -1,4 +1,4 @@
module github.com/akyaiy/GoSally-mvp
module github.com/akyaiy/GoSally-mvp/src
go 1.24.4

View File

@@ -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=

View File

@@ -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"
)
@@ -85,7 +85,7 @@ func InitUUIDHook(_ context.Context, cs *corestate.CoreState, x *app.AppX) {
corestate.NODE_UUID = uuid32
}
// The hook is responsible for checking the initialization stage
// The hook is responsible for checking the initialization stage
// and restarting in some cases
func InitRuntimeHook(_ context.Context, cs *corestate.CoreState, x *app.AppX) {
if *x.Config.Env.ParentStagePID != os.Getpid() {
@@ -138,7 +138,7 @@ func InitRuntimeHook(_ context.Context, cs *corestate.CoreState, x *app.AppX) {
}
// post-init stage
// The hook creates a run.lock file, which contains information
// The hook creates a run.lock file, which contains information
// about the process and the node, in the runtime directory.
func InitRunlockHook(_ context.Context, cs *corestate.CoreState, x *app.AppX) {
NodeApp.Fallback(func(ctx context.Context, cs *corestate.CoreState, x *app.AppX) {
@@ -192,7 +192,7 @@ func InitRunlockHook(_ context.Context, cs *corestate.CoreState, x *app.AppX) {
}
}
// The hook reads the configuration and replaces special expressions
// The hook reads the configuration and replaces special expressions
// (%tmp% and so on) in string fields with the required data.
func InitConfigReplHook(_ context.Context, cs *corestate.CoreState, x *app.AppX) {
if !slices.Contains(*x.Config.Conf.DisableWarnings, "--WNonStdTmpDir") && os.TempDir() != "/tmp" {
@@ -218,7 +218,7 @@ func InitConfigReplHook(_ context.Context, cs *corestate.CoreState, x *app.AppX)
}
}
// The hook is responsible for outputting the
// The hook is responsible for outputting the
// final config and asking for confirmation.
func InitConfigPrintHook(ctx context.Context, cs *corestate.CoreState, x *app.AppX) {
if *x.Config.Conf.Node.ShowConfig {
@@ -234,7 +234,11 @@ func InitConfigPrintHook(ctx context.Context, cs *corestate.CoreState, x *app.Ap
}
}
x.Log.Printf("Starting \"%s\" node", *x.Config.Conf.Node.Name)
if *x.Config.Conf.Node.Name == "noname" {
x.Log.Printf("Starting node")
} else {
x.Log.Printf("Starting \"%s\" node", *x.Config.Conf.Node.Name)
}
}
func InitSLogHook(_ context.Context, cs *corestate.CoreState, x *app.AppX) {
@@ -250,7 +254,7 @@ func InitSLogHook(_ context.Context, cs *corestate.CoreState, x *app.AppX) {
*x.SLog = *newSlog
}
// The method goes through the entire config structure through
// The method goes through the entire config structure through
// reflection and replaces string fields with the required ones.
func processConfig(conf any, replacements map[string]any) error {
val := reflect.ValueOf(conf)

View File

@@ -11,17 +11,18 @@ 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/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/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/akyaiy/GoSally-mvp/src/internal/server/sv2"
"github.com/go-chi/chi/v5"
"github.com/go-chi/cors"
"github.com/spf13/cobra"
@@ -65,13 +66,20 @@ func RunHook(ctx context.Context, cs *corestate.CoreState, x *app.AppX) error {
Ver: "v1",
})
sv2 := sv2.InitServer(&sv2.HandlerInitStruct{
X: x,
CS: cs,
AllowedCmd: regexp.MustCompile(AllowedCmdPattern),
Ver: "v2",
})
session_manager := session.New(*x.Config.Conf.HTTPServer.SessionTTL)
s := gateway.InitGateway(&gateway.GatewayServerInit{
SM: session_manager,
CS: cs,
X: x,
}, serverv1)
}, serverv1, sv2)
r := chi.NewRouter()
r.Use(cors.Handler(cors.Options{

View File

@@ -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.

View File

@@ -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 {

View File

@@ -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"
)

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -61,6 +61,7 @@ func (c *Compositor) LoadConf(path string) error {
v.SetDefault("log.json_format", "false")
v.SetDefault("log.level", "info")
v.SetDefault("log.output", "%2%")
v.SetDefault("disable_warnings", []string{})
if err := v.ReadInConfig(); err != nil {
return fmt.Errorf("error reading config: %w", err)

View File

@@ -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) {

View File

@@ -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"
)

View File

@@ -15,7 +15,7 @@ func NewLuaPool() *LuaPool {
pool: sync.Pool{
New: func() any {
L := lua.NewState()
return L
},
},
@@ -30,6 +30,6 @@ func (lp *LuaPool) Put(L *lua.LState) {
L.Close()
newL := lua.NewState()
lp.pool.Put(newL)
}

View File

@@ -3,21 +3,20 @@ 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 {
HttpRequest *http.Request
JSONRPCRequest *rpc.RPCRequest
SessionUUID string
ScriptPath string
SessionUUID string
ScriptPath string
}
type LuaEngineContract interface {
Handle(deps *LuaEngineDeps) *rpc.RPCResponse
}
type LuaEngine struct {

View File

@@ -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.

View File

@@ -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.

View File

@@ -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"
)

View File

@@ -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"
)

View File

@@ -80,10 +80,10 @@ func loadDBMod(llog *slog.Logger, sid string) func(*lua.LState) int {
mt := L.NewTypeMetatable("gosally_db")
L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{
"exec": dbExec,
"query": dbQuery,
"exec": dbExec,
"query": dbQuery,
"query_row": dbQueryRow,
"close": dbClose,
"close": dbClose,
}))
L.SetField(dbMod, "__seed", lua.LString(sid))
@@ -215,43 +215,43 @@ func dbExec(L *lua.LState) int {
}
func dbQueryRow(L *lua.LState) int {
ud := L.CheckUserData(1)
conn, ok := ud.Value.(*DBConnection)
if !ok {
L.Push(lua.LNil)
L.Push(lua.LString("invalid database connection"))
return 2
}
ud := L.CheckUserData(1)
conn, ok := ud.Value.(*DBConnection)
if !ok {
L.Push(lua.LNil)
L.Push(lua.LString("invalid database connection"))
return 2
}
query := L.CheckString(2)
query := L.CheckString(2)
var args []any
if L.GetTop() >= 3 {
params := L.CheckTable(3)
params.ForEach(func(k lua.LValue, v lua.LValue) {
args = append(args, ConvertLuaTypesToGolang(v))
})
}
var args []any
if L.GetTop() >= 3 {
params := L.CheckTable(3)
params.ForEach(func(k lua.LValue, v lua.LValue) {
args = append(args, ConvertLuaTypesToGolang(v))
})
}
if conn.log {
conn.logger.Info("DB QueryRow",
slog.String("query", query),
slog.Any("params", args))
}
if conn.log {
conn.logger.Info("DB QueryRow",
slog.String("query", query),
slog.Any("params", args))
}
mtx := getDBMutex(conn.dbPath)
mtx.RLock()
defer mtx.RUnlock()
mtx := getDBMutex(conn.dbPath)
mtx.RLock()
defer mtx.RUnlock()
db, err := sql.Open("sqlite", conn.dbPath+"?_busy_timeout=5000&_journal_mode=WAL&_sync=NORMAL&_cache_size=-10000")
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
defer db.Close()
db, err := sql.Open("sqlite", conn.dbPath+"?_busy_timeout=5000&_journal_mode=WAL&_sync=NORMAL&_cache_size=-10000")
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
defer db.Close()
row := db.QueryRow(query, args...)
row := db.QueryRow(query, args...)
columns := []string{}
stmt, err := db.Prepare(query)
@@ -278,36 +278,36 @@ func dbQueryRow(L *lua.LState) int {
columns = append(columns, c)
}
colCount := len(columns)
values := make([]any, colCount)
valuePtrs := make([]any, colCount)
for i := range columns {
valuePtrs[i] = &values[i]
}
colCount := len(columns)
values := make([]any, colCount)
valuePtrs := make([]any, colCount)
for i := range columns {
valuePtrs[i] = &values[i]
}
err = row.Scan(valuePtrs...)
if err != nil {
if err == sql.ErrNoRows {
L.Push(lua.LNil)
return 1
}
L.Push(lua.LNil)
L.Push(lua.LString(fmt.Sprintf("scan failed: %v", err)))
return 2
}
err = row.Scan(valuePtrs...)
if err != nil {
if err == sql.ErrNoRows {
L.Push(lua.LNil)
return 1
}
L.Push(lua.LNil)
L.Push(lua.LString(fmt.Sprintf("scan failed: %v", err)))
return 2
}
rowTable := L.NewTable()
for i, col := range columns {
val := values[i]
if val == nil {
L.SetField(rowTable, col, lua.LNil)
} else {
L.SetField(rowTable, col, ConvertGolangTypesToLua(L, val))
}
}
rowTable := L.NewTable()
for i, col := range columns {
val := values[i]
if val == nil {
L.SetField(rowTable, col, lua.LNil)
} else {
L.SetField(rowTable, col, ConvertGolangTypesToLua(L, val))
}
}
L.Push(rowTable)
return 1
L.Push(rowTable)
return 1
}
func dbQuery(L *lua.LState) int {

View File

@@ -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 {

View File

@@ -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"
)

View File

@@ -6,7 +6,7 @@ import (
"path/filepath"
"strings"
"github.com/akyaiy/GoSally-mvp/internal/server/rpc"
"github.com/akyaiy/GoSally-mvp/src/internal/server/rpc"
)
var RPCMethodSeparator = "."

View File

@@ -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 = "v1"
// HandlerV1InitStruct structure is only for initialization
type HandlerV1InitStruct struct {
Ver string

View File

@@ -0,0 +1,12 @@
package sv2
import (
"context"
"net/http"
"github.com/akyaiy/GoSally-mvp/src/internal/server/rpc"
)
func (h *Handler) Handle(_ context.Context, sid string, r *http.Request, req *rpc.RPCRequest) *rpc.RPCResponse {
return nil
}

View File

@@ -0,0 +1,43 @@
// SV2 works with binaries, scripts, and anything else that has access to stdin/stdout.
// Modules run in a separate process and communicate via I/O.
package sv2
import (
"regexp"
"github.com/akyaiy/GoSally-mvp/src/internal/core/corestate"
"github.com/akyaiy/GoSally-mvp/src/internal/engine/app"
)
// HandlerV2InitStruct structure is only for initialization
type HandlerInitStruct struct {
Ver string
CS *corestate.CoreState
X *app.AppX
AllowedCmd *regexp.Regexp
}
type Handler struct {
cs *corestate.CoreState
x *app.AppX
// allowedCmd and listAllowedCmd are regular expressions used to validate command names.
allowedCmd *regexp.Regexp
ver string
}
func InitServer(o *HandlerInitStruct) *Handler {
return &Handler{
cs: o.CS,
x: o.X,
allowedCmd: o.AllowedCmd,
ver: o.Ver,
}
}
// GetVersion returns the API version of the HandlerV1, which is set during initialization.
// This version is used to identify the API version in the request routing.
func (h *Handler) GetVersion() string {
return h.ver
}

View File

@@ -2,7 +2,7 @@
package main
import (
"github.com/akyaiy/GoSally-mvp/cmd"
"github.com/akyaiy/GoSally-mvp/src/cmd"
_ "modernc.org/sqlite"
)