feat: in-app support form with email + Gitea issue integration (v0.49.0)
Users can now report bugs, suggestions, or questions from a new /support page. Each submission sends a confirmation email and, when GITEA_URL/ GITEA_TOKEN/GITEA_REPO are configured in admin Settings, opens a labeled issue on that repo automatically (best-effort — failure doesn't block the ticket). Admins get a Support section to triage status and retry failed Gitea issue creation. New support_tickets table, gitea.ts client, site-settings entries for the three new secrets, and OpenAPI docs for the two user-facing endpoints. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
CREATE TYPE "public"."support_ticket_status" AS ENUM('open', 'triaged', 'closed');--> statement-breakpoint
|
||||
CREATE TYPE "public"."support_ticket_type" AS ENUM('bug', 'suggestion', 'question');--> statement-breakpoint
|
||||
CREATE TABLE "support_tickets" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"type" "support_ticket_type" NOT NULL,
|
||||
"title" text NOT NULL,
|
||||
"description" text NOT NULL,
|
||||
"status" "support_ticket_status" DEFAULT 'open' NOT NULL,
|
||||
"gitea_issue_url" text,
|
||||
"gitea_error" text,
|
||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "support_tickets" ADD CONSTRAINT "support_tickets_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "support_tickets_user_idx" ON "support_tickets" USING btree ("user_id","created_at");--> statement-breakpoint
|
||||
CREATE INDEX "support_tickets_status_idx" ON "support_tickets" USING btree ("status","created_at");
|
||||
File diff suppressed because it is too large
Load Diff
@@ -316,6 +316,13 @@
|
||||
"when": 1784326930573,
|
||||
"tag": "0044_polite_living_mummy",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 45,
|
||||
"version": "7",
|
||||
"when": 1784366396485,
|
||||
"tag": "0045_overjoyed_moondragon",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7,3 +7,4 @@ export * from "./webhooks";
|
||||
export * from "./messaging";
|
||||
export * from "./billing";
|
||||
export * from "./ai-chat";
|
||||
export * from "./support";
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { pgTable, text, timestamp, index, pgEnum } from "drizzle-orm/pg-core";
|
||||
import { relations } from "drizzle-orm";
|
||||
import { users } from "./users";
|
||||
|
||||
export const supportTicketTypeEnum = pgEnum("support_ticket_type", ["bug", "suggestion", "question"]);
|
||||
export const supportTicketStatusEnum = pgEnum("support_ticket_status", ["open", "triaged", "closed"]);
|
||||
|
||||
export const supportTickets = pgTable("support_tickets", {
|
||||
id: text("id").primaryKey(),
|
||||
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
type: supportTicketTypeEnum("type").notNull(),
|
||||
title: text("title").notNull(),
|
||||
description: text("description").notNull(),
|
||||
status: supportTicketStatusEnum("status").notNull().default("open"),
|
||||
giteaIssueUrl: text("gitea_issue_url"),
|
||||
giteaError: text("gitea_error"),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||
}, (t) => [
|
||||
index("support_tickets_user_idx").on(t.userId, t.createdAt),
|
||||
index("support_tickets_status_idx").on(t.status, t.createdAt),
|
||||
]);
|
||||
|
||||
export const supportTicketsRelations = relations(supportTickets, ({ one }) => ({
|
||||
user: one(users, { fields: [supportTickets.userId], references: [users.id] }),
|
||||
}));
|
||||
Reference in New Issue
Block a user