119 Commits

Author SHA1 Message Date
Andrey Antukh
89e91ba372
✨ Add observability improvements (#11854)
* 🐳 Add upstream diagnostics to nginx access log

Enrich every access-log line with the internal journey of the request:
the status the backend answered (us), the time spent connecting to it
(uct), the time spent waiting for its answer (urt) and the internal
address that served the request (ua).

A plain 502 line used to say nothing about where the request died. With
this format, the tail of the line classifies the failure: connection
rejected, backend accepted and hung (uct + urt under 1s), or backend
stuck until read timeout. This was the missing witness in the Sep 20
incident, where nginx received connection resets with zero timeouts and
zero rejections.

Applied both to the production image template and the devenv config.
With proxy_pass on variables there is no upstream keepalive, so uct
measures one real TCP connection per request.

Parsing the new fields (us, uct, urt, ua) on the log shipper is left to
ops, so they can be filtered in Loki.

AI-assisted-by: glm-5.3-flash

* 🐳 Add stub_status endpoint for nginx metrics

Add a dedicated localhost-only server (listen 127.0.0.1:8082) exposing
/stub_status next to every other location of the public server. Ops can
run the official nginx-prometheus-exporter as a sidecar against
http://127.0.0.1:8082/stub_status and get nginx_connections_active,
accepted vs handled, reading/writing/waiting and request rates in
Prometheus.

Binding it to localhost and its own server keeps it unreachable from
outside the host and out of the public surface, and access_log off
avoids polluting Loki with one line per Prometheus scrape. The base
image already ships stub_status compiled in, so no image rebuild is
needed.

Applied both to the production image template and the devenv config.

AI-assisted-by: glm-5.3-flash

* ✨ Expose http server gate metrics (worker and connector)

The backend already measured dispatch latency but nothing reported the
state of the "house door": the xnio worker queue and threads, and the
monitor-level listener counters. This was the exact blind spot of the
Sep 20 incident, where the server kept answering health checks while it
accepted connections and dropped them without response.

Add a periodic metrics sampler that lives and dies with the http
server (single daemon thread, 15s interval, each sample guarded so an
unexpected error does not cancel subsequent runs) and publishes:

- worker (xnio MXBean gauges): penpot_http_worker_queue_size,
  busy_threads, pool_size and max_pool_size. Negative samples are
  discarded: the MXBean transiently reports -1 on the busy thread
  count (verified live), and a stale negative would read as zero.
- listener (Undertow connector statistics, enabled via the new
  :server/statistics yetti option): penpot_http_connector_active*
  _connections gauge and requests_total / errors_total counters.
  Undertow exposes absolute totals, so the sampler keeps a watermark
  atom and publishes deltas, skipping (and moving forward past) a
  counter reset.

The connector-level part depends on yetti v11.11, which now accepts
a :server/statistics server option (patch authored and released
upstream; before it, ListenerInfo#getConnectorStatistics always
returned nil).

New tests cover the samplers with fake MXBean/collector statistics
against real prometheus collectors, including the negative-sample
filter, the delta/watermark logic and the sampler lifecycle.

AI-assisted-by: glm-5.3-flash

* 🐛 Include jdk.management in the backend runtime JRE

The production image builds a trimmed JRE with jlink and omitted
jdk.management. Without that module the OS MXBean is
sun.management.BaseOperatingSystemImpl, which has no
getProcessCpuTime, getOpenFileDescriptorCount nor
getMaxFileDescriptorCount. The prometheus client StandardExports
reads those getters reflectively and collect() swallows the
NoSuchMethodException, so process_open_fds, process_max_fds and
process_cpu_seconds_total silently disappeared from /metrics while
the other process_* families kept flowing.

Verified against Prometheus: the app job only ever exposed
process_start_time_seconds, process_virtual_memory_bytes and
process_resident_memory_bytes; the fd and cpu families were absent.
Reproduced locally by running the backend metrics registry on a JRE
built with the same jlink module list (false/false/false) and on one
with jdk.management added (true/true/true).

Add the module to --add-modules and pin the metric contract with
backend-tests.metrics-test.

AI-assisted-by: deepseek-v4.1-flash

* ♻️ Build the http metrics sampler on promesa.exec

Replace the hand-rolled ScheduledThreadPoolExecutor and ThreadFactory
with promesa.exec primitives: px/scheduled-executor with a daemon
thread factory, and a px/schedule chain that reschedules the next
sample when the current one finishes.

Beyond fitting the existing periodic-task pattern (worker/cron,
rpc/rlimit), the chained schedule makes the docstring promise real:
with scheduleAtFixedRate an exception escaping the runnable cancelled
the following executions, while the reschedule now happens in a
finally block.

The sampler shutdown uses px/shutdown-now (shutdown! is deprecated in
promesa 12.0.0) to cancel the pending sample, keeping the previous
halt semantics.

The lifecycle test moves to the promesa predicates and a new test
covers the error-resilience promise: the first sample runs, throws,
and the next one is still scheduled.

AI-assisted-by: deepseek-v4.1-flash

* ♻️ Tighten the http metrics samplers

The samplers are leaf functions: they receive what they need and
publish it. Drop the internal nil guards (if there is no metrics
instance or no mxbean there is nothing to call them for) and move the
checks to the boundary, where the optional data is resolved:
sample-http-metrics now short-circuits with some-> and when-let.

Write the four worker gauges as four static operations instead of a
vector of pairs walked by doseq: the set is fixed, so the collection
only adds an allocation and hides each operation.

Drop the ! suffix from the sample-*-metrics family: ! marks a function
whose contract is to mutate state, while these report, and the mutation
happens in the mtx/run! they call. The constant true return, which only
existed so the removed guard tests could assert it, goes away too.

Tests follow the move: the internal-guard tests are replaced by one
boundary test (a nil server publishes nothing).

AI-assisted-by: deepseek-v4.1-flash

* 📚 Add the function design rules memory

Document the rules that came out of the http metrics sampler review:
preconditions are checked at the boundary instead of re-checked in the
core, optional-by-design data is guarded where the optionality is born,
a fixed set of operations is written statically, ! marks mutation and
not reporting, and production code is not shaped for tests.

Also state in the memory maintenance guide that memories must not use
manual line wrapping.

Linked from critical-info so it is read when designing a solution or
an API, not only when touching the samplers.

AI-assisted-by: deepseek-v4.1-flash

* 📚 Unwrap the critical-info memory lines

The memory maintenance guide forbids manual line wrapping, so rewrite
critical-info with one line per bullet and paragraph. A stray `*` at
the start of one continuation line is dropped.

AI-assisted-by: deepseek-v4.1-flash

* ♻️ Drop the redundant guard in the http server halt

create-metrics-sampler always returns the scheduler, so the sampler is
always present when integrant calls halt-key!; the nil check was dead
code, same as the yt/stop! call next to it.

AI-assisted-by: deepseek-v4.1-flash

* ✨ Add srepl helper to delete profiles by email

Add `delete-profiles-by-email!` to app.srepl.main. It accepts a
single email, a comma separated list of emails or a coll of emails,
resolves each profile, logs it to audit and enqueues the
delete-object task. The deleted-at is backdated with the configured
deletion-delay so profiles and their owned teams are purged on the
next gc pass.

Extract the per-email deletion logic into a private fn and reuse it
from `delete-profiles-in-bulk!`. Add tests for the new
`parse-emails` helper.

AI-assisted-by: glm-5.3-flash
2026-09-23 12:50:18 +02:00
elhombretecla
a91d81c695 🎉 Add link preview metadata for shared links 2026-09-15 17:10:54 +02:00
Andrey Antukh
81c3b3cd56
📎 Update copyright name on file header (#11346) 2026-08-25 11:55:10 +02:00
Yamila Moreno
ddba2ffa75
📎 Update Kaleidos Copyright (#9929) 2026-05-29 11:24:58 +02:00
Andrey Antukh
1b8afccba2 ✨ Remove usage of multipart body size config on backend 2026-02-23 14:44:44 +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
2c1a8b59ba ✨ Add client header check middleware
As an additional csrf protection for API requests
2025-10-07 12:47:14 +02:00
Andrey Antukh
47d9c6f282 ✨ Add sec-fetch metadata middleware support 2025-10-07 12:47:14 +02:00
Andrey Antukh
c1058c7fdb ♻️ Add minor refactor for internal concurrency model
Replace general usage of virtual threads with platform threads
and use virtual threads for lightweight procs such that websocket
connections. This decision is made mainly because virtual threads
does not appear on thread dumps in an easy way so debugging issues
becomes very difficult.

The threads requirement of penpot for serving http requests
is not very big so having so this decision does not really affects
the resource usage.
2025-09-26 14:35:06 +02:00
Andrey Antukh
ad0ef82ffc 🎉 Add management http api 2025-08-25 10:53:12 +02:00
Andrey Antukh
ccc3ca0948 ✨ Disable virtual threads on http server 2025-08-05 20:34:47 +02:00
Andrey Antukh
46969585ed ✨ Disable native buffers usage on xnio
A temporal change for investigate native memory leak
2025-08-04 22:13:08 +02:00
Andrey Antukh
80d719353c ✨ Make auth data available before request parsing
For properly report profile-id
2025-04-14 09:23:41 +02:00
Andrey Antukh
47f80cf3db 🐛 Make error middleware capture profile-id 2025-04-01 12:30:51 +02:00
Andrey Antukh
47f1ca9627 ✨ Change backend defaults for http body 2025-01-29 12:26:30 +01:00
Andrey Antukh
88fb5e7ab5 ♻️ Update integrant to latest version
This upgrade also includes complete elimination of use spec
from the backend codebase, completing the long running migration
to fully use malli for validation and decoding.
2024-11-13 19:09:19 +01:00
Andrey Antukh
51ecbf15a9 ⬆️ Update yetti and http server dependency 2024-10-22 20:23:38 +02:00
Andrey Antukh
af389fe63a 🐛 Fix error reporting regression 2024-07-25 17:17:49 +02:00
Andrey Antukh
06bab212b5 🐛 Set correct order for http middlewares 2024-06-19 07:48:12 +02:00
Andrey Antukh
504f833a53 🐛 Fix global error handler incorrect body encoding 2024-06-19 07:48:12 +02:00
Andrey Antukh
91118bec70 ✨ Improve internal naming of setup/props
This reverts commit a6f70c77cbe72875875ab83587d5dcd9cfc61e34.
2024-03-14 10:48:23 +01:00
alonso.torres
a6f70c77cb Revert "✨ Improve internal naming of setup/props"
This reverts commit f525c6df5e9785df9b534b6de1b5308084627699.
2024-03-13 16:21:12 +01:00
Andrey Antukh
f525c6df5e ✨ Improve internal naming of setup/props 2024-03-13 11:39:53 +01:00
Andrey Antukh
5573f467b7 📎 Increase devenv limits for multipart requests 2024-02-19 14:28:39 +01:00
Andrey Antukh
bb5a4c0fa5 ✨ Update yetti and adapt for ring-2.0 2023-11-27 14:25:12 +01:00
Andrey Antukh
5ca3d01ea1 🎉 Add malli based validation and coersion subsystem 2023-05-17 16:05:29 +02:00
Andrey Antukh
f1c42a698d 📎 Increase http socket backlog 2023-04-25 16:25:49 +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
d549fcb2ae 🐛 Pass a valid executor instance to yetti http server 2023-03-14 12:30:27 +01:00
Andrey Antukh
0c48f76911 ✨ Add better spec validation on http module 2023-02-07 18:16:55 +01:00
Andrey Antukh
db689d151e ♻️ Refactor profile and session handling
- makes the profile access more efficient (replace in-app joins to a
  simple select query on profile table
- add partial support for access-tokens (still missing some RPC methods)
- move router definitions to specific modules and simplify the main http
  module definitions to simple includes
- simplifiy authentication code related to access-tokens and sessions
- normalize db parameters with proper namespaced props
- more work on convert all modules initialization to use proper specs
  with fully-qualified keyword config props
2023-01-18 10:51:58 +01:00
Andrey Antukh
095dc2ad11 ✨ Do not merge path params into params
makes conflict with possible params coming from user
2023-01-13 10:19:39 +01:00
Andrey Antukh
7f589b09ca ♻️ Move audit http handler to RPC 2022-12-13 16:17:31 +01:00
Andrey Antukh
43ab19f690 ♻️ Refactor (minor) of http session code
The rationale behind the refactor:
- Make available profile data to other middlewares without
  the need to access to the database (mainly for error reporting).
- Align with codestyle with the rest of internal modules.
- Simplify code.
2022-11-07 08:37:43 +01:00
luz paz
e30bea0b6f 🔧 Fix typos in source code
Found via `codespell -q 3 -S *.po,./frontend/yarn.lock -L childs,clen,fpr,inflight,ody,ot,ro,te,trys,ue`
2022-10-04 10:40:34 +02:00
Andrey Antukh
41134f22e9 📎 Update license header 2022-09-20 23:23:22 +02:00
Andrey Antukh
028e0c5b70 🎉 Add /readyz http endpoint for backend & exporter 2022-09-13 11:03:41 +02:00
Andrey Antukh
fd973d87fd ♻️ Refactor metrics namespace 2022-08-31 12:55:48 +02:00
Andrey Antukh
d6d9d25fce ♻️ Refactor token generation API 2022-08-31 11:01:07 +02:00
Andrey Antukh
14d1cb90bd ♻️ Refactor auth code 2022-07-04 11:23:33 +02:00
Andrey Antukh
b944d977bb 🎉 Add binfile import/export internal functionality 2022-06-27 11:12:00 +02:00
Andrey Antukh
b2607b28ff 🎉 Add build date and changelog to the bundle 2022-04-06 11:20:48 +02:00
Andrey Antukh
c026d05bc3 ✨ Set consistent max body size
And make it configurable
2022-04-05 13:23:39 +02:00
Andrey Antukh
9abf4b126c ✨ Improve error handling 2022-03-28 17:24:52 +02:00
Andrey Antukh
f33d6610e7 📎 Properly log error on audit archive task fail 2022-03-11 16:21:11 +01:00
Andrey Antukh
a8f5604718 📎 Improve http server configuration 2022-03-11 15:01:49 +01:00
Andrey Antukh
cf4f999b6a 📎 Improve api ergonomy of http server module 2022-03-11 09:50:49 +01:00
Andrey Antukh
f7bb08382c ✨ Fix issues from previous refactor peer review 2022-03-10 13:32:06 +01:00
Andrey Antukh
1b444a42f2 ♻️ Refactor http server layer
Make it fully asynchronous.
2022-03-09 17:18:06 +01:00
Andrey Antukh
9e4a50fb15 ♻️ Refactor backend to be more async friendly 2022-03-03 16:05:52 +01:00