Compare commits
No commits in common. "e05369431cfd76e405b84e2a5d3141fa23782ed0" and "8b4a3b27c085d2a625df265a6590cee1c98b4dba" have entirely different histories.
e05369431c
...
8b4a3b27c0
File diff suppressed because it is too large
Load Diff
@ -96,7 +96,7 @@ func NewWorkerPool(size int, masterState *luajit.State, stateCreator StateCreato
|
|||||||
workerCount: size,
|
workerCount: size,
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range size {
|
for i := 0; i < size; i++ {
|
||||||
worker, err := pool.createWorker(i)
|
worker, err := pool.createWorker(i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pool.Close()
|
pool.Close()
|
||||||
|
|||||||
@ -490,16 +490,3 @@ func (store *Store) save() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloseAllStores saves and closes all open stores
|
|
||||||
func CloseAllStores() {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
|
|
||||||
for name, store := range stores {
|
|
||||||
if store.filename != "" {
|
|
||||||
store.save()
|
|
||||||
}
|
|
||||||
delete(stores, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
211
modules/sessions/sessions.lua
Normal file
211
modules/sessions/sessions.lua
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
local kv = require("kv")
|
||||||
|
local crypto = require("crypto")
|
||||||
|
|
||||||
|
local sessions = {}
|
||||||
|
local stores = {}
|
||||||
|
local default_store = nil
|
||||||
|
|
||||||
|
-- ======================================================================
|
||||||
|
-- CORE FUNCTIONS
|
||||||
|
-- ======================================================================
|
||||||
|
|
||||||
|
function sessions.init(store_name, filename)
|
||||||
|
store_name = store_name or "sessions"
|
||||||
|
if not kv.open(store_name, filename) then return false end
|
||||||
|
stores[store_name] = true
|
||||||
|
if not default_store then default_store = store_name end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function sessions.create(session_id, data, store_name)
|
||||||
|
if type(session_id) ~= "string" then error("session ID must be a string", 2) end
|
||||||
|
if data ~= nil and type(data) ~= "table" then error("data must be a table", 2) end
|
||||||
|
|
||||||
|
store_name = store_name or default_store
|
||||||
|
if not store_name then error("No session store initialized", 2) end
|
||||||
|
|
||||||
|
local session_data = {
|
||||||
|
data = data or {},
|
||||||
|
_created = os.time(),
|
||||||
|
_last_accessed = os.time()
|
||||||
|
}
|
||||||
|
|
||||||
|
return kv.set(store_name, "session:" .. session_id, json.encode(session_data))
|
||||||
|
end
|
||||||
|
|
||||||
|
function sessions.get(session_id, store_name)
|
||||||
|
if type(session_id) ~= "string" then error("session ID must be a string", 2) end
|
||||||
|
|
||||||
|
store_name = store_name or default_store
|
||||||
|
if not store_name then error("No session store initialized", 2) end
|
||||||
|
|
||||||
|
local json_str = kv.get(store_name, "session:" .. session_id)
|
||||||
|
if not json_str then return nil end
|
||||||
|
|
||||||
|
local session_data = json.decode(json_str)
|
||||||
|
if not session_data then return nil end
|
||||||
|
|
||||||
|
-- Update last accessed
|
||||||
|
session_data._last_accessed = os.time()
|
||||||
|
kv.set(store_name, "session:" .. session_id, json.encode(session_data))
|
||||||
|
|
||||||
|
-- Return flattened data with metadata
|
||||||
|
local result = session_data.data or {}
|
||||||
|
result._created = session_data._created
|
||||||
|
result._last_accessed = session_data._last_accessed
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function sessions.update(session_id, data, store_name)
|
||||||
|
if type(session_id) ~= "string" then error("session ID must be a string", 2) end
|
||||||
|
if type(data) ~= "table" then error("data must be a table", 2) end
|
||||||
|
|
||||||
|
store_name = store_name or default_store
|
||||||
|
if not store_name then error("No session store initialized", 2) end
|
||||||
|
|
||||||
|
local json_str = kv.get(store_name, "session:" .. session_id)
|
||||||
|
if not json_str then return false end
|
||||||
|
|
||||||
|
local session_data = json.decode(json_str)
|
||||||
|
if not session_data then return false end
|
||||||
|
|
||||||
|
session_data.data = data
|
||||||
|
session_data._last_accessed = os.time()
|
||||||
|
|
||||||
|
return kv.set(store_name, "session:" .. session_id, json.encode(session_data))
|
||||||
|
end
|
||||||
|
|
||||||
|
function sessions.delete(session_id, store_name)
|
||||||
|
if type(session_id) ~= "string" then error("session ID must be a string", 2) end
|
||||||
|
|
||||||
|
store_name = store_name or default_store
|
||||||
|
if not store_name then error("No session store initialized", 2) end
|
||||||
|
return kv.delete(store_name, "session:" .. session_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
function sessions.cleanup(max_age, store_name)
|
||||||
|
store_name = store_name or default_store
|
||||||
|
if not store_name then error("No session store initialized", 2) end
|
||||||
|
|
||||||
|
local keys = kv.keys(store_name)
|
||||||
|
local current_time = os.time()
|
||||||
|
local deleted = 0
|
||||||
|
|
||||||
|
for _, key in ipairs(keys) do
|
||||||
|
if key:match("^session:") then
|
||||||
|
local json_str = kv.get(store_name, key)
|
||||||
|
if json_str then
|
||||||
|
local session_data = json.decode(json_str)
|
||||||
|
if session_data and session_data._last_accessed then
|
||||||
|
if current_time - session_data._last_accessed > max_age then
|
||||||
|
kv.delete(store_name, key)
|
||||||
|
deleted = deleted + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return deleted
|
||||||
|
end
|
||||||
|
|
||||||
|
function sessions.close(store_name)
|
||||||
|
local success = kv.close(store_name)
|
||||||
|
stores[store_name] = nil
|
||||||
|
if default_store == store_name then
|
||||||
|
default_store = next(stores)
|
||||||
|
end
|
||||||
|
return success
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ======================================================================
|
||||||
|
-- UTILITIES
|
||||||
|
-- ======================================================================
|
||||||
|
|
||||||
|
function sessions.generate_id()
|
||||||
|
return crypto.random_alphanumeric(32)
|
||||||
|
end
|
||||||
|
|
||||||
|
function sessions.exists(session_id, store_name)
|
||||||
|
store_name = store_name or default_store
|
||||||
|
if not store_name then error("No session store initialized", 2) end
|
||||||
|
return kv.has(store_name, "session:" .. session_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
function sessions.list(store_name)
|
||||||
|
store_name = store_name or default_store
|
||||||
|
if not store_name then error("No session store initialized", 2) end
|
||||||
|
|
||||||
|
local keys = kv.keys(store_name)
|
||||||
|
local session_ids = {}
|
||||||
|
|
||||||
|
for _, key in ipairs(keys) do
|
||||||
|
local session_id = key:match("^session:(.+)")
|
||||||
|
if session_id then
|
||||||
|
table.insert(session_ids, session_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return session_ids
|
||||||
|
end
|
||||||
|
|
||||||
|
function sessions.count(store_name)
|
||||||
|
return #sessions.list(store_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
function sessions.reset()
|
||||||
|
stores = {}
|
||||||
|
default_store = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ======================================================================
|
||||||
|
-- OOP INTERFACE
|
||||||
|
-- ======================================================================
|
||||||
|
|
||||||
|
local SessionStore = {}
|
||||||
|
SessionStore.__index = SessionStore
|
||||||
|
|
||||||
|
function sessions.create_store(store_name, filename)
|
||||||
|
if not sessions.init(store_name, filename) then
|
||||||
|
error("Failed to initialize store '" .. store_name .. "'", 2)
|
||||||
|
end
|
||||||
|
return setmetatable({name = store_name}, SessionStore)
|
||||||
|
end
|
||||||
|
|
||||||
|
function SessionStore:create(session_id, data)
|
||||||
|
return sessions.create(session_id, data, self.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
function SessionStore:get(session_id)
|
||||||
|
return sessions.get(session_id, self.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
function SessionStore:update(session_id, data)
|
||||||
|
return sessions.update(session_id, data, self.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
function SessionStore:delete(session_id)
|
||||||
|
return sessions.delete(session_id, self.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
function SessionStore:cleanup(max_age)
|
||||||
|
return sessions.cleanup(max_age, self.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
function SessionStore:exists(session_id)
|
||||||
|
return sessions.exists(session_id, self.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
function SessionStore:list()
|
||||||
|
return sessions.list(self.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
function SessionStore:count()
|
||||||
|
return sessions.count(self.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
function SessionStore:close()
|
||||||
|
return sessions.close(self.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
return sessions
|
||||||
@ -642,7 +642,7 @@ function string.template(template_str, vars)
|
|||||||
if type(template_str) ~= "string" then error("string.template: first argument must be a string", 2) end
|
if type(template_str) ~= "string" then error("string.template: first argument must be a string", 2) end
|
||||||
if type(vars) ~= "table" then error("string.template: second argument must be a table", 2) end
|
if type(vars) ~= "table" then error("string.template: second argument must be a table", 2) end
|
||||||
|
|
||||||
return template_str:gsub("%${([%w_%.-]+)}", function(path)
|
return template_str:gsub("%${([%w_%.]+)}", function(path)
|
||||||
local value = vars
|
local value = vars
|
||||||
|
|
||||||
-- Handle simple variables (no dots)
|
-- Handle simple variables (no dots)
|
||||||
|
|||||||
@ -59,11 +59,6 @@ func runOnce(scriptPath string) {
|
|||||||
go func() {
|
go func() {
|
||||||
<-sigChan
|
<-sigChan
|
||||||
fmt.Println("\nShutting down...")
|
fmt.Println("\nShutting down...")
|
||||||
|
|
||||||
// Close main state first (saves KV stores)
|
|
||||||
luaState.Close()
|
|
||||||
// Then stop servers (closes worker states)
|
|
||||||
http.StopAllServers()
|
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"Moonshark/modules"
|
"Moonshark/modules"
|
||||||
"Moonshark/modules/http"
|
"Moonshark/modules/http"
|
||||||
"Moonshark/modules/kv"
|
|
||||||
|
|
||||||
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
||||||
)
|
)
|
||||||
@ -285,10 +284,6 @@ func (s *State) IsWorker() bool {
|
|||||||
// Close cleans up the state and releases resources
|
// Close cleans up the state and releases resources
|
||||||
func (s *State) Close() {
|
func (s *State) Close() {
|
||||||
if s.State != nil {
|
if s.State != nil {
|
||||||
if !s.isWorker {
|
|
||||||
kv.CloseAllStores()
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Cleanup()
|
s.Cleanup()
|
||||||
s.State.Close()
|
s.State.Close()
|
||||||
s.State = nil
|
s.State = nil
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"session:x5joQraQyEkfzzRMrcP4o8yK0xjgwtCW": "{\"todos\":[{\"text\":\"asdasd\",\"completed\":true,\"id\":\"1753414744_8147\",\"created_at\":1753414744},{\"text\":\"fsdf\",\"completed\":true,\"id\":\"1753414748_8147\",\"created_at\":1753414748},{\"text\":\"asdasd\",\"completed\":false,\"id\":\"1753415063_8147\",\"created_at\":1753415063},{\"id\":\"1753415066_8147\",\"completed\":false,\"text\":\"asdkjfhaslkjdhflkasjhdf\",\"created_at\":1753415066},{\"text\":\"alsdhnfpuihawepiufhbpioweHBFIOEWBSF\",\"completed\":false,\"id\":\"1753415069_8147\",\"created_at\":1753415069}]}"
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user