- 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.
88 lines
2.5 KiB
TypeScript
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;
|
|
}
|