feat: add LB migration and reorganize configuration

This commit is contained in:
Leon.Schmidt
2026-08-17 02:08:31 +02:00
parent d9ada55ebc
commit 3e35a12efd
24 changed files with 5603 additions and 956 deletions
+1 -1
View File
@@ -903,7 +903,7 @@ local function cleanup_retained_data()
},
})
if not success then
print("[sky_phone] Companies retention cleanup failed.")
Bridge.Debug("error", "[sky_phone] Companies retention cleanup failed.")
end
end
@@ -11,10 +11,10 @@ local function get_calling_resource(export_name)
return owner_resource
end
print(("[%s] %s rejected: the export must be called by another resource."):format(
Bridge.Debug("warn", "[%s] %s rejected: the export must be called by another resource.",
RESOURCE_NAME,
export_name
))
)
return nil, "invalid_owner"
end
@@ -31,11 +31,11 @@ local function add_high_application(app_name, data, locales)
locales
)
if not definition then
print(("[%s] High Phone registration rejected for %s: %s."):format(
Bridge.Debug("warn", "[%s] High Phone registration rejected for %s: %s.",
RESOURCE_NAME,
owner_resource,
definition_error
))
)
return false, definition_error
end
@@ -77,9 +77,9 @@ end
RegisterNetEvent("sky_phone:compat:high:server:requestSnapshot", function()
local player_source = source
if player_source <= 0 then
print(("[%s] Rejected High Phone snapshot request without a player source."):format(
Bridge.Debug("warn", "[%s] Rejected High Phone snapshot request without a player source.",
RESOURCE_NAME
))
)
return
end
@@ -91,10 +91,10 @@ RegisterNetEvent("sky_phone:compat:high:server:requestSnapshot", function()
local log_elapsed = last_log and now - last_log or SNAPSHOT_REJECTION_LOG_COOLDOWN_MS
if log_elapsed < 0 or log_elapsed >= SNAPSHOT_REJECTION_LOG_COOLDOWN_MS then
snapshot_rejection_logs[player_source] = now
print(("[%s] Rate-limited High Phone snapshot request from player %s."):format(
Bridge.Debug("warn", "[%s] Rate-limited High Phone snapshot request from player %s.",
RESOURCE_NAME,
player_source
))
)
end
return
end
+7 -3
View File
@@ -53,9 +53,13 @@ local function register_policy(policy)
local app_id = policy.id
local existing = policies_by_id[app_id]
if existing then
print((
"[sky_phone] Rejected duplicate custom app policy '%s' from '%s'; it is already owned by '%s'."
):format(app_id, policy.ownerResource, existing.ownerResource))
Bridge.Debug(
"warn",
"[sky_phone] Rejected duplicate custom app policy '%s' from '%s'; it is already owned by '%s'.",
app_id,
policy.ownerResource,
existing.ownerResource
)
return false, "duplicate_app_id"
end
+65
View File
@@ -233,6 +233,71 @@ local schema = {
},
tableOptions = "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
},
{
name = "sky_phone_migrations",
columns = {
{ name = "name", type = "VARCHAR(96) NOT NULL", characterSet = "ascii", collation = "ascii_bin" },
{ name = "source", type = "VARCHAR(32) NOT NULL", characterSet = "ascii", collation = "ascii_bin" },
{ name = "stats", type = "LONGTEXT NULL" },
{ name = "completed_at", type = "DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP" },
},
primaryKey = "name",
indexes = {
{ name = "idx_sky_phone_migrations_source", columns = "(`source`, `completed_at`)" },
},
tableOptions = "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
},
{
name = "sky_phone_migration_numbers",
columns = {
{ name = "source", type = "VARCHAR(32) NOT NULL", characterSet = "ascii", collation = "ascii_bin" },
{ name = "source_number", type = "VARCHAR(64) NOT NULL" },
{ name = "phone_number", type = "VARCHAR(24) NOT NULL", characterSet = "ascii", collation = "ascii_bin" },
{ name = "sim_id", type = "CHAR(36) NOT NULL", characterSet = "ascii", collation = "ascii_bin" },
{ name = "owned", type = "TINYINT(1) NOT NULL DEFAULT 0" },
{ name = "created_at", type = "DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP" },
},
primaryKey = { "source", "source_number" },
uniqueKeys = {
{ name = "uniq_sky_phone_migration_number", columns = "(`source`, `phone_number`)" },
},
indexes = {
{ name = "idx_sky_phone_migration_numbers_sim", columns = "(`sim_id`)" },
},
foreignKeys = {
{ column = "sim_id", references = "`sky_phone_sims` (`id`) ON DELETE CASCADE" },
},
tableOptions = "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
},
{
name = "sky_phone_migration_owners",
columns = {
{ name = "source", type = "VARCHAR(32) NOT NULL", characterSet = "ascii", collation = "ascii_bin" },
{ name = "source_phone_id", type = "VARCHAR(100) NOT NULL" },
{ name = "source_owner_id", type = "VARCHAR(100) NOT NULL" },
{ name = "owner_identifier", type = "VARCHAR(80) NOT NULL", characterSet = "ascii", collation = "ascii_bin" },
{ name = "source_phone_number", type = "VARCHAR(64) NOT NULL" },
{ name = "device_imei", type = "CHAR(15) NOT NULL", characterSet = "ascii", collation = "ascii_bin" },
{ name = "sim_id", type = "CHAR(36) NOT NULL", characterSet = "ascii", collation = "ascii_bin" },
{ name = "account_id", type = "BIGINT UNSIGNED NULL" },
{ name = "created_at", type = "DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP" },
},
primaryKey = { "source", "source_phone_id" },
uniqueKeys = {
{ name = "uniq_sky_phone_migration_owner_number", columns = "(`source`, `source_phone_number`)" },
},
indexes = {
{ name = "idx_sky_phone_migration_owner", columns = "(`source`, `owner_identifier`)" },
{ name = "idx_sky_phone_migration_owner_device", columns = "(`device_imei`)" },
{ name = "idx_sky_phone_migration_owner_account", columns = "(`account_id`)" },
},
foreignKeys = {
{ column = "device_imei", references = "`sky_phone_devices` (`imei`) ON DELETE CASCADE" },
{ column = "sim_id", references = "`sky_phone_sims` (`id`) ON DELETE CASCADE" },
{ column = "account_id", references = "`sky_phone_accounts` (`id`) ON DELETE SET NULL" },
},
tableOptions = "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
},
{
name = "sky_phone_device_data",
columns = {
+8 -6
View File
@@ -14,14 +14,12 @@ local custom_music_extensions = {
wav = true,
webm = true,
}
local password_pepper = GetConvar(Config.FlipTok.PasswordPepperConvar, "")
local password_pepper = tostring(Config.Server.FlipTokPasswordPepper or "")
if password_pepper == "" then
Bridge.Debug(
"warn",
"[sky_phone] FlipTok password pepper convar '%s' is empty; configure it before production use.",
Config.FlipTok.PasswordPepperConvar,
{ always = true }
"[sky_phone] Config.Server.FlipTokPasswordPepper is empty; configure it in config/config.lua before production use."
)
end
@@ -921,7 +919,7 @@ RegisterCommand(Config.FlipTok.VerifyCommand, function(source, arguments)
end
local function send_command_feedback(message, notification_type)
if source == 0 then
print(message)
Bridge.Debug(notification_type == "error" and "error" or "info", message)
return
end
@@ -932,7 +930,11 @@ RegisterCommand(Config.FlipTok.VerifyCommand, function(source, arguments)
end
if source ~= 0 and not Bridge.Framework.HasAdminGroup(source, Config.FlipTok.AdminGroups) then
send_command_feedback(command_locale.noPermission, "error")
print(("[sky_phone] Player %d attempted to use the FlipTok verification command without an admin group."):format(source))
Bridge.Debug(
"warn",
"[sky_phone] Player %d attempted to use the FlipTok verification command without an admin group.",
source
)
return
end
local handle = type(arguments[1]) == "string" and arguments[1]:lower():gsub("^@", "") or ""
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -970,6 +970,9 @@ AddEventHandler("playerDropped", function()
end)
if not api_configured() then
print("^3[sky_phone] Camera uploads and FiveManage imports are disabled until Config.Media.FiveManage.ApiKey is set in config/media.lua.^7")
Bridge.Debug(
"warn",
"[sky_phone] Camera uploads and FiveManage imports are disabled until Config.Media.FiveManage.ApiKey is set in config/config.lua."
)
end
end)
+56 -4
View File
@@ -10,13 +10,11 @@ local auth_attempts = {}
local operation_attempts = {}
local character_device_cache = {}
local max_device_data_bytes = 100000
local passcode_pepper = GetConvar(Config.Security.PasscodePepperConvar, "")
local passcode_pepper = tostring(Config.Server.PasscodePepper or "")
if passcode_pepper == "" then
Bridge.Debug(
"warn",
"[sky_phone] Passcode pepper convar '%s' is empty; configure it before production use.",
Config.Security.PasscodePepperConvar,
{ always = true }
"[sky_phone] Config.Server.PasscodePepper is empty; configure it in config/config.lua before production use."
)
end
local allowed_device_namespaces = {
@@ -86,6 +84,36 @@ local function character_identifier(source)
return identifier
end
local function migrated_device_for_character(source)
local identifier = character_identifier(source)
if not identifier then
return nil
end
local rows = Bridge.Database.Query([[
SELECT mapping.`device_imei`
FROM `sky_phone_migration_owners` mapping
JOIN `sky_phone_character_devices` character_device
ON character_device.`owner_identifier` = mapping.`owner_identifier`
AND character_device.`device_imei` = mapping.`device_imei`
JOIN `sky_phone_devices` device ON device.`imei` = mapping.`device_imei`
WHERE mapping.`source` = 'lb-phone' AND mapping.`owner_identifier` = ?
ORDER BY mapping.`created_at` DESC
LIMIT 1
]], { identifier })
local imei = rows[1] and rows[1].device_imei or nil
if imei and not SkyPhoneImei.IsValid(imei) then
Bridge.Debug(
"error",
"[sky_phone] LB Phone migration mapping contains invalid IMEI '%s' for '%s'.",
tostring(imei),
identifier
)
return nil
end
return imei
end
local function load_character_device(source, identifier)
identifier = identifier or character_identifier(source)
if not identifier then
@@ -304,6 +332,30 @@ local function ensure_device(source, slot)
end
if not imei then
imei = migrated_device_for_character(source)
if imei then
metadata.imei = imei
local metadata_written = Bridge.Inventory.SetSlotMetadata(source, slot.slot, metadata)
if not metadata_written then
Bridge.Debug(
"error",
"[sky_phone] Inventory '%s' could not adopt migrated phone metadata for source %s slot %s.",
Bridge.Inventory.GetResourceName(),
tostring(source),
tostring(slot.slot)
)
return nil, "metadata_unsupported"
end
Bridge.Debug(
"info",
"[sky_phone] Adopted LB Phone migrated device %s for source %s.",
imei,
tostring(source),
{ always = true }
)
return imei
end
imei = reserve_imei()
metadata.imei = imei
Bridge.Debug(
+3 -5
View File
@@ -1,13 +1,11 @@
Bridge.Database.AfterMigration("sky_phone", function()
local report_reasons = {}
local password_pepper = GetConvar(Config.Picstagram.PasswordPepperConvar, "")
local password_pepper = tostring(Config.Server.PicstagramPasswordPepper or "")
if password_pepper == "" then
Bridge.Debug(
"warn",
"[sky_phone] Picstagram password pepper convar '%s' is empty; configure it before production use.",
Config.Picstagram.PasswordPepperConvar,
{ always = true }
"[sky_phone] Config.Server.PicstagramPasswordPepper is empty; configure it in config/config.lua before production use."
)
end
@@ -1444,7 +1442,7 @@ RegisterCommand(Config.Picstagram.VerifyCommand, function(source, args)
end
local function send_command_feedback(message, notification_type)
if source == 0 then
print(message)
Bridge.Debug(notification_type == "error" and "error" or "info", message)
return
end
TriggerClientEvent("sky_phone:picstagram:command-feedback", source, {