ultisuite-client/lib/api/public-share.ts
R3D347HR4Y 6ec95262af Add OnlyOffice integration and update project configurations
- Updated .env.example to include configuration for OnlyOffice Document Server.
- Modified the workspace configuration to remove the drive-suite path.
- Adjusted TypeScript environment imports for consistency.
- Enhanced Next.js configuration to disable canvas in Webpack.
- Updated package.json to include new dependencies for OnlyOffice and PDF.js.
- Added global styles for OnlyOffice theme integration in the CSS.
- Created new layout and page components for the Drive feature, including public sharing and editing functionalities.
- Updated metadata handling across various layouts to reflect the new app structure.
2026-06-07 15:49:21 +02:00

136 lines
4.4 KiB
TypeScript

import type { DriveFileInfo } from "@/lib/api/types"
import { blobForPreview } from "@/lib/api/drive-download"
export interface PublicShareView {
token: string
name: string
item_type: "file" | "folder"
path: string
permissions: number
owner_id?: string
owner_displayname?: string
files?: DriveFileInfo[]
file?: DriveFileInfo
}
/** Label for "Partagé par …" — display name, then local part of owner id, then fallback. */
export function publicShareOwnerLabel(data: Pick<PublicShareView, "owner_displayname" | "owner_id">): string {
const display = data.owner_displayname?.trim()
if (display) return display
const id = data.owner_id?.trim()
if (!id) return "Utilisateur"
const at = id.indexOf("@")
return at > 0 ? id.slice(0, at) : id
}
export function publicShareHref(token: string, path = "/"): string {
const base = `/drive/s/${encodeURIComponent(token)}`
const trimmed = path.replace(/^\/+|\/+$/g, "")
if (!trimmed) return base
return `${base}/${trimmed.split("/").map(encodeURIComponent).join("/")}`
}
export function folderPathFromPublicSegments(segments: string[] | undefined): string {
if (!segments?.length) return "/"
return "/" + segments.map((s) => decodeURIComponent(s)).join("/")
}
/** Extract NC or Ultidrive public share token from a share URL. */
export function extractShareTokenFromURL(url: string): string | null {
const trimmed = url.trim()
if (!trimmed) return null
try {
const parsed = new URL(trimmed, "http://localhost")
const match =
parsed.pathname.match(/\/index\.php\/s\/([^/]+)/) ??
parsed.pathname.match(/\/drive\/s\/([^/]+)/) ??
parsed.pathname.match(/\/s\/([^/]+)/)
return match ? decodeURIComponent(match[1]) : null
} catch {
return null
}
}
/** Prefer Ultidrive `/drive/s/{token}` URLs (absolute when origin known). */
export function normalizePublicShareURL(
url: string,
token?: string,
origin?: string
): string {
const resolvedToken = token?.trim() || extractShareTokenFromURL(url)
if (!resolvedToken) return url
const href = publicShareHref(resolvedToken)
const base = origin ?? (typeof window !== "undefined" ? window.location.origin : "")
return base ? `${base.replace(/\/$/, "")}${href}` : href
}
export function publicShareDownloadApiPath(
token: string,
filePath: string,
password?: string
): string {
const parts = filePath
.replace(/^\/+/, "")
.split("/")
.filter(Boolean)
.map((seg) => encodeURIComponent(seg))
const base = `/api/v1/drive/public/shares/${encodeURIComponent(token)}/download/${parts.join("/")}`
if (!password) return base
return `${base}?password=${encodeURIComponent(password)}`
}
export function publicSharePreviewApiPath(
token: string,
file: { path: string; name: string },
password?: string,
width = 400,
height = 300
): string {
const params = new URLSearchParams({
path: publicShareDownloadPath(file),
w: String(width),
h: String(height),
})
if (password) params.set("password", password)
return `/api/v1/drive/public/shares/${encodeURIComponent(token)}/preview?${params.toString()}`
}
export async function fetchPublicShare(
token: string,
path = "/",
password?: string
): Promise<PublicShareView> {
const params = new URLSearchParams()
if (path && path !== "/") params.set("path", path)
if (password) params.set("password", password)
const qs = params.toString()
const res = await fetch(
`/api/v1/drive/public/shares/${encodeURIComponent(token)}${qs ? `?${qs}` : ""}`
)
if (!res.ok) {
throw new Error(res.status === 403 ? "password_required" : "share_unavailable")
}
return (await res.json()) as PublicShareView
}
export function publicShareDownloadPath(file: { path: string; name: string }): string {
const path = (file.path ?? "").trim() || "/"
if (path === "/") return "/"
if (path.endsWith(`/${file.name}`) || path === `/${file.name}`) return path
return `${path.replace(/\/$/, "")}/${file.name}`.replace(/\/+/g, "/")
}
export async function fetchPublicShareBlob(
token: string,
file: { path: string; name: string; mime_type?: string },
password?: string
): Promise<Blob> {
const logical = publicShareDownloadPath(file)
const res = await fetch(publicShareDownloadApiPath(token, logical, password))
if (!res.ok) {
throw new Error("download_failed")
}
const raw = await res.blob()
return blobForPreview(raw, file.mime_type ?? "", file.name)
}