Files
tekton/server/nakama/lua/user.lua
T
2026-05-19 17:30:29 +08:00

261 lines
9.5 KiB
Lua

local nk = require("nakama")
local user = {}
function user.rpc_get_user_profile(context, payload)
local request = nk.json_decode(payload or "{}")
local targetUserId = request.user_id or context.user_id
local status, account = pcall(nk.account_get_id, targetUserId)
if not status then error("Account not found") end
local metadata = {}
if account.user.metadata then
status, metadata = pcall(nk.json_decode, account.user.metadata)
if not status then metadata = {} end
end
if metadata.banned and targetUserId == context.user_id then
if metadata.ban_expires then
-- Note: ban_expires stored as Unix time in Lua (seconds) or ISO string depending on how it was stored
-- Let's check against current os.time() assuming Unix time
local expiresAt = tonumber(metadata.ban_expires)
if not expiresAt and type(metadata.ban_expires) == "string" then
-- basic check if we stored iso string
-- We assume it's valid ISO string and lua os.time might not parse it easily without custom function
-- As a fallback, we'll keep the ban if we can't parse it
error("Account banned until " .. metadata.ban_expires .. ". Reason: " .. (metadata.ban_reason or ""))
end
if expiresAt and expiresAt <= os.time() then
metadata.banned = nil
metadata.ban_reason = nil
metadata.ban_expires = nil
nk.account_update_id(targetUserId, nil, nil, nil, nil, nil, nil, nk.json_encode(metadata))
else
error("Account banned until " .. tostring(metadata.ban_expires) .. ". Reason: " .. (metadata.ban_reason or ""))
end
else
error("Account permanently banned. Reason: " .. (metadata.ban_reason or ""))
end
end
return nk.json_encode({
user_id = account.user.id,
username = account.user.username,
display_name = account.user.display_name,
avatar_url = account.user.avatar_url,
create_time = account.user.create_time,
role = metadata.role or "player"
})
end
function user.rpc_update_user_profile(context, payload)
if not context.user_id then error("Not authenticated") end
local request = nk.json_decode(payload)
local status, err = pcall(nk.account_update_id,
context.user_id,
nil,
request.display_name or nil,
nil,
nil,
nil,
request.avatar_url or nil,
nil
)
if not status then
nk.logger_error("Failed to update profile: " .. tostring(err))
error("Failed to update profile")
end
return nk.json_encode({ success = true })
end
function user.rpc_search_users(context, payload)
if not context.user_id then error("Not authenticated") end
local request = nk.json_decode(payload or "{}")
local query = request.query or ""
local users = {}
local sql = ""
local params = {}
if query == "" then
sql = "SELECT id, username, display_name, metadata FROM users WHERE id != '00000000-0000-0000-0000-000000000000' ORDER BY create_time DESC LIMIT 100"
else
sql = "SELECT id, username, display_name, metadata FROM users WHERE (username ILIKE $1 OR display_name ILIKE $1) AND id != '00000000-0000-0000-0000-000000000000' ORDER BY create_time DESC LIMIT 100"
params = {"%" .. query .. "%"}
end
local status, rows = pcall(nk.sql_query, sql, params)
if status and rows then
for _, row in ipairs(rows) do
local metadata = {}
if row.metadata then
local s, m = pcall(nk.json_decode, row.metadata)
if s then metadata = m end
end
table.insert(users, {
user_id = row.id,
username = row.username or "",
display_name = row.display_name or row.username or "",
avatar_url = metadata.avatar_url or ""
})
end
end
return nk.json_encode({ users = users })
end
function user.rpc_change_credentials(context, payload)
if not context.user_id then error("Not authenticated") end
local req = nk.json_decode(payload or "{}")
local account = nk.account_get_id(context.user_id)
if account.email then
if not req.current_password then error("Current password required") end
local status = pcall(nk.authenticate_email, account.email, req.current_password, false)
if not status then error("Incorrect current password.") end
nk.unlink_email(context.user_id, account.email, req.current_password)
end
local status, err = pcall(nk.link_email, context.user_id, req.new_email, req.new_password)
if not status then
if account.email then pcall(nk.link_email, context.user_id, account.email, req.current_password) end
error("Failed to set new credentials: " .. tostring(err))
end
return nk.json_encode({ success = true })
end
function user.rpc_send_lobby_invite(context, payload)
if not context.user_id then error("Not authenticated") end
local req = nk.json_decode(payload or "{}")
if not req.to_user_id or not req.match_id then error("Missing to_user_id or match_id") end
local sender = nk.account_get_id(context.user_id)
local senderName = sender.user.display_name or sender.user.username or "Someone"
nk.notification_send(
req.to_user_id,
senderName .. " invited you to their lobby",
nk.json_encode({ match_id = req.match_id, from_name = senderName }),
1001,
context.user_id,
true
)
nk.logger_info("Lobby invite sent from " .. context.user_id .. " to " .. req.to_user_id .. " for match " .. req.match_id)
return nk.json_encode({ success = true })
end
function user.rpc_send_friend_request(context, payload)
if not context.user_id then error("Not authenticated") end
local request = nk.json_decode(payload or "{}")
local targetUserId = request.user_id or ""
if targetUserId == "" then error("user_id is required") end
if targetUserId == context.user_id then error("Cannot add yourself") end
local senderAccount = nk.account_get_id(context.user_id)
local senderName = senderAccount.user.display_name or senderAccount.user.username or "Someone"
nk.notification_send(
targetUserId,
"Friend Request",
nk.json_encode({ from_user_id = context.user_id, from_name = senderName }),
1002,
context.user_id,
true
)
nk.logger_info("Friend request notification sent from " .. context.user_id .. " to " .. targetUserId)
return nk.json_encode({ success = true })
end
function user.after_authenticate(context, out, payload)
if not context.user_id then return end
-- We store the last 10 logins in user metadata or a dedicated collection
local login_entry = {
time = os.time(),
ip = context.client_ip or "unknown"
}
local status, result = pcall(nk.storage_read, {{collection = "history", key = "logins", user_id = context.user_id}})
local logins = {}
if status and result and #result > 0 then
logins = result[1].value.logins or {}
end
table.insert(logins, 1, login_entry)
-- Keep only last 20 logins to save space
while #logins > 20 do table.remove(logins) end
pcall(nk.storage_write, {{
collection = "history",
key = "logins",
user_id = context.user_id,
value = { logins = logins },
permission_read = 0,
permission_write = 0
}})
end
function user.rpc_admin_get_user_history(context, payload)
local utils = require("lua.utils")
utils.require_admin(context)
local request = nk.json_decode(payload or "{}")
local targetUserId = request.user_id
if not targetUserId then error("user_id is required") end
local history = {
wallet_ledger = {},
logins = {},
matches = {}
}
-- 1. Fetch Wallet Ledger (Economy History)
local status_wallet, wallet_result = pcall(nk.wallet_ledger_list, targetUserId, 50)
if status_wallet and wallet_result then
history.wallet_ledger = wallet_result.items or {}
end
-- 2. Fetch Login History
local status_logins, login_result = pcall(nk.storage_read, {{collection = "history", key = "logins", user_id = targetUserId}})
if status_logins and login_result and #login_result > 0 then
history.logins = login_result[1].value.logins or {}
end
-- 3. Fetch Match History (If stored in collection 'matches')
local status_matches, match_result = pcall(nk.storage_list, targetUserId, "matches", 50, "")
if status_matches and match_result then
for _, obj in ipairs(match_result.objects or {}) do
table.insert(history.matches, obj.value)
end
end
return nk.json_encode({ history = history })
end
nk.register_rpc(user.rpc_get_user_profile, "get_user_profile")
nk.register_rpc(user.rpc_update_user_profile, "update_user_profile")
nk.register_rpc(user.rpc_search_users, "search_users")
nk.register_rpc(user.rpc_change_credentials, "change_credentials")
nk.register_rpc(user.rpc_send_lobby_invite, "send_lobby_invite")
nk.register_rpc(user.rpc_send_friend_request, "send_friend_request")
nk.register_rpc(user.rpc_admin_get_user_history, "admin_get_user_history")
nk.register_req_after(user.after_authenticate, "AuthenticateDevice")
nk.register_req_after(user.after_authenticate, "AuthenticateEmail")
nk.register_req_after(user.after_authenticate, "AuthenticateCustom")
nk.logger_info("LUA TEST: user module loaded")
return user