Commit Graph
13 Commits
Author SHA1 Message Date
thePR0M3TH3ANandClaude Opus 4.8 6e85c2a32b [storage] fix B2 / generic-S3 uploads ("S3 endpoint is required") — prepareUpload contract
Uploading to a Backblaze B2 (or any generic-S3) bucket failed with "S3 endpoint is
required." even though the connection tested fine.

Root cause: the two upload services have different prepareUpload contracts —
r2Service.prepareUpload(npub, { credentials }) vs s3UploadService.prepareUpload(settings).
MediaUploader calls both the r2 way, so for S3/B2 the npub STRING became the settings →
validateS3Connection saw no endpoint. Compounding it, a storageService.getConnection()
result keeps bucket/endpoint/region under `.meta`, which validateS3Connection reads at
the top level. (R2 worked because its service already uses the (npub, {credentials})
contract.)

Fix:
- s3UploadService.prepareUpload now accepts (npub, { credentials }) AND a direct settings
  object (back-compat), flattening meta → top-level via flattenS3Settings.
- The storage Test now derives/passes the B2 public URL (else the prefix), so it stops
  falsely warning "Public Bucket URL is missing. Uploads will fail."

Tests: tests/services/s3-upload-prepare-contract.test.mjs (3 — meta flatten, npub ignored
when credentials given, direct-settings back-compat). Build + lint clean; upload/storage
suites 46/46.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-30 16:02:37 -04:00
thePR0M3TH3ANandClaude Opus 4.8 fdebf05e1a feat(s3): bring generic S3 to parity with R2 (cleanup + CORS guidance)
Two gaps where generic S3 trailed Cloudflare R2:

1. Object cleanup on delete/edit didn't work for generic S3. deleteVideoStorage
   built its client via makeR2Client, which hard-forces path-style and ignores
   the connection's forcePathStyle -- so vhost-style S3 deletes were misaddressed
   and cleanup silently failed (S3 users' deleted/edited files orphaned). It is
   now provider-aware (mirrors uploadFile): R2 -> makeR2Client (path-style),
   generic S3 -> makeS3Client honoring forcePathStyle. makeS3Client is now
   injectable for testing.

2. CORS guidance on upload failure was R2-only. Extracted isLikelyCorsError +
   a generic-S3 guidance builder into js/utils/uploadErrorHints.js (re-exported
   from r2Service for back-compat); s3UploadService now appends actionable CORS
   guidance on an opaque "Failed to fetch" too.

Generic S3 is now at functional parity with R2 for upload -> publish -> cleanup.

Tests: r2-delete-video-storage (adds generic-S3 forcePathStyle routing),
s3-upload-cors-guidance (new). Full suite green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-17 06:13:38 -04:00
thePR0M3TH3ANandClaude Opus 4.8 3d6ffe997b fix(upload): surface thumbnail/.torrent upload failures to the user
These optional-asset uploads are non-fatal, but failures were warn-only — the
user got a silently-missing thumbnail or torrent with no signal. Both upload
services now emit a 'warning' status at the point of failure and append a note
to the final 'Published' status (downgraded to warning variant) listing what
failed. Cloudflare-upload audit #5.

Test: tests/services/s3-upload-optional-asset-failures.test.mjs.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-16 20:19:27 -04:00
thePR0M3TH3ANandClaude Opus 4.8 6b603cc21c fix(upload): surface an 'Analyzing' status during pre-upload file hashing
Computing the torrent info-hash (and the content-hash fallback) reads the entire
file before the upload starts and before any progress is emitted, so on
multi-GB videos there was a long silent pause that looked like a freeze. Both
upload services now emit an 'Analyzing video…' status when a full-file hash is
required (i.e. no valid info-hash was pre-supplied). Cloudflare-upload audit #2.

Test: tests/services/s3-upload-analyzing-status.test.mjs.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-16 20:14:21 -04:00
thePR0M3TH3ANandClaude Opus 4.8 066d98febb fix(upload): content-address storage keys to stop URL-first overwrites
buildR2Key fell back to a static "uploads" namespace (and "video" slug) when
no torrent info-hash was available, so two distinct URL-first uploads sharing a
filename produced the SAME R2/S3 key — the second silently overwrote the first
object while the first video note still pointed at that URL (data loss).
WebTorrent uploads were already safe because the info-hash namespaces the key.

Add computeStorageContentHash(file) in js/r2.js: when no info-hash is present it
derives a deterministic content-based namespace — full SHA-256 of the bytes for
files ≤512 MB (idempotent: re-uploading the same file reuses the key), and a
SHA-256 fingerprint over name/size/mtime + sampled head/tail bytes above that,
so we never buffer a multi-GB video in memory. Wired into BOTH upload paths
(r2Service + s3UploadService) via a new storageIdentifier kept separate from the
info-hash, so magnet generation is unchanged.

Test: tests/storage-key-collision.test.mjs (distinct same-named uploads get
distinct keys; same content is idempotent; large-file fingerprint path samples
edges without buffering and stays deterministic). Full unit suite green.

test_integrity_note:
  change_type: ["new_tests"]
  scenarios:
    - id: SCN-upload-key-no-collision
      given: "a URL-first upload with no torrent info-hash"
      when: "the storage key is derived (computeStorageContentHash -> buildR2Key)"
      then: "distinct same-named files get distinct keys, same content is idempotent, and no key uses the colliding 'uploads' namespace"
  observable_outcomes:
    - "returned key strings for real Blob/File content and a fake >512MB file"
  determinism_controls:
    - "fixed byte content; fake large file with stubbed slice() and fixed lastModified"
  anti_cheat_rationale:
    prevents:
      - "hard-coded return value"
      - "over-mocking internal logic"
  relaxation:
    did_relax_any_assertion: false

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-16 16:56:12 -04:00
google-labs-jules[bot]andPR0M3TH3AN 3078c46b89 feat(tests): add S3UploadService tests and refactor for testability
- Refactor `S3UploadService` to accept dependencies in constructor
- Move `resolveUploadIdentifier` to class method for mocking
- Add comprehensive unit tests for `S3UploadService` covering event emitter, validation, and upload logic
- Ensure `WebTorrent` dependency is mocked or polyfilled in test environment

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-02-11 14:42:01 +00:00
thePR0M3TH3AN 419a3f419c Add storage pointer tags to video posts 2026-01-26 22:07:32 -05:00
thePR0M3TH3AN ee31e4318a Compute upload identifiers before building R2 keys 2026-01-26 21:43:39 -05:00
google-labs-jules[bot] f321477455 fix(s3): remove duplicate normalizedInfoHash declaration
Removes a redundant `const normalizedInfoHash` declaration in `s3UploadService.js` that caused a `SyntaxError` and prevented the application from loading. The variable is already declared in the outer scope of the `try` block.
2026-01-26 18:29:42 +00:00
thePR0M3TH3AN c02db755ac Refactor R2 key prefix for deterministic identifiers 2026-01-26 12:47:05 -05:00
thePR0M3TH3AN 82986650ce Store video file fingerprints alongside series IDs 2026-01-26 12:25:54 -05:00
google-labs-jules[bot] 070c054043 Refactor upload flow for immediate uploads and fix logging crash
- Refactored `UploadModal.js` to trigger uploads immediately on file selection.
- Added progress bars and result fields (Video URL, Magnet, Torrent) to the UI.
- Fixed a crash in `r2Service.js` caused by `userLogger.debug` usage.
- Exposed `prepareUpload` and `uploadFile` methods in `r2Service` and `s3UploadService` for granular control.
- Updated `upload-modal.html` with new UI elements.
2026-01-25 20:53:16 +00:00
thePR0M3TH3AN 999235d71e Add S3 storage option to upload modal 2026-01-24 20:00:00 -05:00