mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-29 02:01:40 +00:00
FIX - match FiveManage camera upload flow
This commit is contained in:
@@ -484,12 +484,7 @@ async function uploadReady(ready: UploadReady): Promise<void> {
|
||||
})
|
||||
|
||||
const form = new FormData()
|
||||
form.append('path', ready.uploadPath)
|
||||
form.append('file', blob, fileName)
|
||||
form.append(
|
||||
'metadata',
|
||||
JSON.stringify({ captureToken: ready.captureToken, source: 'sky_phone' }),
|
||||
)
|
||||
const controller = new AbortController()
|
||||
const timeout = window.setTimeout(
|
||||
() => controller.abort(),
|
||||
@@ -511,11 +506,10 @@ async function uploadReady(ready: UploadReady): Promise<void> {
|
||||
})
|
||||
const text = await response.text()
|
||||
const body = JSON.parse(text) as {
|
||||
data?: { id?: string; originalUrl?: string; url?: string }
|
||||
data?: { id?: string; url?: string }
|
||||
error?: string
|
||||
id?: string
|
||||
message?: string
|
||||
originalUrl?: string
|
||||
url?: string
|
||||
}
|
||||
const uploaded = body.data ?? body
|
||||
@@ -529,7 +523,6 @@ async function uploadReady(ready: UploadReady): Promise<void> {
|
||||
debugStage = 'completion_callback'
|
||||
const completion = await nuiCall('media:completeUpload', {
|
||||
correlationId: ready.correlationId,
|
||||
originalUrl: uploaded.originalUrl,
|
||||
remoteId: uploaded.id,
|
||||
requestId: ready.requestId,
|
||||
url: uploaded.url,
|
||||
|
||||
@@ -28,6 +28,10 @@ const mediaServer = readFileSync(
|
||||
new URL('../../sky_phone/source/server/media.lua', import.meta.url),
|
||||
'utf8',
|
||||
)
|
||||
const memoServer = readFileSync(
|
||||
new URL('../../sky_phone/source/server/memos.lua', import.meta.url),
|
||||
'utf8',
|
||||
)
|
||||
const mediaCapture = readFileSync(
|
||||
new URL('./components/PhoneMediaCapture.vue', import.meta.url),
|
||||
'utf8',
|
||||
@@ -58,15 +62,25 @@ describe('FiveManage server configuration contract', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('resolves uploads through their server-generated FiveManage path', () => {
|
||||
it('uses the direct FiveManage upload response flow for Camera media', () => {
|
||||
expect(mediaConfig).not.toContain('VerificationRetryDelaysMs')
|
||||
expect(mediaServer).toContain('upload_path = "sky_phone-" .. capture_token')
|
||||
expect(mediaCapture).toContain("form.append('file', blob, fileName)")
|
||||
expect(mediaCapture).not.toContain("form.append('path'")
|
||||
expect(mediaCapture).not.toContain("form.append(\n 'metadata'")
|
||||
expect(mediaServer).toContain('if state.media_type ~= "audio" then')
|
||||
expect(mediaServer).toContain(
|
||||
'Accepting the direct FiveManage camera upload response',
|
||||
)
|
||||
expect(mediaServer).toContain('remote_id = remote_id')
|
||||
expect(mediaServer).toContain('url = uploaded_url')
|
||||
})
|
||||
|
||||
it('keeps server-generated upload path verification for voice memos', () => {
|
||||
expect(memoServer).toContain('upload_path = "sky_phone-" .. capture_token')
|
||||
expect(mediaServer).toContain(
|
||||
'"?limit=100&page=1&path=" .. SkyPhoneMediaImport.UrlEncode(state.upload_path)',
|
||||
)
|
||||
expect(mediaCapture).toContain("form.append('path', ready.uploadPath)")
|
||||
expect(memoRecorder).toContain("form.append('path', ready.uploadPath)")
|
||||
expect(mediaCapture).toContain('originalUrl: uploaded.originalUrl')
|
||||
expect(memoRecorder).toContain('originalUrl: uploaded.originalUrl')
|
||||
expect(mediaServer).toContain(
|
||||
'local verified_url = remote.url or remote.originalUrl',
|
||||
@@ -76,7 +90,7 @@ describe('FiveManage server configuration contract', () => {
|
||||
it('binds metadata verification to the FiveManage host that issued the upload URL', () => {
|
||||
expect(mediaServer).toContain('["api.fivemanage.com"] = true')
|
||||
expect(mediaServer).toContain('["fmapi.net"] = true')
|
||||
expect(mediaServer).toContain('provider_base_url = provider_base_url')
|
||||
expect(memoServer).toContain('provider_base_url = provider_base_url')
|
||||
expect(mediaServer).toContain('state.provider_base_url')
|
||||
})
|
||||
|
||||
|
||||
@@ -70,7 +70,6 @@ export type MediaImportResult = {
|
||||
}
|
||||
|
||||
export type UploadReady = {
|
||||
captureToken: string
|
||||
correlationId: string
|
||||
mediaType: MediaType
|
||||
photo?: {
|
||||
@@ -79,7 +78,6 @@ export type UploadReady = {
|
||||
}
|
||||
presignedUrl: string
|
||||
requestId: string
|
||||
uploadPath: string
|
||||
uploadTimeoutMs?: number
|
||||
video?: {
|
||||
BitrateKbps?: number
|
||||
|
||||
@@ -500,6 +500,27 @@ local function verify_remote_upload(state, remote_id, uploaded_url, original_url
|
||||
)
|
||||
return nil, "invalid_upload"
|
||||
end
|
||||
|
||||
if state.media_type ~= "audio" then
|
||||
local uploaded_host = uploaded_url:match("^https://([^/%?#]+)")
|
||||
if not uploaded_host or uploaded_host:lower() ~= "r2.fivemanage.com" then
|
||||
Bridge.Debug(
|
||||
"error",
|
||||
"[sky_phone][media-debug] FiveManage camera upload returned an unexpected media host."
|
||||
)
|
||||
return nil, "invalid_upload"
|
||||
end
|
||||
media_debug(
|
||||
"Accepting the direct FiveManage camera upload response (type=%s).",
|
||||
tostring(state.media_type)
|
||||
)
|
||||
return {
|
||||
mime_type = state.mime_type,
|
||||
remote_id = remote_id,
|
||||
url = uploaded_url,
|
||||
}, nil, true
|
||||
end
|
||||
|
||||
media_debug("Verifying uploaded file with FiveManage (type=%s).", tostring(state.media_type))
|
||||
local remote, remote_error, remote_path = get_remote_file(state, remote_id, uploaded_url)
|
||||
if not remote then
|
||||
@@ -909,7 +930,7 @@ RegisterNetEvent("sky_phone:media:request-upload", function(data)
|
||||
diagnostic_text(correlation_id, 80),
|
||||
owner.account_id and "account" or "device"
|
||||
)
|
||||
local presigned_url, presigned_error, provider_base_url = request_presigned_url()
|
||||
local presigned_url, presigned_error = request_presigned_url()
|
||||
if not presigned_url then
|
||||
Bridge.Debug(
|
||||
"error",
|
||||
@@ -921,33 +942,28 @@ RegisterNetEvent("sky_phone:media:request-upload", function(data)
|
||||
upload_result(src, correlation_id, false, presigned_error)
|
||||
return
|
||||
end
|
||||
local ids = Bridge.Database.Query("SELECT UUID() AS `request_id`, UUID() AS `capture_token`", {})
|
||||
local ids = Bridge.Database.Query("SELECT UUID() AS `request_id`", {})
|
||||
local request_id = ids[1] and ids[1].request_id
|
||||
local capture_token = ids[1] and ids[1].capture_token
|
||||
if type(request_id) ~= "string" or type(capture_token) ~= "string" then
|
||||
if type(request_id) ~= "string" then
|
||||
Bridge.Debug(
|
||||
"error",
|
||||
"[sky_phone][media-debug] Database did not generate upload session identifiers (source=%s, correlation=%s, rows=%s, request-id=%s, capture-token=%s).",
|
||||
"[sky_phone][media-debug] Database did not generate an upload request ID (source=%s, correlation=%s, rows=%s, request-id=%s).",
|
||||
tostring(src),
|
||||
diagnostic_text(correlation_id, 80),
|
||||
tostring(type(ids) == "table" and #ids or 0),
|
||||
type(request_id),
|
||||
type(capture_token)
|
||||
type(request_id)
|
||||
)
|
||||
upload_result(src, correlation_id, false, "request_failed")
|
||||
return
|
||||
end
|
||||
pending_uploads[request_id] = {
|
||||
capture_token = capture_token,
|
||||
correlation_id = correlation_id,
|
||||
media_type = media_type,
|
||||
mime_type = media_type == "video" and "video/webm"
|
||||
or ({ png = "image/png", webp = "image/webp" })[tostring(Config.Media.Photo.Encoding):lower()]
|
||||
or "image/jpeg",
|
||||
owner = owner,
|
||||
provider_base_url = provider_base_url,
|
||||
source = src,
|
||||
upload_path = "sky_phone-" .. capture_token,
|
||||
}
|
||||
SetTimeout(tonumber(Config.Media.UploadSessionTimeoutMs) or 60000, function()
|
||||
expire_upload(request_id)
|
||||
@@ -959,13 +975,11 @@ RegisterNetEvent("sky_phone:media:request-upload", function(data)
|
||||
tostring(media_type)
|
||||
)
|
||||
TriggerClientEvent("sky_phone:media:upload-ready", src, {
|
||||
captureToken = capture_token,
|
||||
correlationId = correlation_id,
|
||||
mediaType = media_type,
|
||||
photo = Config.Media.Photo,
|
||||
presignedUrl = presigned_url,
|
||||
requestId = request_id,
|
||||
uploadPath = pending_uploads[request_id].upload_path,
|
||||
uploadTimeoutMs = Config.Media.FiveManage.UploadTimeoutMs,
|
||||
video = Config.Media.Video,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user