Integrations
FiveM resource
CDNForge provides an official FiveM server-side resource for uploads, structured logs, and file helpers. Install from the GitHub repository or release ZIP, set scoped API keys as server convars, then call exports from your own resources.
Install
Official distribution: github.com/CDNForge/cdnforge. The repository root is the FiveM resource.
Option 1: Download the release
- Open the latest release and download
cdnforge.zip. - Extract into your FXServer
resourcesfolder so you haveresources/cdnforge/fxmanifest.lua. - Add the convars below to
server.cfg, thenensure cdnforge.
Option 2: Git clone
cd resources
git clone https://github.com/CDNForge/cdnforge.gitset cdnforge_url "https://app.back2rp.org"
set cdnforge_key_image "cnf_img_..."
set cdnforge_key_video "cnf_vid_..."
set cdnforge_key_audio "cnf_aud_..."
set cdnforge_key_file "cnf_fil_..."
set cdnforge_key_logs "cnf_log_..."
ensure cdnforgeKeys stay on the server
set (not replicated setr). Never put API keys in client scripts, NUI, or shared scripts.Resource order
Start cdnforge before any resource that calls exports.cdnforge. This resource has no client_scripts.
Convars
cdnforge_url— CDNForge app origin (for examplehttps://app.back2rp.org). Legacy fallback:gamestack_url.cdnforge_key_image—cnf_img_(legacygsk_img_also accepted)cdnforge_key_video—cnf_vid_cdnforge_key_audio—cnf_aud_cdnforge_key_file—cnf_fil_for generic uploads, GetFile, and DeleteFilecdnforge_key_logs—cnf_log_cdnforge_api_key— logs-only legacy fallback ifcdnforge_key_logsis unset (must still be a logs key). Legacy name:gamestack_api_key.
Keys with the wrong prefix are rejected. Defaults to http://localhost:3000 only when cdnforge_url is empty.
Exports
UploadImage(data, metadata, callback, progressCallback)UploadVideo(data, metadata, callback, progressCallback)UploadAudio(data, metadata, callback, progressCallback)UploadFile(data, metadata, callback, progressCallback)LogMessage(level, message, metadata, logFileName)GetFile(fileId, callback)DeleteFile(fileId, callback)
Upload, GetFile, and DeleteFile callbacks use (result, err). On success err is nil. Upload success fields include id, url, publicId, size, mimeType, and originalName. Errors include code, message, and status.
LogMessage is fire-and-forget: it has no callback. Failures are printed to the server console.
Data input
- Raw binary Lua string
- Base64 with
base64:prefix, adata:...;base64,...URL, ormetadata.encoding = "base64" - Server-readable path via
metadata.path,metadata.fromPath = true(including relative paths), or an absolute path string with/or\
Prefer explicit base64 or path flags. Heuristic base64 only applies to longer ASCII-looking strings and will not decode short raw binary.
Metadata
filename— upload file namefolder— folder path on the CDNForge sidefolderId— existing dashboard folder idplayerSource— stored with uploads and logspath/fromPath/encoding— input helpers (not sent as API metadata)- Any other fields are forwarded as JSON metadata
Upload routing
- Image ≤ 3.5 MB →
POST /api/v1/upload - Image > 3.5 MB →
/api/v1/upload/chunked/* - Video → always
/api/v1/upload/chunked/* - Audio → always
/api/v1/upload/chunked/* - Generic file ≤ 3.5 MB →
POST /api/v1/file - Generic file > 3.5 MB →
/api/v1/file/chunked/*
The 3.5 MB cutoff is this resource's client threshold (to stay under typical serverless body limits), not a separate hard limit on the API. Maximum upload size is 100 MB. Chunk size: 4 MB. At most two uploads run at once; extras queue. Full HTTP protocol: large files & chunked uploads.
Chunked uploads
- Init returns a server-issued
uploadId(and other session fields the client does not need to trust for completion). - Each part is uploaded with multipart fields uploadId, partNumber, and chunk.
- Complete sends only
{ uploadId }. The API loadspublicId, storage bucket, and MIME from the upload session. Do not send client-chosen bucket or public id.
Sessions expire after about one hour (server TTL). Common statuses: 404 (missing/wrong org), 409 (already completed), 410 (expired), 413 (too large).
Progress callbacks
Shape: progressCallback(partNumber, totalParts, bytesUploaded, totalBytes).
- FiveM
PerformHttpRequestdoes not expose true byte-level upload progress during a single request. - Direct uploads call
(0, 1, 0, size)when the request starts, then(1, 1, size, size)only after a successful response. Failures never report 100%. - Chunked uploads report after each finished part except the last. Full progress (
bytesUploaded = totalBytes) is reported only after complete succeeds.
Retries and errors
- HTTP 429 and 5xx are retried up to three times with exponential backoff
- Other 4xx responses are not retried
- Missing or wrong-prefix keys return
MISSING_KEYto upload/file callbacks
LogMessage
- Levels:
debug,info,warn,error - Optional 4th argument
logFileNamesets the log dataset name (or usemetadata.name) - Requires a logs key (
cnf_log_) - Invalid levels are rejected locally and printed to console
GetFile / DeleteFile
This resource uses the file-scoped key (cdnforge_key_file). GetFile returns the file object from GET /api/v1/files/{id}. DeleteFile expects { ok: true } from DELETE /api/v1/files/{id}. Organization and scope checks happen on the server.
Security
- API keys must remain server-side only
- Do not use replicated convars or client events to pass keys
- Use scoped keys:
cnf_img_,cnf_vid_,cnf_aud_,cnf_fil_,cnf_log_ - Validate player actions in your resource before calling uploads
- Do not trust player-supplied paths for
metadata.path/fromPath - Chunked sessions are bound server-side; complete uses only the init
uploadId - For browser or NUI capture without server bytes, use presigned uploads instead of embedding keys in client code
Examples
Call exports from your own server scripts after validating the player action.
exports.cdnforge:LogMessage(
"info",
"Player connecting: " .. name,
{ playerSource = src, action = "connect" },
"player-events"
)exports.cdnforge:UploadImage(imageBytes, {
filename = "shot-" .. src .. ".png",
playerSource = src,
folder = "screenshots",
}, function(result, err)
if err then print(err.message) return end
print(result.url)
end)exports.cdnforge:UploadVideo("/path/on/server/clip.mp4", {
fromPath = true,
filename = "clip.mp4",
folder = "clips",
}, function(result, err)
if err then print(err.message) return end
print(result.id)
end, function(part, total, uploaded, totalBytes)
print(part, total, uploaded, totalBytes)
end)exports.cdnforge:UploadFile(fileBytes, {
filename = "backup.zip",
folder = "backups",
}, function(result, err)
if err then print(err.message) return end
print(result.url)
end)exports.cdnforge:GetFile("fil_...", function(file, err)
if err then print(err.message) return end
print(file.url)
end)
exports.cdnforge:DeleteFile("fil_...", function(result, err)
if err then print(err.message) return end
print(result.ok)
end)Updating
- Download the latest release, replace
resources/cdnforge, and restart the resource. - Or run
git pullinsideresources/cdnforge, then restart.
