2025-09-11 23:38:41 -06:00
|
|
|
/*
|
|
|
|
|
* Amsterdam Web Communities System
|
|
|
|
|
* Copyright (c) 2025 Erbosoft Metaverse Design Solutions, All Rights Reserved
|
|
|
|
|
*
|
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
2026-03-13 14:49:28 -06:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: MPL-2.0
|
2025-09-11 23:38:41 -06:00
|
|
|
*/
|
2025-09-14 22:05:17 -06:00
|
|
|
|
|
|
|
|
// Package main contains the high-level Amsterdam logic.
|
2025-09-05 21:46:10 -06:00
|
|
|
package main
|
|
|
|
|
|
2026-03-11 23:16:01 -06:00
|
|
|
// Directive generating the Tailwind CSS file.
|
|
|
|
|
//go:generate tailwindcss -i ui/styles.css -o ui/static/css/tailwind.css
|
|
|
|
|
|
2025-09-05 21:46:10 -06:00
|
|
|
import (
|
2025-09-17 23:07:42 -06:00
|
|
|
"context"
|
2025-09-20 15:57:29 -06:00
|
|
|
"fmt"
|
2025-09-17 23:07:42 -06:00
|
|
|
"net/http"
|
|
|
|
|
"os"
|
|
|
|
|
"os/signal"
|
2026-02-16 15:38:00 -07:00
|
|
|
"syscall"
|
2025-09-17 23:07:42 -06:00
|
|
|
"time"
|
|
|
|
|
|
2025-09-17 17:26:54 -06:00
|
|
|
"git.erbosoft.com/amy/amsterdam/config"
|
2025-09-20 15:57:29 -06:00
|
|
|
"git.erbosoft.com/amy/amsterdam/database"
|
2025-10-01 16:41:57 -06:00
|
|
|
"git.erbosoft.com/amy/amsterdam/email"
|
2025-10-30 22:33:39 -06:00
|
|
|
"git.erbosoft.com/amy/amsterdam/htmlcheck"
|
2025-09-13 17:21:12 -06:00
|
|
|
"git.erbosoft.com/amy/amsterdam/ui"
|
2025-12-19 23:23:58 -07:00
|
|
|
"git.erbosoft.com/amy/amsterdam/util"
|
2025-09-05 21:46:10 -06:00
|
|
|
"github.com/labstack/echo/v4"
|
2025-09-14 16:10:29 -06:00
|
|
|
"github.com/labstack/echo/v4/middleware"
|
2025-10-17 11:53:30 -06:00
|
|
|
log "github.com/sirupsen/logrus"
|
2025-09-05 21:46:10 -06:00
|
|
|
)
|
|
|
|
|
|
2026-02-20 15:52:36 -07:00
|
|
|
// GetAndPost is used to have functions that respond to both GET and POST on a URI.
|
2026-02-16 22:44:59 -07:00
|
|
|
var GetAndPost = []string{http.MethodGet, http.MethodPost}
|
|
|
|
|
|
2025-09-14 22:05:17 -06:00
|
|
|
// setupEcho creates, configures, and returns a new Echo instance.
|
2025-09-11 23:38:41 -06:00
|
|
|
func setupEcho() *echo.Echo {
|
2025-09-05 21:46:10 -06:00
|
|
|
e := echo.New()
|
2026-02-26 16:19:24 -07:00
|
|
|
e.HideBanner = true
|
2025-09-14 16:45:29 -06:00
|
|
|
e.Logger = &EchoLogrusAdapter{}
|
|
|
|
|
e.Renderer = &ui.TemplateRenderer{}
|
2025-10-19 16:06:08 -06:00
|
|
|
e.HTTPErrorHandler = AmErrorHandler
|
2025-10-17 11:53:30 -06:00
|
|
|
if !config.CommandLine.DebugPanic {
|
2025-10-19 16:06:08 -06:00
|
|
|
e.Use(middleware.RecoverWithConfig(middleware.RecoverConfig{
|
|
|
|
|
LogErrorFunc: LogrusPanicLogging,
|
|
|
|
|
}))
|
2025-10-17 11:53:30 -06:00
|
|
|
} else {
|
|
|
|
|
log.Warn("WARNING: --debug-panic in effect - DO NOT use this in production!")
|
|
|
|
|
}
|
2026-02-22 22:18:16 -07:00
|
|
|
e.Use(LogrusMiddleware)
|
2025-09-14 16:57:52 -06:00
|
|
|
|
2026-03-10 22:59:35 -06:00
|
|
|
// set up the rate limiter from the configuration
|
|
|
|
|
rateLimiter := AmSetupRateLimiter()
|
|
|
|
|
|
2026-02-22 22:18:16 -07:00
|
|
|
// This is the set of all middleware functions used by the UI, as opposed to other things.
|
2026-03-10 22:59:35 -06:00
|
|
|
uiset := []echo.MiddlewareFunc{ui.SessionStoreInjector, ui.ContextCreator, rateLimiter, ui.IPBanTest, ui.CookieLoginTest}
|
2026-02-22 22:18:16 -07:00
|
|
|
|
2026-02-22 23:04:49 -07:00
|
|
|
e.RouteNotFound("/*", ui.AmWrap(AmNotFoundHandler), uiset...)
|
2026-03-04 16:34:19 -07:00
|
|
|
fs, err := config.AmOpenExternalContentPath()
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
if fs != nil {
|
2026-02-24 23:20:56 -07:00
|
|
|
e.StaticFS("/ext", fs)
|
|
|
|
|
e.GET("/fext/*", ui.AmWrap(ui.AmStaticFramePage(fs, "/fext/")), uiset...)
|
|
|
|
|
}
|
2026-02-22 22:18:16 -07:00
|
|
|
e.Match(GetAndPost, "/TODO/*", ui.AmWrap(NotImplPage), uiset...)
|
2026-01-18 22:29:20 -07:00
|
|
|
e.GET("/img/*", ui.AmServeImage)
|
2026-03-02 22:52:45 -07:00
|
|
|
e.GET("/images/*", ui.AmServeImage)
|
2026-02-23 22:41:59 -07:00
|
|
|
if config.GlobalConfig.Rendering.VeniceCompatibleImageURLs {
|
|
|
|
|
e.GET("/venice/imagedata/:id", ui.AmServeVeniceCompatibleImage)
|
|
|
|
|
}
|
2025-11-05 21:47:58 -07:00
|
|
|
e.GET("/static/*", ui.AmStaticFileHandler())
|
2026-04-06 13:41:09 -06:00
|
|
|
e.GET("/go/:postlink", ui.AmWrap(JumpToShortcut), uiset...)
|
2025-09-24 23:30:17 -06:00
|
|
|
|
2026-02-22 22:18:16 -07:00
|
|
|
e.GET("/", ui.AmWrap(TopPage), uiset...)
|
|
|
|
|
e.GET("/about", ui.AmWrap(AboutPage), uiset...)
|
2026-03-04 22:18:57 -07:00
|
|
|
e.GET("/policy", ui.AmWrap(PolicyPage), uiset...)
|
2026-02-22 22:18:16 -07:00
|
|
|
e.GET("/login", ui.AmWrap(LoginForm), uiset...)
|
|
|
|
|
e.POST("/login", ui.AmWrap(Login), uiset...)
|
|
|
|
|
e.GET("/logout", ui.AmWrap(Logout), uiset...)
|
|
|
|
|
e.GET("/newacct", ui.AmWrap(NewAccountUserAgreement), uiset...)
|
|
|
|
|
e.GET("/newacct2", ui.AmWrap(NewAccountForm), uiset...)
|
|
|
|
|
e.POST("/newacct2", ui.AmWrap(NewAccount), uiset...)
|
|
|
|
|
e.GET("/verify", ui.AmWrap(VerifyEmailForm), uiset...)
|
|
|
|
|
e.POST("/verify", ui.AmWrap(VerifyEMail), uiset...)
|
|
|
|
|
e.GET("/passrecovery/:uid/:auth", ui.AmWrap(PasswordRecovery), uiset...)
|
|
|
|
|
e.GET("/profile", ui.AmWrap(EditProfileForm), uiset...)
|
|
|
|
|
e.POST("/profile", ui.AmWrap(EditProfile), uiset...)
|
|
|
|
|
e.GET("/profile_photo", ui.AmWrap(ProfilePhotoForm), uiset...)
|
|
|
|
|
e.POST("/profile_photo", ui.AmWrap(ProfilePhoto), uiset...)
|
|
|
|
|
e.GET("/find", ui.AmWrap(FindPage), uiset...)
|
|
|
|
|
e.POST("/find", ui.AmWrap(Find), uiset...)
|
|
|
|
|
e.GET("/user/:uname", ui.AmWrap(ShowProfile), uiset...)
|
|
|
|
|
e.POST("/quick_email", ui.AmWrap(QuickEMail), uiset...)
|
|
|
|
|
e.GET("/hotlist", ui.AmWrap(Hotlist), uiset...)
|
|
|
|
|
e.GET("/sideboxes", ui.AmWrap(ManageSideboxes), uiset...)
|
|
|
|
|
e.POST("/sideboxes", ui.AmWrap(AddSidebox), uiset...)
|
|
|
|
|
e.GET("/create_comm", ui.AmWrap(CreateCommunityForm), uiset...)
|
|
|
|
|
e.POST("/create_comm", ui.AmWrap(CreateCommunity), uiset...)
|
|
|
|
|
e.GET("/manage_comm", ui.AmWrap(ManageCommunities), uiset...)
|
|
|
|
|
e.POST("/attachment_upload", ui.AmWrap(AttachmentUpload), uiset...)
|
|
|
|
|
e.GET("/attachment/:post", ui.AmWrap(AttachmentSend), uiset...)
|
|
|
|
|
e.POST("/__invite_send", ui.AmWrap(InviteSend), uiset...)
|
|
|
|
|
sysGroup := e.Group("/sysadmin", uiset...)
|
2026-02-22 21:52:55 -07:00
|
|
|
sysGroup.GET("", ui.AmWrap(SysAdminMenu))
|
|
|
|
|
sysGroup.GET("/globals", ui.AmWrap(GlobalPropertiesForm))
|
|
|
|
|
sysGroup.POST("/globals", ui.AmWrap(GlobalPropertiesSet))
|
|
|
|
|
sysGroup.Match(GetAndPost, "/users", ui.AmWrap(UserManagementSearch))
|
|
|
|
|
sysGroup.GET("/users/:uname", ui.AmWrap(UserManagementForm))
|
|
|
|
|
sysGroup.POST("/users/:uname", ui.AmWrap(UserManagementSave))
|
|
|
|
|
sysGroup.GET("/users/:uname/photo", ui.AmWrap(AdminUserPhotoForm))
|
|
|
|
|
sysGroup.POST("/users/:uname/photo", ui.AmWrap(AdminUserPhoto))
|
|
|
|
|
sysGroup.GET("/ipban", ui.AmWrap(IPBanList))
|
|
|
|
|
sysGroup.GET("/ipban/add", ui.AmWrap(AddIPBanForm))
|
|
|
|
|
sysGroup.POST("/ipban/add", ui.AmWrap(AddIPBan))
|
|
|
|
|
sysGroup.Match(GetAndPost, "/audit", ui.AmWrap(SystemAudit))
|
2026-02-26 23:14:06 -07:00
|
|
|
sysGroup.Match(GetAndPost, "/import", ui.AmWrap(UserImport))
|
2026-03-07 12:36:30 -07:00
|
|
|
sysGroup.GET("/sysstat", ui.AmWrap(SysStats))
|
2025-12-06 23:14:55 -07:00
|
|
|
|
2025-12-07 21:54:38 -07:00
|
|
|
// community group
|
2026-02-22 22:18:16 -07:00
|
|
|
uiset2 := make([]echo.MiddlewareFunc, len(uiset), len(uiset)+1)
|
|
|
|
|
copy(uiset2, uiset)
|
|
|
|
|
commGroup := e.Group("/comm/:cid", append(uiset2, ui.SetCommunity)...)
|
2026-02-16 22:44:59 -07:00
|
|
|
fn := ui.AmWrap(ShowCommunity)
|
2026-02-09 23:27:57 -07:00
|
|
|
commGroup.GET("", fn)
|
|
|
|
|
commGroup.GET("/profile", fn)
|
2025-12-06 23:14:55 -07:00
|
|
|
commGroup.GET("/join", ui.AmWrap(JoinCommunity))
|
|
|
|
|
commGroup.POST("/join", ui.AmWrap(JoinCommunityWithKey))
|
2026-02-15 23:08:22 -07:00
|
|
|
commGroup.GET("/unjoin", ui.AmWrap(UnjoinCommunity("prof")))
|
|
|
|
|
commGroup.GET("/unj", ui.AmWrap(UnjoinCommunity("manage")))
|
2025-12-06 23:14:55 -07:00
|
|
|
commGroup.POST("/unjoin", ui.AmWrap(UnjoinCommunityConfirm))
|
|
|
|
|
commGroup.GET("/members", ui.AmWrap(MemberList))
|
|
|
|
|
commGroup.POST("/members", ui.AmWrap(MemberSearch))
|
2026-02-26 16:19:24 -07:00
|
|
|
commGroup.GET("/members/export", ui.AmWrap(ExportCommunityMembers))
|
2026-01-29 23:14:24 -07:00
|
|
|
commGroup.GET("/invite", ui.AmWrap(InviteToCommunity))
|
2026-01-31 23:25:44 -07:00
|
|
|
commGroup.GET("/find", ui.AmWrap(FindPostsPageCommunity))
|
2026-02-01 14:42:32 -07:00
|
|
|
commGroup.POST("/find", ui.AmWrap(FindPostsCommunity))
|
2026-02-22 21:52:55 -07:00
|
|
|
adminGroup := commGroup.Group("/admin")
|
|
|
|
|
adminGroup.GET("", ui.AmWrap(CommunityAdminMenu))
|
|
|
|
|
adminGroup.GET("/profile", ui.AmWrap(CommunityProfileForm))
|
|
|
|
|
adminGroup.POST("/profile", ui.AmWrap(EditCommunityProfile))
|
|
|
|
|
adminGroup.GET("/logo", ui.AmWrap(CommunityLogoForm))
|
|
|
|
|
adminGroup.POST("/logo", ui.AmWrap(EditCommunityLogo))
|
|
|
|
|
adminGroup.Match(GetAndPost, "/audit", ui.AmWrap(CommunityAudit))
|
|
|
|
|
adminGroup.GET("/category", ui.AmWrap(CommunityCategory))
|
2026-02-23 15:19:30 -07:00
|
|
|
adminGroup.Match(GetAndPost, "/members", ui.AmWrap(CommunityMembers))
|
2026-02-22 21:52:55 -07:00
|
|
|
adminGroup.GET("/massmail", ui.AmWrap(CommunityEmailForm))
|
|
|
|
|
adminGroup.POST("/massmail", ui.AmWrap(CommunityEmail))
|
2026-03-01 22:51:22 -07:00
|
|
|
adminGroup.GET("/delete", ui.AmWrap(DeleteCommunity))
|
2025-12-06 23:14:55 -07:00
|
|
|
|
2025-12-07 21:54:38 -07:00
|
|
|
// conference group
|
2026-02-02 16:22:43 -07:00
|
|
|
commGroup.GET("/create_conf", ui.AmWrap(CreateConferenceForm))
|
2026-02-02 22:24:26 -07:00
|
|
|
commGroup.POST("/create_conf", ui.AmWrap(CreateConference))
|
2026-02-20 22:33:27 -07:00
|
|
|
commGroup.GET("/manage_conf", ui.AmWrap(ManageConferenceList), ui.ValidateConference)
|
|
|
|
|
commGroup.GET("/manage_conf/del/:confid", ui.AmWrap(ManageDeleteConference), ui.ValidateConference, ui.SetConference)
|
2025-12-07 21:54:38 -07:00
|
|
|
commGroup.GET("/conf", ui.AmWrap(Conferences), ui.ValidateConference)
|
2026-02-20 22:33:27 -07:00
|
|
|
|
2025-12-07 21:54:38 -07:00
|
|
|
confGroup := commGroup.Group("/conf/:confid", ui.ValidateConference, ui.SetConference)
|
2025-12-16 15:14:18 -07:00
|
|
|
confGroup.GET("", ui.AmWrap(Topics))
|
2025-12-06 23:14:55 -07:00
|
|
|
confGroup.GET("/new_topic", ui.AmWrap(NewTopicForm))
|
|
|
|
|
confGroup.POST("/new_topic", ui.AmWrap(NewTopic))
|
2026-01-31 23:25:44 -07:00
|
|
|
confGroup.GET("/find", ui.AmWrap(FindPostsPageConference))
|
2026-02-01 14:42:32 -07:00
|
|
|
confGroup.POST("/find", ui.AmWrap(FindPostsConference))
|
2026-01-27 23:12:26 -07:00
|
|
|
confGroup.GET("/manage", ui.AmWrap(ConfManage))
|
2026-01-30 16:53:49 -07:00
|
|
|
confGroup.POST("/pseud", ui.AmWrap(SetPseud))
|
2026-01-30 18:20:58 -07:00
|
|
|
confGroup.GET("/fixseen", ui.AmWrap(ConfFixseen))
|
2026-02-01 23:16:58 -07:00
|
|
|
confGroup.GET("/edit", ui.AmWrap(EditConferenceForm))
|
2026-02-02 15:58:08 -07:00
|
|
|
confGroup.POST("/edit", ui.AmWrap(EditConference))
|
2026-02-07 23:15:26 -07:00
|
|
|
confGroup.GET("/aliases", ui.AmWrap(ConferenceAliasForm))
|
2026-02-08 22:23:15 -07:00
|
|
|
confGroup.POST("/aliases", ui.AmWrap(ConferenceAliasAdd))
|
2026-02-16 22:44:59 -07:00
|
|
|
confGroup.Match(GetAndPost, "/members", ui.AmWrap(ConferenceMembers))
|
2026-02-11 23:16:38 -07:00
|
|
|
confGroup.GET("/custom", ui.AmWrap(ConfCustomForm))
|
|
|
|
|
confGroup.POST("/custom", ui.AmWrap(ConfCustom))
|
2026-02-12 23:06:34 -07:00
|
|
|
confGroup.GET("/activity", ui.AmWrap(ConfReports))
|
2026-02-13 16:30:15 -07:00
|
|
|
confGroup.GET("/email", ui.AmWrap(ConferenceEmailForm))
|
2026-02-13 21:56:05 -07:00
|
|
|
confGroup.POST("/email", ui.AmWrap(ConferenceEmail))
|
2026-02-13 22:30:54 -07:00
|
|
|
confGroup.GET("/export", ui.AmWrap(ConferenceExportForm))
|
2026-02-14 23:09:53 -07:00
|
|
|
confGroup.POST("/export", ui.AmWrap(ConferenceExport))
|
2026-02-27 21:57:17 -07:00
|
|
|
confGroup.Match(GetAndPost, "/import", ui.AmWrap(ConferenceImport))
|
2026-02-20 22:43:36 -07:00
|
|
|
confGroup.GET("/delete", ui.AmWrap(DeleteConference))
|
2026-01-26 23:13:48 -07:00
|
|
|
confGroup.GET("/hotlist", ui.AmWrap(AddToHotlist))
|
2026-01-29 23:14:24 -07:00
|
|
|
confGroup.GET("/invite", ui.AmWrap(InviteToConference))
|
2026-01-18 22:29:20 -07:00
|
|
|
confGroup.GET("/r/:topic", ui.AmWrap(ReadPosts), ui.SetTopic)
|
|
|
|
|
confGroup.POST("/r/:topic", ui.AmWrap(PostInTopic), ui.SetTopic)
|
2026-02-20 22:33:27 -07:00
|
|
|
|
2026-01-19 22:10:38 -07:00
|
|
|
opsGroup := confGroup.Group("/op/:topic", ui.SetTopic)
|
2026-01-31 23:25:44 -07:00
|
|
|
opsGroup.GET("/find", ui.AmWrap(FindPostsPageTopic))
|
2026-02-01 14:42:32 -07:00
|
|
|
opsGroup.POST("/find", ui.AmWrap(FindPostsTopic))
|
2026-01-19 22:10:38 -07:00
|
|
|
opsGroup.GET("/hide", ui.AmWrap(HideTopic))
|
2026-01-29 11:36:03 -07:00
|
|
|
opsGroup.GET("/freeze", ui.AmWrap(FreezeTopic))
|
|
|
|
|
opsGroup.GET("/archive", ui.AmWrap(ArchiveTopic))
|
|
|
|
|
opsGroup.GET("/stick", ui.AmWrap(StickTopic))
|
2026-01-30 16:02:55 -07:00
|
|
|
opsGroup.GET("/delete", ui.AmWrap(DeleteTopic))
|
2026-01-21 22:56:45 -07:00
|
|
|
opsGroup.GET("/hide/:msg", ui.AmWrap(HideMessage))
|
2026-01-28 15:40:13 -07:00
|
|
|
opsGroup.GET("/scribble/:msg", ui.AmWrap(ScribbleMessage))
|
2026-01-28 18:59:38 -07:00
|
|
|
opsGroup.GET("/nuke/:msg", ui.AmWrap(NukeMessage))
|
2026-02-21 15:36:48 -07:00
|
|
|
opsGroup.GET("/prune/:msg", ui.AmWrap(PruneMessageAttachment))
|
2026-01-30 15:09:19 -07:00
|
|
|
opsGroup.GET("/publish/:msg", ui.AmWrap(PublishMessage))
|
2026-01-29 18:16:23 -07:00
|
|
|
opsGroup.GET("/move/:msg", ui.AmWrap(MoveMessageForm))
|
2026-01-29 22:38:27 -07:00
|
|
|
opsGroup.POST("/move/:msg", ui.AmWrap(MoveMessage))
|
2026-01-28 15:19:18 -07:00
|
|
|
opsGroup.GET("/manage", ui.AmWrap(TopicManage))
|
2026-01-29 16:12:27 -07:00
|
|
|
opsGroup.GET("/subscribe", ui.AmWrap(TopicSetSubscribe))
|
2026-01-29 23:14:24 -07:00
|
|
|
opsGroup.GET("/invite", ui.AmWrap(InviteToTopic))
|
2026-01-28 23:16:46 -07:00
|
|
|
opsGroup.GET("/rmbozo/:uid", ui.AmWrap(TopicRemoveBozo))
|
2025-09-14 16:57:52 -06:00
|
|
|
|
2025-09-11 23:38:41 -06:00
|
|
|
return e
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-19 23:23:58 -07:00
|
|
|
// ampool is the worker pool for one-shot background tasks.
|
|
|
|
|
var ampool *util.WorkerPool
|
|
|
|
|
|
2026-03-07 12:36:30 -07:00
|
|
|
// SystemStartTime records the time since the system was started.
|
|
|
|
|
var SystemStartTime time.Time
|
|
|
|
|
|
2025-09-14 22:05:17 -06:00
|
|
|
// main is Ye Olde Main Function.
|
2025-09-11 23:38:41 -06:00
|
|
|
func main() {
|
2026-03-07 12:36:30 -07:00
|
|
|
SystemStartTime = time.Now()
|
2025-09-17 22:50:30 -06:00
|
|
|
// Configure the system.
|
2025-09-17 17:26:54 -06:00
|
|
|
config.SetupConfig()
|
2026-03-08 23:10:07 -06:00
|
|
|
closer := SetupLogging()
|
|
|
|
|
defer closer()
|
2025-10-04 13:32:47 -06:00
|
|
|
closer, err := database.SetupDb()
|
2025-09-20 15:57:29 -06:00
|
|
|
if err != nil {
|
|
|
|
|
panic(fmt.Sprintf("Database open failure: %v", err))
|
|
|
|
|
}
|
2025-10-04 13:32:47 -06:00
|
|
|
defer closer()
|
|
|
|
|
closer = email.SetupMailSender()
|
|
|
|
|
defer closer()
|
2025-10-30 22:33:39 -06:00
|
|
|
htmlcheck.SetupDicts()
|
2026-02-20 15:52:36 -07:00
|
|
|
closer = ui.SetupUILayer()
|
2025-10-16 23:05:34 -06:00
|
|
|
defer closer()
|
2025-09-14 16:10:29 -06:00
|
|
|
|
2026-02-21 22:05:33 -07:00
|
|
|
// Determine my IP address and the admin user.
|
|
|
|
|
myIP, err := util.MyIPAddress()
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-16 15:38:00 -07:00
|
|
|
// Set up to trap SIGINT/SIGTERM and shut down gracefully
|
|
|
|
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
2025-09-17 23:07:42 -06:00
|
|
|
defer stop()
|
|
|
|
|
|
2025-12-19 23:23:58 -07:00
|
|
|
// Set up ampool.
|
2026-02-19 23:24:49 -07:00
|
|
|
ampool = util.AmNewPool(ctx, config.GlobalConfig.Tuning.WorkerTasks, config.GlobalConfig.Tuning.Queues.WorkerTasks)
|
2025-12-19 23:23:58 -07:00
|
|
|
go func() {
|
|
|
|
|
<-ctx.Done()
|
|
|
|
|
ampool.Shutdown()
|
|
|
|
|
}()
|
|
|
|
|
|
2025-12-20 22:29:26 -07:00
|
|
|
// Set up Echo.
|
|
|
|
|
e := setupEcho()
|
|
|
|
|
|
2026-02-21 22:05:33 -07:00
|
|
|
// Audit the startup
|
2026-02-21 22:58:57 -07:00
|
|
|
database.AmStoreAudit(database.AmNewAudit(database.AuditStartup, 0, myIP.String(),
|
2026-02-21 22:05:33 -07:00
|
|
|
fmt.Sprintf("version=%s", config.AMSTERDAM_VERSION)))
|
2026-02-22 22:18:16 -07:00
|
|
|
defer func() {
|
|
|
|
|
// Audit the shutdown
|
|
|
|
|
database.AmStoreAudit(database.AmNewAudit(database.AuditShutdown, 0, myIP.String()))
|
|
|
|
|
}()
|
2026-02-21 22:05:33 -07:00
|
|
|
|
2026-03-07 12:36:30 -07:00
|
|
|
stime := time.Since(SystemStartTime)
|
2026-02-26 23:14:06 -07:00
|
|
|
log.Infof("Amsterdam %s startup sequence completed in %v", config.AMSTERDAM_VERSION, stime)
|
2026-02-23 15:19:30 -07:00
|
|
|
|
2025-09-17 23:07:42 -06:00
|
|
|
// Start server
|
|
|
|
|
go func() {
|
2026-03-05 16:25:33 -07:00
|
|
|
if err := e.Start(config.GlobalComputedConfig.Listen); err != nil && err != http.ErrServerClosed {
|
2025-09-17 23:07:42 -06:00
|
|
|
e.Logger.Fatalf("shutting down the server: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
// Wait for the interrupt signal and then gracefully shut the server down.
|
|
|
|
|
<-ctx.Done()
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
|
|
|
defer cancel()
|
|
|
|
|
if err := e.Shutdown(ctx); err != nil {
|
|
|
|
|
e.Logger.Fatal(err)
|
|
|
|
|
}
|
2025-09-05 21:46:10 -06:00
|
|
|
}
|