mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-30 17:59:02 +00:00
FIX - harden server startup diagnostics
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const manifest = readFileSync(
|
||||
new URL('../../sky_phone/fxmanifest.lua', import.meta.url),
|
||||
'utf8',
|
||||
)
|
||||
const phoneServer = readFileSync(
|
||||
new URL('../../sky_phone/source/server/phone.lua', import.meta.url),
|
||||
'utf8',
|
||||
)
|
||||
|
||||
describe('server startup contracts', () => {
|
||||
it('registers the development-open callback before the database migration runs', () => {
|
||||
expect(manifest.indexOf("'source/server/phone.lua'")).toBeLessThan(
|
||||
manifest.indexOf("'source/server/db_migrate.lua'"),
|
||||
)
|
||||
expect(
|
||||
phoneServer.indexOf(
|
||||
'Bridge.Callbacks.Register("sky_phone:device:development-open"',
|
||||
),
|
||||
).toBeLessThan(
|
||||
phoneServer.indexOf('Bridge.Database.AfterMigration("sky_phone"'),
|
||||
)
|
||||
})
|
||||
|
||||
it('queues early development opens until the resource has fully started', () => {
|
||||
expect(phoneServer).toContain(
|
||||
'AddEventHandler("onServerResourceStart", function(resource_name)',
|
||||
)
|
||||
expect(phoneServer).toContain('pending_development_opens[source] = true')
|
||||
expect(phoneServer).toContain('development_open_handler = open_phone')
|
||||
})
|
||||
})
|
||||
@@ -65,12 +65,12 @@ server_scripts {
|
||||
'source/bridge/server/voice.lua',
|
||||
'source/server/custom_apps.lua',
|
||||
'source/server/custom_app_compat.lua',
|
||||
'source/server/db_migrate.lua',
|
||||
'source/server/lb_phone_migration.lua',
|
||||
'source/server/media_metadata.lua',
|
||||
'source/server/companies.lua',
|
||||
'source/server/sim.lua',
|
||||
'source/server/phone.lua',
|
||||
'source/server/db_migrate.lua',
|
||||
'source/server/lb_phone_migration.lua',
|
||||
'source/server/custom_app_storage.lua',
|
||||
'source/server/payphones.lua',
|
||||
'source/server/calls.lua',
|
||||
|
||||
@@ -19,7 +19,8 @@ local password_pepper = tostring(Config.Server.FlipTokPasswordPepper or "")
|
||||
if password_pepper == "" then
|
||||
Bridge.Debug(
|
||||
"warn",
|
||||
"[sky_phone] Config.Server.FlipTokPasswordPepper is empty; configure it in config/config.lua before production use."
|
||||
"[sky_phone] Config.Server.FlipTokPasswordPepper is empty. FlipTok passwords still work, but their hashes lack the required server-side secret. Set a stable random value in config/config.lua before production; changing it later invalidates existing FlipTok passwords.",
|
||||
{ always = true }
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -975,7 +975,8 @@ end)
|
||||
if not api_configured() then
|
||||
Bridge.Debug(
|
||||
"warn",
|
||||
"[sky_phone] Camera uploads and FiveManage imports are disabled until Config.Media.FiveManage.ApiKey is set in config/media.lua."
|
||||
"[sky_phone] FiveManage media integration is disabled because Config.Media.FiveManage.ApiKey is empty in config/media.lua. Camera photo and video uploads, Voice Memo uploads, remote Gallery deletion, and FiveManage imports are unavailable. Add a FiveManage V3 token with Media access and restart sky_phone.",
|
||||
{ always = true }
|
||||
)
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -251,12 +251,23 @@ local function build_registry()
|
||||
end
|
||||
websites[website.Id] = website
|
||||
else
|
||||
Bridge.Debug(
|
||||
"warn",
|
||||
"[sky_phone] Media import website at index %s was disabled: %s.",
|
||||
tostring(index),
|
||||
tostring(website_error)
|
||||
)
|
||||
local source_name = type(definition) == "table" and definition.Id or nil
|
||||
if website_error == "missing_api_key" then
|
||||
Bridge.Debug(
|
||||
"warn",
|
||||
"[sky_phone] Media import source '%s' at index %s is disabled because Config.Media.FiveManage.ApiKey is empty in server-only config/media.lua. Add a FiveManage V3 token with Media access and restart sky_phone.",
|
||||
tostring(source_name or "unknown"),
|
||||
tostring(index)
|
||||
)
|
||||
else
|
||||
Bridge.Debug(
|
||||
"warn",
|
||||
"[sky_phone] Media import source '%s' at index %s is disabled because its configuration is invalid: %s.",
|
||||
tostring(source_name or "unknown"),
|
||||
tostring(index),
|
||||
tostring(website_error)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,47 @@
|
||||
local development_open_handler
|
||||
local pending_development_opens = {}
|
||||
local server_started = false
|
||||
|
||||
Bridge.Callbacks.Register("sky_phone:device:development-open", function(source)
|
||||
if not Config.Phone.DevelopmentCommand then
|
||||
return { success = false, error = "disabled" }
|
||||
end
|
||||
if not server_started or not development_open_handler then
|
||||
pending_development_opens[source] = true
|
||||
return { success = true, data = { queued = true } }
|
||||
end
|
||||
return { success = development_open_handler(source, nil) }
|
||||
end)
|
||||
|
||||
AddEventHandler("playerDropped", function()
|
||||
pending_development_opens[source] = nil
|
||||
end)
|
||||
|
||||
AddEventHandler("onServerResourceStart", function(resource_name)
|
||||
if resource_name ~= GetCurrentResourceName() then
|
||||
return
|
||||
end
|
||||
|
||||
server_started = true
|
||||
if not development_open_handler then
|
||||
Bridge.Debug("error", "[sky_phone] Server initialization did not register the development phone handler.")
|
||||
return
|
||||
end
|
||||
|
||||
for player_source in pairs(pending_development_opens) do
|
||||
pending_development_opens[player_source] = nil
|
||||
local success, opened = pcall(development_open_handler, player_source, nil)
|
||||
if not success then
|
||||
Bridge.Debug(
|
||||
"error",
|
||||
"[sky_phone] Queued development phone open failed for source %s: %s",
|
||||
tostring(player_source),
|
||||
tostring(opened)
|
||||
)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
Bridge.Database.AfterMigration("sky_phone", function()
|
||||
Bridge.Debug("debug", "[sky_phone] Server initialization started after database migration.", { always = true })
|
||||
|
||||
@@ -14,7 +58,8 @@ local passcode_pepper = tostring(Config.Server.PasscodePepper or "")
|
||||
if passcode_pepper == "" then
|
||||
Bridge.Debug(
|
||||
"warn",
|
||||
"[sky_phone] Config.Server.PasscodePepper is empty; configure it in config/config.lua before production use."
|
||||
"[sky_phone] Config.Server.PasscodePepper is empty. Device passcodes still work, but their hashes lack the required server-side secret. Set a stable random value in config/config.lua before production; changing it later invalidates existing device passcodes.",
|
||||
{ always = true }
|
||||
)
|
||||
end
|
||||
local allowed_device_namespaces = {
|
||||
@@ -965,6 +1010,8 @@ local function open_phone(source, used_item)
|
||||
return true
|
||||
end
|
||||
|
||||
development_open_handler = open_phone
|
||||
|
||||
function SkyPhone.OpenDeviceForCall(source, imei)
|
||||
local matches = find_device_slots(source, imei)
|
||||
if not matches[1] then
|
||||
@@ -1123,13 +1170,6 @@ Bridge.Callbacks.Register("sky_phone:security:disable-passcode", function(source
|
||||
return { success = true, data = { security = security_status(session.imei) } }
|
||||
end)
|
||||
|
||||
Bridge.Callbacks.Register("sky_phone:device:development-open", function(source)
|
||||
if not Config.Phone.DevelopmentCommand then
|
||||
return { success = false, error = "disabled" }
|
||||
end
|
||||
return { success = open_phone(source, nil) }
|
||||
end)
|
||||
|
||||
Bridge.Callbacks.Register("sky_phone:device:save", function(source, data)
|
||||
if not SkyPhone.AllowOperation(source, "device_save", 120, 60) then
|
||||
return { success = false, error = "rate_limited" }
|
||||
|
||||
@@ -5,7 +5,8 @@ local password_pepper = tostring(Config.Server.PicstagramPasswordPepper or "")
|
||||
if password_pepper == "" then
|
||||
Bridge.Debug(
|
||||
"warn",
|
||||
"[sky_phone] Config.Server.PicstagramPasswordPepper is empty; configure it in config/config.lua before production use."
|
||||
"[sky_phone] Config.Server.PicstagramPasswordPepper is empty. Picstagram passwords still work, but their hashes lack the required server-side secret. Set a stable random value in config/config.lua before production; changing it later invalidates existing Picstagram passwords.",
|
||||
{ always = true }
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user