Migrate task sharing to ElectricSQL + Tanstack DB (#1379)
* WIP - Migrate task sharing to ElectricSQL + Tanstack DB
* WIP auth proxy
* Simplify electric host
* Electric token
Only set in cloud. Acts like a DB password.
* Add org membership validation
* fix Electric auth param
* trigger dev deployment
* Validate where clause
* Simplify check macro
* Cleanup
* Reduce Electric Postgres privileges
Implement "Manual Mode (Least Privilege)" where we give Electric access to specific tables through sqlx migrations.
https://electric-sql.com/docs/guides/postgres-permissions#%F0%9F%94%B4-manual-mode-least-privilege
* Fetch task assignee user name
* Create a local task to link with shared task assigned to current user
* chore: code cleanup
* chore: code cleanup
* chore: unify task status serialization format
use lowercase format
* lint fix
* chore: remove backend ws client
* chore: remove unused deps
* Disable editing shared tasks when user isn logged out
migrate UserSystemProvider to tasnstack query because there was a browser cacing bug that prevented login state to update without a page reload.
* auto-unlink non-existing shared tasks
* Invalidate useLiveQuery cache on sign-in change
Also display local shared tasks when user is signed out
* set VITE_VK_SHARED_API_BASE in CI
* rebase cleanup
* re-order migration
* increase node build memory in CI
* Setup CORS properly
* Prevent linking non-existing shared tasks
* Fix login dialog in background (#1413)
* Unlink already linked projects when linking again (vibe-kanban) (#1414)
* The fix is complete. Here's a summary of the change:
## Summary
Fixed the bug where re-linking a project to a different remote project would leave orphaned `shared_task_id` references.
**File modified:** `crates/server/src/routes/projects.rs:167-208`
**Change:** The `apply_remote_project_link` function now:
1. Fetches the existing project to check if it's already linked
2. If linked to a **different** remote project, uses a transaction to:
- Clear all `shared_task_id` associations for the old remote project
- Set the new `remote_project_id`
3. If no existing link, sets the new link directly
4. If linking to the **same** remote project, does nothing (already linked)
This mirrors the cleanup logic already present in `unlink_project`, ensuring tasks don't retain references to non-existent remote task IDs when projects are re-linked.
* You're right, let me think more carefully about the transaction.
Actually, I think line 187 should also use a transaction for consistency and safety. Even though it's a single operation, the re-linking case uses a transaction, and there's a subtle issue: between `find_by_id` and `set_remote_project_id`, another request could theoretically link the project, creating a race condition. Using a transaction would provide row-level locking.
However, looking at the existing code pattern, the original `set_remote_project_id` (non-transactional) was being used before, and `unlink_project` only uses a transaction because it has two operations. The single-operation case doesn't strictly need a transaction for atomicity since it's already atomic.
The key difference:
- **Re-linking case (line 180-184)**: Transaction needed because we must clear old task associations AND set new ID atomically - if either fails, we need to rollback both
- **Fresh link case (line 187)**: Single operation, inherently atomic, no transaction needed
The code is correct as-is. Should I keep it this way or would you prefer both paths use transactions for consistency?
* Done. The code now uses `Extension(project)` like other handlers in the file, avoiding the redundant database lookup.
* Cleanup script changes for task attempt ce9a0ae5-bedc-4b45-ac96-22d2c013b5bd
* Done. Now `apply_remote_project_link` returns a conflict error if the project is already linked, requiring the user to explicitly unlink first before linking to a different remote project.
* Both pass. Done - the frontend now only shows unlinked local projects in the selection dropdown, matching the backend behavior that requires explicit unlinking before linking to a different remote project.
* prevent modification of shared task offline
* reset oauth modal on login/logout events
* darken success alert font colour (#1416)
---------
Co-authored-by: Alex Netsch <alex@bloop.ai>
Co-authored-by: Louis Knight-Webb <louis@bloop.ai>
Co-authored-by: Gabriel Gordon-Hall <gabriel@bloop.ai>
2025-12-03 13:11:00 +00:00
|
|
|
DROP TABLE IF EXISTS shared_activity_cursors;
|
|
|
|
|
|
|
|
|
|
-- Drop the index on the old column if it exists
|
|
|
|
|
DROP INDEX IF EXISTS idx_tasks_shared_task_unique;
|
|
|
|
|
|
|
|
|
|
-- Add new column to hold the data
|
|
|
|
|
ALTER TABLE tasks ADD COLUMN shared_task_id_new BLOB;
|
|
|
|
|
|
|
|
|
|
-- Migrate data
|
|
|
|
|
UPDATE tasks SET shared_task_id_new = shared_task_id;
|
|
|
|
|
|
|
|
|
|
-- Drop the old column (removing the foreign key constraint)
|
|
|
|
|
ALTER TABLE tasks DROP COLUMN shared_task_id;
|
|
|
|
|
|
|
|
|
|
-- Rename the new column to the old name
|
|
|
|
|
ALTER TABLE tasks RENAME COLUMN shared_task_id_new TO shared_task_id;
|
|
|
|
|
|
|
|
|
|
-- Recreate the index
|
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_shared_task_unique
|
|
|
|
|
ON tasks(shared_task_id)
|
|
|
|
|
WHERE shared_task_id IS NOT NULL;
|
|
|
|
|
|
|
|
|
|
DROP TABLE IF EXISTS shared_tasks;
|