Implement file sharing functionality with public share links and associated API endpoints

This commit is contained in:
Leon Bösche
2026-01-24 21:06:18 +01:00
parent 4770380e38
commit 6bbdc157cb
12 changed files with 883 additions and 7 deletions

View File

@@ -9,6 +9,7 @@ import (
"time"
"github.com/google/uuid"
"go.b0esche.cloud/backend/internal/models"
)
type DB struct {
@@ -1149,4 +1150,69 @@ func (db *DB) MarkChallengeUsed(ctx context.Context, challenge []byte) error {
return err
}
// UpdateFileSize updates the size and last_modified timestamp of a file
// FileShareLink methods
// CreateFileShareLink creates a new share link for a file
func (db *DB) CreateFileShareLink(ctx context.Context, token string, fileID, orgID, createdByUserID uuid.UUID) (*models.FileShareLink, error) {
var link models.FileShareLink
err := db.QueryRowContext(ctx, `
INSERT INTO file_share_links (token, file_id, org_id, created_by_user_id)
VALUES ($1, $2, $3, $4)
RETURNING id, token, file_id, org_id, created_by_user_id, created_at, updated_at, expires_at, is_revoked
`, token, fileID, orgID, createdByUserID).Scan(
&link.ID, &link.Token, &link.FileID, &link.OrgID, &link.CreatedByUserID,
&link.CreatedAt, &link.UpdatedAt, &link.ExpiresAt, &link.IsRevoked)
return &link, err
}
// GetFileShareLinkByFileID gets the active share link for a file
func (db *DB) GetFileShareLinkByFileID(ctx context.Context, fileID uuid.UUID) (*models.FileShareLink, error) {
var link models.FileShareLink
var expiresAtNull sql.NullTime
err := db.QueryRowContext(ctx, `
SELECT id, token, file_id, org_id, created_by_user_id, created_at, updated_at, expires_at, is_revoked
FROM file_share_links
WHERE file_id = $1 AND is_revoked = FALSE AND (expires_at IS NULL OR expires_at > NOW())
ORDER BY created_at DESC
LIMIT 1
`, fileID).Scan(
&link.ID, &link.Token, &link.FileID, &link.OrgID, &link.CreatedByUserID,
&link.CreatedAt, &link.UpdatedAt, &expiresAtNull, &link.IsRevoked)
if err != nil {
return nil, err
}
if expiresAtNull.Valid {
link.ExpiresAt = &expiresAtNull.Time
}
return &link, nil
}
// GetFileShareLinkByToken gets a share link by token
func (db *DB) GetFileShareLinkByToken(ctx context.Context, token string) (*models.FileShareLink, error) {
var link models.FileShareLink
var expiresAtNull sql.NullTime
err := db.QueryRowContext(ctx, `
SELECT id, token, file_id, org_id, created_by_user_id, created_at, updated_at, expires_at, is_revoked
FROM file_share_links
WHERE token = $1 AND is_revoked = FALSE AND (expires_at IS NULL OR expires_at > NOW())
`, token).Scan(
&link.ID, &link.Token, &link.FileID, &link.OrgID, &link.CreatedByUserID,
&link.CreatedAt, &link.UpdatedAt, &expiresAtNull, &link.IsRevoked)
if err != nil {
return nil, err
}
if expiresAtNull.Valid {
link.ExpiresAt = &expiresAtNull.Time
}
return &link, nil
}
// RevokeFileShareLink revokes a share link
func (db *DB) RevokeFileShareLink(ctx context.Context, fileID uuid.UUID) error {
_, err := db.ExecContext(ctx, `
UPDATE file_share_links
SET is_revoked = TRUE, updated_at = NOW()
WHERE file_id = $1 AND is_revoked = FALSE
`, fileID)
return err
}