mirror of
https://github.com/HeyPuter/puter.git
synced 2026-08-24 23:17:23 +00:00
feat(share): extend the share table into an index of active shares
This commit is contained in:
@@ -27,7 +27,7 @@ import { DatabaseClientFactory } from './index.js';
|
||||
import { SqliteDatabaseClient } from './SqliteDatabaseClient.js';
|
||||
|
||||
/** Highest schema version the migration table can reach. */
|
||||
const CURRENT_SCHEMA_VERSION = 60;
|
||||
const CURRENT_SCHEMA_VERSION = 61;
|
||||
const SYSTEM_USER_UUID = '5d4adce0-a381-4982-9c02-6e2540026238';
|
||||
|
||||
const sqliteConfig = (
|
||||
|
||||
@@ -94,6 +94,7 @@ const AVAILABLE_MIGRATIONS: [number, string[]][] = [
|
||||
[57, ['0062_blocked-app-origins.sql']],
|
||||
[58, ['0063_add_suspended_reason.sql']],
|
||||
[59, ['0064_abuse-moderation-events.sql']],
|
||||
[60, ['0065_share_entries.sql']],
|
||||
];
|
||||
|
||||
export class SqliteDatabaseClient extends AbstractDatabaseClient {
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
-- Copyright (C) 2024-present Puter Technologies Inc.
|
||||
--
|
||||
-- This file is part of Puter.
|
||||
--
|
||||
-- Puter is free software: you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU Affero General Public License as published
|
||||
-- by the Free Software Foundation, either version 3 of the License, or
|
||||
-- (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU Affero General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU Affero General Public License
|
||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
-- Grow `share` from a pending-email-invite table into the index of active
|
||||
-- shares. See sqlite/0065_share_entries.sql for the column rationale.
|
||||
--
|
||||
-- Idempotent: columns go through _puter_add_col (from mig_1), indexes and
|
||||
-- foreign keys through the guarded procedure below. There is no per-file
|
||||
-- applied-state tracking, so every statement must tolerate a re-run.
|
||||
|
||||
CALL _puter_add_col('share', 'holder_user_id', '`holder_user_id` int unsigned DEFAULT NULL');
|
||||
CALL _puter_add_col('share', 'fsentry_id', '`fsentry_id` int unsigned DEFAULT NULL');
|
||||
CALL _puter_add_col('share', 'mode', '`mode` varchar(20) DEFAULT NULL');
|
||||
CALL _puter_add_col('share', 'applied_at', '`applied_at` timestamp NULL DEFAULT NULL');
|
||||
|
||||
DROP PROCEDURE IF EXISTS _puter_add_share_index_constraints;
|
||||
DELIMITER //
|
||||
CREATE PROCEDURE _puter_add_share_index_constraints()
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'share'
|
||||
AND INDEX_NAME = 'idx_share_holder'
|
||||
) THEN
|
||||
ALTER TABLE `share` ADD INDEX `idx_share_holder` (`holder_user_id`, `id`);
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'share'
|
||||
AND INDEX_NAME = 'idx_share_fsentry'
|
||||
) THEN
|
||||
ALTER TABLE `share` ADD INDEX `idx_share_fsentry` (`fsentry_id`);
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'share'
|
||||
AND INDEX_NAME = 'idx_share_holder_entry_issuer'
|
||||
) THEN
|
||||
ALTER TABLE `share` ADD UNIQUE INDEX `idx_share_holder_entry_issuer`
|
||||
(`holder_user_id`, `fsentry_id`, `issuer_user_id`);
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'share'
|
||||
AND CONSTRAINT_NAME = 'share_holder_user_fk'
|
||||
) THEN
|
||||
ALTER TABLE `share` ADD CONSTRAINT `share_holder_user_fk`
|
||||
FOREIGN KEY (`holder_user_id`) REFERENCES `user` (`id`)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
END IF;
|
||||
|
||||
-- The cascade that retires a share with its file.
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'share'
|
||||
AND CONSTRAINT_NAME = 'share_fsentry_fk'
|
||||
) THEN
|
||||
ALTER TABLE `share` ADD CONSTRAINT `share_fsentry_fk`
|
||||
FOREIGN KEY (`fsentry_id`) REFERENCES `fsentries` (`id`)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END//
|
||||
DELIMITER ;
|
||||
|
||||
CALL _puter_add_share_index_constraints();
|
||||
|
||||
DROP PROCEDURE IF EXISTS _puter_add_share_index_constraints;
|
||||
@@ -0,0 +1,34 @@
|
||||
-- Copyright (C) 2024-present Puter Technologies Inc.
|
||||
--
|
||||
-- This file is part of Puter.
|
||||
--
|
||||
-- Puter is free software: you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU Affero General Public License as published
|
||||
-- by the Free Software Foundation, either version 3 of the License, or
|
||||
-- (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU Affero General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU Affero General Public License
|
||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
-- Grow share from a pending-email-invite table into the index of active
|
||||
-- shares. See sqlite/0065_share_entries.sql for the column rationale.
|
||||
|
||||
ALTER TABLE share
|
||||
ADD COLUMN IF NOT EXISTS holder_user_id integer
|
||||
REFERENCES "user" (id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
ADD COLUMN IF NOT EXISTS fsentry_id integer
|
||||
REFERENCES fsentries (id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
ADD COLUMN IF NOT EXISTS mode varchar(20),
|
||||
ADD COLUMN IF NOT EXISTS applied_at timestamp;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_share_holder
|
||||
ON share (holder_user_id, id);
|
||||
CREATE INDEX IF NOT EXISTS idx_share_fsentry
|
||||
ON share (fsentry_id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_share_holder_entry_issuer
|
||||
ON share (holder_user_id, fsentry_id, issuer_user_id);
|
||||
@@ -0,0 +1,51 @@
|
||||
-- Copyright (C) 2024-present Puter Technologies Inc.
|
||||
--
|
||||
-- This file is part of Puter.
|
||||
--
|
||||
-- Puter is free software: you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU Affero General Public License as published
|
||||
-- by the Free Software Foundation, either version 3 of the License, or
|
||||
-- (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU Affero General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU Affero General Public License
|
||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
-- Grow `share` from a pending-email-invite table into the index of active
|
||||
-- shares. Permissions stay the source of truth for access; this is what makes
|
||||
-- shares listable and gives them a lifecycle.
|
||||
--
|
||||
-- - `holder_user_id` : NULL while an invite awaits signup.
|
||||
-- - `fsentry_id` : the shared node. ON DELETE CASCADE retires the share
|
||||
-- with the file, which permissions alone don't do.
|
||||
-- - `mode` : see|list|read|write|manage. Unconstrained on purpose —
|
||||
-- ACLService owns the mode set and adding one shouldn't
|
||||
-- need a three-dialect migration.
|
||||
-- - `applied_at` : set when an invite is claimed. Claiming updates the
|
||||
-- row rather than deleting it, so the share stays
|
||||
-- queryable.
|
||||
|
||||
ALTER TABLE `share` ADD COLUMN `holder_user_id` INTEGER DEFAULT NULL
|
||||
REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE `share` ADD COLUMN `fsentry_id` INTEGER DEFAULT NULL
|
||||
REFERENCES `fsentries` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE `share` ADD COLUMN `mode` TEXT DEFAULT NULL;
|
||||
ALTER TABLE `share` ADD COLUMN `applied_at` TIMESTAMP DEFAULT NULL;
|
||||
|
||||
-- "Shared with me", keyset-paginated: ORDER BY ends in `id` as the tiebreaker.
|
||||
CREATE INDEX IF NOT EXISTS `idx_share_holder`
|
||||
ON `share` (`holder_user_id`, `id`);
|
||||
|
||||
-- Who has access to one node — also how an owner sees a manage-delegate's
|
||||
-- re-grants, which the issuer/holder permission tables can't answer.
|
||||
CREATE INDEX IF NOT EXISTS `idx_share_fsentry` ON `share` (`fsentry_id`);
|
||||
|
||||
-- One row per (holder, node, issuer). Pending invites have a NULL
|
||||
-- holder_user_id and so aren't covered here; dedup for those belongs with the
|
||||
-- invite flow.
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS `idx_share_holder_entry_issuer`
|
||||
ON `share` (`holder_user_id`, `fsentry_id`, `issuer_user_id`);
|
||||
Reference in New Issue
Block a user