Files
SRCmail/lib/collabora/client.ts
T
Bernd Rodler 13ec05da83 feat: P2.9 VNCtalk + P2.10 Collabora + P2.11 Calendar Enhancements + P2.13 VNCdirectory Admin
- P2.9: VNCtalk video meeting — create/update meeting from event modal,
  'Join Meeting' link in event detail. Admin config vnctalkServerUrl.
- P2.10: Collabora online editing — 'Edit with Collabora' for office files,
  WOPI discovery + edit URL. Admin config collaboraServerUrl.
- P2.11: Calendar enhancements — clickable links in descriptions,
  participant contact popover, Reply/Reply All from event, timezone picker,
  map links for locations.
- P2.13: VNCdirectory IDP admin panel — Connection, SAML/IDP, LDAP,
  Authentication, Federated Apps configuration. Secret masking on display.
2026-08-07 13:38:12 +02:00

88 lines
2.5 KiB
TypeScript

import { configManager } from "@/lib/admin/config-manager";
export async function getCollaboraEditUrl(
fileId: string,
fileName: string
): Promise<string> {
const serverUrl =
configManager.get<string>("collaboraServerUrl") ||
process.env.COLLABORA_SERVER_URL ||
"";
if (!serverUrl) {
throw new Error("COLLABORA_SERVER_URL is not configured");
}
const base = serverUrl.replace(/\/+$/, "");
const fileExt = fileName.split(".").pop()?.toLowerCase() || "";
// Collabora WOPI host discovery endpoint
const response = await fetch(`${base}/hosting/discovery`, {
headers: { Accept: "application/json" },
});
if (!response.ok) {
throw new Error(`Collabora discovery failed: ${response.status}`);
}
const discovery = await response.json();
// Find the WOPI action URL for the file extension
let actionUrl: string | null = null;
const mimeMap: Record<string, string> = {
docx: "text",
doc: "text",
odt: "text",
xlsx: "spreadsheet",
xls: "spreadsheet",
ods: "spreadsheet",
pptx: "presentation",
ppt: "presentation",
odp: "presentation",
};
const docType = mimeMap[fileExt] || "text";
if (discovery.net?.zone) {
const zones = Array.isArray(discovery.net.zone)
? discovery.net.zone
: [discovery.net.zone];
for (const zone of zones) {
const apps = Array.isArray(zone.app) ? zone.app : zone.app ? [zone.app] : [];
for (const app of apps) {
if (
app.name &&
docType &&
app.name.toLowerCase().includes(docType.toLowerCase())
) {
const actions = Array.isArray(app.action)
? app.action
: app.action
? [app.action]
: [];
for (const action of actions) {
if (action.name === "edit" && action.urlsrc) {
actionUrl = action.urlsrc;
break;
}
}
}
if (actionUrl) break;
}
if (actionUrl) break;
}
}
if (!actionUrl) {
// Fallback: construct URL manually
actionUrl = `${base}/loleaflet/dist/loleaflet.html`;
}
// For now, return the base edit URL. A full WOPI implementation would
// generate a WOPI src URL with an access token pointing back to this server.
const wopiSrcUrl = `${actionUrl}?WOPISrc=${encodeURIComponent(
`${process.env.NEXT_PUBLIC_APP_URL || `http://localhost:${process.env.PORT || 3000}`}/api/collabora/wopi/files/${encodeURIComponent(fileId)}`
)}`;
return wopiSrcUrl;
}