90 Commits

Author SHA1 Message Date
Andrey Antukh
6eccffb8bb
🐛 Fix incorrect handlig of version restore operation (#9041)
- Add session ID tracking to RPC layer (backend and frontend)
- Send session ID header with RPC requests for request correlation
- Rename file-restore to file-restored for consistency
- Extract initialize-file function from initialize-workspace flow
- Improve file restoration initialization with wait-for-persistence
- Extract initialize-version event handler for version restoration
- Fix viewport key generation with file version numbers for proper re-renders
- Update layout item schema and constraints to use internal sizing state
- Add v-sizing state retrieval in layout-size-constraints component
- Refactor file-change notifications stream handling with rx/map
- Fix team-id lookup in restore-version-from-plugins

Improves request traceability across frontend/backend sessions and streamlines
the workspace initialization flow for file restoration scenarios.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-21 19:19:51 +02:00
Andrey Antukh
6fa440cf92 🎉 Add chunked upload API for large media and binary files
Introduce a purpose-agnostic three-step session-based upload API that
allows uploading large binary blobs (media files and .penpot imports)
without hitting multipart size limits.

Backend:
- Migration 0147: new `upload_session` table (profile_id, total_chunks,
  created_at) with indexes on profile_id and created_at.
- Three new RPC commands in media.clj:
    * `create-upload-session`  – allocates a session row; enforces
      `upload-sessions-per-profile` and `upload-chunks-per-session`
      quota limits (configurable in config.clj, defaults 5 / 20).
    * `upload-chunk`           – stores each slice as a storage object;
      validates chunk index bounds and profile ownership.
    * `assemble-file-media-object` – reassembles chunks via the shared
      `assemble-chunks!` helper and creates the final media object.
- `assemble-chunks!` is a public helper in media.clj shared by both
  `assemble-file-media-object` and `import-binfile`.
- `import-binfile` (binfile.clj): accepts an optional `upload-id` param;
  when provided, materialises the temp file from chunks instead of
  expecting an inline multipart body, removing the 200 MiB body limit
  on .penpot imports.  Schema updated with an `:and` validator requiring
  either `:file` or `:upload-id`.
- quotes.clj: new `upload-sessions-per-profile` quota check.
- Background GC task (`tasks/upload_session_gc.clj`): deletes stalled
  (never-completed) sessions older than 1 hour; scheduled daily at
  midnight via the cron system in main.clj.
- backend/AGENTS.md: document the background-task wiring pattern.

Frontend:
- New `app.main.data.uploads` namespace: generic `upload-blob-chunked`
  helper drives steps 1–2 (create session + upload all chunks with a
  concurrency cap of 2) and emits `{:session-id uuid}` for callers.
- `config.cljs`: expose `upload-chunk-size` (default 25 MiB, overridable
  via `penpotUploadChunkSize` global).
- `workspace/media.cljs`: blobs ≥ chunk-size go through the chunked path
  (`upload-blob-chunked` → `assemble-file-media-object`); smaller blobs
  use the existing direct `upload-file-media-object` path.
  `handle-media-error` simplified; `on-error` callback removed.
- `worker/import.cljs`: new `import-blob-via-upload` helper replaces the
  inline multipart approach for both binfile-v1 and binfile-v3 imports.
- `repo.cljs`: `:upload-chunk` derived as a `::multipart-upload`;
  `form-data?` removed from `import-binfile` (JSON params only).

Tests:
- Backend (rpc_media_test.clj): happy path, idempotency, permission
  isolation, invalid media type, missing chunks, session-not-found,
  chunk-index out-of-range, and quota-limit scenarios.
- Frontend (uploads_test.cljs): session creation and chunk-count
  correctness for `upload-blob-chunked`.
- Frontend (workspace_media_test.cljs): direct-upload path for small
  blobs, chunked path for large blobs, and chunk-count correctness for
  `process-blobs`.
- `helpers/http.cljs`: shared fetch-mock helpers (`install-fetch-mock!`,
  `make-json-response`, `make-transit-response`, `url->cmd`).

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-16 19:43:57 +02:00
Andrey Antukh
3767ee05bb Add retry mechanism for idenpotent get repo requests on frontend (#8792)
* ♻️ Handle fetch-error gracefully with toast instead of full-page error

Network-level failures (lost connectivity, DNS failure, etc.) on RPC
calls were propagating as :internal/:fetch-error to the global error
handler, which replaced the entire UI with a full-page error screen.

Now the :internal handler distinguishes :fetch-error from other internal
errors and shows a non-intrusive toast notification instead, allowing
the user to continue working.

*  Add automatic retry with backoff for idempotent RPC requests

Idempotent (GET) RPC requests are now automatically retried up to 3
times with exponential back-off (1s, 2s, 4s) when a transient error
occurs.  Retryable errors include: network-level failures
(:fetch-error), 502 Bad Gateway, 503 Service Unavailable, and browser
offline (status 0).

Mutation (POST) requests are never retried to avoid unintended
side-effects.  Non-transient errors (4xx client errors, auth errors,
validation errors) propagate immediately without retry.

* ♻️ Make retry helpers public with configurable parameters

Make retryable-error? and with-retry public functions, and replace
private constants with a default-retry-config map.  with-retry now
accepts an optional config map (:max-retries, :base-delay-ms) enabling
callers and tests to customize retry behavior.

*  Add tests for RPC retry mechanism

Comprehensive tests for the retry helpers in app.main.repo:
- retryable-error? predicate: covers all retryable types (fetch-error,
  bad-gateway, service-unavailable, offline) and non-retryable types
  (validation, authentication, authorization, plain errors)
- with-retry observable wrapper: verifies immediate success, recovery
  after transient failures, max-retries exhaustion, no retry for
  non-retryable errors, fetch-error retry, custom config, and mixed
  error scenarios

* ♻️ Introduce :network error type for fetch-level failures

Replace the awkward {:type :internal :code :fetch-error} combination
with a proper {:type :network} type in app.util.http/fetch.  This makes
the error taxonomy self-explanatory and removes the special-case branch
in the :internal handler.

Consequences:
- http.cljs: emit {:type :network} instead of {:type :internal :code :fetch-error}
- errors.cljs: add a dedicated ptk/handle-error :network method (toast);
  restore :internal handler to its original unconditional full-page error form
- repo.cljs: simplify retryable-types and retryable-error? — :network
  replaces the former :internal special-case, no code check needed
- repo_test.cljs: update tests to use {:type :network}

* 📚 Add comment explaining the use of bit-shift-left
2026-03-30 12:20:02 +02:00
Andrey Antukh
0dfa62a5b6
🐛 Improve error reporting on request parsing failures (#8805)
Include request URI and status in frontend handle-response error data,
and add request path/context to backend IOException handler logs and
response body. Previously these errors had no identifying information
about which endpoint or request caused the failure.
2026-03-26 15:42:49 +01:00
Andrey Antukh
0dfac801a4 Improve error handling and exception formatting (#8757)
*  Improve error handling and exception formatting

- Enhance exception formatting with visual separators and cause chaining
- Add new handler for :internal error type
- Refine error types: change assertion-related errors to :assertion type
- Improve error messages and hints consistency
- Clean up error handling in zip utilities and HTTP modules

* 🐛 Properly handle AbortError on fetch request unsubscription

When a fetch request in-flight is cancelled due to RxJS unsubscription
(e.g. navigating away from the workspace while thumbnail loads are
pending), the AbortController.abort() call triggers a catch handler
that previously relied solely on a @unsubscribed? flag to suppress the
error.

This was unreliable: nested observables spawned inside rx/mapcat (such
as datauri->blob-uri conversions within get-file-object-thumbnails)
could abort independently, with their own AbortController instances,
meaning the outer unsubscribed? flag was never set and the AbortError
propagated as an unhandled exception.

Add an explicit AbortError name check as a disjunctive condition so
that abort errors originating from any observable in the chain are
suppressed at the source, regardless of subscription state.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-24 19:55:23 +01:00
Andrey Antukh
7b5817f407
♻️ Make several adjustments to the dashboard deleted page (#7999)
* ♻️ Make several sustantial adjustments to the dashboard deleted page

* 📎 Add PR feedback changes
2025-12-30 09:52:29 +01:00
María Valderrama
c670aac339 🎉 Added deleted files to dashboard 2025-12-22 20:16:41 +01:00
Andrey Antukh
e9d177eae3 Make the binfile export process more resilent to errors
The current binfile export process uses a streaming technique. The
major problem with the streaming approach is the case when an error
happens on the middle of generation, because we have no way to
notify the user about the error (because the response is already
is sent and contents are streaming directly to the user
client/browser).

This commit replaces the streaming with temporal files and SSE
encoded response for emit the export progress events; once the
exportation is finished, a temporal uri to the exported artifact
is emited to the user via "end" event and the frontend code
will automatically trigger the download.

Using the SSE approach removes possible transport timeouts on export
large files by sending progress data over the open connection.

This commit also removes obsolete code related to old binfile
formats.
2025-11-19 17:28:55 +01:00
Andrey Antukh
363b4e3778
♻️ Make the SSO code more modular (#7575)
* 📎 Disable by default social auth on devenv

* 🎉 Add the ability to import profile picture from SSO provider

* 📎 Add srepl helper for insert custom sso config

* 🎉 Add custom SSO auth flow
2025-11-12 12:49:10 +01:00
Andrey Antukh
28cf67e7ff
🎉 Add management RPC API (#7700)
* 🎉 Add management RPC API

And refactor internal http auth flow

* 📎 Adjust final url namings

* 📚 Update changelog
2025-11-10 17:10:59 +01:00
Andrey Antukh
27e311277a Add logging to frontend repo namespace 2025-09-29 12:07:49 +02:00
Yamila Moreno
beb3d16693
📎 Fix minor typo (#6661) 2025-06-09 10:25:16 +02:00
Andrey Antukh
c6b96a81f1 ♻️ Rename data.events to data.event 2024-12-03 12:15:52 +01:00
Andrey Antukh
8618cb950f 🎉 Add binfile-v3 export/import file format 2024-10-18 17:19:29 +02:00
Andrey Antukh
d88f28f5c2 Add support for optional human challenge 2024-09-05 15:35:39 +02:00
Alejandro Alonso
8dcd538bd2 Improve external origin events for audit_log 2024-07-02 08:14:22 +02:00
Andrey Antukh
6a253871b0 Improve internal handling of external-session-id 2024-06-19 16:15:48 +02:00
Andrey Antukh
00b4013385 Forward external session id to backend 2024-06-19 07:45:27 +02:00
Andrey Antukh
f62d2085e8 Add the ability to download a report on internal error page 2024-02-12 15:37:29 +01:00
Andrey Antukh
833871df65 💄 Format frontend code 2024-01-08 09:32:50 +01:00
Andrey Antukh
96f5a33f5f ⬆️ Upgrade to beicon2 (part1) 2023-12-26 14:14:20 +01:00
Andrey Antukh
03518a8da1 Add the ability to stream events on rpc methods 2023-12-13 14:20:07 +01:00
Andrey Antukh
925f2dc30f Remove duplicated rpc method for creating file object thumbnails 2023-11-24 10:41:27 +01:00
Aitor
e24309b883 ♻️ Refactor thumbnails persistence 2023-11-10 14:15:54 +01:00
Andrey Antukh
0aefd044dc Remove atom wrapping on public-uri 2023-06-21 20:10:49 +02:00
Andrey Antukh
d11b007795 Add thumbnail renderer
And integrate the dashboard thumbnails to use that service
2023-06-21 20:10:49 +02:00
Aitor
48834f96d3 ♻️ Refactor thumbnail rendering on workspace 2023-05-22 10:56:46 +02:00
Andrey Antukh
e8ffcbae69 🎉 Add support for multipart upload of thumbnails
and improve the thumbnails storage to offloading it
to the storage subsystem
2023-05-05 17:00:35 +02:00
Andrey Antukh
8fb62628d2 Add the abiltiy to forward command params as query-string 2023-04-25 16:25:30 +02:00
Andrey Antukh
aafbf6bc15 ♻️ Refactor cocurrency model on backend
Mainly the followin changes:

- Pass majority of code to the old and plain synchronous style
  and start using virtual threads for the RPC (and partially some
  HTTP server middlewares).
- Make some improvements on how CLIMIT is handled, simplifying code
- Improve considerably performance reducing the reflection and
  unnecesary funcion calls on the whole stack-trace of an RPC call.
- Improve efficiency reducing considerably the total threads number.
2023-03-14 12:30:27 +01:00
Andrey Antukh
6a699d7f09 Properly move viewer queries to commands
And change deprecation version on viewer queries
2023-01-18 10:51:58 +01:00
Andrey Antukh
ba2729fa4a Move fonts queries and mutations to commands 2023-01-18 10:51:58 +01:00
Andrey Antukh
d8faff47a8 ♻️ Move profile queries and mutations to commands 2023-01-18 10:51:58 +01:00
Andrey Antukh
ecb757bcaf 🎉 Move user feedback http handler to RPC command method 2023-01-18 10:51:58 +01:00
Andrey Antukh
60fb3f3d0e 🐛 Fix storage/pointer-map support issues on thumbnails and libs loading 2022-12-28 09:10:06 +01:00
Andrey Antukh
7a9172560d ♻️ Move teams queries and mutations to commands 2022-12-14 16:22:13 +01:00
Andrey Antukh
240e480b2e 🎉 Allow application/json on Accept header 2022-12-13 16:17:31 +01:00
Andrey Antukh
706714d557 🎉 Restore removed by mistake search rpc method 2022-11-25 10:42:17 +01:00
Andrey Antukh
bbf95434d8 🎉 Add lazy loading and storage/pointer-map support on workspace
This also rewrites the workspace load process making it a bit more
efficient independently if lazy loading is used.
2022-11-22 14:06:14 +01:00
Andrey Antukh
b20d2badfe Load workspace thumbnails in a separated request 2022-11-10 14:54:50 +01:00
Andrey Antukh
76333cec26 🎉 Integrate storage/pointer-map file feature 2022-11-08 13:02:14 +01:00
Andrey Antukh
41134f22e9 📎 Update license header 2022-09-20 23:23:22 +02:00
Andrey Antukh
a1fccd46ff Improve public-uri handling
This enables use penpot under subdirectory
2022-09-05 09:41:19 +02:00
Andrey Antukh
0b3d25a890 Make frontend use new cmd based repo methods for comments queries 2022-08-08 09:51:11 +02:00
Andrey Antukh
d60f849089 💄 Cosmetic refactor of binfile internal API impl 2022-07-07 13:08:18 +02:00
Alejandro Alonso
17645bb2a7 Frontend support for binary files 2022-07-07 11:37:34 +02:00
Andrey Antukh
14d1cb90bd ♻️ Refactor auth code 2022-07-04 11:23:33 +02:00
Andrey Antukh
fa06da36ac 🐛 Remove unused setting on exporter
That causes many troubles on configuring exporter on the onpremise
instances but serves for nothing because it is completly unused.
2022-06-24 16:37:27 +02:00
Pablo Alba
ba139d7d2c 🐛 Fix unathorized redirect 2022-05-20 12:37:57 +02:00
alonso.torres
c7e23c1b58 🐛 Fix problem when export/importing guides attached to frame 2022-04-28 14:43:44 +02:00