Files
2026-08-09 04:32:01 +02:00

162 lines
4.8 KiB
Lua

Bridge.Database.AfterMigration("sky_phone", function()
local marker_colors = {
blue = true,
green = true,
orange = true,
purple = true,
red = true,
}
local function affected_rows(result)
if type(result) == "number" then
return result
end
return type(result) == "table" and tonumber(result.affectedRows) or 0
end
local function text_length(value)
return type(value) == "string" and utf8.len(value) or nil
end
local function marker_dto(row)
return {
id = row.id,
label = row.label,
color = row.color,
coords = {
x = tonumber(row.position_x) or 0.0,
y = tonumber(row.position_y) or 0.0,
z = tonumber(row.position_z) or 0.0,
},
}
end
local function validate_marker(data)
if type(data) ~= "table" or type(data.coords) ~= "table" then
return nil
end
local label = type(data.label) == "string" and data.label:match("^%s*(.-)%s*$") or nil
local label_length = text_length(label)
local color = data.color
local x = tonumber(data.coords.x)
local y = tonumber(data.coords.y)
local z = tonumber(data.coords.z)
if not label_length
or label_length < 1
or label_length > Config.MapMarkers.LabelMaxLength
or not marker_colors[color]
or not x
or not y
or (data.coords.z ~= nil and not z)
or x ~= x
or y ~= y
or (z and z ~= z)
or math.abs(x) > 10000.0
or math.abs(y) > 10000.0
or (z and z < -1000.0)
or (z and z > 3000.0)
then
return nil
end
return {
label = label,
color = color,
x = x,
y = y,
z = z or 0.0,
}
end
Bridge.Callbacks.Register("sky_phone:map:markers", function(source)
local session, error_response = SkyPhone.RequireSession(source)
if not session then
return error_response
end
local rows = Bridge.Database.Query([[
SELECT `id`, `label`, `color`, `position_x`, `position_y`, `position_z`
FROM `sky_phone_map_markers`
WHERE `device_imei` = ?
ORDER BY `created_at`, `id`
LIMIT ?
]], { session.imei, Config.MapMarkers.MaximumMarkers })
local markers = {}
for index = 1, #rows do
markers[index] = marker_dto(rows[index])
end
return { success = true, data = markers }
end)
Bridge.Callbacks.Register("sky_phone:map:create-marker", function(source, data)
if not SkyPhone.AllowOperation(source, "map_marker_write", Config.MapMarkers.ActionsPerMinute, 60) then
return { success = false, error = "rate_limited" }
end
local session, error_response = SkyPhone.RequireSession(source)
if not session then
return error_response
end
local marker = validate_marker(data)
if not marker then
return { success = false, error = "invalid_marker" }
end
local count_rows = Bridge.Database.Query([[
SELECT COUNT(*) AS `count`
FROM `sky_phone_map_markers`
WHERE `device_imei` = ?
]], { session.imei })
if (tonumber(count_rows[1] and count_rows[1].count) or 0) >= Config.MapMarkers.MaximumMarkers then
return { success = false, error = "marker_limit" }
end
local ids = Bridge.Database.Query("SELECT UUID() AS `id`", {})
local id = ids[1] and ids[1].id
if type(id) ~= "string" then
error("[sky_phone] Database did not generate a map marker id.")
end
Bridge.Database.Query([[
INSERT INTO `sky_phone_map_markers`
(`id`, `device_imei`, `label`, `color`, `position_x`, `position_y`, `position_z`)
VALUES (?, ?, ?, ?, ?, ?, ?)
]], { id, session.imei, marker.label, marker.color, marker.x, marker.y, marker.z })
return {
success = true,
data = {
id = id,
label = marker.label,
color = marker.color,
coords = { x = marker.x, y = marker.y, z = marker.z },
},
}
end)
Bridge.Callbacks.Register("sky_phone:map:delete-marker", function(source, data)
if not SkyPhone.AllowOperation(source, "map_marker_write", Config.MapMarkers.ActionsPerMinute, 60) then
return { success = false, error = "rate_limited" }
end
local session, error_response = SkyPhone.RequireSession(source)
if not session then
return error_response
end
if type(data) ~= "table" or type(data.id) ~= "string" or #data.id ~= 36 then
return { success = false, error = "invalid_marker" }
end
local result = Bridge.Database.Query(
"DELETE FROM `sky_phone_map_markers` WHERE `id` = ? AND `device_imei` = ?",
{ data.id, session.imei }
)
if affected_rows(result) ~= 1 then
return { success = false, error = "marker_not_found" }
end
return { success = true }
end)
end)