// @ts-check
/**
* AP 30 — Rechte-Vererbung fuer Bestands-Shares materialisieren.
*
* Neue Shares ziehen ihren Rechte-Satz beim Anlegen/Verlinken nach
* (`./20260924-project-share-target-model` nur strukturell, die Vererbung selbst
* in `RouterProject/projectShare/access.js`). Der **Bestand** hat nur die
* Owner-Zeile: die Projekt-Rechte sind nirgends am Share materialisiert, und es
* gibt kein Rechte-Event, aus dem eine Projektion sie gewinnen koennte.
*
* Dieses Modul holt das nach — je Share:
* 1. den Filter-Satz des Projekts spiegeln (dieselbe Struktur wie bei Listen),
* 2. `Visible` neu materialisieren und die `/add|/remove/{shareType}/{level}/{uid}`-
* Events melden (macht `listRebuildAccess` selbst).
*
* Damit wird `share_grants` in der RAG-Projektion **ableitbar** statt gerechnet:
* die Kette Members (`Visible`) → Event → Projektion steht dann lueckenlos.
*
* Idempotent. Die Organisation kommt ueber die Link-Kette
* (`getOrganizationForShare`) — das Modul enthaelt keine UID.
*/
export const id = '20260924-project-share-access-backfill';
export const name = 'Rechte-Vererbung: Visible/Events fuer Bestands-Shares materialisieren';
/** Enum-Werte der Share-Objekte in `ObjectBase.Type`. */
const SHARE_TYPES_SQL = `('repositoryShare','directoryShare')`;
/**
* Fuehrt den Backfill aus.
*
* @param {{ query: Function, HEX2uuid: Function }} api
* @returns {Promise<void>}
*/
export const migrate = async ({ query, HEX2uuid }) => {
// Nur Shares, die an (mindestens) einem Projekt haengen — alle anderen haben
// nichts zu erben. Beide Link-Richtungen (alt: Projekt -> Share, neu: umgekehrt).
const shares = await query(
`SELECT DISTINCT s.UID, s.Type FROM ObjectBase AS s
JOIN Links AS l ON (l.UID = s.UID AND l.Type = 'memberA'
AND l.UIDTarget IN (SELECT UID FROM ObjectBase WHERE Type = 'project'))
WHERE s.Type IN ${SHARE_TYPES_SQL}
UNION
SELECT DISTINCT s.UID, s.Type FROM ObjectBase AS s
JOIN Links AS l ON (l.UIDTarget = s.UID AND l.Type IN ('memberA','member','projectShare')
AND l.UID IN (SELECT UID FROM ObjectBase WHERE Type = 'project'))
WHERE s.Type IN ${SHARE_TYPES_SQL}`,
[],
);
if (shares.length === 0) return;
// Beide Helfer erst hier laden: die Migration laeuft beim Start, und der
// Baum-/Queue-Code soll das Modul nicht bei jedem Import mitziehen.
const { syncShareFilters } = await import('../../RouterProject/projectShare/access.js');
const { listRebuildAccess } = await import('../../tree/rebuildList.js');
const { getOrganizationForShare } = await import('../../utils/organizationUtils.js');
const seen = new Set();
for (const share of shares) {
const uid = HEX2uuid(share.UID);
if (!uid || seen.has(uid)) continue;
seen.add(uid);
const organization = await getOrganizationForShare(share.UID);
if (!organization) continue;
// 1. Struktur: Projekt-Filter auf den Share spiegeln.
await syncShareFilters(share.UID);
// 2. Materialisieren + Events. `listRebuildAccess` loescht die Zeilen
// des Targets und leitet sie aus den (jetzt gespiegelten) Filtern
// neu ab; die `member`-Verbindung des Shares sorgt fuer die
// Owner-Zeile, `memberSys` fuer den Org-Superadmin.
await listRebuildAccess(share.UID, organization);
}
};