154 lines
4.2 KiB
TypeScript
154 lines
4.2 KiB
TypeScript
import { Router } from "@oak/oak";
|
|
import { getMongoClient } from "../db/mongo.ts";
|
|
import { authMiddleware } from "../middleware/auth.ts";
|
|
import { ObjectId } from "mongodb";
|
|
|
|
const router = new Router({ prefix: "/api/app" });
|
|
|
|
interface AppRelease {
|
|
_id?: ObjectId;
|
|
version: string;
|
|
versionCode: number;
|
|
downloadUrl: string;
|
|
releaseNotes: string;
|
|
size?: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
}
|
|
|
|
async function getReleasesCollection() {
|
|
const client = await getMongoClient();
|
|
const db = client.db("ams");
|
|
return db.collection<AppRelease>("app_releases");
|
|
}
|
|
|
|
// Öffentlich: Version prüfen (App ruft das ohne Auth auf)
|
|
router.get("/update/check", async (ctx) => {
|
|
const currentVersion = ctx.request.url.searchParams.get("currentVersion") || "0";
|
|
const currentCode = parseInt(ctx.request.url.searchParams.get("currentCode") || "0");
|
|
|
|
const col = await getReleasesCollection();
|
|
const latest = await col.findOne({}, { sort: { versionCode: -1 } });
|
|
|
|
if (!latest) {
|
|
ctx.response.body = { updateAvailable: false };
|
|
return;
|
|
}
|
|
|
|
const updateAvailable = latest.versionCode > currentCode;
|
|
|
|
ctx.response.body = {
|
|
updateAvailable,
|
|
currentVersion,
|
|
latestVersion: latest.version,
|
|
latestVersionCode: latest.versionCode,
|
|
downloadUrl: latest.downloadUrl,
|
|
releaseNotes: latest.releaseNotes,
|
|
size: latest.size,
|
|
};
|
|
});
|
|
|
|
// Öffentlich: Download-Redirect zur APK
|
|
router.get("/update/download", async (ctx) => {
|
|
const col = await getReleasesCollection();
|
|
const latest = await col.findOne({}, { sort: { versionCode: -1 } });
|
|
|
|
if (!latest) {
|
|
ctx.response.status = 404;
|
|
ctx.response.body = { error: "Kein Release verfügbar" };
|
|
return;
|
|
}
|
|
|
|
ctx.response.redirect(latest.downloadUrl);
|
|
});
|
|
|
|
// Admin: Neues Release anlegen
|
|
router.post("/update/release", authMiddleware, async (ctx) => {
|
|
const body = await ctx.request.body.json();
|
|
const { version, versionCode, downloadUrl, releaseNotes, size } = body;
|
|
|
|
if (!version || !versionCode || !downloadUrl) {
|
|
ctx.response.status = 400;
|
|
ctx.response.body = { error: "version, versionCode und downloadUrl sind Pflichtfelder" };
|
|
return;
|
|
}
|
|
|
|
const col = await getReleasesCollection();
|
|
const now = new Date();
|
|
|
|
const result = await col.insertOne({
|
|
version,
|
|
versionCode,
|
|
downloadUrl,
|
|
releaseNotes: releaseNotes || "",
|
|
size: size || 0,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
});
|
|
|
|
ctx.response.status = 201;
|
|
ctx.response.body = { id: result.insertedId, version, versionCode };
|
|
});
|
|
|
|
// Admin: Alle Releases auflisten
|
|
router.get("/update/releases", authMiddleware, async (ctx) => {
|
|
const col = await getReleasesCollection();
|
|
const releases = await col.find({}).sort({ versionCode: -1 }).toArray();
|
|
ctx.response.body = { releases };
|
|
});
|
|
|
|
// Admin: Release löschen
|
|
router.delete("/update/release/:id", authMiddleware, async (ctx) => {
|
|
const id = ctx.params.id;
|
|
const col = await getReleasesCollection();
|
|
await col.deleteOne({ _id: new ObjectId(id) });
|
|
ctx.response.body = { deleted: true };
|
|
});
|
|
|
|
// Service-API: Release per API-Key anlegen (für CI-Pipeline)
|
|
router.post("/update/release/ci", async (ctx) => {
|
|
const apiKey = ctx.request.headers.get("X-API-Key") ||
|
|
ctx.request.url.searchParams.get("apiKey");
|
|
const expectedKey = Deno.env.get("AMS_SERVICE_API_KEY");
|
|
|
|
if (!apiKey || apiKey !== expectedKey) {
|
|
ctx.response.status = 401;
|
|
ctx.response.body = { error: "Ungültiger API-Key" };
|
|
return;
|
|
}
|
|
|
|
const body = await ctx.request.body.json();
|
|
const { version, versionCode, downloadUrl, releaseNotes, size } = body;
|
|
|
|
if (!version || !versionCode || !downloadUrl) {
|
|
ctx.response.status = 400;
|
|
ctx.response.body = { error: "version, versionCode und downloadUrl sind Pflichtfelder" };
|
|
return;
|
|
}
|
|
|
|
const col = await getReleasesCollection();
|
|
const now = new Date();
|
|
|
|
// Upsert by versionCode
|
|
await col.updateOne(
|
|
{ versionCode },
|
|
{
|
|
$set: {
|
|
version,
|
|
versionCode,
|
|
downloadUrl,
|
|
releaseNotes: releaseNotes || "",
|
|
size: size || 0,
|
|
updatedAt: now,
|
|
},
|
|
$setOnInsert: { createdAt: now },
|
|
},
|
|
{ upsert: true }
|
|
);
|
|
|
|
ctx.response.status = 201;
|
|
ctx.response.body = { success: true, version, versionCode };
|
|
});
|
|
|
|
export const appUpdateRouter = router;
|