Commit Graph
157 Commits
Author SHA1 Message Date
Daniel Salazar 09887747e0 fix: app uuid fixes (#3980) 2026-09-28 19:43:23 -07:00
Nariman Jelveh 7ceb2090b7 feat: app user feedback system (#3546)
* feat: app user feedback system

Add puter.ui.showFeedbackDialog(), letting users send feedback to an
app's developer. In the app environment the Puter desktop renders the
dialog; on a third-party website a puter.com popup hosts it. The message
is stored in a new app_feedback table and emailed to the app owner's
confirmed email — it never passes through the app's own code.

Feedback is strictly opt-in per app via a new apps.feedback_enabled
column (a real column, not an app-metadata key, so Dev Center's
whole-blob metadata saves can't silently erase it), settable through the
existing puter.apps.update path (feedbackEnabled).

Backend follows the layered stack: AppFeedbackStore (durable count
queries) -> AppFeedbackService (opt-in check, message normalization,
abuse caps, best-effort owner email) -> AppFeedbackController
(POST /app-feedback, GET /app-feedback/target). New app-user-feedback
email template uses the escaping-safe nl2br triple-stash.

Defensive by design:
- requireUserActor blocks app tokens, so feedback can't be submitted
  programmatically; guiOriginOnly keeps cross-origin pages out.
- App identity comes only from the validated IPC sender (desktop) or the
  browser-attested opener origin (popup), never from message contents.
- The send-feedback popup action is in NON_AUTH_POPUP_ACTIONS, so it
  never delivers a token to the opener.
- Layered limits: route rate limits, plus DB-count caps that fail closed
  when the limiter backend is down, plus a per-app daily owner-email cap.
- Owner email is fully best-effort: an unconfigured transport,
  unconfirmed/unsubscribed/suspended owner, or send failure never fails
  the request or blocks storage.
- The dialog and SDK method are resolve-only and always settle, so a
  caller is never left hanging.

Migrations for sqlite/mysql/postgres, puter.js types, docs, backend
tests (sqlite + postgres), and a Playwright e2e spec are included.

* feat: add feedback control to the dashboard app-drawer

Surface the feedback dialog directly from the app window's chrome in
dashboard mode: apps that opt in (apps.feedbackEnabled) get a "Send
Feedback" button in the dashboard app-drawer, next to minimize/close.
It opens the same UIWindowAppFeedback dialog, targeting this app by uid.

The control is only rendered when the app opted in — feedback_enabled is
threaded from the launched app's metadata into the window options — and
the dialog still re-checks opt-in server-side, so a stale flag can't send
anywhere. Reuses the existing .dashboard-app-drawer-btn styling and the
app_feedback_title i18n string, so no new CSS or strings.

Adds e2e coverage: the control appears and opens the dialog for an
opted-in app, and is absent for an app that hasn't opted in.

* feat: enable app feedback by default in Dev Center

New apps created in Dev Center now have feedbackEnabled set on creation,
so users can send the developer feedback without any extra setup. A "User
Feedback" toggle in the app's settings lets developers turn it off (and
back on); it's wired into the save payload, the dirty-state tracking, and
the reset-to-original path like the neighboring toggles.

The Save update omits feedbackEnabled unless the toggle is present, and
the backend leaves an omitted field untouched, so the default survives
the create-then-save flow Dev Center runs. Add an SDK apps-suite guard
covering that round-trip (create-on -> unrelated update keeps it -> can
be turned off).

* fix: feedback modal polish + share sender email

Address four issues with the app feedback UI:

- Dashboard app-drawer: the extra "feedback" control pushed the close
  button past the drawer's derived width and clipped it. A `has-feedback`
  modifier widens the surface by one button + gap so all three controls
  fit. The control's glyph is now a message bubble with text lines, which
  reads more clearly at 14px than the previous bare speech bubble.

- The feedback dialog is no longer a UIWindow. It's a from-scratch
  overlay modal in the spirit of the dashboard modals (uninstall,
  add-app): a fixed scrim + centered card with self-contained,
  theme-aware color tokens (light default + dark override), a bottom-sheet
  layout on narrow screens, backdrop/Escape close, and an entrance
  transition. This renders consistently across the three contexts it's
  opened from (desktop app-IPC, dashboard drawer, standalone popup), so
  the callers no longer pass UIWindow-specific window_options.

- Feedback now shares the sender's email (not just their username) with
  the developer so they can respond: the owner email sets Reply-To to the
  sender and shows the address in the body — but only when the sender's
  email is verified (an unverified address could be anyone's, so it's
  never used as a reply target). EmailClient.send gains an optional
  replyTo. The dialog note now says the email will be shared.

Tests: e2e updated for the new modal (7 pass); backend feedback suite
covers the verified/unverified sender-email split (sqlite + postgres);
EmailClient + GUI unit suites pass; type-check clean.

* fix: resolve 'app-'-prefixed app names in feedback target lookup

APP_NAME_REGEX allows names beginning with "app-" (e.g. the seeded
app-center), but resolveTargetApp's startsWith('app-') heuristic sent
those to a uid-only lookup with no name fallback, so feedback for such
apps 403'd even when enabled. Use AppStore.resolveApp (uid, then name)
like the rest of the codebase.

* fix: make feedback daily caps fail closed under concurrent submissions

The per-user and per-user-per-app caps were check-then-insert and the
per-app email cap was count-then-send, so parallel requests (or multiple
nodes, or the route limiter failing open) could all read a stale
under-cap count and push past every limit — the exact scenario the
DB-backed caps exist to stop.

Now the user caps recount after the insert (own row included) and roll
the row back with 429 if a burst breached them, and the email cap claims
its slot (email_sent=1) before sending, recounts, and releases the slot
if over cap or if the send fails.

* docs: disclose the Dev Center's feedback-on-by-default in SDK docs/types

The Dev Center deliberately creates apps with feedbackEnabled (see
32c950de5), but the SDK docs, apps.d.ts, and AppFeedbackService's class
doc all described feedback as strictly opt-in / default-false with no
qualification — so a Dev Center developer reading them would wrongly
conclude feedback is off for their app. State the Dev Center behavior
alongside the API default, and correct the update-path docs: an omitted
feedbackEnabled leaves the current value unchanged rather than
defaulting to false.

* fix: don't mint a user-app token as a side effect of the feedback popup

Every embedded_in_popup boot ran the user-app token exchange, and the
exchange is a write: /auth/get-user-app-token bootstraps an app row for
the opener origin, grants flag:app-is-authenticated (what makes the
site count as connected to the account), and creates its AppData dir.
So merely opening — or immediately cancelling — a send-feedback popup
recorded a user<->site relationship the read-only feedback flow never
needs: the server resolves the feedback target from the attested origin
without any of it.

Gate the exchange behind runsUserAppTokenExchange(action) in all three
popup paths that mint (main postAuthActions exchange, temp-user signup
success, manual signup fallback). request-permission keeps the exchange
since grants are written against the app row it bootstraps.

* fix: refuse feedback when the deployment cannot deliver it

With no email transport configured (the common self-hosted default),
submissions were stored in app_feedback — a table with no read path
beyond the abuse-cap COUNTs — the owner email was silently skipped, and
the sender was still shown 'Feedback sent. Thank you!'. The developer
never learns the feedback exists while the user believes it was
delivered.

Gate acceptsFeedback on clients.email.isConfigured so the pre-flight
reports enabled:false (the dialog shows its 'not accepting feedback'
pane) and submit returns 403 instead of swallowing messages. Owner-level
store-without-email cases (unconfirmed owner email, per-app email cap
overflow) keep their existing deliberate semantics.

* fix: settle showFeedbackDialog instead of hanging on older host GUIs

In the app environment showFeedbackDialog awaited an IPC reply with no
capability check. A host GUI that predates this feature (self-hosted
Puter running the live js.puter.com SDK) has no handler for the message
and never replies, so the promise documented as 'never rejects' also
never resolved.

The GUI now advertises the IPC dialogs it can answer via a
puter.gui_features param on the app iframe URL, and the SDK resolves
false when 'feedback-dialog' isn't listed. A reply timeout could not
substitute: legitimate replies only arrive when the user closes the
dialog, so any timeout would false-negative while the user is typing.
Older SDKs ignore the extra param.

* fix: guard app-triggered feedback dialog against desktop-lockout loops

The showFeedbackDialog IPC handler had no re-entry or abuse guard, and
the dialog it opens is a full-viewport overlay above the taskbar and
every window — so 'while (true) await puter.ui.showFeedbackDialog()'
kept the desktop permanently covered (for signed-out users, the same
loop spams the full-page signup window instead). Every dismissal just
settled the promise and let the app immediately reopen it.

Allow one dialog at a time, and back off reopens per app after each
dismissal that sent nothing: 10s, then 60s, then blocked until page
reload. A successful send resets the backoff, and user-initiated paths
(dashboard drawer) are unaffected since they don't go through IPC.

* fix: carry feedback_enabled in suggested/recommended app summaries

launch_app uses options.app_obj verbatim when provided, and the
suggested-apps launch paths (open_item.js, UIWindowSearch.js,
UIDesktop.js) pass summaries from toAppSummary — which omitted
feedback_enabled. So an opted-in editor launched by opening a .txt file
showed no Send Feedback control in the dashboard drawer, while the same
app launched from the Apps tab (full puter.apps.get object) did.

* fix: gate feedback dialog Cancel/X on an in-flight submit

Escape and backdrop clicks were already ignored while the POST was
pending, but the X, Close, and Cancel buttons weren't — clicking one
mid-send settled the promise false and tore down the overlay while the
submission still landed server-side: the developer got the email, the
app was told sent=false, and a user who resubmitted 'the failed one'
sent a duplicate and burned a daily-cap slot.

Apply the same !sending gate to the buttons and disable them visually
while the send is in flight.

* fix: reject feedback origins longer than the source_origin column

readTargetParam accepted values up to 3000 chars but the raw origin is
stored verbatim into source_origin VARCHAR(2048) (MySQL/Postgres), so a
2049-3000 char origin passed every validation and then blew up the
INSERT with an HTTP 500 on Postgres/strict MySQL — or was silently
truncated on non-strict MySQL, corrupting the abuse-forensics value the
column exists for. Cap the param at the column size.

* fix: make the feedback dialog's privacy note match what is shared

The note unconditionally said 'Your email address will be shared with
the developer so they can respond', but AppFeedbackService shares the
username always and the email only when it exists and is verified — an
unverified or temp-user sender was promised a reply path that never
materializes, and nobody was told about the username.

Show 'username and email' when the signed-in user's email is verified,
and 'username' otherwise.

* fix: stop HTML-escaping email subject lines

Subjects were compiled with default Handlebars escaping, so the
app-user-feedback subject rendered a title like "Bob's App & Games" as
"Bob&#x27;s App &amp; Games" — literal entities in the recipient's mail
client. Subjects are plain-text headers, not HTML; compile them with
noEscape. Header safety is unaffected: the transport encodes newlines
and free-form values collapse whitespace upstream.

* docs: state exactly what showFeedbackDialog shares with the developer

The doc claimed the dialog 'tells the user their username will be
shared', while the dialog's note talked only about the email address and
the implementation shares the username always plus the email (as
Reply-To) only when verified. Describe the actual disclosure: username
always, email when verified.

* docs: document the COOP false-resolve limitation of showFeedbackDialog

Under COOP the popup's opener link is severed, so the SDK deliberately
resolves false while the popup stays open and the user can still submit
(a feedback submission has no server read-back the way a permission
grant does). The documented contract ('resolves to true if the user
submitted feedback') was silently wrong on cross-origin-isolated pages —
state the limitation in the doc and the SDK jsdoc: false means 'not
confirmed', not 'not sent'.

* feat: point the feedback email footer at the Dev Center

The footer told developers to turn feedback off via a puter.apps.update
one-liner, but the toggle lives in the Dev Center app settings — and
Dev Center is where apps get feedback enabled by default in the first
place. Reword it to 'manage it in the Dev Center' with a link built
from config.origin (like app_link) so it holds on self-hosted
deployments.

* test: cover the feedback service and store layers directly

The service owns every feedback business rule — target resolution,
eligibility, message normalization, the durable caps, and the owner-email
preconditions — but was only reachable through the controller's tests. Give
it and the store their own suites so a regression names the layer it broke.

Service coverage adds the branches the route tests could not reach: a
blocked origin resolving to null rather than surfacing a 403, owners who are
suspended or unsubscribed, length measured after normalization, the 24h cap
window boundary, subject-header injection via the app title, and the email
links being rooted at config.origin.

The two describes already labelled `AppFeedbackService ...` move out of the
controller test, which keeps only the caller-facing promise that a failed
send still returns success.

* refactor: prep the feedback cap error once instead of via a factory

Both throw sites are in one call and only one can ever run, so a plain
const reads the same and drops a function that existed only to defer a
constructor.

* Require verified users for app feedback API

Add `requireVerified: true` to both app feedback routes (`GET /target` and `POST /`) in `AppFeedbackController`. This tightens access control so only verified user accounts can fetch feedback targets or submit app feedback.
2026-08-12 09:49:24 -07:00
Daniel Salazar 116d6e6663 tests: big test push for better coverage (#3490) 2026-08-01 14:29:59 -07:00
Daniel Salazar 5a157197b6 fix: PUT-1398 (#3478)
Maintain Release Merge PR / update-release-pr (push) Canceled after 0s
Notify HeyPuter / notify (push) Canceled after 0s
release-please / release-please (push) Canceled after 0s
2026-07-30 01:42:40 -07:00
Daniel Salazar 89f9f9728f fix: PUT-1355 PUT-1351 PUT-1208 (#3420)
* fix: PUT-1355 PUT-1351 PUT-1208

* fix: dev center header
2026-07-21 14:17:33 -07:00
tamilselvanp57 58a763bcd1 Add Dev Center UI for viewing app users (#3349) (#3417)
Adds a 'View Users' link on the app Analytics tab that opens a modal
listing users who have authenticated into the app (username + email,
where the user has granted email:read to the app).

Consumes the existing puter.apps.get(...).users()/.getUsers() client
SDK method, backed by the app-telemetry driver's get_users - no new
backend work needed, per discussion on the issue.

Relates to #3349
2026-07-21 13:52:48 -07:00
tamilselvanp57 a4bb57bb79 Fix static hosting domain fallback for self-hosted custom domains (#3382)
Maintain Release Merge PR / update-release-pr (push) Has been cancelled
Notify HeyPuter / notify (push) Has been cancelled
release-please / release-please (push) Has been cancelled
2026-07-15 11:45:10 -07:00
Daniel Salazar b2108c0216 fix: update email for supprot (#3292) 2026-06-23 15:43:12 -07:00
Daniel Salazar 954f8b7b8d fix: puter self host guides + default bug (#3281)
Maintain Release Merge PR / update-release-pr (push) Has been cancelled
Notify HeyPuter / notify (push) Has been cancelled
release-please / release-please (push) Has been cancelled
* fix: oss dev center html_encode hardening

* fix: puter self host guides + default bug
2026-06-21 22:32:43 -07:00
Sauhbagya Prasad 529a3d026b Improve UI for App Deployment Options in Dev Center (#2266)
Docker Image CI / build-and-push-image (push) Has been cancelled
Maintain Release Merge PR / update-release-pr (push) Has been cancelled
release-please / release-please (push) Has been cancelled
test / test-backend (24.x) (push) Has been cancelled
test / API tests (node env, api-test) (24.x) (push) Has been cancelled
test / puterjs (node env, vitest) (24.x) (push) Has been cancelled
* Improve UI for App Deployment Options in Dev Center

- Added CSS styling for deployment option cards
- Implemented active state switching between deployment methods with guardrails
- Abstracted saveEditApp for reuse across Deploy and Edit tabs
- Added error handling for invalid URLs in the Deploy tab

* updated mode's description and fixed function's error

* updated by npm install

* Revert "updated by npm install"

This reverts commit 82cd962f62.

* in url mode the deploy button is enabled by default
2026-01-13 10:54:08 -08:00
jelveh dade7a6cba Add preview images upload for app listings 2026-01-07 17:32:24 -08:00
Daniel Salazar 11e057557d fix: eslint autofixable errors (#2002)
Docker Image CI / build-and-push-image (push) Has been cancelled
Maintain Release Merge PR / update-release-pr (push) Has been cancelled
release-please / release-please (push) Has been cancelled
test / test (20.x) (push) Has been cancelled
test / test (22.x) (push) Has been cancelled
test / backend (node env, api-test) (22.x) (push) Has been cancelled
test / puterjs (browser env, playwright) (22.x) (push) Has been cancelled
test / puterjs (node env, vitest) (22.x) (push) Has been cancelled
2025-11-21 13:22:19 -08:00
Daniel Salazar 5ff8c7e040 feat: rawBody parser, user metadata column + other misc fixes (#1904) 2025-11-04 18:19:55 -08:00
Daniel Salazar 85432c886e fix: adding more benign changes (#1891)
Docker Image CI / build-and-push-image (push) Has been cancelled
Maintain Release Merge PR / update-release-pr (push) Has been cancelled
release-please / release-please (push) Has been cancelled
test / test (20.x) (push) Has been cancelled
test / test (22.x) (push) Has been cancelled
test / backend (node env, api-test) (22.x) (push) Has been cancelled
test / puterjs (browser env, playwright) (22.x) (push) Has been cancelled
test / puterjs (node env, vitest) (22.x) (push) Has been cancelled
2025-11-02 06:48:56 -08:00
Daniel Salazar 758bef0582 Revert: commits for user metadata changes (#1887) 2025-11-02 06:09:13 -08:00
Daniel Salazar 71fb04cd55 fix: allow stripe header and commit db migration (#1877)
Docker Image CI / build-and-push-image (push) Has been cancelled
Maintain Release Merge PR / update-release-pr (push) Has been cancelled
release-please / release-please (push) Has been cancelled
test / test (20.x) (push) Has been cancelled
test / test (22.x) (push) Has been cancelled
test / backend (node env, api-test) (22.x) (push) Has been cancelled
test / puterjs (browser env, playwright) (22.x) (push) Has been cancelled
test / puterjs (node env, vitest) (22.x) (push) Has been cancelled
* fix: allow stripe header

* fix: commit db migration
2025-11-01 16:16:49 -07:00
Daniel Salazar 603746951b feat: ui for dev account + util decorators for controllers in extension (#1864)
* fix: IPC trigger for dev account setup + util decorators for ext controllers

* feat: allow extension to bypass json

* fix: ts building in volatile
2025-10-31 15:48:21 -07:00
Nariman Jelveh 2cedb05a8a add option to automatically set window title to opened file's name (#1678)
* add option to automatically set window title to opened file's name

A new checkbox in the app edit section to allow users to automatically set the window title to the name of the opened file. Updated related functions to handle this new setting, ensuring it is saved and reset correctly.

* Update launch_app.js
2025-10-04 12:13:57 -07:00
Nariman Jelveh 91a45c25de Update apps.js
Docker Image CI / build-and-push-image (push) Has been cancelled
Maintain Release Merge PR / update-release-pr (push) Has been cancelled
release-please / release-please (push) Has been cancelled
test / test (18.x) (push) Has been cancelled
test / test (20.x) (push) Has been cancelled
test / test (22.x) (push) Has been cancelled
2025-08-20 14:53:01 -07:00
jelveh caa77e61e6 Update apps.js 2025-08-18 00:35:47 -07:00
jelveh c10e6ccd8f Update index.html 2025-08-07 11:48:04 -07:00
jelveh d647c7686d Create icon.png 2025-08-06 22:27:14 -07:00
Nariman Jelveh e8f6b06723 Add spinner visibility for worker creation process in dev center 2025-08-06 18:56:17 -07:00
Nariman Jelveh ac35dc55f0 Hide dev-center spinner at the correct time when refreshing lists 2025-08-06 15:41:37 -07:00
jelveh f06abf96b3 Update apps.js 2025-08-06 09:34:21 -07:00
Nariman Jelveh a73d10826c adjust website and worker counters in dev center after a deletion
Docker Image CI / build-and-push-image (push) Has been cancelled
Maintain Release Merge PR / update-release-pr (push) Has been cancelled
release-please / release-please (push) Has been cancelled
test / test (18.x) (push) Has been cancelled
test / test (20.x) (push) Has been cancelled
test / test (22.x) (push) Has been cancelled
2025-08-05 19:32:31 -07:00
jelveh 23d2d504f0 Improve table heading visuals 2025-08-05 09:48:50 -07:00
jelveh 64a5de3cc4 fix dev docs link 2025-08-05 09:09:41 -07:00
jelveh 6bccf7dc7d Update style.css 2025-08-05 09:03:35 -07:00
jelveh 5ccdd627ee Add functionality to remove app, website, and worker cards with dedicated functions; streamline deletion process and improve UI updates for empty states. 2025-08-05 08:49:30 -07:00
jelveh 636bf6b8b3 Implement shift-click functionality for checkbox selection in apps, websites, and workers; improve UI responsiveness by updating row activation based on checkbox state. 2025-08-04 23:18:01 -07:00
jelveh 90f1232b87 Refactor dropped items handling in dev-center.js to use window scope and improve UI updates; minor UI adjustment in websites.js and handle empty file paths in workers.js 2025-08-04 22:47:17 -07:00
jelveh 162aa485d0 Add directory change functionality for websites and update UI alert handling 2025-08-04 22:27:07 -07:00
jelveh b6fcb78f6e wip 2025-08-04 22:08:32 -07:00
jelveh 9f8c0d3227 Update index.html 2025-08-04 15:25:14 -07:00
jelveh ab8cdb2d57 Add requestEmailConfirmation to puter.ui 2025-08-04 14:11:55 -07:00
jelveh 39bd825866 fix search functionality by managing 'has-value' class for input fields in websites and workers search 2025-08-04 13:24:36 -07:00
jelveh dc8b7ecb34 update app count when a new app is created 2025-08-04 13:09:50 -07:00
jelveh 1d1b484bca Update workers.js 2025-08-04 00:04:11 -07:00
Nariman Jelveh a340c9a0b7 fix sorting in websites tab 2025-08-03 19:47:45 -07:00
Nariman Jelveh b59b19f8b3 Creating workers or websites start with selecting a file/folder 2025-08-03 19:25:18 -07:00
Nariman Jelveh 59c297db49 search is now isolated to the active tab 2025-08-03 19:06:45 -07:00
Nariman Jelveh 3df0bac9dc count websites and workers at start 2025-08-03 18:39:14 -07:00
Nariman Jelveh 2691818f6e Update index.html 2025-08-03 18:24:02 -07:00
Nariman Jelveh 1bc8de8112 make workers sortable by file path 2025-08-03 18:24:02 -07:00
Nariman Jelveh 12f574ed70 move libs back to original location 2025-08-03 18:24:02 -07:00
Nariman Jelveh 8f18a841b3 Add file column to worker table and enable file path launching 2025-08-03 18:24:02 -07:00
Nariman Jelveh 80ccb2d48b Update workers.js 2025-08-03 18:24:02 -07:00
jelveh 2d0d253f96 Update apps.js 2025-08-03 18:24:02 -07:00
jelveh 30db54f062 Remove context menu from entries 2025-08-03 18:24:02 -07:00