261 lines
9.5 KiB
Lua
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
|