feat: retire oCIS — the hub runs entirely on R2 object storage

Files payloads moved to R2 in 041; this removes the remaining oCIS coupling:
- delete the oCIS WebDAV client; verifyWhileStreaming moves to its own
  storage-agnostic module (verify-stream.ts)
- group-files: storage_key is the only payload source; legacy rows answer
  'retired storage' instead of touching oCIS
- collective share: the ocis-resource item kind is gone (peertube links stay);
  share-into-library was R2-backed since the previous commit
- import-drive endpoint removed (no web caller); import-chat (homeserver) stays
- web: Drive tab + embedded oCIS page removed; Library is the Files view
Legacy e2e rows (2) deleted; every remaining archive row is R2-backed.
This commit is contained in:
glass beadian 2026-10-16 01:32:56 +00:00
parent 4cbb63920a
commit e7bf870de9
13 changed files with 88 additions and 750 deletions

View file

@ -1,79 +0,0 @@
"use client";
import { EmbeddedApp } from "../../../../components/embed/embedded-app";
import { useFilesLang } from "../../../../components/files/i18n";
import { useApiResource } from "../../../../lib/use-api";
import { useIsMobile } from "../../../../hooks/use-is-mobile";
/**
* Drive — the oCIS web UI (personal space, shares, office documents) embedded
* full height under the Files tabs. The group Library tab keeps the Hub-owned
* features (upload into a collective, share with a collective, retention).
*
* Sign-in follows GET /drive/session's authMode: "interactive" means oCIS's
* own identity provider — the user signs in once inside the frame and oCIS
* web keeps its tokens (tokenStorageLocal) on the files origin, which this
* frame shares because app.* and files.* are the same site. "oidc" means oCIS
* and the Hub share an issuer and the frame signs in silently.
*/
/** Same host FILES_PUBLIC_BASE defaults to; used only while the API is offline. */
const FALLBACK_DRIVE_URL = "https://files.occupymedia.cc";
interface DriveSession {
driveUrl: string | null;
authMode: "interactive" | "oidc";
}
const driveView = (session: DriveSession): DriveSession | null => session;
export default function DrivePage(): React.ReactElement {
const { t } = useFilesLang();
const drive = useApiResource<DriveSession, DriveSession | null>("/drive/session", driveView, null);
const isMobile = useIsMobile();
const driveUrl = drive.source === "sample" ? FALLBACK_DRIVE_URL : drive.data?.driveUrl ?? null;
const notConfigured = drive.source === "live" && drive.error === null && driveUrl === null;
const error =
drive.error ??
(notConfigured ? t({ en: "The drive is not configured on this server (FILES_DRIVE_URL).", es: "La unidad no está configurada en este servidor (FILES_DRIVE_URL)." }) : null);
const notice =
drive.data?.authMode === "oidc"
? t({ en: "Collective files live in the Library tab.", es: "Los archivos de los colectivos están en la pestaña Biblioteca." })
: t({
en: "Sign in once inside the drive — it stays signed in. Collective files live in the Library tab.",
es: "Inicia sesión una vez dentro de la unidad y quedará abierta. Los archivos de los colectivos están en la pestaña Biblioteca.",
});
if (isMobile) {
// Mobile: the oCIS file browser under the bottom tab bar breaks (nested
// scrolling, double chrome) — hand off to the standalone drive instead
// (panel consensus: gateway pattern). The Library tab is fully native.
return (
<div className="flex h-full flex-col justify-center px-6 py-10 text-center">
<h1 className="text-lg font-bold">{t({ en: "Drive", es: "Unidad" })}</h1>
<p className="mx-auto mt-2 max-w-xs text-xs leading-relaxed text-text-dim">{error ?? notice}</p>
<div className="mx-auto mt-8 w-full max-w-xs">
<a
href={`${driveUrl ?? FALLBACK_DRIVE_URL}/`}
className="flex min-h-12 items-center justify-center rounded-xl bg-accent px-4 text-sm font-semibold text-white transition-opacity hover:opacity-90"
>
{t({ en: "Open drive ↗", es: "Abrir unidad ↗" })}
</a>
</div>
</div>
);
}
return (
<EmbeddedApp
title={t({ en: "Drive", es: "Unidad" })}
src={drive.source === "loading" || error !== null ? null : `${driveUrl ?? FALLBACK_DRIVE_URL}/`}
externalHref={`${driveUrl ?? FALLBACK_DRIVE_URL}/`}
error={error}
notice={notice}
onRetry={drive.error !== null ? drive.reload : undefined}
allow="fullscreen; clipboard-read; clipboard-write"
/>
);
}

View file

@ -8,7 +8,7 @@ import { FilesTabs } from "../../../components/files/files-tabs";
* Files shell. Like the wiki, the language lives in localStorage per browser
* (lang-context explains why it is not an account setting) and is provided here
* so every Files screen and sub-component reads it through one context. The tab
* strip switches between the group Library and the embedded oCIS Drive.
* strip switches between the group Library views.
*/
export default function FilesLayout({ children }: { children: ReactNode }): React.ReactElement {
return (

View file

@ -6,20 +6,16 @@ import { useFilesLang, type LocalizedText } from "./i18n";
const TABS: { href: string; label: LocalizedText }[] = [
{ href: "/files", label: { en: "Library", es: "Biblioteca" } },
{ href: "/files/drive", label: { en: "Drive", es: "Unidad" } },
];
/**
* Files section switcher: the Hub's group library (upload, share with a
* collective, retention) and the embedded oCIS drive side by side.
*/
/** Files section switcher: the Hub's group library (upload, share, retention). */
export function FilesTabs(): React.ReactElement {
const pathname = usePathname();
const { t } = useFilesLang();
return (
<nav aria-label={t({ en: "Files views", es: "Vistas de archivos" })} className="flex shrink-0 gap-1 border-b border-border-subtle bg-surface px-3 pt-2">
{TABS.map((tab) => {
const active = tab.href === "/files" ? pathname === "/files" : pathname.startsWith(tab.href);
const active = pathname === "/files";
return (
<Link
key={tab.href}