Skip to content

deploy main#61

Merged
RETR0-OS merged 8 commits intodeployfrom
main
Feb 23, 2026
Merged

deploy main#61
RETR0-OS merged 8 commits intodeployfrom
main

Conversation

@RETR0-OS
Copy link
Member

This pull request introduces a new project sharing feature, allowing users to publicly share projects via a unique link. The backend adds fields and endpoints to support sharing, while the frontend implements read-only views for shared projects and ensures the UI disables editing in these contexts.

Project Sharing Feature

  • Added share_token (UUID) and is_shared (Boolean) fields to the Project model, including migration and serializer updates to expose these fields as read-only. [1] [2] [3] [4]
  • Implemented sharing endpoints: enable/disable sharing (for owners), and public endpoints to fetch shared project metadata and architecture by token. [1] [2] [3]

Frontend Support for Shared Projects

  • Added route and component for /shared/:shareToken, rendering a read-only project canvas for shared projects. [1] [2]
  • Modified Canvas and related components to support a readOnly mode, disabling editing, keyboard shortcuts, and context menus when viewing a shared project. [1] [2] [3] [4] [5] [6] [7]
  • Updated ConfigPanel to respect readOnly mode, disabling editing controls and modal dialogs for custom blocks in shared project views. [1] [2] [3] [4] [5] [6] [7]

RETR0-OS and others added 8 commits February 17, 2026 00:56
Allows project owners to generate a stable shareable URL for their
architecture graphs. Key behaviours:

- Owner flow: "Share" button in header opens a dialog with a toggle to
  enable/disable sharing. Enabling generates a UUID token once (reused
  on re-enable so the URL never changes). Disabling deactivates the
  link without clearing the token.

- Viewer (anonymous): /shared/<token> loads a fully interactive
  read-only ReactFlow canvas — pan, zoom, click nodes to inspect
  config — with no editing capabilities. Shows project name, framework
  badge, owner attribution, and a "Read-only" badge.

- Viewer gated actions: "Make a Copy" and "Export Code" are always
  visible but show a login modal for unauthenticated users. Once
  signed in, "Make a Copy" creates a new project in the viewer's
  workspace and redirects them to it. "Export Code" runs the same
  export flow as the header, generating the original framework code.

Security: read-only enforcement is prop-based (readOnly={true} passed
into Canvas from SharedProjectCanvas), not a flippable global store
flag. All backend write endpoints already enforce ownership via
firebase_uid, providing a second layer of defence.

Backend:
- Project model: share_token (UUID, unique, nullable) + is_shared bool
- Migration 0005_project_sharing
- 4 new view functions in sharing_views.py (2 public GET, 2 auth POST/DELETE)
- New URL routes: /shared/<token>/, /shared/<token>/architecture/,
  /projects/<id>/share/, /projects/<id>/unshare/

Frontend:
- projectApi.ts: SharedProjectResponse type + 4 API helpers
- types.ts: optional share_token/is_shared on Project
- Canvas.tsx: readOnly prop disables drag, connect, delete, undo/redo,
  context menu, HistoryToolbar
- ShareDialog.tsx: new component (toggle + copy-link)
- Header.tsx: Share button + ShareDialog rendered for authed owners
- SharedProjectCanvas.tsx: new standalone page component
- App.tsx: /shared/:shareToken route

https://claude.ai/code/session_01FbQd5qajyLMQPjBae4R25k
…-only view

- Canvas: pass filtered onNodesChange in read-only mode so ReactFlow still
  propagates selection changes (node.selected=true). Without this the group
  block expand button (gated by `selected`) never appeared, and ConfigPanel
  had no selected node to display.
- ConfigPanel: add readOnly prop — disables all inputs (text, number, boolean,
  select, file), hides Delete Block button, Repeat Block section, and quick
  shape presets.
- InternalNodeConfigPanel: add readOnly prop — disables inputs, hides Reset /
  Reset All buttons, updates footer copy.
- SharedProjectCanvas: import and mount <ConfigPanel readOnly> alongside the
  canvas; add selectedNodeId to destructured store so the panel appears only
  when a node is selected.

https://claude.ai/code/session_01FbQd5qajyLMQPjBae4R25k
…ent API responses

Co-authored-by: RETR0-OS <74290459+RETR0-OS@users.noreply.github.com>
Fix inconsistent sharing fields between ProjectSerializer and ProjectDetailSerializer
Copilot AI review requested due to automatic review settings February 23, 2026 05:53
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a comprehensive project sharing feature enabling users to publicly share their neural network architectures via unique, secure links. The implementation includes backend support for managing sharing state and frontend read-only views that preserve full interactivity (pan, zoom, inspect) while preventing modifications.

Changes:

  • Added database fields (share_token, is_shared) to the Project model with proper migration and unique indexing
  • Implemented backend endpoints for enabling/disabling sharing (owner-only) and fetching shared project data (public, no auth)
  • Created a dedicated SharedProjectCanvas component mirroring the main canvas with read-only mode, login-gated copy/export, and graceful error handling
  • Extended Canvas, ConfigPanel, and InternalNodeConfigPanel with readOnly prop to disable all editing interactions while preserving selection and viewing
  • Added ShareDialog component in the main Header for authenticated users to toggle sharing and copy shareable links

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
project/block_manager/models.py Added share_token (UUID, unique, indexed) and is_shared (Boolean) fields to Project model
project/block_manager/migrations/0005_project_sharing.py Database migration adding sharing fields with proper constraints
project/block_manager/serializers.py Exposed share_token and is_shared as read-only fields in Project and ProjectDetail serializers
project/block_manager/views/sharing_views.py Implemented 4 endpoints: public shared project/architecture fetch, owner-only enable/disable sharing
project/block_manager/urls.py Added URL routes for sharing endpoints with UUID parameters
project/frontend/src/lib/types.ts Extended Project interface with optional share_token and is_shared fields
project/frontend/src/lib/projectApi.ts Added API client functions for fetching shared data and managing sharing state
project/frontend/src/components/SharedProjectCanvas.tsx New component rendering read-only shared project view with login-gated copy/export actions
project/frontend/src/components/ShareDialog.tsx New dialog component for toggling sharing and copying shareable URLs
project/frontend/src/components/Canvas.tsx Added readOnly prop, custom node change handler filtering mutations, disabled editing interactions
project/frontend/src/components/ConfigPanel.tsx Added readOnly prop, conditionally rendering controls and passing to InternalNodeConfigPanel
project/frontend/src/components/InternalNodeConfigPanel.tsx Added readOnly prop, disabled all form inputs and mutation buttons when active
project/frontend/src/components/Header.tsx Added Share button (authenticated users only) and integrated ShareDialog
project/frontend/src/App.tsx Added /shared/:shareToken route rendering SharedProjectCanvas component

Comment on lines +10 to +35
@api_view(['GET'])
def get_shared_project(request, share_token):
"""
Public endpoint — returns project metadata for a shared project.
No authentication required.
Returns 404 if the token doesn't exist or sharing is disabled.
"""
try:
project = Project.objects.get(share_token=share_token, is_shared=True)
except Project.DoesNotExist:
return Response(
{'error': 'Shared project not found or link is no longer active'},
status=status.HTTP_404_NOT_FOUND
)

owner_display_name = None
if project.user:
owner_display_name = project.user.display_name or "Anonymous"

return Response({
'name': project.name,
'description': project.description,
'framework': project.framework,
'owner_display_name': owner_display_name,
'share_token': str(project.share_token),
})
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shared project endpoints should be exempt from Firebase authentication in the middleware. Currently, the middleware will attempt to verify tokens for these public endpoints. Add /api/shared/ to the exempt_paths list in FirebaseAuthenticationMiddleware.process_request() to avoid unnecessary token verification overhead and potential edge-case failures for unauthenticated users accessing shared projects.

Copilot uses AI. Check for mistakes.
handleExport()
}
setPendingAction(null)
}, [user])
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The useEffect at line 119 has incomplete dependencies. It calls handleMakeCopy() and handleExport() which depend on projectMeta, nodes, edges, and groupDefinitions, but these are not in the dependency array. This can cause stale closures where the effect uses outdated values when the user logs in after the data has changed. Add projectMeta, nodes, edges, groupDefinitions, navigate, handleMakeCopy, and handleExport to the dependency array, or use refs for the data values, or restructure to avoid closures by inlining the logic.

Suggested change
}, [user])
}, [
user,
pendingAction,
projectMeta,
nodes,
edges,
groupDefinitions,
navigate,
handleMakeCopy,
handleExport,
])

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +104
} catch {
setNotFound(true)
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handling at line 103 is too generic. All errors are treated as "not found" which could be misleading. Network errors, server errors (500), or authentication failures would all show the same "Link not found" message. Consider catching errors more granularly and showing appropriate messages: network errors should suggest checking connectivity, 404s can show "link not found", and 500s should indicate a server issue. This improves user experience and aids debugging.

Suggested change
} catch {
setNotFound(true)
} catch (error: unknown) {
// Log the error for debugging and distinguish common failure modes.
console.error('Failed to load shared project', error)
const anyError = error as { status?: number; response?: { status?: number }; name?: string }
const status = anyError?.status ?? anyError?.response?.status
if (status === 404) {
// Preserve existing "not found" behavior for real 404s.
setNotFound(true)
toast.error('Shared link not found.')
} else if (status === 401 || status === 403) {
toast.error('You are not authorized to view this shared project.')
} else if (typeof status === 'number' && status >= 500 && status < 600) {
toast.error('Server error while loading shared project. Please try again later.')
} else if (anyError?.name === 'TypeError' && status == null) {
// Fetch often throws TypeError on network failures with no status code.
toast.error('Network error while loading shared project. Check your connection and try again.')
} else {
toast.error('Unexpected error while loading shared project.')
}

Copilot uses AI. Check for mistakes.
Comment on lines +53 to +56
try:
architecture = project.architecture
except Exception:
return Response({'nodes': [], 'edges': [], 'groupDefinitions': []})
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exception handling at line 53-56 is too broad and silently swallows errors. This catches all exceptions (including programming errors, database failures, etc.) and returns an empty architecture. This could hide real issues in production. Instead, catch specific exceptions like Project.architecture.RelatedObjectDoesNotExist or AttributeError and log unexpected exceptions before returning a fallback response. This will help with debugging and monitoring.

Copilot uses AI. Check for mistakes.
Comment on lines +26 to +27
if project.user:
owner_display_name = project.user.display_name or "Anonymous"
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential information disclosure in shared project endpoint. The owner_display_name field at line 27 falls back to "Anonymous" when display_name is not set, but this reveals that a user exists even if they haven't set a display name. Consider using a consistent anonymization approach or omitting the field entirely when display_name is None/empty to avoid revealing user existence patterns.

Suggested change
if project.user:
owner_display_name = project.user.display_name or "Anonymous"
if project.user and project.user.display_name:
owner_display_name = project.user.display_name

Copilot uses AI. Check for mistakes.
@RETR0-OS
Copy link
Member Author

@claude
Review and implement the relevant changes. DO NOT implement changes that are not relevant in the context of the task

@claude
Copy link

claude bot commented Feb 23, 2026

Claude finished @RETR0-OS's task in 5m 3s —— View job


Reviewing and Implementing Changes

Analysis of Copilot Review Comments:

# Issue Action
1 Add /api/v1/shared/ to Firebase middleware exempt_paths Implemented
2 Broad except Exception in sharing_views.py silently swallows errors Implemented
3 Incomplete useEffect deps in SharedProjectCanvas.tsx (stale closures) Implemented
4 Generic error handling treats all failures as "not found" Implemented
5 owner_display_name info disclosure Skipped — already addressed by commit 6aaf28b (Anonymous fallback), which was an intentional design decision

Tasks:

  • Gather context and read relevant files
  • Analyze copilot review comments for relevance
  • Fix Firebase middleware exemption for /api/v1/shared/ endpoints
  • Fix broad exception handling in sharing_views.py
  • Update projectApi.ts to expose HTTP status on thrown errors (required for granular error handling)
  • Fix useEffect deps + improve error handling in SharedProjectCanvas.tsx
  • Commit and push changes to fix/sharing-review-fixes branch

All 4 relevant fixes have been implemented and pushed to the fix/sharing-review-fixes branch. Since main is a protected branch, the changes need to be merged via a PR. Please create a PR from fix/sharing-review-fixesmain.

Changes made:

authentication/middleware.py

  • Added /api/v1/shared/ to exempt_paths so the two public sharing endpoints (GET /api/v1/shared/<token>/ and GET /api/v1/shared/<token>/architecture/) skip Firebase token verification entirely, avoiding overhead and edge-case failures for unauthenticated visitors

block_manager/views/sharing_views.py

  • Added import logging and from django.core.exceptions import ObjectDoesNotExist
  • Split the except Exception into except ObjectDoesNotExist (expected case — project has no architecture yet) and a fallback except Exception with logger.exception(...) so unexpected errors (DB failures, programming errors) surface in logs rather than being silently swallowed

frontend/src/lib/projectApi.ts

  • Updated fetchSharedProject and fetchSharedArchitecture to attach err.status = response.status to thrown errors, so callers can distinguish 404 vs 5xx vs auth errors

frontend/src/components/SharedProjectCanvas.tsx

  • Replaced the bare catch { setNotFound(true) } with status-based handling: 404/no-status → not-found screen + toast, 401/403 → authorization toast, 5xx → server error toast, TypeError → network error toast
  • Added all missing useEffect dependencies to the post-login pending action handler (pendingAction, projectMeta, nodes, edges, groupDefinitions, navigate, handleMakeCopy, handleExport) to prevent stale closure issues
    |

@RETR0-OS RETR0-OS merged commit 112e80d into deploy Feb 23, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants