mirror of
https://github.com/HeyPuter/puter.git
synced 2026-08-24 23:17:23 +00:00
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.
65 lines
2.9 KiB
TypeScript
65 lines
2.9 KiB
TypeScript
/*
|
|
* Copyright (C) 2024-present Puter Technologies Inc.
|
|
*
|
|
* This file is part of Puter.
|
|
*
|
|
* Puter is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published
|
|
* by the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import { AppController } from './apps/AppController.js';
|
|
import { AppFeedbackController } from './feedback/AppFeedbackController.js';
|
|
import { AuthController } from './auth/AuthController.js';
|
|
import { BroadcastController } from './broadcast/BroadcastController.js';
|
|
import { DesktopController } from './desktop/DesktopController.js';
|
|
import { DriverController } from './drivers/DriverController.js';
|
|
import { FSController } from './fs/FSController.js';
|
|
import { HomepageController } from './homepage/HomepageController.js';
|
|
import { HostingController } from './hosting/HostingController.js';
|
|
import { LegacyFSController } from './fs/LegacyFSController.js';
|
|
import { NotificationController } from './notification/NotificationController.js';
|
|
import { OIDCController } from './oidc/OIDCController.js';
|
|
import { PuterAIController } from './puterai/PuterAIController.js';
|
|
import { ShareController } from './share/ShareController.js';
|
|
import { StaticAssetsController } from './static/StaticAssetsController.js';
|
|
import { StaticPagesController } from './static/StaticPagesController.js';
|
|
import { SystemController } from './system/SystemController.js';
|
|
import { WebDAVController } from './webdav/WebDAVController.js';
|
|
import { WispController } from './wisp/WispController.js';
|
|
import type { IPuterControllerRegistry } from './types.js';
|
|
import { PeerController } from './peer/PeerController.js';
|
|
|
|
export const puterControllers = {
|
|
staticAssets: StaticAssetsController,
|
|
staticPages: StaticPagesController,
|
|
auth: AuthController,
|
|
apps: AppController,
|
|
appFeedback: AppFeedbackController,
|
|
desktop: DesktopController,
|
|
hosting: HostingController,
|
|
system: SystemController,
|
|
fs: FSController,
|
|
legacyFs: LegacyFSController,
|
|
puterAi: PuterAIController,
|
|
drivers: DriverController,
|
|
broadcast: BroadcastController,
|
|
notification: NotificationController,
|
|
share: ShareController,
|
|
webdav: WebDAVController,
|
|
oidc: OIDCController,
|
|
wisp: WispController,
|
|
peer: PeerController,
|
|
// Last so its catch-all static fallback doesn't shadow earlier routes.
|
|
homepage: HomepageController,
|
|
} satisfies IPuterControllerRegistry;
|