feat: add snippet sharing via unique public links#98
Open
Codex723 wants to merge 1 commit into
Open
Conversation
- Add snippet_shares table with UUID-based secure tokens - Support read-only permissions for shared snippets - Support link expiration via expires_at field - Add SHARE and REVOKESHARE activity logging - Create ShareRepository for database operations - Create ShareService for business logic - Create POST /api/snippets/[id]/share endpoint for generating share links - Create DELETE /api/snippets/[id]/share endpoint for revoking share links - Create GET /api/snippets/shared/[token] endpoint for accessing shared snippets - Add unit tests for ShareService
|
@Codex723 is attempting to deploy a commit to the Sudipta 's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
@Codex723 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Owner
|
@Codex723 resolve conflicts |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #72
Summary
This PR implements secure snippet sharing via unique public links with read-only permissions and expiration support.
Changes
Database Schema (
scripts/add-snippet-shares.sql)snippet_sharestable with the following columns:id- UUID primary key for each share recordsnippet_id- Foreign key referencing snippets table (CASCADE delete)share_token- 64-character hex string (cryptographically secure random)is_read_only- Boolean flag (default: true) for access controlexpires_at- Optional timestamp for automatic link expirationcreated_by_wallet_address- Stellar wallet address of the snippet ownerrevoked_at- Timestamp when share was revoked (nullable)revoked_by_wallet_address- Wallet address that revoked the share (nullable)created_at- Creation timestampidx_snippet_shares_tokenon share_token for O(1) lookupsidx_snippet_shares_snippet_idon snippet_id for ownership queriesidx_snippet_shares_expires_atfor expiration cleanupidx_snippet_shares_activecomposite index for active share queriesRepository Layer (
app/api/snippets/share.repository.ts)ShareRepositoryclass with methods:generateSecureToken()- Creates 32-byte random hex token (64 chars)createShare(dto)- Inserts new share record with secure tokenfindByToken(token)- Finds active, non-expired share by tokenfindActiveShareBySnippet(snippetId)- Gets existing active share for snippetrevokeShare(snippetId, revokedByWalletAddress)- Revokes active sharerevokeByToken(shareToken, revokedByWalletAddress)- Revokes by token directlygetShareDetails(snippetId)- Gets share history for a snippetService Layer (
app/api/snippets/share.service.ts)ShareServiceclass with methods:createShareLink(data)- Creates share link, returns existing if already existsgetSharedSnippet(shareToken)- Retrieves snippet via share token with read-only flagrevokeShare(snippetId, revokedByWalletAddress)- Revokes share with activity loggingActivityLoggerfor SHARE and REVOKESHARE audit trailsAPI Routes
app/api/snippets/[id]/share/route.tsPOST- Generate share link for a snippetOwnershipMiddleware{ isReadOnly?: boolean, expiresAt?: ISO string }{ shareToken, shareUrl, isReadOnly, expiresAt }DELETE- Revoke existing share link{ message: "Share link revoked successfully" }app/api/snippets/shared/[token]/route.tsGET- Access shared snippet via public link (no auth required)/api/snippets/shared/{share_token}{ snippet, isReadOnly, shareUrl }Activity Logger (
lib/activity-logger.ts)SHAREandREVOKESHAREtoActivityActiontypeTests (
lib/share.service.test.ts)ShareService:createShareLink- snippet not found, existing share, new share creationgetSharedSnippet- invalid token, valid token scenariosrevokeShare- no active share, successful revocationSecurity Features
expiresAtparameter for time-limited sharesAPI Response Format
Consistent JSON responses for frontend integration:
Database Migration Required
Run
scripts/add-snippet-shares.sqlon your Neon database to create thesnippet_sharestable and indexes.