fix(deploy): reuse shared redis and serve dashboard at root
This commit is contained in:
+6
-3
@@ -7,10 +7,13 @@ GOCHAT_SERVER_MODE=release
|
|||||||
GOCHAT_SERVER_CORS_ALLOWED_ORIGINS=https://chat.CHANGE_ME.example.com
|
GOCHAT_SERVER_CORS_ALLOWED_ORIGINS=https://chat.CHANGE_ME.example.com
|
||||||
GOCHAT_SERVER_TRUSTED_PROXIES=10.0.0.0/8
|
GOCHAT_SERVER_TRUSTED_PROXIES=10.0.0.0/8
|
||||||
GOCHAT_DATABASE_DSN=postgres://gochat:CHANGE_ME@db.CHANGE_ME.example.com:5432/gochat_production?sslmode=verify-full
|
GOCHAT_DATABASE_DSN=postgres://gochat:CHANGE_ME@db.CHANGE_ME.example.com:5432/gochat_production?sslmode=verify-full
|
||||||
GOCHAT_REDIS_DSN=rediss://:CHANGE_ME@redis.CHANGE_ME.example.com:6380/0
|
GOCHAT_REDIS_DSN=redis://redis:6379/1
|
||||||
|
|
||||||
# Release mode requires external PostgreSQL/Redis endpoints with verified TLS.
|
# Release mode requires external PostgreSQL and Redis endpoints. PostgreSQL must
|
||||||
# Set a complete GOCHAT_DATABASE_DSN with sslmode=verify-ca or verify-full.
|
# set exactly one sslmode: use disable only on a trusted private network, or use
|
||||||
|
# verify-ca/verify-full with the certificate settings below.
|
||||||
|
# Redis may omit authentication only on a trusted private Docker network. Use
|
||||||
|
# rediss:// with authentication when traffic crosses an untrusted network.
|
||||||
# Compose passes this value unchanged to every database client. Keep these fixed
|
# Compose passes this value unchanged to every database client. Keep these fixed
|
||||||
# container paths in the DSN and point the host variables at untracked files.
|
# container paths in the DSN and point the host variables at untracked files.
|
||||||
# GOCHAT_DATABASE_DSN='postgres://user:pass@db.example.com:5432/gochat?sslmode=verify-full&sslrootcert=/run/secrets/external-db-ca.crt&sslcert=/run/secrets/external-db-client.crt&sslkey=/run/secrets/external-db-client.key'
|
# GOCHAT_DATABASE_DSN='postgres://user:pass@db.example.com:5432/gochat?sslmode=verify-full&sslrootcert=/run/secrets/external-db-ca.crt&sslcert=/run/secrets/external-db-client.crt&sslkey=/run/secrets/external-db-client.key'
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ log/
|
|||||||
# Storage
|
# Storage
|
||||||
storage/
|
storage/
|
||||||
/backend/uploads/
|
/backend/uploads/
|
||||||
|
/deploy/docker/data/
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
vendor/
|
vendor/
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ type sourceDecl struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var criticalRoutes = []route{
|
var criticalRoutes = []route{
|
||||||
|
{Method: "GET", Path: "/", Controller: "dashboard#index", Source: "routes.rb:17"},
|
||||||
{Method: "GET", Path: "/app", Controller: "dashboard#index", Source: "routes.rb:19"},
|
{Method: "GET", Path: "/app", Controller: "dashboard#index", Source: "routes.rb:19"},
|
||||||
{Method: "GET", Path: "/app/*params", Controller: "dashboard#index", Source: "routes.rb:20"},
|
{Method: "GET", Path: "/app/*params", Controller: "dashboard#index", Source: "routes.rb:20"},
|
||||||
{Method: "GET", Path: "/.well-known/assetlinks.json", Controller: "android_app#assetlinks", Source: "routes.rb:657"},
|
{Method: "GET", Path: "/.well-known/assetlinks.json", Controller: "android_app#assetlinks", Source: "routes.rb:657"},
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ func TestValidate_ReleaseRejectsShortSearchKey(t *testing.T) {
|
|||||||
assert.ErrorContains(t, Validate(cfg), "search API key must be at least 16 bytes")
|
assert.ErrorContains(t, Validate(cfg), "search API key must be at least 16 bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidate_ReleaseDatabaseTLS(t *testing.T) {
|
func TestValidate_ReleaseDatabaseTransport(t *testing.T) {
|
||||||
cfg := &Config{
|
cfg := &Config{
|
||||||
Server: ServerConfig{Host: "localhost", Port: 8080, Mode: "release", CORS: CORSConfig{AllowedOrigins: []string{"https://chat.acme.test"}}},
|
Server: ServerConfig{Host: "localhost", Port: 8080, Mode: "release", CORS: CORSConfig{AllowedOrigins: []string{"https://chat.acme.test"}}},
|
||||||
Redis: RedisConfig{DSN: "rediss://:redis-secret@redis.acme.test:6379"},
|
Redis: RedisConfig{DSN: "rediss://:redis-secret@redis.acme.test:6379"},
|
||||||
@@ -292,7 +292,7 @@ func TestValidate_ReleaseDatabaseTLS(t *testing.T) {
|
|||||||
dsn string
|
dsn string
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"external disable", "postgres://gochat:database-secret@db.example.test:5432/gochat?sslmode=disable", true},
|
{"external disable", "postgres://gochat:database-secret@db.example.test:5432/gochat?sslmode=disable", false},
|
||||||
{"external missing sslmode", "postgres://gochat:database-secret@db.example.test:5432/gochat", true},
|
{"external missing sslmode", "postgres://gochat:database-secret@db.example.test:5432/gochat", true},
|
||||||
{"external duplicate downgrade", "postgres://gochat:database-secret@db.example.test:5432/gochat?sslmode=disable&sslmode=verify-full", true},
|
{"external duplicate downgrade", "postgres://gochat:database-secret@db.example.test:5432/gochat?sslmode=disable&sslmode=verify-full", true},
|
||||||
{"external duplicate allowed", "postgres://gochat:database-secret@db.example.test:5432/gochat?sslmode=verify-full&sslmode=verify-full", true},
|
{"external duplicate allowed", "postgres://gochat:database-secret@db.example.test:5432/gochat?sslmode=verify-full&sslmode=verify-full", true},
|
||||||
@@ -329,13 +329,12 @@ func TestValidateProductionDatabaseDSN_RejectsHostMatrix(t *testing.T) {
|
|||||||
require.NoError(t, scanner.Err())
|
require.NoError(t, scanner.Err())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProductionDatabaseTLSRunbookContract(t *testing.T) {
|
func TestProductionDatabaseTransportRunbookContract(t *testing.T) {
|
||||||
runbook, err := os.ReadFile("../../../docs/ops/02-production-operations.md")
|
runbook, err := os.ReadFile("../../../docs/ops/02-production-operations.md")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
runbookText := string(runbook)
|
runbookText := string(runbook)
|
||||||
assert.Equal(t, 1, strings.Count(runbookText, "`sslmode=verify-ca|verify-full`"))
|
assert.Contains(t, runbookText, "use `disable` only on a trusted private network")
|
||||||
assert.Equal(t, 1, strings.Count(runbookText, "sslmode="))
|
assert.Contains(t, runbookText, "`verify-ca`/`verify-full`")
|
||||||
assert.NotContains(t, runbookText, "sslmode=disable")
|
|
||||||
assert.NotContains(t, runbookText, "sslmode=require")
|
assert.NotContains(t, runbookText, "sslmode=require")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ func TestReleaseTransportAndCORSValidationFailsClosed(t *testing.T) {
|
|||||||
mutate func(*Config)
|
mutate func(*Config)
|
||||||
message string
|
message string
|
||||||
}{
|
}{
|
||||||
{"database certificate not verified", func(c *Config) { c.Database.DSN = "postgres://user:pass@db.acme.test/gochat?sslmode=require" }, "verify-full or verify-ca"},
|
{"database TLS without identity verification", func(c *Config) { c.Database.DSN = "postgres://user:pass@db.acme.test/gochat?sslmode=require" }, "sslmode=disable, verify-full or verify-ca"},
|
||||||
{"plain redis", func(c *Config) { c.Redis.DSN = "redis://:redis-secret@redis.acme.test:6379" }, "must use rediss"},
|
|
||||||
{"redis certificate not verified", func(c *Config) { c.Redis.DSN = "rediss://:redis-secret@redis.acme.test:6379?insecure_skip_verify=true" }, "cannot be disabled"},
|
{"redis certificate not verified", func(c *Config) { c.Redis.DSN = "rediss://:redis-secret@redis.acme.test:6379?insecure_skip_verify=true" }, "cannot be disabled"},
|
||||||
{"placeholder origin", func(c *Config) { c.Server.CORS.AllowedOrigins = []string{"https://example.com"} }, "not deployable"},
|
{"placeholder origin", func(c *Config) { c.Server.CORS.AllowedOrigins = []string{"https://example.com"} }, "not deployable"},
|
||||||
{"non HTTPS origin", func(c *Config) { c.Server.CORS.AllowedOrigins = []string{"http://chat.acme.test"} }, "exact HTTPS origin"},
|
{"non HTTPS origin", func(c *Config) { c.Server.CORS.AllowedOrigins = []string{"http://chat.acme.test"} }, "exact HTTPS origin"},
|
||||||
@@ -45,6 +44,18 @@ func TestReleaseTransportAndCORSValidationFailsClosed(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReleaseAllowsPasswordProtectedRedisWithoutTLS(t *testing.T) {
|
||||||
|
cfg := validReleaseConfig()
|
||||||
|
cfg.Redis.DSN = "redis://:redis-secret@redis.acme.test:6379"
|
||||||
|
require.NoError(t, Validate(cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReleaseAllowsPrivateRedisWithoutCredentials(t *testing.T) {
|
||||||
|
cfg := validReleaseConfig()
|
||||||
|
cfg.Redis.DSN = "redis://redis:6379/1"
|
||||||
|
require.NoError(t, Validate(cfg))
|
||||||
|
}
|
||||||
|
|
||||||
func TestSecuritySettingsLoadFromEnvironment(t *testing.T) {
|
func TestSecuritySettingsLoadFromEnvironment(t *testing.T) {
|
||||||
workingDir, err := os.Getwd()
|
workingDir, err := os.Getwd()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -128,16 +128,12 @@ func Validate(cfg *Config) error {
|
|||||||
if err := ValidateProductionDatabaseDSN(cfg.Database.DSN); err != nil {
|
if err := ValidateProductionDatabaseDSN(cfg.Database.DSN); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if redisURL.User == nil {
|
if redisURL.User != nil {
|
||||||
return fmt.Errorf("production Redis credentials are required")
|
if password, ok := redisURL.User.Password(); !ok || password == "" || containsPlaceholder(password) {
|
||||||
|
return fmt.Errorf("production Redis password is required and must not contain placeholders")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if password, ok := redisURL.User.Password(); !ok || password == "" || containsPlaceholder(password) {
|
if redisURL.Scheme == "rediss" && strings.EqualFold(redisURL.Query().Get("insecure_skip_verify"), "true") {
|
||||||
return fmt.Errorf("production Redis password is required and must not contain placeholders")
|
|
||||||
}
|
|
||||||
if redisURL.Scheme != "rediss" {
|
|
||||||
return fmt.Errorf("production Redis DSN must use rediss with certificate verification")
|
|
||||||
}
|
|
||||||
if strings.EqualFold(redisURL.Query().Get("insecure_skip_verify"), "true") {
|
|
||||||
return fmt.Errorf("production Redis TLS certificate verification cannot be disabled")
|
return fmt.Errorf("production Redis TLS certificate verification cannot be disabled")
|
||||||
}
|
}
|
||||||
if len(cfg.Server.CORS.AllowedOrigins) == 0 {
|
if len(cfg.Server.CORS.AllowedOrigins) == 0 {
|
||||||
@@ -205,8 +201,8 @@ func ValidateProductionDatabaseDSN(dsn string) error {
|
|||||||
if len(modes) != 1 {
|
if len(modes) != 1 {
|
||||||
return fmt.Errorf("production database DSN must contain exactly one sslmode")
|
return fmt.Errorf("production database DSN must contain exactly one sslmode")
|
||||||
}
|
}
|
||||||
if modes[0] != "verify-ca" && modes[0] != "verify-full" {
|
if modes[0] != "disable" && modes[0] != "verify-ca" && modes[0] != "verify-full" {
|
||||||
return fmt.Errorf("production database DSN must use sslmode=verify-full or verify-ca")
|
return fmt.Errorf("production database DSN must use sslmode=disable, verify-full or verify-ca")
|
||||||
}
|
}
|
||||||
|
|
||||||
fixedPaths := map[string]string{
|
fixedPaths := map[string]string{
|
||||||
|
|||||||
@@ -219,8 +219,9 @@ func RegisterRoutes(
|
|||||||
engine.GET("/tiktok/callback", tiktokChannelCallback(db))
|
engine.GET("/tiktok/callback", tiktokChannelCallback(db))
|
||||||
|
|
||||||
// Dashboard shell routes used by Chatwoot mailer and push deep links.
|
// Dashboard shell routes used by Chatwoot mailer and push deep links.
|
||||||
// Reference: Chatwoot routes.rb `get '/app'`, `get '/app/*params'` -> DashboardController#index.
|
// Reference: Chatwoot routes.rb root, `get '/app'`, `get '/app/*params'` -> DashboardController#index.
|
||||||
engine.GET("/runtime-config.js", dashboardRuntimeConfig)
|
engine.GET("/runtime-config.js", dashboardRuntimeConfig)
|
||||||
|
engine.GET("/", dashboardIndex)
|
||||||
engine.GET("/app", dashboardIndex)
|
engine.GET("/app", dashboardIndex)
|
||||||
engine.GET("/app/*params", dashboardIndex)
|
engine.GET("/app/*params", dashboardIndex)
|
||||||
engine.NoRoute(dashboardStatic)
|
engine.NoRoute(dashboardStatic)
|
||||||
@@ -2266,10 +2267,6 @@ func dashboardRuntimeConfig(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func dashboardStatic(c *gin.Context) {
|
func dashboardStatic(c *gin.Context) {
|
||||||
if c.Request.URL.Path == "/" {
|
|
||||||
c.Status(http.StatusNotFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
http.FileServer(http.Dir(frontendDistDir())).ServeHTTP(c.Writer, c.Request)
|
http.FileServer(http.Dir(frontendDistDir())).ServeHTTP(c.Writer, c.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ func TestRegisterRoutesBootsWithChatwootParityConflictGroups(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expected := []string{
|
expected := []string{
|
||||||
|
"GET /",
|
||||||
"GET /.well-known/assetlinks.json",
|
"GET /.well-known/assetlinks.json",
|
||||||
"GET /.well-known/apple-app-site-association",
|
"GET /.well-known/apple-app-site-association",
|
||||||
"GET /.well-known/microsoft-identity-association.json",
|
"GET /.well-known/microsoft-identity-association.json",
|
||||||
|
|||||||
@@ -116,11 +116,8 @@ exec /usr/bin/install "$@"
|
|||||||
EOF
|
EOF
|
||||||
chmod +x "$tmp/bin/psql" "$tmp/bin/pg_dump" "$tmp/bin/pg_restore" "$tmp/bin/tar" "$tmp/bin/install"
|
chmod +x "$tmp/bin/psql" "$tmp/bin/pg_dump" "$tmp/bin/pg_restore" "$tmp/bin/tar" "$tmp/bin/install"
|
||||||
|
|
||||||
if GOCHAT_DATABASE_DSN='postgres://test@db.example.test/test?sslmode=disable' "$tmp/scripts/database_client_entrypoint.sh" --check >"$tmp/rejected" 2>&1; then
|
GOCHAT_DATABASE_DSN='postgres://test@db.example.test/test?sslmode=disable' \
|
||||||
echo 'backup gate accepted sslmode=disable' >&2
|
"$tmp/scripts/database_client_entrypoint.sh" --check
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
grep -F 'sslmode must be verify-ca or verify-full' "$tmp/rejected" >/dev/null
|
|
||||||
while IFS='|' read -r name dsn; do
|
while IFS='|' read -r name dsn; do
|
||||||
if GOCHAT_DATABASE_DSN=$dsn "$tmp/scripts/database_client_entrypoint.sh" --check >"$tmp/rejected" 2>&1; then
|
if GOCHAT_DATABASE_DSN=$dsn "$tmp/scripts/database_client_entrypoint.sh" --check >"$tmp/rejected" 2>&1; then
|
||||||
echo "backup gate accepted $name" >&2
|
echo "backup gate accepted $name" >&2
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ validate_production_database_dsn() {
|
|||||||
echo "$database_dsn_label must contain exactly one sslmode" >&2
|
echo "$database_dsn_label must contain exactly one sslmode" >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
if [ "$sslmode" != verify-ca ] && [ "$sslmode" != verify-full ]; then
|
if [ "$sslmode" != disable ] && [ "$sslmode" != verify-ca ] && [ "$sslmode" != verify-full ]; then
|
||||||
echo "$database_dsn_label sslmode must be verify-ca or verify-full" >&2
|
echo "$database_dsn_label sslmode must be disable, verify-ca or verify-full" >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -42,10 +42,10 @@ x-gochat-environment: &gochat-environment
|
|||||||
GOCHAT_SERVER_PORT: 3000
|
GOCHAT_SERVER_PORT: 3000
|
||||||
GOCHAT_SERVER_MODE: release
|
GOCHAT_SERVER_MODE: release
|
||||||
GOCHAT_SERVER_CORS_ALLOWED_ORIGINS: ${GOCHAT_SERVER_CORS_ALLOWED_ORIGINS:?set production CORS origins}
|
GOCHAT_SERVER_CORS_ALLOWED_ORIGINS: ${GOCHAT_SERVER_CORS_ALLOWED_ORIGINS:?set production CORS origins}
|
||||||
GOCHAT_DATABASE_DSN: &gochat-database-dsn ${GOCHAT_DATABASE_DSN:?set an external PostgreSQL DSN with sslmode=verify-ca or verify-full}
|
GOCHAT_DATABASE_DSN: &gochat-database-dsn ${GOCHAT_DATABASE_DSN:?set an external PostgreSQL DSN with an explicit sslmode}
|
||||||
GOCHAT_DATABASE_RUN_MIGRATIONS: "false"
|
GOCHAT_DATABASE_RUN_MIGRATIONS: "false"
|
||||||
GOCHAT_DATABASE_MIGRATIONS_PATH: /app/migrations
|
GOCHAT_DATABASE_MIGRATIONS_PATH: /app/migrations
|
||||||
GOCHAT_REDIS_DSN: ${GOCHAT_REDIS_DSN:?set an external Redis rediss:// DSN}
|
GOCHAT_REDIS_DSN: ${GOCHAT_REDIS_DSN:?set an external Redis DSN}
|
||||||
GOCHAT_SEARCH_ENGINE: meilisearch
|
GOCHAT_SEARCH_ENGINE: meilisearch
|
||||||
GOCHAT_SEARCH_HOST: http://meilisearch:7700
|
GOCHAT_SEARCH_HOST: http://meilisearch:7700
|
||||||
GOCHAT_SEARCH_API_KEY: ${MEILI_MASTER_KEY:?set MEILI_MASTER_KEY}
|
GOCHAT_SEARCH_API_KEY: ${MEILI_MASTER_KEY:?set MEILI_MASTER_KEY}
|
||||||
@@ -68,7 +68,7 @@ services:
|
|||||||
MEILI_MASTER_KEY: ${MEILI_MASTER_KEY:?set MEILI_MASTER_KEY}
|
MEILI_MASTER_KEY: ${MEILI_MASTER_KEY:?set MEILI_MASTER_KEY}
|
||||||
MEILI_NO_ANALYTICS: "true"
|
MEILI_NO_ANALYTICS: "true"
|
||||||
volumes:
|
volumes:
|
||||||
- meili_data:/meili_data
|
- ./data/meilisearch:/meili_data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "wget", "--no-verbose", "--spider", "http://127.0.0.1:7700/health"]
|
test: ["CMD", "wget", "--no-verbose", "--spider", "http://127.0.0.1:7700/health"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
@@ -90,7 +90,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:${GOCHAT_PORT:-3000}:3000"
|
- "127.0.0.1:${GOCHAT_PORT:-3000}:3000"
|
||||||
volumes:
|
volumes:
|
||||||
- gochat_storage:/app/storage
|
- ./data/storage:/app/storage
|
||||||
- *database-client-entrypoint
|
- *database-client-entrypoint
|
||||||
- *postgres-tls-ca
|
- *postgres-tls-ca
|
||||||
- *postgres-tls-client-cert
|
- *postgres-tls-client-cert
|
||||||
@@ -129,7 +129,7 @@ services:
|
|||||||
<<: *gochat-environment
|
<<: *gochat-environment
|
||||||
GOCHAT_DATABASE_RUN_MIGRATIONS: "false"
|
GOCHAT_DATABASE_RUN_MIGRATIONS: "false"
|
||||||
volumes:
|
volumes:
|
||||||
- gochat_storage:/app/storage
|
- ./data/storage:/app/storage
|
||||||
- *database-client-entrypoint
|
- *database-client-entrypoint
|
||||||
- *postgres-tls-ca
|
- *postgres-tls-ca
|
||||||
- *postgres-tls-client-cert
|
- *postgres-tls-client-cert
|
||||||
@@ -177,16 +177,16 @@ services:
|
|||||||
GOCHAT_BACKUP_METRICS_FILE: /metrics/gochat_backup.prom
|
GOCHAT_BACKUP_METRICS_FILE: /metrics/gochat_backup.prom
|
||||||
GOCHAT_VERSION: ${GOCHAT_IMAGE_REF}
|
GOCHAT_VERSION: ${GOCHAT_IMAGE_REF}
|
||||||
volumes:
|
volumes:
|
||||||
- gochat_storage:/source/storage:ro
|
- ./data/storage:/source/storage:ro
|
||||||
- shangwutong_backups:/source/connector:ro
|
- ./data/connector-backups:/source/connector:ro
|
||||||
- ${GOCHAT_BACKUP_DIR:-./backups/local}:/backup/local
|
- ${GOCHAT_BACKUP_DIR:-./data/backups/local}:/backup/local
|
||||||
- type: bind
|
- type: bind
|
||||||
source: ${GOCHAT_BACKUP_OFFSITE_DIR:?set an existing external off-site mount point}
|
source: ${GOCHAT_BACKUP_OFFSITE_DIR:?set an existing external off-site mount point}
|
||||||
target: /backup/offsite
|
target: /backup/offsite
|
||||||
bind:
|
bind:
|
||||||
create_host_path: false
|
create_host_path: false
|
||||||
- ${GOCHAT_BACKUP_PASSPHRASE_FILE:-./.secrets/backup-passphrase}:/run/secrets/backup-passphrase:ro
|
- ${GOCHAT_BACKUP_PASSPHRASE_FILE:-./.secrets/backup-passphrase}:/run/secrets/backup-passphrase:ro
|
||||||
- backup_metrics:/metrics
|
- ./data/backup-metrics:/metrics
|
||||||
- *database-client-entrypoint
|
- *database-client-entrypoint
|
||||||
- *postgres-tls-ca
|
- *postgres-tls-ca
|
||||||
- *postgres-tls-client-cert
|
- *postgres-tls-client-cert
|
||||||
@@ -207,8 +207,8 @@ services:
|
|||||||
GOCHAT_CONNECTOR_DB_PATH: /restore/connector/connector.db
|
GOCHAT_CONNECTOR_DB_PATH: /restore/connector/connector.db
|
||||||
GOCHAT_BACKUP_PASSPHRASE_FILE: /run/secrets/backup-passphrase
|
GOCHAT_BACKUP_PASSPHRASE_FILE: /run/secrets/backup-passphrase
|
||||||
volumes:
|
volumes:
|
||||||
- gochat_storage:/restore/storage
|
- ./data/storage:/restore/storage
|
||||||
- shangwutong_data:/restore/connector
|
- ./data/connector:/restore/connector
|
||||||
- type: bind
|
- type: bind
|
||||||
source: ${GOCHAT_BACKUP_OFFSITE_DIR:?set an existing external off-site mount point}
|
source: ${GOCHAT_BACKUP_OFFSITE_DIR:?set an existing external off-site mount point}
|
||||||
target: /backup/offsite
|
target: /backup/offsite
|
||||||
@@ -236,8 +236,8 @@ services:
|
|||||||
SWT_OUTBOUND_WORKERS: ${SWT_OUTBOUND_WORKERS:-8}
|
SWT_OUTBOUND_WORKERS: ${SWT_OUTBOUND_WORKERS:-8}
|
||||||
SWT_SHUTDOWN_TIMEOUT: ${SWT_SHUTDOWN_TIMEOUT:-30s}
|
SWT_SHUTDOWN_TIMEOUT: ${SWT_SHUTDOWN_TIMEOUT:-30s}
|
||||||
volumes:
|
volumes:
|
||||||
- shangwutong_data:/data
|
- ./data/connector:/data
|
||||||
- shangwutong_backups:/backup
|
- ./data/connector-backups:/backup
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "wget", "-q", "-T", "3", "-O", "/dev/null", "http://127.0.0.1:9100/readyz"]
|
test: ["CMD", "wget", "-q", "-T", "3", "-O", "/dev/null", "http://127.0.0.1:9100/readyz"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
@@ -261,8 +261,8 @@ services:
|
|||||||
- "127.0.0.1:24224:24224"
|
- "127.0.0.1:24224:24224"
|
||||||
volumes:
|
volumes:
|
||||||
- ../fluentd/fluent.conf:/fluentd/etc/fluent.conf:ro
|
- ../fluentd/fluent.conf:/fluentd/etc/fluent.conf:ro
|
||||||
- fluentd_logs:/fluentd/log
|
- ./data/fluentd/log:/fluentd/log
|
||||||
- fluentd_buffer:/fluentd/buffer
|
- ./data/fluentd/buffer:/fluentd/buffer
|
||||||
|
|
||||||
postgres-exporter:
|
postgres-exporter:
|
||||||
image: quay.io/prometheuscommunity/postgres-exporter:v0.17.1@sha256:38606faa38c54787525fb0ff2fd6b41b4cfb75d455c1df294927c5f611699b17
|
image: quay.io/prometheuscommunity/postgres-exporter:v0.17.1@sha256:38606faa38c54787525fb0ff2fd6b41b4cfb75d455c1df294927c5f611699b17
|
||||||
@@ -284,7 +284,7 @@ services:
|
|||||||
image: oliver006/redis_exporter:v1.72.1@sha256:f90cae1e7ecc6ac223d04bdb0c95e084918baced9f81f08c3d01c2f11bff72bf
|
image: oliver006/redis_exporter:v1.72.1@sha256:f90cae1e7ecc6ac223d04bdb0c95e084918baced9f81f08c3d01c2f11bff72bf
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
REDIS_ADDR: ${GOCHAT_REDIS_DSN:?set an external Redis rediss:// DSN}
|
REDIS_ADDR: ${GOCHAT_REDIS_DSN:?set an external Redis DSN}
|
||||||
logging: *gochat-logging
|
logging: *gochat-logging
|
||||||
|
|
||||||
blackbox-exporter:
|
blackbox-exporter:
|
||||||
@@ -300,7 +300,7 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
command: ["--collector.disable-defaults", "--collector.textfile", "--collector.textfile.directory=/var/lib/node_exporter/textfile_collector"]
|
command: ["--collector.disable-defaults", "--collector.textfile", "--collector.textfile.directory=/var/lib/node_exporter/textfile_collector"]
|
||||||
volumes:
|
volumes:
|
||||||
- backup_metrics:/var/lib/node_exporter/textfile_collector:ro
|
- ./data/backup-metrics:/var/lib/node_exporter/textfile_collector:ro
|
||||||
logging: *gochat-logging
|
logging: *gochat-logging
|
||||||
|
|
||||||
cadvisor:
|
cadvisor:
|
||||||
@@ -331,7 +331,7 @@ services:
|
|||||||
read_only: true
|
read_only: true
|
||||||
bind:
|
bind:
|
||||||
create_host_path: false
|
create_host_path: false
|
||||||
- alertmanager_data:/alertmanager
|
- ./data/alertmanager:/alertmanager
|
||||||
logging: *gochat-logging
|
logging: *gochat-logging
|
||||||
|
|
||||||
prometheus:
|
prometheus:
|
||||||
@@ -350,16 +350,5 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ../prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
- ../prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
||||||
- ../../backend/configs/prometheus_alerts.yml:/etc/prometheus/rules/gochat.yml:ro
|
- ../../backend/configs/prometheus_alerts.yml:/etc/prometheus/rules/gochat.yml:ro
|
||||||
- prometheus_data:/prometheus
|
- ./data/prometheus:/prometheus
|
||||||
logging: *gochat-logging
|
logging: *gochat-logging
|
||||||
|
|
||||||
volumes:
|
|
||||||
meili_data:
|
|
||||||
gochat_storage:
|
|
||||||
shangwutong_data:
|
|
||||||
shangwutong_backups:
|
|
||||||
backup_metrics:
|
|
||||||
prometheus_data:
|
|
||||||
alertmanager_data:
|
|
||||||
fluentd_logs:
|
|
||||||
fluentd_buffer:
|
|
||||||
|
|||||||
@@ -105,9 +105,10 @@ expect_failure 'a mutable production image' 'must be pinned to a sha256 digest'
|
|||||||
unset TEST_MUTABLE_IMAGE
|
unset TEST_MUTABLE_IMAGE
|
||||||
|
|
||||||
export GOCHAT_DATABASE_DSN='postgres://external_user:external_password@db.example.test:5432/gochat?sslmode=disable'
|
export GOCHAT_DATABASE_DSN='postgres://external_user:external_password@db.example.test:5432/gochat?sslmode=disable'
|
||||||
expect_failure 'an external database without verified TLS' 'sslmode must be verify-ca or verify-full'
|
run_preflight
|
||||||
|
grep -F 'production preflight passed' "$tmp/output" >/dev/null
|
||||||
GOCHAT_DATABASE_DSN='postgres://external_user:external_password@db.example.test:5432/gochat?sslmode=require'
|
GOCHAT_DATABASE_DSN='postgres://external_user:external_password@db.example.test:5432/gochat?sslmode=require'
|
||||||
expect_failure 'an external database without certificate verification' 'sslmode must be verify-ca or verify-full'
|
expect_failure 'an external database without certificate verification' 'sslmode must be disable, verify-ca or verify-full'
|
||||||
GOCHAT_DATABASE_DSN='postgres://external_user:external_password@db.example.test:5432/gochat?sslmode=disable&sslmode=verify-full'
|
GOCHAT_DATABASE_DSN='postgres://external_user:external_password@db.example.test:5432/gochat?sslmode=disable&sslmode=verify-full'
|
||||||
expect_failure 'duplicate sslmode parameters with a disabling first value' 'must contain exactly one sslmode'
|
expect_failure 'duplicate sslmode parameters with a disabling first value' 'must contain exactly one sslmode'
|
||||||
GOCHAT_DATABASE_DSN='postgres://external_user:external_password@db.example.test:5432/gochat?sslmode=verify-full&sslmode=verify-full'
|
GOCHAT_DATABASE_DSN='postgres://external_user:external_password@db.example.test:5432/gochat?sslmode=verify-full&sslmode=verify-full'
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ Production web and worker processes never migrate on startup. The Compose file
|
|||||||
also requires `GOCHAT_IMAGE_REF` to be an immutable `image@sha256:digest`; tags such
|
also requires `GOCHAT_IMAGE_REF` to be an immutable `image@sha256:digest`; tags such
|
||||||
as `latest` are not an acceptable rollback record.
|
as `latest` are not an acceptable rollback record.
|
||||||
|
|
||||||
The bundled `gochat_storage` volume is shared and durable for replicas on one
|
Persistent files live under `deploy/docker/data/` beside the production Compose
|
||||||
Docker host. Multi-host replicas must provision that volume with a shared
|
file. Multi-host replicas must mount shared storage at that path; never use
|
||||||
volume driver/filesystem; never use separate node-local volumes.
|
separate node-local attachment directories.
|
||||||
|
|
||||||
## Recovery objectives
|
## Recovery objectives
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ export GOCHAT_CONNECTOR_BACKUP_NAME="connector-$(date -u +%Y%m%dT%H%M%SZ).db"
|
|||||||
```
|
```
|
||||||
|
|
||||||
Backup/restore are audited one-shot containers and run as root only to read or
|
Backup/restore are audited one-shot containers and run as root only to read or
|
||||||
rebuild Docker volumes; web and worker remain non-root.
|
rebuild bind-mounted data directories; web and worker remain non-root.
|
||||||
For an external database using certificate files, keep the three DSN paths
|
For an external database using certificate files, keep the three DSN paths
|
||||||
fixed as documented in `.env.example`. Set the host files' group to
|
fixed as documented in `.env.example`. Set the host files' group to
|
||||||
`GOCHAT_DATABASE_TLS_GID` and grant that group read permission (`0640` for the
|
`GOCHAT_DATABASE_TLS_GID` and grant that group read permission (`0640` for the
|
||||||
@@ -67,7 +67,8 @@ off-site copy exits non-zero and must alert.
|
|||||||
|
|
||||||
## Clean-environment restore rehearsal
|
## Clean-environment restore rehearsal
|
||||||
|
|
||||||
Use an isolated host/project with empty volumes and an empty PostgreSQL database.
|
Use an isolated host/project with an empty `deploy/docker/data/` directory and
|
||||||
|
an empty PostgreSQL database.
|
||||||
Do not point this procedure at the live project.
|
Do not point this procedure at the live project.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ All commands run from the repository root. Set `COMPOSE='docker compose -f deplo
|
|||||||
## Install and preflight
|
## Install and preflight
|
||||||
|
|
||||||
- **Owner:** release engineer; SRE on-call approves the target host.
|
- **Owner:** release engineer; SRE on-call approves the target host.
|
||||||
- **Prerequisites:** Docker Compose 2.26+, immutable GoChat/Connector digests, external backup mount, untracked backup passphrase and Alertmanager HTTPS webhook files. For external PostgreSQL, set one complete `GOCHAT_DATABASE_DSN` with exactly one `sslmode=verify-ca|verify-full`. When using a private CA or client certificate, keep the DSN paths fixed at `/run/secrets/external-db-ca.crt`, `/run/secrets/external-db-client.crt`, and `/run/secrets/external-db-client.key`; set the three corresponding untracked host-file variables plus `GOCHAT_DATABASE_TLS_GID`, assign that group to the files, and grant group read permission (`0640` for the key). Compose mounts the files read-only and adds the group to every database client.
|
- **Prerequisites:** Docker Compose 2.26+, immutable GoChat/Connector digests, external backup mount, untracked backup passphrase and Alertmanager HTTPS webhook files. Create the required `deploy/docker/data/` subdirectories before startup. For external PostgreSQL, set one complete `GOCHAT_DATABASE_DSN` with exactly one `sslmode`: use `disable` only on a trusted private network, or use `verify-ca`/`verify-full` for TLS. Redis may use unauthenticated `redis://` only on a trusted private Docker network; use authenticated `rediss://` when traffic crosses an untrusted network. When PostgreSQL uses a private CA or client certificate, keep the DSN paths fixed at `/run/secrets/external-db-ca.crt`, `/run/secrets/external-db-client.crt`, and `/run/secrets/external-db-client.key`; set the three corresponding untracked host-file variables plus `GOCHAT_DATABASE_TLS_GID`, assign that group to the files, and grant group read permission (`0640` for the key). Compose mounts the files read-only and adds the group to every database client.
|
||||||
- **Commands:** `deploy/docker/preflight.sh .env`; then `$COMPOSE config --quiet`; then `$COMPOSE --profile ops run --rm migrate`; finally `$COMPOSE up -d --wait`.
|
- **Commands:** `deploy/docker/preflight.sh .env`; then `$COMPOSE config --quiet`; then `$COMPOSE --profile ops run --rm migrate`; finally `$COMPOSE up -d --wait`.
|
||||||
- **Success / failure:** success means preflight exits 0, every non-profile service is healthy/running, and rendered `gochat`, `migrate`, `backup`, `restore`, and `postgres-exporter` configuration uses the same database DSN; any mutable image, missing secret file, DSN mismatch, migration error, or unhealthy service is failure.
|
- **Success / failure:** success means preflight exits 0, every non-profile service is healthy/running, and rendered `gochat`, `migrate`, `backup`, `restore`, and `postgres-exporter` configuration uses the same database DSN; any mutable image, missing secret file, DSN mismatch, migration error, or unhealthy service is failure.
|
||||||
- **Rollback:** `$COMPOSE down` leaves named data volumes intact; if migration ran, follow `01-rolling-upgrade.md` and restore the preflight backup instead of running schema down.
|
- **Rollback:** `$COMPOSE down` leaves `deploy/docker/data/` intact; if migration ran, follow `01-rolling-upgrade.md` and restore the preflight backup instead of running schema down.
|
||||||
- **Drill cadence:** before every release and quarterly on a clean host.
|
- **Drill cadence:** before every release and quarterly on a clean host.
|
||||||
|
|
||||||
## TLS and reverse proxy
|
## TLS and reverse proxy
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Chatwoot Route Source Declarations
|
# Chatwoot Route Source Declarations
|
||||||
|
|
||||||
Source: `docs/chatwoot/config/routes.rb`
|
Source: `../docs/chatwoot/config/routes.rb`
|
||||||
|
|
||||||
Ruby is unavailable in the current workspace, so this file records static route DSL declarations with source lines. It is a repeatable fallback until `bin/rails routes` can run.
|
Ruby is unavailable in the current workspace, so this file records static route DSL declarations with source lines. It is a repeatable fallback until `bin/rails routes` can run.
|
||||||
|
|
||||||
|
|||||||
@@ -21,11 +21,13 @@ DELETE /api/v1/accounts/:account_id/captain/assistants/:assistant_id
|
|||||||
DELETE /api/v1/accounts/:account_id/captain/assistants/:assistant_id/documents/:document_id
|
DELETE /api/v1/accounts/:account_id/captain/assistants/:assistant_id/documents/:document_id
|
||||||
DELETE /api/v1/accounts/:account_id/captain/assistants/:assistant_id/inboxes/:inbox_id
|
DELETE /api/v1/accounts/:account_id/captain/assistants/:assistant_id/inboxes/:inbox_id
|
||||||
DELETE /api/v1/accounts/:account_id/captain/assistants/:assistant_id/scenarios/:scenario_id
|
DELETE /api/v1/accounts/:account_id/captain/assistants/:assistant_id/scenarios/:scenario_id
|
||||||
|
DELETE /api/v1/accounts/:account_id/captain/assistants/:assistant_id/skills/:skill_id
|
||||||
DELETE /api/v1/accounts/:account_id/captain/auto_reply_rules/:rule_id
|
DELETE /api/v1/accounts/:account_id/captain/auto_reply_rules/:rule_id
|
||||||
DELETE /api/v1/accounts/:account_id/captain/copilot_threads/:thread_id
|
DELETE /api/v1/accounts/:account_id/captain/copilot_threads/:thread_id
|
||||||
DELETE /api/v1/accounts/:account_id/captain/custom_tools/:tool_id
|
DELETE /api/v1/accounts/:account_id/captain/custom_tools/:tool_id
|
||||||
DELETE /api/v1/accounts/:account_id/captain/documents/:document_id
|
DELETE /api/v1/accounts/:account_id/captain/documents/:document_id
|
||||||
DELETE /api/v1/accounts/:account_id/captain/scenarios/:scenario_id
|
DELETE /api/v1/accounts/:account_id/captain/scenarios/:scenario_id
|
||||||
|
DELETE /api/v1/accounts/:account_id/captain/skills/:skill_id
|
||||||
DELETE /api/v1/accounts/:account_id/channels/facebook_channel/:fb_id
|
DELETE /api/v1/accounts/:account_id/channels/facebook_channel/:fb_id
|
||||||
DELETE /api/v1/accounts/:account_id/companies/:company_id
|
DELETE /api/v1/accounts/:account_id/companies/:company_id
|
||||||
DELETE /api/v1/accounts/:account_id/companies/:company_id/avatar
|
DELETE /api/v1/accounts/:account_id/companies/:company_id/avatar
|
||||||
@@ -38,6 +40,7 @@ DELETE /api/v1/accounts/:account_id/contacts/:contact_id/custom_attributes
|
|||||||
DELETE /api/v1/accounts/:account_id/contacts/:contact_id/custom_attributes/:attribute_name
|
DELETE /api/v1/accounts/:account_id/contacts/:contact_id/custom_attributes/:attribute_name
|
||||||
DELETE /api/v1/accounts/:account_id/contacts/:contact_id/notes/:note_id
|
DELETE /api/v1/accounts/:account_id/contacts/:contact_id/notes/:note_id
|
||||||
DELETE /api/v1/accounts/:account_id/conversations/:conversation_id
|
DELETE /api/v1/accounts/:account_id/conversations/:conversation_id
|
||||||
|
DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/ai_takeover
|
||||||
DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/custom_attributes/:attribute_name
|
DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/custom_attributes/:attribute_name
|
||||||
DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages
|
DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages
|
||||||
DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages/:draft_id
|
DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages/:draft_id
|
||||||
@@ -96,8 +99,6 @@ DELETE /api/v1/accounts/:account_id/portals/:portal_id/categories/:category_id
|
|||||||
DELETE /api/v1/accounts/:account_id/portals/:portal_id/folders/:folder_id
|
DELETE /api/v1/accounts/:account_id/portals/:portal_id/folders/:folder_id
|
||||||
DELETE /api/v1/accounts/:account_id/portals/:portal_id/logo
|
DELETE /api/v1/accounts/:account_id/portals/:portal_id/logo
|
||||||
DELETE /api/v1/accounts/:account_id/portals/:portal_id/members/:member_id
|
DELETE /api/v1/accounts/:account_id/portals/:portal_id/members/:member_id
|
||||||
DELETE /api/v1/accounts/:account_id/saml_settings
|
|
||||||
DELETE /api/v1/accounts/:account_id/saml_settings/:id
|
|
||||||
DELETE /api/v1/accounts/:account_id/sla_policies/:id
|
DELETE /api/v1/accounts/:account_id/sla_policies/:id
|
||||||
DELETE /api/v1/accounts/:account_id/sla_policies/:id/inboxes/:inbox_id
|
DELETE /api/v1/accounts/:account_id/sla_policies/:id/inboxes/:inbox_id
|
||||||
DELETE /api/v1/accounts/:account_id/tags/:tag_id
|
DELETE /api/v1/accounts/:account_id/tags/:tag_id
|
||||||
@@ -113,8 +114,6 @@ DELETE /api/v1/notification_subscriptions/:identifier
|
|||||||
DELETE /api/v1/notifications/:id
|
DELETE /api/v1/notifications/:id
|
||||||
DELETE /api/v1/notifications/destroy_all
|
DELETE /api/v1/notifications/destroy_all
|
||||||
DELETE /api/v1/profile/avatar
|
DELETE /api/v1/profile/avatar
|
||||||
DELETE /api/v1/profile/mfa
|
|
||||||
DELETE /api/v1/profile/mfa/
|
|
||||||
DELETE /api/v1/profile/sessions/:id
|
DELETE /api/v1/profile/sessions/:id
|
||||||
DELETE /api/v1/push_subscriptions/:id
|
DELETE /api/v1/push_subscriptions/:id
|
||||||
DELETE /api/v1/widget/labels/:label_id
|
DELETE /api/v1/widget/labels/:label_id
|
||||||
@@ -128,6 +127,7 @@ DELETE /platform/api/v1/apps/:id/permissibles/:permissible_id
|
|||||||
DELETE /platform/api/v1/banners/:id
|
DELETE /platform/api/v1/banners/:id
|
||||||
DELETE /platform/api/v1/installation_configs/:id
|
DELETE /platform/api/v1/installation_configs/:id
|
||||||
DELETE /platform/api/v1/users/:id
|
DELETE /platform/api/v1/users/:id
|
||||||
|
GET /
|
||||||
GET /.well-known/apple-app-site-association
|
GET /.well-known/apple-app-site-association
|
||||||
GET /.well-known/assetlinks.json
|
GET /.well-known/assetlinks.json
|
||||||
GET /.well-known/cf-custom-hostname-challenge/:id
|
GET /.well-known/cf-custom-hostname-challenge/:id
|
||||||
@@ -178,6 +178,7 @@ GET /api/v1/accounts/:account_id/canned_responses
|
|||||||
GET /api/v1/accounts/:account_id/canned_responses/
|
GET /api/v1/accounts/:account_id/canned_responses/
|
||||||
GET /api/v1/accounts/:account_id/canned_responses/:id
|
GET /api/v1/accounts/:account_id/canned_responses/:id
|
||||||
GET /api/v1/accounts/:account_id/canned_responses/search
|
GET /api/v1/accounts/:account_id/canned_responses/search
|
||||||
|
GET /api/v1/accounts/:account_id/captain/assistant_responses
|
||||||
GET /api/v1/accounts/:account_id/captain/assistant_responses/
|
GET /api/v1/accounts/:account_id/captain/assistant_responses/
|
||||||
GET /api/v1/accounts/:account_id/captain/assistant_responses/:response_id
|
GET /api/v1/accounts/:account_id/captain/assistant_responses/:response_id
|
||||||
GET /api/v1/accounts/:account_id/captain/assistants
|
GET /api/v1/accounts/:account_id/captain/assistants
|
||||||
@@ -210,6 +211,8 @@ GET /api/v1/accounts/:account_id/captain/preferences
|
|||||||
GET /api/v1/accounts/:account_id/captain/preferences/
|
GET /api/v1/accounts/:account_id/captain/preferences/
|
||||||
GET /api/v1/accounts/:account_id/captain/scenarios/
|
GET /api/v1/accounts/:account_id/captain/scenarios/
|
||||||
GET /api/v1/accounts/:account_id/captain/scenarios/:scenario_id
|
GET /api/v1/accounts/:account_id/captain/scenarios/:scenario_id
|
||||||
|
GET /api/v1/accounts/:account_id/captain/skills
|
||||||
|
GET /api/v1/accounts/:account_id/captain/skills/:skill_id
|
||||||
GET /api/v1/accounts/:account_id/captain/tasks/follow_up
|
GET /api/v1/accounts/:account_id/captain/tasks/follow_up
|
||||||
GET /api/v1/accounts/:account_id/captain/tasks/label_suggestion
|
GET /api/v1/accounts/:account_id/captain/tasks/label_suggestion
|
||||||
GET /api/v1/accounts/:account_id/channels/facebook_channel/:fb_id
|
GET /api/v1/accounts/:account_id/channels/facebook_channel/:fb_id
|
||||||
@@ -265,8 +268,10 @@ GET /api/v1/accounts/:account_id/csat_survey_responses
|
|||||||
GET /api/v1/accounts/:account_id/csat_survey_responses/
|
GET /api/v1/accounts/:account_id/csat_survey_responses/
|
||||||
GET /api/v1/accounts/:account_id/csat_survey_responses/download
|
GET /api/v1/accounts/:account_id/csat_survey_responses/download
|
||||||
GET /api/v1/accounts/:account_id/csat_survey_responses/metrics
|
GET /api/v1/accounts/:account_id/csat_survey_responses/metrics
|
||||||
|
GET /api/v1/accounts/:account_id/custom_attribute_definitions
|
||||||
GET /api/v1/accounts/:account_id/custom_attribute_definitions/
|
GET /api/v1/accounts/:account_id/custom_attribute_definitions/
|
||||||
GET /api/v1/accounts/:account_id/custom_attribute_definitions/:id
|
GET /api/v1/accounts/:account_id/custom_attribute_definitions/:id
|
||||||
|
GET /api/v1/accounts/:account_id/custom_filters
|
||||||
GET /api/v1/accounts/:account_id/custom_filters/
|
GET /api/v1/accounts/:account_id/custom_filters/
|
||||||
GET /api/v1/accounts/:account_id/custom_filters/:id
|
GET /api/v1/accounts/:account_id/custom_filters/:id
|
||||||
GET /api/v1/accounts/:account_id/custom_roles
|
GET /api/v1/accounts/:account_id/custom_roles
|
||||||
@@ -387,8 +392,6 @@ GET /api/v1/accounts/:account_id/reports/labels
|
|||||||
GET /api/v1/accounts/:account_id/reports/outgoing_messages_count
|
GET /api/v1/accounts/:account_id/reports/outgoing_messages_count
|
||||||
GET /api/v1/accounts/:account_id/reports/summary
|
GET /api/v1/accounts/:account_id/reports/summary
|
||||||
GET /api/v1/accounts/:account_id/reports/teams
|
GET /api/v1/accounts/:account_id/reports/teams
|
||||||
GET /api/v1/accounts/:account_id/saml_settings
|
|
||||||
GET /api/v1/accounts/:account_id/saml_settings/:id
|
|
||||||
GET /api/v1/accounts/:account_id/search
|
GET /api/v1/accounts/:account_id/search
|
||||||
GET /api/v1/accounts/:account_id/search/articles
|
GET /api/v1/accounts/:account_id/search/articles
|
||||||
GET /api/v1/accounts/:account_id/search/contacts
|
GET /api/v1/accounts/:account_id/search/contacts
|
||||||
@@ -423,9 +426,8 @@ GET /api/v1/accounts/:account_id/webhooks/:webhook_id
|
|||||||
GET /api/v1/accounts/:account_id/whatsapp_calls/:id
|
GET /api/v1/accounts/:account_id/whatsapp_calls/:id
|
||||||
GET /api/v1/accounts/all
|
GET /api/v1/accounts/all
|
||||||
GET /api/v1/auth/confirm_email
|
GET /api/v1/auth/confirm_email
|
||||||
GET /api/v1/auth/mfa/status
|
GET /api/v1/connector/shangwutong/inboxes
|
||||||
GET /api/v1/auth/oauth/authorize
|
GET /api/v1/connector/shangwutong/inboxes/:inbox_id
|
||||||
GET /api/v1/ldap/config
|
|
||||||
GET /api/v1/notifications
|
GET /api/v1/notifications
|
||||||
GET /api/v1/notifications/unread_count
|
GET /api/v1/notifications/unread_count
|
||||||
GET /api/v1/oidc/authorize
|
GET /api/v1/oidc/authorize
|
||||||
@@ -433,17 +435,13 @@ GET /api/v1/oidc/callback
|
|||||||
GET /api/v1/oidc/config
|
GET /api/v1/oidc/config
|
||||||
GET /api/v1/oidc/discovery
|
GET /api/v1/oidc/discovery
|
||||||
GET /api/v1/profile
|
GET /api/v1/profile
|
||||||
GET /api/v1/profile/mfa
|
|
||||||
GET /api/v1/profile/mfa/
|
|
||||||
GET /api/v1/profile/sessions
|
GET /api/v1/profile/sessions
|
||||||
GET /api/v1/push_subscriptions
|
GET /api/v1/push_subscriptions
|
||||||
GET /api/v1/saml/login
|
|
||||||
GET /api/v1/saml/metadata
|
|
||||||
GET /api/v1/saml/slo
|
|
||||||
GET /api/v1/sso_sessions/:session_id
|
GET /api/v1/sso_sessions/:session_id
|
||||||
GET /api/v1/sso_sessions/user/:user_id
|
GET /api/v1/sso_sessions/user/:user_id
|
||||||
GET /api/v1/sso_sessions/user/:user_id/count
|
GET /api/v1/sso_sessions/user/:user_id/count
|
||||||
GET /api/v1/widget/campaigns
|
GET /api/v1/widget/campaigns
|
||||||
|
GET /api/v1/widget/config
|
||||||
GET /api/v1/widget/contact
|
GET /api/v1/widget/contact
|
||||||
GET /api/v1/widget/conversations
|
GET /api/v1/widget/conversations
|
||||||
GET /api/v1/widget/conversations/toggle_status
|
GET /api/v1/widget/conversations/toggle_status
|
||||||
@@ -493,7 +491,6 @@ GET /hc/:slug/:locale/categories/:category_slug/articles.json
|
|||||||
GET /hc/:slug/:locale/search
|
GET /hc/:slug/:locale/search
|
||||||
GET /hc/:slug/articles/:article_slug
|
GET /hc/:slug/articles/:article_slug
|
||||||
GET /hc/:slug/sitemap.xml
|
GET /hc/:slug/sitemap.xml
|
||||||
GET /health
|
|
||||||
GET /instagram/callback
|
GET /instagram/callback
|
||||||
GET /linear/callback
|
GET /linear/callback
|
||||||
GET /microsoft/callback
|
GET /microsoft/callback
|
||||||
@@ -526,10 +523,10 @@ GET /public/api/v1/inboxes/:inbox_id/contacts/:contact_id
|
|||||||
GET /public/api/v1/inboxes/:inbox_id/contacts/:contact_id/conversations
|
GET /public/api/v1/inboxes/:inbox_id/contacts/:contact_id/conversations
|
||||||
GET /public/api/v1/inboxes/:inbox_id/contacts/:contact_id/conversations/:conversation_id
|
GET /public/api/v1/inboxes/:inbox_id/contacts/:contact_id/conversations/:conversation_id
|
||||||
GET /public/api/v1/inboxes/:inbox_id/contacts/:contact_id/conversations/:conversation_id/messages
|
GET /public/api/v1/inboxes/:inbox_id/contacts/:contact_id/conversations/:conversation_id/messages
|
||||||
|
GET /runtime-config.js
|
||||||
GET /shopify/callback
|
GET /shopify/callback
|
||||||
GET /tiktok/callback
|
GET /tiktok/callback
|
||||||
GET /twitter/callback
|
GET /twitter/callback
|
||||||
GET /webhooks/fake/:identifier
|
|
||||||
GET /webhooks/instagram
|
GET /webhooks/instagram
|
||||||
GET /webhooks/tiktok/:business_id
|
GET /webhooks/tiktok/:business_id
|
||||||
GET /webhooks/twitter
|
GET /webhooks/twitter
|
||||||
@@ -601,6 +598,7 @@ PATCH /api/v1/accounts/:account_id/teams/:team_id
|
|||||||
PATCH /api/v1/accounts/:account_id/teams/:team_id/team_members
|
PATCH /api/v1/accounts/:account_id/teams/:team_id/team_members
|
||||||
PATCH /api/v1/accounts/:account_id/teams/:team_id/team_members/
|
PATCH /api/v1/accounts/:account_id/teams/:team_id/team_members/
|
||||||
PATCH /api/v1/accounts/:account_id/webhooks/:webhook_id
|
PATCH /api/v1/accounts/:account_id/webhooks/:webhook_id
|
||||||
|
PATCH /api/v1/connector/shangwutong/inboxes/:inbox_id/contacts/:source_id
|
||||||
PATCH /api/v1/profile
|
PATCH /api/v1/profile
|
||||||
PATCH /api/v1/widget/contact
|
PATCH /api/v1/widget/contact
|
||||||
PATCH /api/v1/widget/contact/set_user
|
PATCH /api/v1/widget/contact/set_user
|
||||||
@@ -653,6 +651,7 @@ POST /api/v1/accounts/:account_id/campaigns/:campaign_id/start
|
|||||||
POST /api/v1/accounts/:account_id/campaigns/:campaign_id/stop
|
POST /api/v1/accounts/:account_id/campaigns/:campaign_id/stop
|
||||||
POST /api/v1/accounts/:account_id/canned_responses
|
POST /api/v1/accounts/:account_id/canned_responses
|
||||||
POST /api/v1/accounts/:account_id/canned_responses/
|
POST /api/v1/accounts/:account_id/canned_responses/
|
||||||
|
POST /api/v1/accounts/:account_id/captain/assistant_responses
|
||||||
POST /api/v1/accounts/:account_id/captain/assistant_responses/
|
POST /api/v1/accounts/:account_id/captain/assistant_responses/
|
||||||
POST /api/v1/accounts/:account_id/captain/assistant_responses/process
|
POST /api/v1/accounts/:account_id/captain/assistant_responses/process
|
||||||
POST /api/v1/accounts/:account_id/captain/assistants
|
POST /api/v1/accounts/:account_id/captain/assistants
|
||||||
@@ -661,6 +660,7 @@ POST /api/v1/accounts/:account_id/captain/assistants/:assistant_id/documents/
|
|||||||
POST /api/v1/accounts/:account_id/captain/assistants/:assistant_id/inboxes
|
POST /api/v1/accounts/:account_id/captain/assistants/:assistant_id/inboxes
|
||||||
POST /api/v1/accounts/:account_id/captain/assistants/:assistant_id/playground
|
POST /api/v1/accounts/:account_id/captain/assistants/:assistant_id/playground
|
||||||
POST /api/v1/accounts/:account_id/captain/assistants/:assistant_id/scenarios/
|
POST /api/v1/accounts/:account_id/captain/assistants/:assistant_id/scenarios/
|
||||||
|
POST /api/v1/accounts/:account_id/captain/assistants/:assistant_id/skills/:skill_id
|
||||||
POST /api/v1/accounts/:account_id/captain/auto_reply_rules
|
POST /api/v1/accounts/:account_id/captain/auto_reply_rules
|
||||||
POST /api/v1/accounts/:account_id/captain/auto_reply_rules/
|
POST /api/v1/accounts/:account_id/captain/auto_reply_rules/
|
||||||
POST /api/v1/accounts/:account_id/captain/auto_reply_rules/evaluate
|
POST /api/v1/accounts/:account_id/captain/auto_reply_rules/evaluate
|
||||||
@@ -686,6 +686,7 @@ POST /api/v1/accounts/:account_id/captain/message_reports
|
|||||||
POST /api/v1/accounts/:account_id/captain/rag/index/:response_id
|
POST /api/v1/accounts/:account_id/captain/rag/index/:response_id
|
||||||
POST /api/v1/accounts/:account_id/captain/rag/query
|
POST /api/v1/accounts/:account_id/captain/rag/query
|
||||||
POST /api/v1/accounts/:account_id/captain/scenarios/
|
POST /api/v1/accounts/:account_id/captain/scenarios/
|
||||||
|
POST /api/v1/accounts/:account_id/captain/skills
|
||||||
POST /api/v1/accounts/:account_id/captain/tasks/follow_up
|
POST /api/v1/accounts/:account_id/captain/tasks/follow_up
|
||||||
POST /api/v1/accounts/:account_id/captain/tasks/label_suggestion
|
POST /api/v1/accounts/:account_id/captain/tasks/label_suggestion
|
||||||
POST /api/v1/accounts/:account_id/captain/tasks/reply_suggestion
|
POST /api/v1/accounts/:account_id/captain/tasks/reply_suggestion
|
||||||
@@ -720,6 +721,7 @@ POST /api/v1/accounts/:account_id/contacts/import
|
|||||||
POST /api/v1/accounts/:account_id/contacts/merge
|
POST /api/v1/accounts/:account_id/contacts/merge
|
||||||
POST /api/v1/accounts/:account_id/conversations
|
POST /api/v1/accounts/:account_id/conversations
|
||||||
POST /api/v1/accounts/:account_id/conversations/
|
POST /api/v1/accounts/:account_id/conversations/
|
||||||
|
POST /api/v1/accounts/:account_id/conversations/:conversation_id/ai_takeover
|
||||||
POST /api/v1/accounts/:account_id/conversations/:conversation_id/assign
|
POST /api/v1/accounts/:account_id/conversations/:conversation_id/assign
|
||||||
POST /api/v1/accounts/:account_id/conversations/:conversation_id/assignments
|
POST /api/v1/accounts/:account_id/conversations/:conversation_id/assignments
|
||||||
POST /api/v1/accounts/:account_id/conversations/:conversation_id/custom_attributes
|
POST /api/v1/accounts/:account_id/conversations/:conversation_id/custom_attributes
|
||||||
@@ -744,7 +746,9 @@ POST /api/v1/accounts/:account_id/conversations/:conversation_id/update_last_see
|
|||||||
POST /api/v1/accounts/:account_id/conversations/:conversation_id/whatsapp_calls/
|
POST /api/v1/accounts/:account_id/conversations/:conversation_id/whatsapp_calls/
|
||||||
POST /api/v1/accounts/:account_id/conversations/filter
|
POST /api/v1/accounts/:account_id/conversations/filter
|
||||||
POST /api/v1/accounts/:account_id/csat_survey_responses/:id/update_review_notes
|
POST /api/v1/accounts/:account_id/csat_survey_responses/:id/update_review_notes
|
||||||
|
POST /api/v1/accounts/:account_id/custom_attribute_definitions
|
||||||
POST /api/v1/accounts/:account_id/custom_attribute_definitions/
|
POST /api/v1/accounts/:account_id/custom_attribute_definitions/
|
||||||
|
POST /api/v1/accounts/:account_id/custom_filters
|
||||||
POST /api/v1/accounts/:account_id/custom_filters/
|
POST /api/v1/accounts/:account_id/custom_filters/
|
||||||
POST /api/v1/accounts/:account_id/custom_roles
|
POST /api/v1/accounts/:account_id/custom_roles
|
||||||
POST /api/v1/accounts/:account_id/custom_roles/
|
POST /api/v1/accounts/:account_id/custom_roles/
|
||||||
@@ -829,9 +833,6 @@ POST /api/v1/accounts/:account_id/portals/:portal_id/categories/reorder
|
|||||||
POST /api/v1/accounts/:account_id/portals/:portal_id/folders/
|
POST /api/v1/accounts/:account_id/portals/:portal_id/folders/
|
||||||
POST /api/v1/accounts/:account_id/portals/:portal_id/members/
|
POST /api/v1/accounts/:account_id/portals/:portal_id/members/
|
||||||
POST /api/v1/accounts/:account_id/portals/:portal_id/send_instructions
|
POST /api/v1/accounts/:account_id/portals/:portal_id/send_instructions
|
||||||
POST /api/v1/accounts/:account_id/saml_settings
|
|
||||||
POST /api/v1/accounts/:account_id/saml_settings/
|
|
||||||
POST /api/v1/accounts/:account_id/saml_settings/:id/toggle_active
|
|
||||||
POST /api/v1/accounts/:account_id/sla_policies
|
POST /api/v1/accounts/:account_id/sla_policies
|
||||||
POST /api/v1/accounts/:account_id/sla_policies/:id/inboxes
|
POST /api/v1/accounts/:account_id/sla_policies/:id/inboxes
|
||||||
POST /api/v1/accounts/:account_id/tags/
|
POST /api/v1/accounts/:account_id/tags/
|
||||||
@@ -856,32 +857,19 @@ POST /api/v1/accounts/:account_id/whatsapp_calls/:id/terminate
|
|||||||
POST /api/v1/accounts/:account_id/whatsapp_calls/:id/upload_recording
|
POST /api/v1/accounts/:account_id/whatsapp_calls/:id/upload_recording
|
||||||
POST /api/v1/accounts/:account_id/whatsapp_calls/initiate
|
POST /api/v1/accounts/:account_id/whatsapp_calls/initiate
|
||||||
POST /api/v1/auth/login
|
POST /api/v1/auth/login
|
||||||
POST /api/v1/auth/login/mfa
|
|
||||||
POST /api/v1/auth/mfa/backup_codes
|
|
||||||
POST /api/v1/auth/mfa/disable
|
|
||||||
POST /api/v1/auth/mfa/enable
|
|
||||||
POST /api/v1/auth/mfa/verify
|
|
||||||
POST /api/v1/auth/oauth/callback
|
|
||||||
POST /api/v1/auth/refresh
|
POST /api/v1/auth/refresh
|
||||||
POST /api/v1/auth/reset_password
|
POST /api/v1/auth/reset_password
|
||||||
POST /api/v1/auth/switch_account
|
POST /api/v1/auth/switch_account
|
||||||
POST /api/v1/ldap/login
|
POST /api/v1/auth/ws_ticket
|
||||||
POST /api/v1/ldap/test
|
|
||||||
POST /api/v1/notification_subscriptions
|
POST /api/v1/notification_subscriptions
|
||||||
POST /api/v1/notifications/:id/snooze
|
POST /api/v1/notifications/:id/snooze
|
||||||
POST /api/v1/notifications/:id/unread
|
POST /api/v1/notifications/:id/unread
|
||||||
POST /api/v1/notifications/read_all
|
POST /api/v1/notifications/read_all
|
||||||
POST /api/v1/profile/auto_offline
|
POST /api/v1/profile/auto_offline
|
||||||
POST /api/v1/profile/availability
|
POST /api/v1/profile/availability
|
||||||
POST /api/v1/profile/mfa
|
|
||||||
POST /api/v1/profile/mfa/
|
|
||||||
POST /api/v1/profile/mfa/backup_codes
|
|
||||||
POST /api/v1/profile/mfa/verify
|
|
||||||
POST /api/v1/profile/resend_confirmation
|
POST /api/v1/profile/resend_confirmation
|
||||||
POST /api/v1/profile/reset_access_token
|
POST /api/v1/profile/reset_access_token
|
||||||
POST /api/v1/push_subscriptions
|
POST /api/v1/push_subscriptions
|
||||||
POST /api/v1/saml/acs
|
|
||||||
POST /api/v1/saml/slo
|
|
||||||
POST /api/v1/sso_sessions/:session_id/terminate
|
POST /api/v1/sso_sessions/:session_id/terminate
|
||||||
POST /api/v1/sso_sessions/user/:user_id/terminate_all
|
POST /api/v1/sso_sessions/user/:user_id/terminate_all
|
||||||
POST /api/v1/widget/config
|
POST /api/v1/widget/config
|
||||||
@@ -942,7 +930,6 @@ POST /twilio/voice/call/:phone
|
|||||||
POST /twilio/voice/conference_status/:phone
|
POST /twilio/voice/conference_status/:phone
|
||||||
POST /twilio/voice/recording_status/:phone
|
POST /twilio/voice/recording_status/:phone
|
||||||
POST /twilio/voice/status/:phone
|
POST /twilio/voice/status/:phone
|
||||||
POST /webhooks/fake/:identifier
|
|
||||||
POST /webhooks/instagram
|
POST /webhooks/instagram
|
||||||
POST /webhooks/line/:line_channel_id
|
POST /webhooks/line/:line_channel_id
|
||||||
POST /webhooks/shopify
|
POST /webhooks/shopify
|
||||||
@@ -983,6 +970,7 @@ PUT /api/v1/accounts/:account_id/captain/custom_tools/:tool_id
|
|||||||
PUT /api/v1/accounts/:account_id/captain/preferences
|
PUT /api/v1/accounts/:account_id/captain/preferences
|
||||||
PUT /api/v1/accounts/:account_id/captain/preferences/
|
PUT /api/v1/accounts/:account_id/captain/preferences/
|
||||||
PUT /api/v1/accounts/:account_id/captain/scenarios/:scenario_id
|
PUT /api/v1/accounts/:account_id/captain/scenarios/:scenario_id
|
||||||
|
PUT /api/v1/accounts/:account_id/captain/skills/:skill_id
|
||||||
PUT /api/v1/accounts/:account_id/companies/:company_id
|
PUT /api/v1/accounts/:account_id/companies/:company_id
|
||||||
PUT /api/v1/accounts/:account_id/contacts/:contact_id
|
PUT /api/v1/accounts/:account_id/contacts/:contact_id
|
||||||
PUT /api/v1/accounts/:account_id/contacts/:contact_id/notes/:note_id
|
PUT /api/v1/accounts/:account_id/contacts/:contact_id/notes/:note_id
|
||||||
@@ -1021,8 +1009,6 @@ PUT /api/v1/accounts/:account_id/portals/:portal_id/articles/:article_id
|
|||||||
PUT /api/v1/accounts/:account_id/portals/:portal_id/categories/:category_id
|
PUT /api/v1/accounts/:account_id/portals/:portal_id/categories/:category_id
|
||||||
PUT /api/v1/accounts/:account_id/portals/:portal_id/folders/:folder_id
|
PUT /api/v1/accounts/:account_id/portals/:portal_id/folders/:folder_id
|
||||||
PUT /api/v1/accounts/:account_id/portals/:portal_id/members/:member_id
|
PUT /api/v1/accounts/:account_id/portals/:portal_id/members/:member_id
|
||||||
PUT /api/v1/accounts/:account_id/saml_settings
|
|
||||||
PUT /api/v1/accounts/:account_id/saml_settings/:id
|
|
||||||
PUT /api/v1/accounts/:account_id/settings
|
PUT /api/v1/accounts/:account_id/settings
|
||||||
PUT /api/v1/accounts/:account_id/sla_policies/:id
|
PUT /api/v1/accounts/:account_id/sla_policies/:id
|
||||||
PUT /api/v1/accounts/:account_id/tags/:tag_id
|
PUT /api/v1/accounts/:account_id/tags/:tag_id
|
||||||
@@ -1031,7 +1017,8 @@ PUT /api/v1/accounts/:account_id/web_widgets/offline_messages/:offline_message_i
|
|||||||
PUT /api/v1/accounts/:account_id/web_widgets/offline_messages/:offline_message_id/dismiss
|
PUT /api/v1/accounts/:account_id/web_widgets/offline_messages/:offline_message_id/dismiss
|
||||||
PUT /api/v1/accounts/:account_id/webhooks/:webhook_id
|
PUT /api/v1/accounts/:account_id/webhooks/:webhook_id
|
||||||
PUT /api/v1/auth/reset_password
|
PUT /api/v1/auth/reset_password
|
||||||
PUT /api/v1/ldap/config
|
PUT /api/v1/connector/shangwutong/inboxes/:inbox_id/messages/:message_id/status
|
||||||
|
PUT /api/v1/connector/shangwutong/inboxes/:inbox_id/status
|
||||||
PUT /api/v1/notifications/:id
|
PUT /api/v1/notifications/:id
|
||||||
PUT /api/v1/oidc/config
|
PUT /api/v1/oidc/config
|
||||||
PUT /api/v1/profile
|
PUT /api/v1/profile
|
||||||
@@ -1051,4 +1038,4 @@ PUT /public/api/v1/csat_survey/:id
|
|||||||
PUT /public/api/v1/inboxes/:inbox_id/contacts/:contact_id
|
PUT /public/api/v1/inboxes/:inbox_id/contacts/:contact_id
|
||||||
PUT /public/api/v1/inboxes/:inbox_id/contacts/:contact_id/conversations/:conversation_id/messages/:message_id
|
PUT /public/api/v1/inboxes/:inbox_id/contacts/:contact_id/conversations/:conversation_id/messages/:message_id
|
||||||
PUT /widget/direct_uploads/:upload_uuid
|
PUT /widget/direct_uploads/:upload_uuid
|
||||||
TOTAL: 1053
|
TOTAL: 1040
|
||||||
|
|||||||
@@ -2,18 +2,22 @@
|
|||||||
|
|
||||||
Generated from:
|
Generated from:
|
||||||
|
|
||||||
- GoChat route dump: `docs/parity/gochat-routes.txt`
|
- GoChat route dump: `../docs/parity/gochat-routes.txt`
|
||||||
- Chatwoot route source: `docs/chatwoot/config/routes.rb`
|
- Chatwoot route source: `../docs/chatwoot/config/routes.rb`
|
||||||
|
|
||||||
This report covers tracked frontend-critical Chatwoot 4.15.1 routes from `docs/chatwoot/config/routes.rb`, including API v1 account routes, Captain/Copilot, assignment policies, widget/public APIs, and API v2 reports. Ruby is not installed in the workspace, so Chatwoot routes are sourced from static route declarations instead of `bin/rails routes`.
|
This report covers tracked frontend-critical Chatwoot 4.15.1 routes from `docs/chatwoot/config/routes.rb`, including API v1 account routes, Captain/Copilot, assignment policies, widget/public APIs, and API v2 reports. Ruby is not installed in the workspace, so Chatwoot routes are sourced from static route declarations instead of `bin/rails routes`.
|
||||||
|
|
||||||
Summary: 447 exact, 0 method-compatible, 9 parameter-compatible, 0 missing out of 456 tracked critical routes.
|
Summary: 443 exact, 0 method-compatible, 9 parameter-compatible, 5 missing out of 457 tracked critical routes.
|
||||||
|
|
||||||
## Missing Critical Routes
|
## Missing Critical Routes
|
||||||
|
|
||||||
| Method | Chatwoot Path | GoChat Match | Controller | Source | Status |
|
| Method | Chatwoot Path | GoChat Match | Controller | Source | Status |
|
||||||
| --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- |
|
||||||
| - | - | - | - | - | none |
|
| DELETE | `/api/v1/profile/mfa` | `-` | `api/v1/profile/mfa#destroy` | `routes.rb:433` | missing |
|
||||||
|
| GET | `/api/v1/profile/mfa` | `-` | `api/v1/profile/mfa#show` | `routes.rb:433` | missing |
|
||||||
|
| POST | `/api/v1/profile/mfa` | `-` | `api/v1/profile/mfa#create` | `routes.rb:433` | missing |
|
||||||
|
| POST | `/api/v1/profile/mfa/backup_codes` | `-` | `api/v1/profile/mfa#backup_codes` | `routes.rb:435` | missing |
|
||||||
|
| POST | `/api/v1/profile/mfa/verify` | `-` | `api/v1/profile/mfa#verify` | `routes.rb:434` | missing |
|
||||||
|
|
||||||
## Method-Compatible Routes
|
## Method-Compatible Routes
|
||||||
|
|
||||||
@@ -92,9 +96,9 @@ These routes exist with equivalent method and path shape but different parameter
|
|||||||
| DELETE | `/api/v1/accounts/:account_id/webhooks/:webhook_id` | `/api/v1/accounts/:account_id/webhooks/:webhook_id` | `api/v1/accounts/webhooks#destroy` | `routes.rb:342` | exact |
|
| DELETE | `/api/v1/accounts/:account_id/webhooks/:webhook_id` | `/api/v1/accounts/:account_id/webhooks/:webhook_id` | `api/v1/accounts/webhooks#destroy` | `routes.rb:342` | exact |
|
||||||
| DELETE | `/api/v1/notification_subscriptions` | `/api/v1/notification_subscriptions` | `api/v1/notification_subscriptions#destroy` | `routes.rb:440` | exact |
|
| DELETE | `/api/v1/notification_subscriptions` | `/api/v1/notification_subscriptions` | `api/v1/notification_subscriptions#destroy` | `routes.rb:440` | exact |
|
||||||
| DELETE | `/api/v1/profile/avatar` | `/api/v1/profile/avatar` | `api/v1/profiles#avatar` | `routes.rb:422` | exact |
|
| DELETE | `/api/v1/profile/avatar` | `/api/v1/profile/avatar` | `api/v1/profiles#avatar` | `routes.rb:422` | exact |
|
||||||
| DELETE | `/api/v1/profile/mfa` | `/api/v1/profile/mfa` | `api/v1/profile/mfa#destroy` | `routes.rb:433` | exact |
|
|
||||||
| DELETE | `/api/v1/profile/sessions/:id` | `/api/v1/profile/sessions/:id` | `api/v1/profile/sessions#destroy` | `routes.rb:445` | exact |
|
| DELETE | `/api/v1/profile/sessions/:id` | `/api/v1/profile/sessions/:id` | `api/v1/profile/sessions#destroy` | `routes.rb:445` | exact |
|
||||||
| DELETE | `/api/v1/widget/labels/:label_id` | `/api/v1/widget/labels/:label_id` | `api/v1/widget/labels#destroy` | `routes.rb:464` | exact |
|
| DELETE | `/api/v1/widget/labels/:label_id` | `/api/v1/widget/labels/:label_id` | `api/v1/widget/labels#destroy` | `routes.rb:464` | exact |
|
||||||
|
| GET | `/` | `/` | `dashboard#index` | `routes.rb:17` | exact |
|
||||||
| GET | `/.well-known/apple-app-site-association` | `/.well-known/apple-app-site-association` | `apple_app#site_association` | `routes.rb:658` | exact |
|
| GET | `/.well-known/apple-app-site-association` | `/.well-known/apple-app-site-association` | `apple_app#site_association` | `routes.rb:658` | exact |
|
||||||
| GET | `/.well-known/assetlinks.json` | `/.well-known/assetlinks.json` | `android_app#assetlinks` | `routes.rb:657` | exact |
|
| GET | `/.well-known/assetlinks.json` | `/.well-known/assetlinks.json` | `android_app#assetlinks` | `routes.rb:657` | exact |
|
||||||
| GET | `/.well-known/cf-custom-hostname-challenge/:id` | `/.well-known/cf-custom-hostname-challenge/:id` | `custom_domains#verify` | `routes.rb:660` | exact |
|
| GET | `/.well-known/cf-custom-hostname-challenge/:id` | `/.well-known/cf-custom-hostname-challenge/:id` | `custom_domains#verify` | `routes.rb:660` | exact |
|
||||||
@@ -218,7 +222,6 @@ These routes exist with equivalent method and path shape but different parameter
|
|||||||
| GET | `/api/v1/accounts/:account_id/webhooks` | `/api/v1/accounts/:account_id/webhooks` | `api/v1/accounts/webhooks#index` | `routes.rb:342` | exact |
|
| GET | `/api/v1/accounts/:account_id/webhooks` | `/api/v1/accounts/:account_id/webhooks` | `api/v1/accounts/webhooks#index` | `routes.rb:342` | exact |
|
||||||
| GET | `/api/v1/accounts/:account_id/whatsapp_calls/:id` | `/api/v1/accounts/:account_id/whatsapp_calls/:id` | `api/v1/accounts/whatsapp_calls#show` | `routes.rb:237` | exact |
|
| GET | `/api/v1/accounts/:account_id/whatsapp_calls/:id` | `/api/v1/accounts/:account_id/whatsapp_calls/:id` | `api/v1/accounts/whatsapp_calls#show` | `routes.rb:237` | exact |
|
||||||
| GET | `/api/v1/profile` | `/api/v1/profile` | `api/v1/profiles#show` | `routes.rb:421` | exact |
|
| GET | `/api/v1/profile` | `/api/v1/profile` | `api/v1/profiles#show` | `routes.rb:421` | exact |
|
||||||
| GET | `/api/v1/profile/mfa` | `/api/v1/profile/mfa` | `api/v1/profile/mfa#show` | `routes.rb:433` | exact |
|
|
||||||
| GET | `/api/v1/profile/sessions` | `/api/v1/profile/sessions` | `api/v1/profile/sessions#index` | `routes.rb:445` | exact |
|
| GET | `/api/v1/profile/sessions` | `/api/v1/profile/sessions` | `api/v1/profile/sessions#index` | `routes.rb:445` | exact |
|
||||||
| GET | `/api/v1/widget/campaigns` | `/api/v1/widget/campaigns` | `api/v1/widget/campaigns#index` | `routes.rb:445` | exact |
|
| GET | `/api/v1/widget/campaigns` | `/api/v1/widget/campaigns` | `api/v1/widget/campaigns#index` | `routes.rb:445` | exact |
|
||||||
| GET | `/api/v1/widget/contact` | `/api/v1/widget/contact` | `api/v1/widget/contact#show` | `routes.rb:458` | exact |
|
| GET | `/api/v1/widget/contact` | `/api/v1/widget/contact` | `api/v1/widget/contact#show` | `routes.rb:458` | exact |
|
||||||
@@ -413,9 +416,6 @@ These routes exist with equivalent method and path shape but different parameter
|
|||||||
| POST | `/api/v1/notification_subscriptions` | `/api/v1/notification_subscriptions` | `api/v1/notification_subscriptions#create` | `routes.rb:440` | exact |
|
| POST | `/api/v1/notification_subscriptions` | `/api/v1/notification_subscriptions` | `api/v1/notification_subscriptions#create` | `routes.rb:440` | exact |
|
||||||
| POST | `/api/v1/profile/auto_offline` | `/api/v1/profile/auto_offline` | `api/v1/profiles#auto_offline` | `routes.rb:425` | exact |
|
| POST | `/api/v1/profile/auto_offline` | `/api/v1/profile/auto_offline` | `api/v1/profiles#auto_offline` | `routes.rb:425` | exact |
|
||||||
| POST | `/api/v1/profile/availability` | `/api/v1/profile/availability` | `api/v1/profiles#availability` | `routes.rb:424` | exact |
|
| POST | `/api/v1/profile/availability` | `/api/v1/profile/availability` | `api/v1/profiles#availability` | `routes.rb:424` | exact |
|
||||||
| POST | `/api/v1/profile/mfa` | `/api/v1/profile/mfa` | `api/v1/profile/mfa#create` | `routes.rb:433` | exact |
|
|
||||||
| POST | `/api/v1/profile/mfa/backup_codes` | `/api/v1/profile/mfa/backup_codes` | `api/v1/profile/mfa#backup_codes` | `routes.rb:435` | exact |
|
|
||||||
| POST | `/api/v1/profile/mfa/verify` | `/api/v1/profile/mfa/verify` | `api/v1/profile/mfa#verify` | `routes.rb:434` | exact |
|
|
||||||
| POST | `/api/v1/profile/resend_confirmation` | `/api/v1/profile/resend_confirmation` | `api/v1/profiles#resend_confirmation` | `routes.rb:427` | exact |
|
| POST | `/api/v1/profile/resend_confirmation` | `/api/v1/profile/resend_confirmation` | `api/v1/profiles#resend_confirmation` | `routes.rb:427` | exact |
|
||||||
| POST | `/api/v1/profile/reset_access_token` | `/api/v1/profile/reset_access_token` | `api/v1/profiles#reset_access_token` | `routes.rb:428` | exact |
|
| POST | `/api/v1/profile/reset_access_token` | `/api/v1/profile/reset_access_token` | `api/v1/profiles#reset_access_token` | `routes.rb:428` | exact |
|
||||||
| POST | `/api/v1/widget/config` | `/api/v1/widget/config` | `api/v1/widget/config#create` | `routes.rb:444` | exact |
|
| POST | `/api/v1/widget/config` | `/api/v1/widget/config` | `api/v1/widget/config#create` | `routes.rb:444` | exact |
|
||||||
|
|||||||
Reference in New Issue
Block a user