2019-11-29 06:59:40 -05:00
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
2017-02-17 05:57:19 -05:00
package api4
import (
2021-02-01 15:18:52 -05:00
"bytes"
2018-12-17 11:51:46 -05:00
"encoding/json"
2018-04-27 15:49:45 -04:00
"fmt"
2020-05-19 23:11:37 -04:00
"io"
2017-02-17 05:57:19 -05:00
"net/http"
2024-09-18 05:51:50 -04:00
"os"
2022-04-04 08:03:31 -04:00
"reflect"
2017-05-31 00:47:27 -04:00
"runtime"
2019-11-27 20:41:09 -05:00
"strconv"
2019-08-22 18:25:50 -04:00
"time"
2017-02-17 05:57:19 -05:00
2020-08-21 14:23:04 -04:00
"github.com/pkg/errors"
2023-06-11 01:24:35 -04:00
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog"
2025-01-13 14:23:09 -05:00
"github.com/mattermost/mattermost/server/public/utils"
2024-08-03 10:11:13 -04:00
"github.com/mattermost/mattermost/server/v8/config"
2023-06-11 01:24:35 -04:00
"github.com/mattermost/mattermost/server/v8/platform/services/cache"
"github.com/mattermost/mattermost/server/v8/platform/services/upgrader"
"github.com/mattermost/mattermost/server/v8/platform/shared/web"
2017-02-17 05:57:19 -05:00
)
2019-11-27 20:41:09 -05:00
const (
2023-09-05 14:04:14 -04:00
RedirectLocationCacheSize = 10000
RedirectLocationMaximumLength = 2100
RedirectLocationCacheExpiry = 1 * time . Hour
DefaultServerBusySeconds = 3600
MaxServerBusySeconds = 86400
2019-11-27 20:41:09 -05:00
)
2019-01-27 21:24:46 -05:00
2024-08-05 23:58:41 -04:00
var redirectLocationDataCache = cache . NewLRU ( & cache . CacheOptions {
2021-01-04 01:02:29 -05:00
Size : RedirectLocationCacheSize ,
2020-06-02 09:01:30 -04:00
} )
2019-01-27 21:24:46 -05:00
2017-09-22 13:54:27 -04:00
func ( api * API ) InitSystem ( ) {
2024-07-15 10:52:03 -04:00
api . BaseRoutes . System . Handle ( "/ping" , api . APIHandler ( getSystemPing ) ) . Methods ( http . MethodGet )
api . BaseRoutes . System . Handle ( "/timezones" , api . APISessionRequired ( getSupportedTimezones ) ) . Methods ( http . MethodGet )
api . BaseRoutes . APIRoot . Handle ( "/audits" , api . APISessionRequired ( getAudits ) ) . Methods ( http . MethodGet )
2024-11-08 07:57:06 -05:00
api . BaseRoutes . APIRoot . Handle ( "/notifications/test" , api . APISessionRequired ( testNotifications ) ) . Methods ( http . MethodPost )
2024-07-15 10:52:03 -04:00
api . BaseRoutes . APIRoot . Handle ( "/email/test" , api . APISessionRequired ( testEmail ) ) . Methods ( http . MethodPost )
api . BaseRoutes . APIRoot . Handle ( "/site_url/test" , api . APISessionRequired ( testSiteURL ) ) . Methods ( http . MethodPost )
api . BaseRoutes . APIRoot . Handle ( "/file/s3_test" , api . APISessionRequired ( testS3 ) ) . Methods ( http . MethodPost )
api . BaseRoutes . APIRoot . Handle ( "/database/recycle" , api . APISessionRequired ( databaseRecycle ) ) . Methods ( http . MethodPost )
api . BaseRoutes . APIRoot . Handle ( "/caches/invalidate" , api . APISessionRequired ( invalidateCaches ) ) . Methods ( http . MethodPost )
api . BaseRoutes . APIRoot . Handle ( "/logs" , api . APISessionRequired ( getLogs ) ) . Methods ( http . MethodGet )
api . BaseRoutes . APIRoot . Handle ( "/logs/download" , api . APISessionRequired ( downloadLogs ) ) . Methods ( http . MethodGet )
api . BaseRoutes . APIRoot . Handle ( "/logs/query" , api . APISessionRequired ( queryLogs ) ) . Methods ( http . MethodPost )
api . BaseRoutes . APIRoot . Handle ( "/logs" , api . APIHandler ( postLog ) ) . Methods ( http . MethodPost )
api . BaseRoutes . APIRoot . Handle ( "/analytics/old" , api . APISessionRequired ( getAnalytics ) ) . Methods ( http . MethodGet )
api . BaseRoutes . APIRoot . Handle ( "/latest_version" , api . APISessionRequired ( getLatestVersion ) ) . Methods ( http . MethodGet )
api . BaseRoutes . APIRoot . Handle ( "/redirect_location" , api . APISessionRequiredTrustRequester ( getRedirectLocation ) ) . Methods ( http . MethodGet )
api . BaseRoutes . APIRoot . Handle ( "/notifications/ack" , api . APISessionRequired ( pushNotificationAck ) ) . Methods ( http . MethodPost )
api . BaseRoutes . APIRoot . Handle ( "/server_busy" , api . APISessionRequired ( setServerBusy ) ) . Methods ( http . MethodPost )
api . BaseRoutes . APIRoot . Handle ( "/server_busy" , api . APISessionRequired ( getServerBusyExpires ) ) . Methods ( http . MethodGet )
api . BaseRoutes . APIRoot . Handle ( "/server_busy" , api . APISessionRequired ( clearServerBusy ) ) . Methods ( http . MethodDelete )
api . BaseRoutes . APIRoot . Handle ( "/upgrade_to_enterprise" , api . APISessionRequired ( upgradeToEnterprise ) ) . Methods ( http . MethodPost )
api . BaseRoutes . APIRoot . Handle ( "/upgrade_to_enterprise/status" , api . APISessionRequired ( upgradeToEnterpriseStatus ) ) . Methods ( http . MethodGet )
2025-08-18 10:40:48 -04:00
api . BaseRoutes . APIRoot . Handle ( "/upgrade_to_enterprise/allowed" , api . APISessionRequired ( isAllowedToUpgradeToEnterprise ) ) . Methods ( http . MethodGet )
2024-07-15 10:52:03 -04:00
api . BaseRoutes . APIRoot . Handle ( "/restart" , api . APISessionRequired ( restart ) ) . Methods ( http . MethodPost )
api . BaseRoutes . System . Handle ( "/notices/{team_id:[A-Za-z0-9]+}" , api . APISessionRequired ( getProductNotices ) ) . Methods ( http . MethodGet )
api . BaseRoutes . System . Handle ( "/notices/view" , api . APISessionRequired ( updateViewedProductNotices ) ) . Methods ( http . MethodPut )
api . BaseRoutes . System . Handle ( "/support_packet" , api . APISessionRequired ( generateSupportPacket ) ) . Methods ( http . MethodGet )
api . BaseRoutes . System . Handle ( "/onboarding/complete" , api . APISessionRequired ( getOnboarding ) ) . Methods ( http . MethodGet )
api . BaseRoutes . System . Handle ( "/onboarding/complete" , api . APISessionRequired ( completeOnboarding ) ) . Methods ( http . MethodPost )
api . BaseRoutes . System . Handle ( "/schema/version" , api . APISessionRequired ( getAppliedSchemaMigrations ) ) . Methods ( http . MethodGet )
2021-02-01 15:18:52 -05:00
}
func generateSupportPacket ( c * Context , w http . ResponseWriter , r * http . Request ) {
const FileMime = "application/zip"
2024-08-03 10:11:13 -04:00
// Support Packet generation is limited to system admins (MM-42271).
2025-04-10 15:22:03 -04:00
if ! c . App . SessionHasPermissionToAndNotRestrictedAdmin ( * c . AppContext . Session ( ) , model . PermissionManageSystem ) {
2022-03-09 11:42:08 -05:00
c . SetPermissionError ( model . PermissionManageSystem )
2021-02-01 15:18:52 -05:00
return
}
2026-01-29 13:29:55 -05:00
auditRec := c . MakeAuditRecord ( model . AuditEventGenerateSupportPacket , model . AuditStatusFail )
defer c . LogAuditRec ( auditRec )
2024-04-12 04:05:58 -04:00
// We support the existing API hence the logs are always included
// if nothing specified.
includeLogs := true
if r . FormValue ( "basic_server_logs" ) == "false" {
includeLogs = false
}
supportPacketOptions := & model . SupportPacketOptions {
IncludeLogs : includeLogs ,
PluginPackets : r . Form [ "plugin_packets" ] ,
}
2026-01-29 13:29:55 -05:00
auditRec . AddMeta ( "include_logs" , supportPacketOptions . IncludeLogs )
auditRec . AddMeta ( "plugin_packets" , supportPacketOptions . PluginPackets )
2021-02-01 15:18:52 -05:00
// Checking to see if the server has a e10 or e20 license (this feature is only permitted for servers with licenses)
2022-03-09 22:30:29 -05:00
if c . App . Channels ( ) . License ( ) == nil {
2021-02-01 15:18:52 -05:00
c . Err = model . NewAppError ( "Api4.generateSupportPacket" , "api.no_license" , nil , "" , http . StatusForbidden )
return
}
2024-04-12 04:05:58 -04:00
fileDatas := c . App . GenerateSupportPacket ( c . AppContext , supportPacketOptions )
2021-02-01 15:18:52 -05:00
now := time . Now ( )
2025-01-13 14:23:09 -05:00
outputZipFilename := supportPacketFileName ( now , c . App . License ( ) . Customer . Company )
2021-02-01 15:18:52 -05:00
2025-07-22 06:25:08 -04:00
// Create a buffer and write the zip file to it
buf := new ( bytes . Buffer )
err := c . App . WriteZipFile ( buf , fileDatas )
2021-02-01 15:18:52 -05:00
if err != nil {
2024-04-22 06:03:28 -04:00
c . Err = model . NewAppError ( "Api4.generateSupportPacket" , "api.unable_to_create_zip_file" , nil , "" , http . StatusForbidden ) . Wrap ( err )
2021-02-01 15:18:52 -05:00
return
}
2026-01-29 13:29:55 -05:00
auditRec . Success ( )
auditRec . AddMeta ( "filename" , outputZipFilename )
2025-06-10 05:39:06 -04:00
// Prevent caching so support packets are always fresh
w . Header ( ) . Set ( "Cache-Control" , "no-cache, no-store, must-revalidate" )
2025-07-22 06:25:08 -04:00
err = web . WriteStreamResponse ( w , buf , outputZipFilename , FileMime , true )
2025-06-10 05:39:06 -04:00
if err != nil {
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
}
2017-02-17 05:57:19 -05:00
}
2025-01-13 14:23:09 -05:00
// supportPacketFileName returns the ZIP file name in the format mm_support_packet_$CUSTOMER_NAME_YYYY-MM-DDTHH-MM.zip.
// Note that this filename is also being checked at the webapp, please update the
// regex within the commercial_support_modal.tsx file if the naming convention ever changes.
func supportPacketFileName ( now time . Time , customerName string ) string {
return fmt . Sprintf ( "mm_support_packet_%s_%s.zip" , utils . SanitizeFileName ( customerName ) , now . Format ( "2006-01-02T15-04" ) )
}
2017-02-17 05:57:19 -05:00
func getSystemPing ( c * Context , w http . ResponseWriter , r * http . Request ) {
2019-06-20 16:06:04 -04:00
reqs := c . App . Config ( ) . ClientRequirements
2017-05-31 00:47:27 -04:00
2024-09-18 05:51:50 -04:00
s := make ( map [ string ] any )
2021-07-12 14:05:36 -04:00
s [ model . STATUS ] = model . StatusOk
2019-06-20 16:06:04 -04:00
s [ "AndroidLatestVersion" ] = reqs . AndroidLatestVersion
s [ "AndroidMinVersion" ] = reqs . AndroidMinVersion
s [ "IosLatestVersion" ] = reqs . IosLatestVersion
s [ "IosMinVersion" ] = reqs . IosMinVersion
2017-08-28 12:22:54 -04:00
2020-09-30 00:41:22 -04:00
testflag := c . App . Config ( ) . FeatureFlags . TestFeature
if testflag != "off" {
s [ "TestFeatureFlag" ] = testflag
}
2019-06-20 16:06:04 -04:00
actualGoroutines := runtime . NumGoroutine ( )
if * c . App . Config ( ) . ServiceSettings . GoroutineHealthThreshold > 0 && actualGoroutines >= * c . App . Config ( ) . ServiceSettings . GoroutineHealthThreshold {
2023-10-11 11:34:49 -04:00
c . Logger . Warn ( "The number of running goroutines is over the health threshold" , mlog . Int ( "goroutines" , actualGoroutines ) , mlog . Int ( "health_threshold" , * c . App . Config ( ) . ServiceSettings . GoroutineHealthThreshold ) )
2021-07-12 14:05:36 -04:00
s [ model . STATUS ] = model . StatusUnhealthy
2019-06-20 16:06:04 -04:00
}
2017-05-31 00:47:27 -04:00
2019-06-20 16:06:04 -04:00
// Enhanced ping health check:
// If an extra form value is provided then perform extra health checks for
// database and file storage backends.
2023-10-19 11:31:09 -04:00
if r . FormValue ( "get_server_status" ) == "true" {
2019-06-20 16:06:04 -04:00
dbStatusKey := "database_status"
2021-07-12 14:05:36 -04:00
s [ dbStatusKey ] = model . StatusOk
2019-08-22 18:25:50 -04:00
2020-10-03 05:38:35 -04:00
writeErr := c . App . DBHealthCheckWrite ( )
2019-08-22 18:25:50 -04:00
if writeErr != nil {
2023-10-11 11:34:49 -04:00
c . Logger . Warn ( "Unable to write to database." , mlog . Err ( writeErr ) )
2021-07-12 14:05:36 -04:00
s [ dbStatusKey ] = model . StatusUnhealthy
s [ model . STATUS ] = model . StatusUnhealthy
2020-05-21 16:29:21 -04:00
}
2020-05-05 10:44:18 -04:00
2020-10-03 05:38:35 -04:00
writeErr = c . App . DBHealthCheckDelete ( )
2020-05-21 16:29:21 -04:00
if writeErr != nil {
2023-10-11 11:34:49 -04:00
c . Logger . Warn ( "Unable to remove ping health check value from database." , mlog . Err ( writeErr ) )
2021-07-12 14:05:36 -04:00
s [ dbStatusKey ] = model . StatusUnhealthy
s [ model . STATUS ] = model . StatusUnhealthy
2020-05-21 16:29:21 -04:00
}
2021-07-12 14:05:36 -04:00
if s [ dbStatusKey ] == model . StatusOk {
2023-10-31 06:00:22 -04:00
c . Logger . Debug ( "Able to write to database." )
2019-06-20 16:06:04 -04:00
}
filestoreStatusKey := "filestore_status"
2021-07-12 14:05:36 -04:00
s [ filestoreStatusKey ] = model . StatusOk
2021-03-02 08:37:21 -05:00
appErr := c . App . TestFileStoreConnection ( )
2020-12-20 06:53:07 -05:00
if appErr != nil {
2024-02-20 08:22:28 -05:00
c . Logger . Warn ( "Unable to test filestore connection." , mlog . Err ( appErr ) )
2021-07-12 14:05:36 -04:00
s [ filestoreStatusKey ] = model . StatusUnhealthy
s [ model . STATUS ] = model . StatusUnhealthy
2019-06-20 16:06:04 -04:00
}
2024-09-18 05:51:50 -04:00
if res , ok := s [ model . STATUS ] . ( string ) ; ok {
w . Header ( ) . Set ( model . STATUS , res )
}
if res , ok := s [ dbStatusKey ] . ( string ) ; ok {
w . Header ( ) . Set ( dbStatusKey , res )
}
if res , ok := s [ filestoreStatusKey ] . ( string ) ; ok {
w . Header ( ) . Set ( filestoreStatusKey , res )
}
// Checking if mattermost is running as root, if the user is system admin
if c . App . SessionHasPermissionTo ( * c . AppContext . Session ( ) , model . PermissionManageSystem ) {
s [ "root_status" ] = os . Geteuid ( ) == 0
}
2019-06-20 16:06:04 -04:00
}
2017-05-31 00:47:27 -04:00
2022-03-02 06:15:58 -05:00
if deviceID := r . FormValue ( "device_id" ) ; deviceID != "" {
2025-08-20 04:17:45 -04:00
s [ "CanReceiveNotifications" ] = c . App . SendTestPushNotification ( c . AppContext , deviceID )
2022-03-02 06:15:58 -05:00
}
2023-04-14 04:58:50 -04:00
s [ "ActiveSearchBackend" ] = c . App . ActiveSearchBackend ( )
2024-02-20 08:22:28 -05:00
if s [ model . STATUS ] != model . StatusOk && r . FormValue ( "use_rest_semantics" ) != "true" {
2017-05-31 00:47:27 -04:00
w . WriteHeader ( http . StatusInternalServerError )
}
2024-09-18 05:51:50 -04:00
2024-10-07 09:37:46 -04:00
if _ , err := w . Write ( model . ToJSON ( s ) ) ; err != nil {
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
}
2017-02-17 05:57:19 -05:00
}
2017-03-13 08:27:27 -04:00
2024-11-08 07:57:06 -05:00
func testNotifications ( c * Context , w http . ResponseWriter , r * http . Request ) {
_ , err := c . App . SendTestMessage ( c . AppContext , c . AppContext . Session ( ) . UserId )
if err != nil {
c . Err = err
return
}
ReturnStatusOK ( w )
}
2017-03-13 11:09:00 -04:00
func testEmail ( c * Context , w http . ResponseWriter , r * http . Request ) {
2022-08-10 04:56:58 -04:00
var cfg * model . Config
err := json . NewDecoder ( r . Body ) . Decode ( & cfg )
if err != nil {
c . Logger . Warn ( "Error decoding the config" , mlog . Err ( err ) )
}
2022-04-05 23:00:16 -04:00
if cfg == nil {
cfg = c . App . Config ( )
2022-04-04 08:03:31 -04:00
}
if checkHasNilFields ( & cfg . EmailSettings ) {
c . Err = model . NewAppError ( "testEmail" , "api.file.test_connection_email_settings_nil.app_error" , nil , "" , http . StatusBadRequest )
return
2017-06-14 08:56:56 -04:00
}
2017-03-13 11:09:00 -04:00
2025-04-10 15:22:03 -04:00
if ! c . App . SessionHasPermissionToAndNotRestrictedAdmin ( * c . AppContext . Session ( ) , model . PermissionTestEmail ) {
2021-07-12 14:05:36 -04:00
c . SetPermissionError ( model . PermissionTestEmail )
2017-03-13 11:09:00 -04:00
return
}
2023-11-29 11:07:54 -05:00
appErr := c . App . TestEmail ( c . AppContext , c . AppContext . Session ( ) . UserId , cfg )
2022-08-10 04:56:58 -04:00
if appErr != nil {
c . Err = appErr
2017-03-20 08:37:34 -04:00
return
}
2019-03-08 13:15:28 -05:00
ReturnStatusOK ( w )
2017-03-20 08:37:34 -04:00
}
2019-09-06 06:52:14 -04:00
func testSiteURL ( c * Context , w http . ResponseWriter , r * http . Request ) {
2025-04-10 15:22:03 -04:00
if ! c . App . SessionHasPermissionToAndNotRestrictedAdmin ( * c . AppContext . Session ( ) , model . PermissionTestSiteURL ) {
2021-08-16 13:46:44 -04:00
c . SetPermissionError ( model . PermissionTestSiteURL )
2019-09-06 06:52:14 -04:00
return
}
2021-09-01 08:43:12 -04:00
props := model . MapFromJSON ( r . Body )
2019-09-06 06:52:14 -04:00
siteURL := props [ "site_url" ]
if siteURL == "" {
c . SetInvalidParam ( "site_url" )
return
}
2020-08-21 16:49:31 -04:00
2023-11-29 11:07:54 -05:00
appErr := c . App . TestSiteURL ( c . AppContext , siteURL )
2022-08-10 04:56:58 -04:00
if appErr != nil {
c . Err = appErr
2019-09-06 06:52:14 -04:00
return
}
ReturnStatusOK ( w )
}
2017-03-21 09:06:08 -04:00
func getAudits ( c * Context , w http . ResponseWriter , r * http . Request ) {
2025-07-16 00:47:03 -04:00
auditRec := c . MakeAuditRecord ( model . AuditEventGetAudits , model . AuditStatusFail )
2020-04-08 00:52:30 -04:00
defer c . LogAuditRec ( auditRec )
2021-07-12 14:05:36 -04:00
if ! c . App . SessionHasPermissionTo ( * c . AppContext . Session ( ) , model . PermissionReadAudits ) {
c . SetPermissionError ( model . PermissionReadAudits )
2017-03-21 09:06:08 -04:00
return
}
2023-11-14 07:34:47 -05:00
audits , appErr := c . App . GetAuditsPage ( c . AppContext , "" , c . Params . Page , c . Params . PerPage )
2022-08-10 04:56:58 -04:00
if appErr != nil {
c . Err = appErr
2017-03-21 09:06:08 -04:00
return
}
2020-04-08 00:52:30 -04:00
auditRec . Success ( )
2025-06-25 20:37:32 -04:00
model . AddEventParameterToAuditRec ( auditRec , "page" , c . Params . Page )
model . AddEventParameterToAuditRec ( auditRec , "audits_per_page" , c . Params . LogsPerPage )
2020-04-08 00:52:30 -04:00
2021-07-26 04:11:02 -04:00
if err := json . NewEncoder ( w ) . Encode ( audits ) ; err != nil {
2022-07-28 11:05:03 -04:00
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
2021-07-26 04:11:02 -04:00
}
2017-03-21 09:06:08 -04:00
}
2017-03-13 11:47:33 -04:00
func databaseRecycle ( c * Context , w http . ResponseWriter , r * http . Request ) {
2025-04-10 15:22:03 -04:00
if ! c . App . SessionHasPermissionToAndNotRestrictedAdmin ( * c . AppContext . Session ( ) , model . PermissionRecycleDatabaseConnections ) {
2021-07-12 14:05:36 -04:00
c . SetPermissionError ( model . PermissionRecycleDatabaseConnections )
2017-03-13 11:47:33 -04:00
return
}
2025-07-16 00:47:03 -04:00
auditRec := c . MakeAuditRecord ( model . AuditEventDatabaseRecycle , model . AuditStatusFail )
2020-03-12 15:50:21 -04:00
defer c . LogAuditRec ( auditRec )
2023-04-13 04:51:11 -04:00
c . App . RecycleDatabaseConnection ( c . AppContext )
2017-03-13 11:47:33 -04:00
2020-03-12 15:50:21 -04:00
auditRec . Success ( )
2017-03-13 11:47:33 -04:00
ReturnStatusOK ( w )
}
2017-03-14 12:06:07 -04:00
func invalidateCaches ( c * Context , w http . ResponseWriter , r * http . Request ) {
2025-04-10 15:22:03 -04:00
if ! c . App . SessionHasPermissionToAndNotRestrictedAdmin ( * c . AppContext . Session ( ) , model . PermissionInvalidateCaches ) {
2021-07-12 14:05:36 -04:00
c . SetPermissionError ( model . PermissionInvalidateCaches )
2017-03-14 12:06:07 -04:00
return
}
2025-07-16 00:47:03 -04:00
auditRec := c . MakeAuditRecord ( model . AuditEventInvalidateCaches , model . AuditStatusFail )
2020-03-12 15:50:21 -04:00
defer c . LogAuditRec ( auditRec )
2023-09-07 11:28:20 -04:00
appErr := c . App . Srv ( ) . InvalidateAllCaches ( )
if appErr != nil {
c . Err = appErr
return
}
2017-03-14 12:06:07 -04:00
2020-03-12 15:50:21 -04:00
auditRec . Success ( )
2017-03-14 12:06:07 -04:00
w . Header ( ) . Set ( "Cache-Control" , "no-cache, no-store, must-revalidate" )
ReturnStatusOK ( w )
}
2017-03-16 14:59:44 -04:00
2023-02-14 05:39:10 -05:00
func queryLogs ( c * Context , w http . ResponseWriter , r * http . Request ) {
2025-07-16 00:47:03 -04:00
auditRec := c . MakeAuditRecord ( model . AuditEventQueryLogs , model . AuditStatusFail )
2023-02-14 05:39:10 -05:00
defer c . LogAuditRec ( auditRec )
2025-04-10 15:22:03 -04:00
if ! c . App . SessionHasPermissionToAndNotRestrictedAdmin ( * c . AppContext . Session ( ) , model . PermissionGetLogs ) {
2023-02-14 05:39:10 -05:00
c . SetPermissionError ( model . PermissionGetLogs )
return
}
var logFilter * model . LogFilter
err := json . NewDecoder ( r . Body ) . Decode ( & logFilter )
2023-10-05 09:55:59 -04:00
if err != nil || logFilter == nil {
2023-02-14 05:39:10 -05:00
c . Err = model . NewAppError ( "queryLogs" , "api.system.logs.invalidFilter" , nil , "" , http . StatusInternalServerError )
return
}
2024-09-27 03:17:16 -04:00
logs , appErr := c . App . QueryLogs ( c . AppContext , c . Params . Page , c . Params . LogsPerPage , logFilter )
if appErr != nil {
c . Err = appErr
2023-02-14 05:39:10 -05:00
return
}
2024-12-15 15:11:36 -05:00
logsJSON := make ( map [ string ] [ ] any )
var result any
2023-02-14 05:39:10 -05:00
for node , logLines := range logs {
for _ , log := range logLines {
2024-09-27 03:17:16 -04:00
err = json . Unmarshal ( [ ] byte ( log ) , & result )
if err != nil {
c . Logger . Warn ( "Error parsing log line in Server Logs" , mlog . String ( "from_node" , node ) , mlog . Err ( err ) )
2023-02-14 05:39:10 -05:00
} else {
2024-09-27 03:17:16 -04:00
logsJSON [ node ] = append ( logsJSON [ node ] , result )
2023-02-14 05:39:10 -05:00
}
}
}
auditRec . AddMeta ( "page" , c . Params . Page )
auditRec . AddMeta ( "logs_per_page" , c . Params . LogsPerPage )
2024-10-07 09:37:46 -04:00
if _ , err := w . Write ( model . ToJSON ( logsJSON ) ) ; err != nil {
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
}
2023-02-14 05:39:10 -05:00
}
2017-03-16 14:59:44 -04:00
func getLogs ( c * Context , w http . ResponseWriter , r * http . Request ) {
2025-07-16 00:47:03 -04:00
auditRec := c . MakeAuditRecord ( model . AuditEventGetLogs , model . AuditStatusFail )
2020-04-08 00:52:30 -04:00
defer c . LogAuditRec ( auditRec )
2025-04-10 15:22:03 -04:00
if ! c . App . SessionHasPermissionToAndNotRestrictedAdmin ( * c . AppContext . Session ( ) , model . PermissionGetLogs ) {
2021-07-12 14:05:36 -04:00
c . SetPermissionError ( model . PermissionGetLogs )
2017-03-16 14:59:44 -04:00
return
}
2023-04-13 04:51:11 -04:00
lines , appErr := c . App . GetLogs ( c . AppContext , c . Params . Page , c . Params . LogsPerPage )
2022-08-10 04:56:58 -04:00
if appErr != nil {
c . Err = appErr
2017-03-16 14:59:44 -04:00
return
}
2025-06-25 20:37:32 -04:00
model . AddEventParameterToAuditRec ( auditRec , "page" , c . Params . Page )
model . AddEventParameterToAuditRec ( auditRec , "logs_per_page" , c . Params . LogsPerPage )
2020-04-08 00:52:30 -04:00
2024-10-07 09:37:46 -04:00
if _ , err := w . Write ( [ ] byte ( model . ArrayToJSON ( lines ) ) ) ; err != nil {
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
}
2017-03-16 14:59:44 -04:00
}
2017-03-27 09:19:53 -04:00
2024-06-24 14:05:23 -04:00
func downloadLogs ( c * Context , w http . ResponseWriter , r * http . Request ) {
2025-07-16 00:47:03 -04:00
auditRec := c . MakeAuditRecord ( model . AuditEventDownloadLogs , model . AuditStatusFail )
2024-06-24 14:05:23 -04:00
defer c . LogAuditRec ( auditRec )
2025-04-10 15:22:03 -04:00
if ! c . App . SessionHasPermissionToAndNotRestrictedAdmin ( * c . AppContext . Session ( ) , model . PermissionGetLogs ) {
2024-06-24 14:05:23 -04:00
c . SetPermissionError ( model . PermissionGetLogs )
return
}
2024-08-03 10:11:13 -04:00
fileData , err := c . App . Srv ( ) . Platform ( ) . GetLogFile ( c . AppContext )
2024-06-24 14:05:23 -04:00
if err != nil {
c . Err = model . NewAppError ( "downloadLogs" , "api.system.logs.download_bytes_buffer.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
reader := bytes . NewReader ( fileData . Body )
2024-08-03 10:11:13 -04:00
web . WriteFileResponse ( config . LogFilename ,
2024-06-24 14:05:23 -04:00
"text/plain" ,
int64 ( len ( fileData . Body ) ) ,
time . Now ( ) ,
* c . App . Config ( ) . ServiceSettings . WebserverMode ,
reader ,
true ,
w ,
r )
}
2017-04-21 05:16:35 -04:00
func postLog ( c * Context , w http . ResponseWriter , r * http . Request ) {
2017-10-20 20:26:12 -04:00
forceToDebug := false
if ! * c . App . Config ( ) . ServiceSettings . EnableDeveloper {
2021-05-11 06:00:44 -04:00
if c . AppContext . Session ( ) . UserId == "" {
2017-10-20 20:26:12 -04:00
c . Err = model . NewAppError ( "postLog" , "api.context.permissions.app_error" , nil , "" , http . StatusForbidden )
return
}
2021-07-12 14:05:36 -04:00
if ! c . App . SessionHasPermissionTo ( * c . AppContext . Session ( ) , model . PermissionManageSystem ) {
2017-10-20 20:26:12 -04:00
forceToDebug = true
}
2017-04-21 05:16:35 -04:00
}
2022-08-10 04:56:58 -04:00
var m map [ string ] string
err := json . NewDecoder ( r . Body ) . Decode ( & m )
if err != nil {
c . Logger . Warn ( "Error decoding request." , mlog . Err ( err ) )
}
if m == nil {
m = map [ string ] string { }
}
2017-04-21 05:16:35 -04:00
lvl := m [ "level" ]
msg := m [ "message" ]
if len ( msg ) > 400 {
msg = msg [ 0 : 399 ]
}
2019-11-04 13:03:59 -05:00
msg = "Client Logs API Endpoint Message: " + msg
fields := [ ] mlog . Field {
mlog . String ( "type" , "client_message" ) ,
2021-05-11 06:00:44 -04:00
mlog . String ( "user_agent" , c . AppContext . UserAgent ( ) ) ,
2019-11-04 13:03:59 -05:00
}
2017-10-20 20:26:12 -04:00
if ! forceToDebug && lvl == "ERROR" {
2019-11-04 13:03:59 -05:00
mlog . Error ( msg , fields ... )
2017-04-21 05:16:35 -04:00
} else {
2019-11-04 13:03:59 -05:00
mlog . Debug ( msg , fields ... )
2017-04-21 05:16:35 -04:00
}
m [ "message" ] = msg
2022-08-10 04:56:58 -04:00
err = json . NewEncoder ( w ) . Encode ( m )
if err != nil {
c . Logger . Warn ( "Error while writing response." , mlog . Err ( err ) )
}
2017-04-21 05:16:35 -04:00
}
2017-06-19 16:35:53 -04:00
func getAnalytics ( c * Context , w http . ResponseWriter , r * http . Request ) {
name := r . URL . Query ( ) . Get ( "name" )
teamId := r . URL . Query ( ) . Get ( "team_id" )
if name == "" {
name = "standard"
}
2021-07-12 14:05:36 -04:00
if ! c . App . SessionHasPermissionTo ( * c . AppContext . Session ( ) , model . PermissionGetAnalytics ) {
c . SetPermissionError ( model . PermissionGetAnalytics )
2017-06-19 16:35:53 -04:00
return
}
2024-03-11 08:27:27 -04:00
rows , appErr := c . App . GetAnalytics ( c . AppContext , name , teamId )
2022-08-10 04:56:58 -04:00
if appErr != nil {
c . Err = appErr
2017-06-19 16:35:53 -04:00
return
}
if rows == nil {
c . SetInvalidParam ( "name" )
return
}
2021-07-26 04:11:02 -04:00
if err := json . NewEncoder ( w ) . Encode ( rows ) ; err != nil {
2022-07-28 11:05:03 -04:00
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
2021-07-26 04:11:02 -04:00
}
2017-06-19 16:35:53 -04:00
}
2018-02-28 18:12:11 -05:00
2022-02-02 09:19:47 -05:00
func getLatestVersion ( c * Context , w http . ResponseWriter , r * http . Request ) {
2025-04-10 15:22:03 -04:00
if ! c . App . SessionHasPermissionToAndNotRestrictedAdmin ( * c . AppContext . Session ( ) , model . PermissionManageSystem ) {
c . SetPermissionError ( model . PermissionManageSystem )
2022-02-02 09:19:47 -05:00
return
}
2023-11-29 11:07:54 -05:00
resp , appErr := c . App . GetLatestVersion ( c . AppContext , "https://api.github.com/repos/mattermost/mattermost-server/releases/latest" )
2022-08-10 04:56:58 -04:00
if appErr != nil {
c . Err = appErr
2022-02-02 09:19:47 -05:00
return
}
2022-08-10 04:56:58 -04:00
b , err := json . Marshal ( resp )
if err != nil {
c . Logger . Warn ( "Unable to marshal JSON for latest version." , mlog . Err ( err ) )
2022-02-02 09:19:47 -05:00
w . WriteHeader ( http . StatusInternalServerError )
}
2024-10-07 09:37:46 -04:00
if _ , err := w . Write ( b ) ; err != nil {
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
}
2022-02-02 09:19:47 -05:00
}
2018-03-22 09:53:43 -04:00
func getSupportedTimezones ( c * Context , w http . ResponseWriter , r * http . Request ) {
2020-02-13 07:26:58 -05:00
supportedTimezones := c . App . Timezones ( ) . GetSupported ( )
2018-12-17 11:51:46 -05:00
if supportedTimezones == nil {
supportedTimezones = make ( [ ] string , 0 )
}
2018-03-22 09:53:43 -04:00
2018-12-17 11:51:46 -05:00
b , err := json . Marshal ( supportedTimezones )
if err != nil {
2021-01-04 09:02:34 -05:00
c . Logger . Warn ( "Unable to marshal JSON in timezones." , mlog . Err ( err ) )
2018-12-17 11:51:46 -05:00
w . WriteHeader ( http . StatusInternalServerError )
2018-03-22 09:53:43 -04:00
}
2024-10-07 09:37:46 -04:00
if _ , err := w . Write ( b ) ; err != nil {
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
}
2018-03-22 09:53:43 -04:00
}
2018-02-28 18:12:11 -05:00
func testS3 ( c * Context , w http . ResponseWriter , r * http . Request ) {
2022-08-10 04:56:58 -04:00
var cfg * model . Config
err := json . NewDecoder ( r . Body ) . Decode ( & cfg )
if err != nil {
c . Logger . Warn ( "Error decoding the config" , mlog . Err ( err ) )
}
2022-04-05 23:00:16 -04:00
if cfg == nil {
cfg = c . App . Config ( )
2022-04-04 08:03:31 -04:00
}
if checkHasNilFields ( & cfg . FileSettings ) {
c . Err = model . NewAppError ( "testS3" , "api.file.test_connection_s3_settings_nil.app_error" , nil , "" , http . StatusBadRequest )
return
2018-02-28 18:12:11 -05:00
}
2021-07-12 14:05:36 -04:00
if ! c . App . SessionHasPermissionTo ( * c . AppContext . Session ( ) , model . PermissionTestS3 ) {
c . SetPermissionError ( model . PermissionTestS3 )
2018-02-28 18:12:11 -05:00
return
}
2022-08-10 04:56:58 -04:00
appErr := c . App . CheckMandatoryS3Fields ( & cfg . FileSettings )
if appErr != nil {
c . Err = appErr
2018-02-28 18:12:11 -05:00
return
}
2021-07-12 14:05:36 -04:00
if * cfg . FileSettings . AmazonS3SecretAccessKey == model . FakeSetting {
2018-03-13 12:26:56 -04:00
cfg . FileSettings . AmazonS3SecretAccessKey = c . App . Config ( ) . FileSettings . AmazonS3SecretAccessKey
}
2022-08-10 04:56:58 -04:00
appErr = c . App . TestFileStoreConnectionWithConfig ( & cfg . FileSettings )
2018-02-28 18:12:11 -05:00
if appErr != nil {
c . Err = appErr
return
}
ReturnStatusOK ( w )
}
2018-08-24 08:49:31 -04:00
func getRedirectLocation ( c * Context , w http . ResponseWriter , r * http . Request ) {
2018-10-02 02:00:50 -04:00
m := make ( map [ string ] string )
m [ "location" ] = ""
2019-01-27 21:24:46 -05:00
2019-02-12 08:37:54 -05:00
if ! * c . App . Config ( ) . ServiceSettings . EnableLinkPreviews {
2024-10-07 09:37:46 -04:00
if _ , err := w . Write ( [ ] byte ( model . MapToJSON ( m ) ) ) ; err != nil {
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
}
2018-10-02 02:00:50 -04:00
return
}
2019-01-27 21:24:46 -05:00
2018-08-24 08:49:31 -04:00
url := r . URL . Query ( ) . Get ( "url" )
2021-01-25 05:15:17 -05:00
if url == "" {
2018-08-24 08:49:31 -04:00
c . SetInvalidParam ( "url" )
return
}
2020-06-02 09:01:30 -04:00
var location string
if err := redirectLocationDataCache . Get ( url , & location ) ; err == nil {
m [ "location" ] = location
2024-10-07 09:37:46 -04:00
if _ , err := w . Write ( [ ] byte ( model . MapToJSON ( m ) ) ) ; err != nil {
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
}
2019-01-27 21:24:46 -05:00
return
}
2020-02-13 07:26:58 -05:00
client := c . App . HTTPService ( ) . MakeClient ( false )
2019-01-27 21:24:46 -05:00
client . CheckRedirect = func ( req * http . Request , via [ ] * http . Request ) error {
return http . ErrUseLastResponse
2018-08-24 08:49:31 -04:00
}
res , err := client . Head ( url )
if err != nil {
2019-01-27 21:24:46 -05:00
// Cache failures to prevent retries.
2024-10-07 09:37:46 -04:00
cacheErr := redirectLocationDataCache . SetWithExpiry ( url , "" , RedirectLocationCacheExpiry )
if cacheErr != nil {
c . Logger . Warn ( "Failed to set cache for URL" , mlog . String ( "url" , url ) , mlog . Err ( err ) )
}
2019-01-27 21:24:46 -05:00
// Always return a success status and a JSON string to limit information returned to client.
2024-10-07 09:37:46 -04:00
if _ , err = w . Write ( [ ] byte ( model . MapToJSON ( m ) ) ) ; err != nil {
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
}
2018-08-24 08:49:31 -04:00
return
}
2020-05-19 23:11:37 -04:00
defer func ( ) {
2024-10-07 09:37:46 -04:00
if _ , err = io . Copy ( io . Discard , res . Body ) ; err != nil {
c . Logger . Warn ( "Error while removing directory" , mlog . Err ( err ) )
}
2020-05-19 23:11:37 -04:00
res . Body . Close ( )
} ( )
2018-08-24 08:49:31 -04:00
2020-06-02 09:01:30 -04:00
location = res . Header . Get ( "Location" )
2023-09-05 14:04:14 -04:00
// If the location length is > 2100, we can probably ignore. Fixes https://mattermost.atlassian.net/browse/MM-54219
if len ( location ) > RedirectLocationMaximumLength {
// Treating as a "failure". Cache failures to prevent retries.
2024-10-07 09:37:46 -04:00
cacheErr := redirectLocationDataCache . SetWithExpiry ( url , "" , RedirectLocationCacheExpiry )
if cacheErr != nil {
c . Logger . Warn ( "Failed to set cache for URL" , mlog . String ( "url" , url ) , mlog . Err ( err ) )
}
2023-09-05 14:04:14 -04:00
// Always return a success status and a JSON string to limit information returned to client.
2024-10-07 09:37:46 -04:00
if _ , err = w . Write ( [ ] byte ( model . MapToJSON ( m ) ) ) ; err != nil {
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
}
2023-09-05 14:04:14 -04:00
return
}
2024-10-07 09:37:46 -04:00
cacheErr := redirectLocationDataCache . SetWithExpiry ( url , location , RedirectLocationCacheExpiry )
if cacheErr != nil {
c . Logger . Warn ( "Failed to set cache for URL" , mlog . String ( "url" , url ) , mlog . Err ( err ) )
}
2019-01-27 21:24:46 -05:00
m [ "location" ] = location
2018-08-24 08:49:31 -04:00
2024-10-07 09:37:46 -04:00
if _ , err := w . Write ( [ ] byte ( model . MapToJSON ( m ) ) ) ; err != nil {
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
}
2018-08-24 08:49:31 -04:00
}
2019-04-30 18:15:29 -04:00
func pushNotificationAck ( c * Context , w http . ResponseWriter , r * http . Request ) {
2021-09-01 08:43:12 -04:00
var ack model . PushNotificationAck
if jsonErr := json . NewDecoder ( r . Body ) . Decode ( & ack ) ; jsonErr != nil {
2020-01-29 04:08:27 -05:00
c . Err = model . NewAppError ( "pushNotificationAck" ,
"api.push_notifications_ack.message.parse.app_error" ,
nil ,
2022-07-28 11:05:03 -04:00
"" ,
2020-01-29 04:08:27 -05:00
http . StatusBadRequest ,
2022-07-28 11:05:03 -04:00
) . Wrap ( jsonErr )
2020-01-29 04:08:27 -05:00
return
}
2019-04-30 18:15:29 -04:00
if ! * c . App . Config ( ) . EmailSettings . SendPushNotifications {
c . Err = model . NewAppError ( "pushNotificationAck" , "api.push_notification.disabled.app_error" , nil , "" , http . StatusNotImplemented )
return
}
2024-04-23 17:31:17 -04:00
if ack . NotificationType == model . PushTypeMessage {
2024-09-11 12:01:21 -04:00
session := c . AppContext . Session ( )
ignoreNotificationACK := session . Props [ model . SessionPropDeviceNotificationDisabled ] == "true"
if ignoreNotificationACK && ack . ClientPlatform == "ios" {
// iOS doesn't send ack when the notificications are disabled
// so we restore the value the moment we receive an ack
2024-10-07 09:37:46 -04:00
if err := c . App . SetExtraSessionProps ( session , map [ string ] string {
2024-09-11 12:01:21 -04:00
model . SessionPropDeviceNotificationDisabled : "false" ,
2024-10-07 09:37:46 -04:00
} ) ; err != nil {
c . Logger . Warn ( "Failed to set extra session props" , mlog . Err ( err ) )
}
2024-09-11 12:01:21 -04:00
c . App . ClearSessionCacheForUser ( session . UserId )
}
if ! ignoreNotificationACK {
c . App . CountNotificationAck ( model . NotificationTypePush , ack . ClientPlatform )
}
2024-04-23 17:31:17 -04:00
}
2024-04-18 10:30:08 -04:00
2026-01-21 05:31:05 -05:00
c . AppContext = c . AppContext . WithLogFields (
2025-08-20 04:17:45 -04:00
mlog . String ( "type" , model . NotificationTypePush ) ,
mlog . String ( "ack_id" , ack . Id ) ,
mlog . String ( "push_type" , ack . NotificationType ) ,
mlog . String ( "post_id" , ack . PostId ) ,
mlog . String ( "ack_type" , ack . NotificationType ) ,
mlog . String ( "device_type" , ack . ClientPlatform ) ,
mlog . Int ( "received_at" , ack . ClientReceivedAt ) ,
2026-01-21 05:31:05 -05:00
)
2025-08-20 04:17:45 -04:00
err := c . App . SendAckToPushProxy ( c . AppContext , & ack )
2019-11-21 17:35:19 -05:00
if ack . IsIdLoaded {
2019-11-18 18:42:51 -05:00
if err != nil {
2024-07-05 03:34:01 -04:00
c . App . CountNotificationReason ( model . NotificationStatusError , model . NotificationTypePush , model . NotificationReasonPushProxySendError , ack . ClientPlatform )
2025-08-20 04:17:45 -04:00
c . AppContext . Logger ( ) . LogM ( mlog . MlvlNotificationError , "Notification ack not sent to push proxy" ,
2024-04-18 10:30:08 -04:00
mlog . String ( "status" , model . NotificationStatusError ) ,
mlog . String ( "reason" , model . NotificationReasonPushProxySendError ) ,
2024-02-29 12:33:05 -05:00
mlog . Err ( err ) ,
2019-11-18 18:42:51 -05:00
)
2024-04-18 10:30:08 -04:00
} else {
2024-07-05 03:34:01 -04:00
c . App . CountNotificationReason ( model . NotificationStatusSuccess , model . NotificationTypePush , model . NotificationReason ( "" ) , ack . ClientPlatform )
2019-11-18 18:42:51 -05:00
}
2021-11-15 00:11:47 -05:00
// Return post data only when PostId is passed.
if ack . PostId != "" && ack . NotificationType == model . PushTypeMessage {
2026-01-20 04:38:27 -05:00
var isMember bool
var appErr * model . AppError
if _ , appErr , isMember = c . App . GetPostIfAuthorized ( c . AppContext , ack . PostId , c . AppContext . Session ( ) , false ) ; appErr != nil {
2021-11-15 00:11:47 -05:00
c . Err = appErr
return
}
notificationInterface := c . App . Notification ( )
if notificationInterface == nil {
c . Err = model . NewAppError ( "pushNotificationAck" , "api.system.id_loaded.not_available.app_error" , nil , "" , http . StatusFound )
return
}
2023-10-06 16:43:21 -04:00
msg , appError := notificationInterface . GetNotificationMessage ( c . AppContext , & ack , c . AppContext . Session ( ) . UserId )
2021-11-15 00:11:47 -05:00
if appError != nil {
2024-04-22 06:03:28 -04:00
c . Err = model . NewAppError ( "pushNotificationAck" , "api.push_notification.id_loaded.fetch.app_error" , nil , "" , http . StatusInternalServerError ) . Wrap ( appError )
2021-11-15 00:11:47 -05:00
return
}
if err2 := json . NewEncoder ( w ) . Encode ( msg ) ; err2 != nil {
2022-07-28 11:05:03 -04:00
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err2 ) )
2021-11-15 00:11:47 -05:00
}
2026-01-20 04:38:27 -05:00
auditRec := c . MakeAuditRecord ( model . AuditEventNotificationAck , model . AuditStatusSuccess )
defer c . LogAuditRec ( auditRec )
model . AddEventParameterToAuditRec ( auditRec , "post_id" , ack . PostId )
if ! isMember {
model . AddEventParameterToAuditRec ( auditRec , "non_channel_member_access" , true )
}
2021-07-26 04:11:02 -04:00
}
2019-11-18 18:42:51 -05:00
return
} else if err != nil {
2024-04-22 06:03:28 -04:00
c . Err = model . NewAppError ( "pushNotificationAck" , "api.push_notifications_ack.forward.app_error" , nil , "" , http . StatusInternalServerError ) . Wrap ( err )
2019-04-30 18:15:29 -04:00
return
}
2024-07-05 03:34:01 -04:00
c . App . CountNotificationReason ( model . NotificationStatusSuccess , model . NotificationTypePush , model . NotificationReason ( "" ) , ack . ClientPlatform )
2019-04-30 18:15:29 -04:00
ReturnStatusOK ( w )
}
2019-11-27 20:41:09 -05:00
func setServerBusy ( c * Context , w http . ResponseWriter , r * http . Request ) {
2021-07-12 14:05:36 -04:00
if ! c . App . SessionHasPermissionTo ( * c . AppContext . Session ( ) , model . PermissionManageSystem ) {
c . SetPermissionError ( model . PermissionManageSystem )
2019-11-27 20:41:09 -05:00
return
}
// number of seconds to keep server marked busy
secs := r . URL . Query ( ) . Get ( "seconds" )
if secs == "" {
2021-01-04 01:02:29 -05:00
secs = strconv . FormatInt ( DefaultServerBusySeconds , 10 )
2019-11-27 20:41:09 -05:00
}
i , err := strconv . ParseInt ( secs , 10 , 64 )
2021-01-04 01:02:29 -05:00
if err != nil || i <= 0 || i > MaxServerBusySeconds {
2021-08-16 13:46:44 -04:00
c . SetInvalidURLParam ( fmt . Sprintf ( "seconds must be 1 - %d" , MaxServerBusySeconds ) )
2019-11-27 20:41:09 -05:00
return
}
2025-07-16 00:47:03 -04:00
auditRec := c . MakeAuditRecord ( model . AuditEventSetServerBusy , model . AuditStatusFail )
2020-03-12 15:50:21 -04:00
defer c . LogAuditRec ( auditRec )
2025-06-25 20:37:32 -04:00
model . AddEventParameterToAuditRec ( auditRec , "seconds" , i )
2020-03-12 15:50:21 -04:00
2022-10-06 04:04:21 -04:00
c . App . Srv ( ) . Platform ( ) . Busy . Set ( time . Second * time . Duration ( i ) )
2023-11-23 04:30:08 -05:00
c . Logger . Warn ( "server busy state activated - non-critical services disabled" , mlog . Int ( "seconds" , i ) )
2020-03-12 15:50:21 -04:00
auditRec . Success ( )
2019-11-27 20:41:09 -05:00
ReturnStatusOK ( w )
}
func clearServerBusy ( c * Context , w http . ResponseWriter , r * http . Request ) {
2021-07-12 14:05:36 -04:00
if ! c . App . SessionHasPermissionTo ( * c . AppContext . Session ( ) , model . PermissionManageSystem ) {
c . SetPermissionError ( model . PermissionManageSystem )
2019-11-27 20:41:09 -05:00
return
}
2020-03-12 15:50:21 -04:00
2025-07-16 00:47:03 -04:00
auditRec := c . MakeAuditRecord ( model . AuditEventClearServerBusy , model . AuditStatusFail )
2020-03-12 15:50:21 -04:00
defer c . LogAuditRec ( auditRec )
2022-10-06 04:04:21 -04:00
c . App . Srv ( ) . Platform ( ) . Busy . Clear ( )
2023-10-11 11:34:49 -04:00
c . Logger . Info ( "server busy state cleared - non-critical services enabled" )
2020-03-12 15:50:21 -04:00
auditRec . Success ( )
2019-11-27 20:41:09 -05:00
ReturnStatusOK ( w )
}
func getServerBusyExpires ( c * Context , w http . ResponseWriter , r * http . Request ) {
2021-07-12 14:05:36 -04:00
if ! c . App . SessionHasPermissionTo ( * c . AppContext . Session ( ) , model . PermissionManageSystem ) {
c . SetPermissionError ( model . PermissionManageSystem )
2019-11-27 20:41:09 -05:00
return
}
2021-07-26 04:11:02 -04:00
2021-09-01 08:43:12 -04:00
// We call to ToJSON because it actually returns a different struct
2021-07-26 04:11:02 -04:00
// along with doing some computations.
2022-10-06 04:04:21 -04:00
sbsJSON , jsonErr := c . App . Srv ( ) . Platform ( ) . Busy . ToJSON ( )
2021-09-01 08:43:12 -04:00
if jsonErr != nil {
2023-10-11 11:34:49 -04:00
c . Logger . Warn ( jsonErr . Error ( ) )
2021-09-01 08:43:12 -04:00
}
if _ , err := w . Write ( sbsJSON ) ; err != nil {
2023-10-11 11:34:49 -04:00
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
2021-07-26 04:11:02 -04:00
}
2019-11-27 20:41:09 -05:00
}
2020-07-22 23:32:21 -04:00
2020-08-21 14:23:04 -04:00
func upgradeToEnterprise ( c * Context , w http . ResponseWriter , r * http . Request ) {
2025-07-16 00:47:03 -04:00
auditRec := c . MakeAuditRecord ( model . AuditEventUpgradeToEnterprise , model . AuditStatusFail )
2020-08-21 14:23:04 -04:00
defer c . LogAuditRec ( auditRec )
2021-07-12 14:05:36 -04:00
if ! c . App . SessionHasPermissionTo ( * c . AppContext . Session ( ) , model . PermissionManageSystem ) {
c . SetPermissionError ( model . PermissionManageSystem )
2020-08-21 14:23:04 -04:00
return
}
if model . BuildEnterpriseReady == "true" {
c . Err = model . NewAppError ( "upgradeToEnterprise" , "api.upgrade_to_enterprise.already-enterprise.app_error" , nil , "" , http . StatusTooManyRequests )
return
}
percentage , _ := c . App . Srv ( ) . UpgradeToE0Status ( )
if percentage > 0 {
c . Err = model . NewAppError ( "upgradeToEnterprise" , "api.upgrade_to_enterprise.app_error" , nil , "" , http . StatusTooManyRequests )
return
}
if percentage == 100 {
c . Err = model . NewAppError ( "upgradeToEnterprise" , "api.upgrade_to_enterprise.already-done.app_error" , nil , "" , http . StatusTooManyRequests )
return
}
if err := c . App . Srv ( ) . CanIUpgradeToE0 ( ) ; err != nil {
var ipErr * upgrader . InvalidPermissions
var iaErr * upgrader . InvalidArch
switch {
case errors . As ( err , & ipErr ) :
2022-07-05 02:46:50 -04:00
params := map [ string ] any {
2020-08-21 14:23:04 -04:00
"MattermostUsername" : ipErr . MattermostUsername ,
"FileUsername" : ipErr . FileUsername ,
"Path" : ipErr . Path ,
}
if ipErr . ErrType == "invalid-user-and-permission" {
2024-04-22 06:03:28 -04:00
c . Err = model . NewAppError ( "upgradeToEnterprise" , "api.upgrade_to_enterprise.invalid-user-and-permission.app_error" , params , "" , http . StatusForbidden ) . Wrap ( err )
2020-08-21 14:23:04 -04:00
} else if ipErr . ErrType == "invalid-user" {
2024-04-22 06:03:28 -04:00
c . Err = model . NewAppError ( "upgradeToEnterprise" , "api.upgrade_to_enterprise.invalid-user.app_error" , params , "" , http . StatusForbidden ) . Wrap ( err )
2020-08-21 14:23:04 -04:00
} else if ipErr . ErrType == "invalid-permission" {
2024-04-22 06:03:28 -04:00
c . Err = model . NewAppError ( "upgradeToEnterprise" , "api.upgrade_to_enterprise.invalid-permission.app_error" , params , "" , http . StatusForbidden ) . Wrap ( err )
2020-08-21 14:23:04 -04:00
}
case errors . As ( err , & iaErr ) :
2024-04-22 06:03:28 -04:00
c . Err = model . NewAppError ( "upgradeToEnterprise" , "api.upgrade_to_enterprise.system_not_supported.app_error" , nil , "" , http . StatusForbidden ) . Wrap ( err )
2020-08-21 14:23:04 -04:00
default :
2024-04-22 06:03:28 -04:00
c . Err = model . NewAppError ( "upgradeToEnterprise" , "api.upgrade_to_enterprise.generic_error.app_error" , nil , "" , http . StatusForbidden ) . Wrap ( err )
2020-08-21 14:23:04 -04:00
}
return
}
c . App . Srv ( ) . Go ( func ( ) {
2024-10-07 09:37:46 -04:00
err := c . App . Srv ( ) . UpgradeToE0 ( )
if err != nil {
c . Logger . Error ( "Error while upgrading to E0" , mlog . Err ( err ) )
}
2020-08-21 14:23:04 -04:00
} )
auditRec . Success ( )
w . WriteHeader ( http . StatusAccepted )
ReturnStatusOK ( w )
}
func upgradeToEnterpriseStatus ( c * Context , w http . ResponseWriter , r * http . Request ) {
2021-07-12 14:05:36 -04:00
if ! c . App . SessionHasPermissionTo ( * c . AppContext . Session ( ) , model . PermissionManageSystem ) {
c . SetPermissionError ( model . PermissionManageSystem )
2020-08-21 14:23:04 -04:00
return
}
percentage , err := c . App . Srv ( ) . UpgradeToE0Status ( )
2022-07-05 02:46:50 -04:00
var s map [ string ] any
2020-08-21 14:23:04 -04:00
if err != nil {
var isErr * upgrader . InvalidSignature
switch {
case errors . As ( err , & isErr ) :
2024-04-22 06:03:28 -04:00
appErr := model . NewAppError ( "upgradeToEnterpriseStatus" , "api.upgrade_to_enterprise_status.app_error" , nil , "" , http . StatusBadRequest ) . Wrap ( isErr )
2022-07-05 02:46:50 -04:00
s = map [ string ] any { "percentage" : 0 , "error" : appErr . Message }
2020-08-21 14:23:04 -04:00
default :
2024-04-22 06:03:28 -04:00
appErr := model . NewAppError ( "upgradeToEnterpriseStatus" , "api.upgrade_to_enterprise_status.signature.app_error" , nil , "" , http . StatusBadRequest ) . Wrap ( err )
2022-07-05 02:46:50 -04:00
s = map [ string ] any { "percentage" : 0 , "error" : appErr . Message }
2020-08-21 14:23:04 -04:00
}
} else {
2022-07-05 02:46:50 -04:00
s = map [ string ] any { "percentage" : percentage , "error" : nil }
2020-08-21 14:23:04 -04:00
}
2024-10-07 09:37:46 -04:00
if _ , err := w . Write ( [ ] byte ( model . StringInterfaceToJSON ( s ) ) ) ; err != nil {
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
}
2020-08-21 14:23:04 -04:00
}
2025-08-18 10:40:48 -04:00
func isAllowedToUpgradeToEnterprise ( c * Context , w http . ResponseWriter , r * http . Request ) {
if ! c . App . SessionHasPermissionTo ( * c . AppContext . Session ( ) , model . PermissionManageSystem ) {
c . SetPermissionError ( model . PermissionManageSystem )
return
}
err := c . App . Srv ( ) . CanIUpgradeToE0 ( )
if err != nil {
var iaErr * upgrader . InvalidArch
if errors . As ( err , & iaErr ) {
c . Err = model . NewAppError ( "isAllowedToUpgradeToEnterprise" , "api.upgrade_to_enterprise.system_not_supported.app_error" , nil , "" , http . StatusForbidden ) . Wrap ( err )
return
}
c . Err = model . NewAppError ( "isAllowedToUpgradeToEnterprise" , err . Error ( ) , nil , "" , http . StatusForbidden ) . Wrap ( err )
return
}
ReturnStatusOK ( w )
}
2020-08-21 14:23:04 -04:00
func restart ( c * Context , w http . ResponseWriter , r * http . Request ) {
2025-07-16 00:47:03 -04:00
auditRec := c . MakeAuditRecord ( model . AuditEventRestartServer , model . AuditStatusFail )
2020-08-21 14:23:04 -04:00
defer c . LogAuditRec ( auditRec )
2021-07-12 14:05:36 -04:00
if ! c . App . SessionHasPermissionTo ( * c . AppContext . Session ( ) , model . PermissionManageSystem ) {
c . SetPermissionError ( model . PermissionManageSystem )
2020-08-21 14:23:04 -04:00
return
}
auditRec . Success ( )
ReturnStatusOK ( w )
time . Sleep ( 1 * time . Second )
go func ( ) {
2024-10-07 09:37:46 -04:00
err := c . App . Srv ( ) . Restart ( )
if err != nil {
c . Logger . Error ( "Error while restarting server" , mlog . Err ( err ) )
}
2020-08-21 14:23:04 -04:00
} ( )
}
2020-09-21 03:28:46 -04:00
func getProductNotices ( c * Context , w http . ResponseWriter , r * http . Request ) {
c . RequireTeamId ( )
if c . Err != nil {
return
}
client , parseError := model . NoticeClientTypeFromString ( r . URL . Query ( ) . Get ( "client" ) )
if parseError != nil {
2023-10-11 11:34:49 -04:00
c . SetInvalidParamWithErr ( "client" , parseError )
2020-09-21 03:28:46 -04:00
return
}
clientVersion := r . URL . Query ( ) . Get ( "clientVersion" )
locale := r . URL . Query ( ) . Get ( "locale" )
2022-08-10 04:56:58 -04:00
notices , appErr := c . App . GetProductNotices ( c . AppContext , c . AppContext . Session ( ) . UserId , c . Params . TeamId , client , clientVersion , locale )
if appErr != nil {
c . Err = appErr
2020-09-21 03:28:46 -04:00
return
}
result , _ := notices . Marshal ( )
_ , _ = w . Write ( result )
}
func updateViewedProductNotices ( c * Context , w http . ResponseWriter , r * http . Request ) {
2025-07-16 00:47:03 -04:00
auditRec := c . MakeAuditRecord ( model . AuditEventUpdateViewedProductNotices , model . AuditStatusFail )
2020-09-21 03:28:46 -04:00
defer c . LogAuditRec ( auditRec )
c . LogAudit ( "attempt" )
2024-02-21 07:13:50 -05:00
ids , err := model . SortedArrayFromJSON ( r . Body )
2024-01-09 12:04:16 -05:00
if err != nil {
c . Err = model . NewAppError ( "updateViewedProductNotices" , model . PayloadParseError , nil , "" , http . StatusBadRequest ) . Wrap ( err )
return
}
2022-08-10 04:56:58 -04:00
appErr := c . App . UpdateViewedProductNotices ( c . AppContext . Session ( ) . UserId , ids )
if appErr != nil {
c . Err = appErr
2020-09-21 03:28:46 -04:00
return
}
2020-09-25 17:59:41 -04:00
2020-09-21 03:28:46 -04:00
auditRec . Success ( )
ReturnStatusOK ( w )
}
2022-02-02 13:45:09 -05:00
2022-02-15 18:46:03 -05:00
func getOnboarding ( c * Context , w http . ResponseWriter , r * http . Request ) {
2025-07-16 00:47:03 -04:00
auditRec := c . MakeAuditRecord ( model . AuditEventGetOnboarding , model . AuditStatusFail )
2022-02-15 18:46:03 -05:00
defer c . LogAuditRec ( auditRec )
c . LogAudit ( "attempt" )
if ! c . App . SessionHasPermissionTo ( * c . AppContext . Session ( ) , model . PermissionManageSystem ) {
c . SetPermissionError ( model . PermissionManageSystem )
return
}
firstAdminCompleteSetupObj , err := c . App . GetOnboarding ( )
if err != nil {
2022-08-10 04:56:58 -04:00
c . Err = model . NewAppError ( "getOnboarding" , "app.system.get_onboarding_request.app_error" , nil , "" , http . StatusInternalServerError ) . Wrap ( err )
2022-02-15 18:46:03 -05:00
return
}
auditRec . Success ( )
if err := json . NewEncoder ( w ) . Encode ( firstAdminCompleteSetupObj ) ; err != nil {
2022-07-28 11:05:03 -04:00
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
2022-02-15 18:46:03 -05:00
}
}
2022-02-02 13:45:09 -05:00
func completeOnboarding ( c * Context , w http . ResponseWriter , r * http . Request ) {
if ! c . App . SessionHasPermissionTo ( * c . AppContext . Session ( ) , model . PermissionManageSystem ) {
c . Err = model . NewAppError ( "completeOnboarding" , "app.system.complete_onboarding_request.no_first_user" , nil , "" , http . StatusForbidden )
return
}
2025-07-16 00:47:03 -04:00
auditRec := c . MakeAuditRecord ( model . AuditEventCompleteOnboarding , model . AuditStatusFail )
2022-02-02 13:45:09 -05:00
defer c . LogAuditRec ( auditRec )
onboardingRequest , err := model . CompleteOnboardingRequestFromReader ( r . Body )
if err != nil {
2022-08-10 04:56:58 -04:00
c . Err = model . NewAppError ( "completeOnboarding" , "app.system.complete_onboarding_request.app_error" , nil , "" , http . StatusBadRequest ) . Wrap ( err )
2022-02-02 13:45:09 -05:00
return
}
2025-06-25 20:37:32 -04:00
model . AddEventParameterToAuditRec ( auditRec , "install_plugin" , onboardingRequest . InstallPlugins )
model . AddEventParameterAuditableToAuditRec ( auditRec , "onboarding_request" , onboardingRequest )
2022-02-02 13:45:09 -05:00
2022-02-09 14:29:00 -05:00
appErr := c . App . CompleteOnboarding ( c . AppContext , onboardingRequest )
2022-02-02 13:45:09 -05:00
if appErr != nil {
c . Err = appErr
return
}
auditRec . Success ( )
ReturnStatusOK ( w )
}
2022-03-15 09:39:23 -04:00
func getAppliedSchemaMigrations ( c * Context , w http . ResponseWriter , r * http . Request ) {
2022-03-23 12:34:03 -04:00
if ! c . App . SessionHasPermissionToAny ( * c . AppContext . Session ( ) , model . SysconsoleReadPermissions ) {
c . SetPermissionError ( model . SysconsoleReadPermissions ... )
2022-03-15 09:39:23 -04:00
return
}
2025-07-16 00:47:03 -04:00
auditRec := c . MakeAuditRecord ( model . AuditEventGetAppliedSchemaMigrations , model . AuditStatusFail )
2022-03-15 09:39:23 -04:00
defer c . LogAuditRec ( auditRec )
migrations , appErr := c . App . GetAppliedSchemaMigrations ( )
if appErr != nil {
c . Err = appErr
return
}
2022-08-10 04:56:58 -04:00
js , err := json . Marshal ( migrations )
if err != nil {
c . Err = model . NewAppError ( "getAppliedMigrations" , "api.marshal_error" , nil , "" , http . StatusInternalServerError ) . Wrap ( err )
2022-03-15 09:39:23 -04:00
return
}
auditRec . Success ( )
2024-10-07 09:37:46 -04:00
if _ , err := w . Write ( js ) ; err != nil {
c . Logger . Warn ( "Error while writing response" , mlog . Err ( err ) )
}
2022-03-15 09:39:23 -04:00
}
2022-04-04 08:03:31 -04:00
// returns true if the data has nil fields
// this is being used for testS3 and testEmail methods
2022-07-05 02:46:50 -04:00
func checkHasNilFields ( value any ) bool {
2022-04-04 08:03:31 -04:00
v := reflect . Indirect ( reflect . ValueOf ( value ) )
if v . Kind ( ) != reflect . Struct {
return false
}
for i := 0 ; i < v . NumField ( ) ; i ++ {
field := v . Field ( i )
if field . Kind ( ) == reflect . Ptr && field . IsNil ( ) {
return true
}
}
return false
}