Fix canned_responses 500: add missing SQL migration
The canned_responses table was registered in autoMigrate() (app.go) but
autoMigrate() is never called on the serve path (Bootstrap -> NewDatabase
-> RunMigrations only). With no SQL migration file, the table never
existed, so POST /api/v1/accounts/:id/canned_responses failed with
'relation does not exist' -> 500.
Add migration 000049 to create the table matching the GORM model
(id, account_id, content, short_code, timestamps) with the account+
short_code partial unique index and deleted_at index.
Also include two related fixes staged in the working tree:
- router.go: register notifications.GET("") alongside GET("/") to avoid
Gin 301 trailing-slash redirect that the Vite proxy doesn't follow
- notifications/actions.js: stop the infinite loader on fetch error to
prevent IntersectionObserver re-fire loop
This commit is contained in:
@@ -1732,6 +1732,11 @@ func registerV1Routes(g *gin.RouterGroup, h *Handlers) {
|
||||
// Notifications — account-scoped Chatwoot routes used by the dashboard frontend.
|
||||
notifications := accountScoped.Group("/notifications")
|
||||
{
|
||||
// Register both "" and "/" so Gin does not 301-redirect between them.
|
||||
// The dashboard frontend requests /notifications (no trailing slash);
|
||||
// without the "" route Gin's RedirectTrailingSlash emits a 301 that
|
||||
// the Vite dev proxy does not follow, causing an infinite-load loop.
|
||||
notifications.GET("", h.Notification.List)
|
||||
notifications.GET("/", h.Notification.List)
|
||||
notifications.POST("/read_all", h.Notification.MarkAllRead)
|
||||
notifications.GET("/unread_count", h.Notification.UnreadCount)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS canned_responses;
|
||||
@@ -0,0 +1,12 @@
|
||||
CREATE TABLE IF NOT EXISTS canned_responses (
|
||||
id SERIAL PRIMARY KEY,
|
||||
account_id INTEGER NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
short_code VARCHAR(255) NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
deleted_at TIMESTAMPTZ
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_canned_responses_account_id ON canned_responses(account_id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_canned_short_code ON canned_responses(account_id, short_code) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_canned_responses_deleted_at ON canned_responses(deleted_at);
|
||||
@@ -39,6 +39,10 @@ export const actions = {
|
||||
}
|
||||
} catch (error) {
|
||||
commit(types.SET_NOTIFICATIONS_UI_FLAG, { isFetching: false });
|
||||
// On error, stop the infinite loader — otherwise IntersectionObserver
|
||||
// keeps firing loadMoreNotifications, incrementing page forever and
|
||||
// re-issuing the failing request in a tight loop.
|
||||
commit(types.SET_ALL_NOTIFICATIONS_LOADED);
|
||||
}
|
||||
},
|
||||
unReadCount: async ({ commit } = {}) => {
|
||||
|
||||
Reference in New Issue
Block a user