138 lines
4.7 KiB
Markdown
138 lines
4.7 KiB
Markdown
# QMAX
|
|
|
|
QMAX is a multi-user Android messenger client backed by a self-hosted bridge server. Each Android user verifies a separate MAX account, while one QMAX server and one PyMax worker can serve all accounts.
|
|
|
|
Current shape:
|
|
|
|
- `server/QMax.Api` - ASP.NET Core API, SQLite cache, JWT pairing auth, SignalR hub, attachment storage, APK update catalog.
|
|
- `pymax-worker` - Python/PyMax worker with an isolated persistent MAX mobile API session per QMAX user.
|
|
- `android` - Kotlin + Jetpack Compose Android client pointed at `https://qmax.kusoft.xyz`.
|
|
- `deploy` - Docker Compose + Caddy for Raspberry Pi 5.
|
|
|
|
## Local Checks
|
|
|
|
```powershell
|
|
dotnet test QMax.slnx
|
|
python -m py_compile pymax-worker/src/server.py
|
|
cd android
|
|
.\gradlew.bat :app:assembleDebug :app:assembleRelease --console=plain --no-daemon
|
|
```
|
|
|
|
## Raspberry Pi Deployment
|
|
|
|
1. Point DNS `qmax.kusoft.xyz` to the Raspberry Pi public IP.
|
|
2. Install Docker Engine and the Docker Compose plugin on the Pi.
|
|
3. Copy the repository or the archive produced by `scripts/deploy-rpi.ps1`.
|
|
4. On the Pi:
|
|
|
|
```bash
|
|
cd ~/qmax/deploy
|
|
cp .env.example .env
|
|
nano .env
|
|
docker compose up -d --build
|
|
```
|
|
|
|
This compose publishes QMAX API on `127.0.0.1:18080`. The existing system Caddy should contain:
|
|
|
|
```caddy
|
|
qmax.kusoft.xyz {
|
|
encode zstd gzip
|
|
reverse_proxy 127.0.0.1:18080
|
|
}
|
|
```
|
|
|
|
Set strong values in `.env`:
|
|
|
|
- `QMAX_JWT_SECRET` - at least 32 random characters.
|
|
- `QMAX_PAIRING_CODE` - optional server registration code shared with allowed users; leave it empty for open registration.
|
|
|
|
Secrets must stay in `.env`, not in git.
|
|
|
|
## MAX Login
|
|
|
|
On the Android login screen enter the QMAX server URL, the user's MAX phone number and, when configured, `QMAX_PAIRING_CODE`. QMAX asks PyMax to start MAX authorization. Enter the code delivered by MAX in the app; only after PyMax reports an authorized session does QMAX issue that user a JWT and refresh token.
|
|
|
|
The worker stores sessions under `accounts/<QMAX user id>/` in the `qmax-pymax-session` Docker volume. Session files and imported-contact mappings are not shared between users.
|
|
|
|
## Android Pairing
|
|
|
|
Build or install:
|
|
|
|
```powershell
|
|
cd android
|
|
.\gradlew.bat :app:assembleRelease --console=plain --no-daemon
|
|
```
|
|
|
|
On first launch:
|
|
|
|
- Server: `https://qmax.kusoft.xyz`
|
|
- Phone: the user's MAX phone number
|
|
- Registration code: `QMAX_PAIRING_CODE` from `.env`, if the server owner configured one
|
|
- MAX code: the confirmation code sent by MAX after the first step
|
|
|
|
The first screen after pairing is the chat list.
|
|
|
|
## APK Update Catalog
|
|
|
|
Create a release package:
|
|
|
|
```powershell
|
|
.\scripts\package-android-release.ps1 -Version 0.1.4 -VersionCode 5
|
|
```
|
|
|
|
The compose file mounts `deploy/releases` as `/data/releases`, so files generated by the packaging script are served automatically after `docker compose up -d`. The server exposes:
|
|
|
|
```text
|
|
GET /api/app-updates/android/latest
|
|
GET /api/app-updates/android/download/{fileName}
|
|
```
|
|
|
|
Use one stable release key for production APKs. Do not install a new APK signed by a different key over an already installed QMAX app.
|
|
|
|
## Push Notifications
|
|
|
|
The server has an FCM HTTP v1 dispatcher. To enable real push delivery on the Raspberry Pi, put a Firebase service account JSON under `deploy/secrets/` and set:
|
|
|
|
```dotenv
|
|
QMAX_FIREBASE_PROJECT_ID=qmax-29df6
|
|
QMAX_FIREBASE_SERVICE_ACCOUNT_PATH=/run/secrets/qmax/firebase-service-account.json
|
|
```
|
|
|
|
The Android app registers its FCM token after pairing. It uses the Google Services Gradle plugin with `android/app/google-services.json` for the package `xyz.kusoft.qmax`. `FirebaseBootstrap` still supports asset-based `firebase.android.json` or `google-services.json` as a fallback for custom local builds.
|
|
|
|
## Argus Publication
|
|
|
|
QMAX is published in Argus with:
|
|
|
|
- base URL: `https://argus.kusoft.xyz`
|
|
- slug: `qmax`
|
|
- platform: `android`
|
|
- channel: `stable`
|
|
|
|
Manifest:
|
|
|
|
```text
|
|
https://argus.kusoft.xyz/api/apps/qmax/manifest?platform=android&channel=stable
|
|
```
|
|
|
|
Latest APK:
|
|
|
|
```text
|
|
https://argus.kusoft.xyz/api/apps/qmax/download/latest?platform=android&channel=stable
|
|
```
|
|
|
|
The Android app checks this manifest, compares semantic versions, downloads the APK to a temporary file, verifies SHA-256, and only then opens Android's package installer.
|
|
|
|
## Implemented MAX Mapping
|
|
|
|
The worker code maps:
|
|
|
|
- chat list and message history through PyMax;
|
|
- text sending through PyMax;
|
|
- attachment upload through PyMax file/photo/video models;
|
|
- image, video, file and voice attachment projection through the API;
|
|
- Android image attachment caching with `.part` downloads before a file is shown from local storage;
|
|
- explicit per-user session status and phone-code re-login from Android settings.
|
|
|
|
An end-to-end login against the live MAX service was not run in this workspace; it must be verified on the target Raspberry Pi with real accounts.
|