HTTP रूट्स
PlainApp, GraphQL API (POST /graphql) के साथ HTTP एंडपॉइंट्स का एक छोटा सेट उजागर करता है। ये एंडपॉइंट्स फ़ाइल सर्विंग, मल्टीपार्ट अपलोड, zip स्ट्रीमिंग, DLNA कास्टिंग, WebSocket इवेंट्स और सिस्टम नियंत्रण को संभालते हैं। सभी file/upload/zip एंडपॉइंट्स को c-id हेडर (और आमतौर पर एक एन्क्रिप्टेड id query पैरामीटर) की आवश्यकता होती है।
प्रकार परिभाषाएँ
# Common headers for protected HTTP endpoints:
c-id: <client-id>
# /fs, /proxyfs, /zip/* use an encrypted "id" query parameter produced
# by the GraphQL files()/fileIds() queries. The id is opaque to the client.
# Upload endpoints additionally require an encrypted "info" multipart part:
# info = chaCha20Encrypt(token, json({ dir, size, replace, isAppFile }))
# file = <raw bytes>
# Chunked uploads use:
# info = chaCha20Encrypt(token, json({ fileId, index, size }))
# Status codes follow HTTP semantics:
# 200 OK – success
# 201 Created – upload stored
# 204 No Content – init pending password
# 400 Bad Request – missing/invalid params
# 401 Unauthorized – bad token
# 403 Forbidden – expired/invalid encrypted id
# 404 Not Found – file/entry missing
# 410 Gone – server shutting down
# 429 Too Many Requests – concurrent zip limit hitऑपरेशन
POST /graphql
मुख्य GraphQL एंडपॉइंट। सभी GraphQL queries और mutations को c-id + Authorization हेडर के साथ यहाँ भेजें। /peer_graphql एंडपॉइंट पेयर किए गए डिवाइसों के बीच उपयोग होने वाला peer-to-peer समतुल्य है।
curl --request POST \
--url http://192.168.1.100:8080/graphql \
--header 'c-id: <client-id>' \
--header 'Authorization: Bearer <api-token>' \
--header 'Content-Type: application/json' \
--data '{"query":"{ app { battery deviceName } }"}'GET /fs
एक फ़ाइल, content:// URI, या पैकेज आइकन सर्व करें। id query पैरामीटर files()/fileIds() द्वारा लौटाया गया एक एन्क्रिप्टेड स्ट्रिंग है। थंबनेल जनरेशन (?w=&h=&cc=), कम-थ्रूपुट ट्रांसपोर्ट के लिए बाइट-रेंज (?offset=&length=), HEIF→PNG रूपांतरण, 3gp→MP4 ट्रांसकोडिंग, और Content-Disposition अटैचमेंट हेडर के साथ डाउनलोड मोड (dl=1) का समर्थन करता है।
# Original (full size, inline)
curl http://192.168.1.100:8080/fs?id=<encrypted-id> -o file.jpg
# Thumbnail (center-cropped to 200x200)
curl 'http://192.168.1.100:8080/fs?id=<encrypted-id>&w=200&h=200' -o thumb.jpg
# Download (attachment)
curl 'http://192.168.1.100:8080/fs?id=<encrypted-id>&dl=1' -OJ
# Byte range (BLE chunked download)
curl 'http://192.168.1.100:8080/fs?id=<encrypted-id>&offset=0&length=4096' \
-o chunk.binGET /proxyfs
एक peer HTTP URL प्रॉक्सी करें। id query पैरामीटर एक पेयर किए गए peer डिवाइस पर पूर्ण http(s) URL में डिक्रिप्ट होता है; सर्वर अपस्ट्रीम प्रतिक्रिया को वापस स्ट्रीम करता है। Wi-Fi Aware पर peer-to-peer फ़ाइल डाउनलोड के लिए उपयोग किया जाता है।
curl http://192.168.1.100:8080/proxyfs?id=<encrypted-peer-url> -o peer-file.jpgPOST /upload
multipart/form-data के माध्यम से एकल फ़ाइल अपलोड करें। "info" पार्ट (ChaCha20-एन्क्रिप्टेड JSON) को "file" पार्ट से पहले होना चाहिए। जब isAppFile=true हो तो बाइट्स content-addressable AppFileStore (हैश द्वारा डिडुप्लिकेटेड) में इम्पोर्ट होते हैं; अन्यथा फ़ाइल info.dir/<fileName> पर लिखी जाती है। अंतिम फ़ाइल नाम लौटाता है (ओवरराइट से बचने पर "(1)" सफ़िक्स हो सकता है)।
curl --request POST \
--url http://192.168.1.100:8080/upload \
--header 'c-id: <client-id>' \
--form 'info=<encrypted-info-bytes>;type=application/octet-stream' \
--form 'file=@/path/to/local.jpg'POST /upload_chunk
रिज़्यूमेबल, चंक्ड अपलोड का एक चंक अपलोड करें। प्रत्येक चंक डिस्क पर upload_tmp/{fileId}/chunk_{index} पर लिखा जाता है। "<index>:<savedSize>" लौटाता है। सभी चंक्स आने के बाद, अंतिम फ़ाइल जोड़ने के लिए mergeChunks GraphQL mutation कॉल करें।
curl --request POST \
--url http://192.168.1.100:8080/upload_chunk \
--header 'c-id: <client-id>' \
--form 'info=<encrypted-chunk-info-bytes>;type=application/octet-stream' \
--form 'file=@/path/to/chunk_0.bin'GET /zip/dir
एकल निर्देशिका को zip आर्काइव के रूप में स्ट्रीम करें। id query पैरामीटर एक निर्देशिका पथ में डिक्रिप्ट होता है। डिवाइस पर एक समय में केवल एक zip ऑपरेशन चलता है — समवर्ती अनुरोधों को HTTP 429 मिलता है।
curl 'http://192.168.1.100:8080/zip/dir?id=<encrypted-dir-id>' \
-o folder.zipGET /zip/files
कई फ़ाइलों (या मीडिया खोज परिणामों) को एकल zip के रूप में स्ट्रीम करें। id { type, query, id, name } में डिक्रिप्ट होता है। FILE प्रकार के लिए फ़ाइल सूची GraphQL files() query द्वारा TempHelper में request.id के अंतर्गत संग्रहीत है; मीडिया प्रकारों के लिए सर्वर searchZipItems(type, query, id) चलाता है।
curl 'http://192.168.1.100:8080/zip/files?id=<encrypted-request>' \
-o selection.zipGET /media/{id}
DLNA मीडिया एंडपॉइंट। पहले से पंजीकृत मीडिया पथ (UrlHelper.getMediaHttpUrl के माध्यम से पंजीकृत) को TV / DLNA रेंडरर को सर्व करता है। URL स्रोत प्रॉक्सी किए जाते हैं, content:// URIs स्ट्रीम किए जाते हैं, छवियाँ जैसे हैं सर्व की जाती हैं, और अन्य सभी फ़ाइलें DLNA-विशिष्ट हेडर्स + HTTP 206 रेंज समर्थन के साथ सर्व की जाती हैं ताकि रेंडरर्स स्ट्रीम स्वीकार करें।
# Play on a DLNA renderer:
curl http://192.168.1.100:8080/media/<id>.mp4 -o video.mp4NOTIFY /callback/cast
DLNA रेंडरर कॉलबैक। रेंडरर के इवेंट NOTIFY XML को प्राप्त करता है और CastPlayer स्थिति अपडेट करता है। TransportState=STOPPED (AVTransportURIMetaData के बिना) पर प्लेयर स्वतः अगले प्लेलिस्ट आइटम पर आगे बढ़ता है। स्थिति अपडेट के लिए RelTime / TrackDuration को भी पार्स करता है।
# Sent by the DLNA renderer (not by the client):
NOTIFY /callback/cast HTTP/1.1
Content-Type: text/xml
<?xml ...><e:propertyset>...TransportState val="PLAYING"...</e:propertyset>GET /health
अनऑथेंटिकेटेड हेल्थ-चेक एंडपॉइंट। ऐप पैकेज नाम को प्लेन टेक्स्ट के रूप में लौटाता है। HTTP सर्वर पहुँच योग्य है यह सत्यापित करने के लिए इसका उपयोग करें।
curl http://192.168.1.100:8080/healthGET /shutdown
HTTP सर्वर बंद करें। केवल localhost से कॉल करने योग्य — रिमोट अनुरोधों को HTTP 403 मिलता है। सभी WebSocket सत्र बंद करता है, ऑनलाइन क्लाइंट सेट साफ़ करता है, और HTTP सर्वर डिस्पोज़ करता है।
# Must be run on the device (e.g. via adb shell):
curl http://localhost:8080/shutdownPOST /init
क्लाइंट सत्र आरंभ करें। c-id हेडर की आवश्यकता। यदि एक मान्य एन्क्रिप्टेड बॉडी मौजूद है (कैश किए गए टोकन के साथ डिक्रिप्टेड) तो सत्र ऑथेंटिकेटेड माना जाता है और सर्वर 200 का उत्तर देता है। अन्यथा: यदि कोई पासवर्ड सेट नहीं है, तो सर्वर एक ताज़ा-रीसेट पासवर्ड (200) के साथ प्रतिक्रिया देता है; यदि पासवर्ड सेट है, तो 204 No Content का उत्तर देता है और क्लाइंट को WebSocket लॉगिन प्रवाह के माध्यम से प्रमाणित करना होगा।
curl --request POST \
--url http://192.168.1.100:8080/init \
--header 'c-id: <client-id>' \
--data-binary '<encrypted-token-bytes>'WS /
मुख्य WebSocket एंडपॉइंट। एक सत्र पंजीकृत करने के लिए ?cid=<client-id> का उपयोग करें; पहली बाइनरी फ्रेम एक एन्क्रिप्टेड टाइमस्टैम्प होनी चाहिए (या, ?auth=1 के साथ, पासवर्ड युक्त एक एन्क्रिप्टेड AuthRequest)। प्रमाणीकरण के बाद सर्वर इस सॉकेट पर एन्क्रिप्टेड इवेंट फ्रेम (नोटिफिकेशन, चैट अपडेट, आदि) पुश करता है।
# Browser / JS example
const ws = new WebSocket('ws://192.168.1.100:8080/?cid=<client-id>')
ws.binaryType = 'arraybuffer'
ws.send(encryptWithToken(token, Date.now().toString()))
// Login variant:
const ws = new WebSocket('ws://192.168.1.100:8080/?cid=<client-id>&auth=1')
ws.send(encryptWithPassword(password, JSON.stringify({ password: hash })))WS /status
Peer-प्रेजेंस WebSocket। पेयर किए गए peer को ऑनलाइन चिह्नित रखने के लिए ?cid=<peer-id> का उपयोग करें। पहली बाइनरी फ्रेम PeerChatParser-एन्क्रिप्टेड अनुरोध होना चाहिए; सफलता पर सर्वर "ok" टेक्स्ट भेजता है और सॉकेट बंद होने तक peer को ऑनलाइन चिह्नित रखता है।
# Peer-to-peer keepalive (typically opened automatically by the chat
# client after pairing — not usually invoked manually)
const ws = new WebSocket('ws://192.168.1.100:8080/status?cid=<peer-id>')
ws.send(peerEncryptedFrame)