node build fixed

This commit is contained in:
ra_ma
2025-09-20 14:08:38 +01:00
parent c6ebbe069d
commit 3d298fa434
1516 changed files with 535727 additions and 2 deletions

View File

@@ -0,0 +1,560 @@
package handlers
import (
"net/http"
"path/filepath"
"seanime/internal/core"
util "seanime/internal/util/proxies"
"strings"
"time"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/rs/zerolog"
"github.com/ziflex/lecho/v3"
)
type Handler struct {
App *core.App
}
func InitRoutes(app *core.App, e *echo.Echo) {
// CORS middleware
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"*"},
AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Cookie", "Authorization",
"X-Seanime-Token", "X-Seanime-Nakama-Token", "X-Seanime-Nakama-Username", "X-Seanime-Nakama-Server-Version", "X-Seanime-Nakama-Peer-Id"},
AllowCredentials: true,
}))
lechoLogger := lecho.From(*app.Logger)
urisToSkip := []string{
"/internal/metrics",
"/_next",
"/icons",
"/events",
"/api/v1/image-proxy",
"/api/v1/mediastream/transcode/",
"/api/v1/torrent-client/list",
"/api/v1/proxy",
}
// Logging middleware
e.Use(lecho.Middleware(lecho.Config{
Logger: lechoLogger,
Skipper: func(c echo.Context) bool {
path := c.Request().URL.RequestURI()
if filepath.Ext(c.Request().URL.Path) == ".txt" ||
filepath.Ext(c.Request().URL.Path) == ".png" ||
filepath.Ext(c.Request().URL.Path) == ".ico" {
return true
}
for _, uri := range urisToSkip {
if uri == path || strings.HasPrefix(path, uri) {
return true
}
}
return false
},
Enricher: func(c echo.Context, logger zerolog.Context) zerolog.Context {
// Add which file the request came from
return logger.Str("file", c.Path())
},
}))
// Recovery middleware
e.Use(middleware.Recover())
// Client ID middleware
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Check if the client has a UUID cookie
cookie, err := c.Cookie("Seanime-Client-Id")
if err != nil || cookie.Value == "" {
// Generate a new UUID for the client
u := uuid.New().String()
// Create a cookie with the UUID
newCookie := new(http.Cookie)
newCookie.Name = "Seanime-Client-Id"
newCookie.Value = u
newCookie.HttpOnly = false // Make the cookie accessible via JS
newCookie.Expires = time.Now().Add(24 * time.Hour)
newCookie.Path = "/"
newCookie.Domain = ""
newCookie.SameSite = http.SameSiteDefaultMode
newCookie.Secure = false
// Set the cookie
c.SetCookie(newCookie)
// Store the UUID in the context for use in the request
c.Set("Seanime-Client-Id", u)
} else {
// Store the existing UUID in the context for use in the request
c.Set("Seanime-Client-Id", cookie.Value)
}
return next(c)
}
})
e.Use(headMethodMiddleware)
h := &Handler{App: app}
e.GET("/events", h.webSocketEventHandler)
v1 := e.Group("/api").Group("/v1") // Commented out for now, will be used later
//
// Auth middleware
//
v1.Use(h.OptionalAuthMiddleware)
imageProxy := &util.ImageProxy{}
v1.GET("/image-proxy", imageProxy.ProxyImage)
v1.GET("/proxy", util.VideoProxy)
v1.HEAD("/proxy", util.VideoProxy)
v1.GET("/status", h.HandleGetStatus)
v1.GET("/log/*", h.HandleGetLogContent)
v1.GET("/logs/filenames", h.HandleGetLogFilenames)
v1.DELETE("/logs", h.HandleDeleteLogs)
v1.GET("/logs/latest", h.HandleGetLatestLogContent)
v1.GET("/memory/stats", h.HandleGetMemoryStats)
v1.GET("/memory/profile", h.HandleGetMemoryProfile)
v1.GET("/memory/goroutine", h.HandleGetGoRoutineProfile)
v1.GET("/memory/cpu", h.HandleGetCPUProfile)
v1.POST("/memory/gc", h.HandleForceGC)
v1.POST("/announcements", h.HandleGetAnnouncements)
// Auth
v1.POST("/auth/login", h.HandleLogin)
v1.POST("/auth/logout", h.HandleLogout)
// Settings
v1.GET("/settings", h.HandleGetSettings)
v1.PATCH("/settings", h.HandleSaveSettings)
v1.POST("/start", h.HandleGettingStarted)
v1.PATCH("/settings/auto-downloader", h.HandleSaveAutoDownloaderSettings)
// Auto Downloader
v1.POST("/auto-downloader/run", h.HandleRunAutoDownloader)
v1.GET("/auto-downloader/rule/:id", h.HandleGetAutoDownloaderRule)
v1.GET("/auto-downloader/rule/anime/:id", h.HandleGetAutoDownloaderRulesByAnime)
v1.GET("/auto-downloader/rules", h.HandleGetAutoDownloaderRules)
v1.POST("/auto-downloader/rule", h.HandleCreateAutoDownloaderRule)
v1.PATCH("/auto-downloader/rule", h.HandleUpdateAutoDownloaderRule)
v1.DELETE("/auto-downloader/rule/:id", h.HandleDeleteAutoDownloaderRule)
v1.GET("/auto-downloader/items", h.HandleGetAutoDownloaderItems)
v1.DELETE("/auto-downloader/item", h.HandleDeleteAutoDownloaderItem)
// Other
v1.POST("/test-dump", h.HandleTestDump)
v1.POST("/directory-selector", h.HandleDirectorySelector)
v1.POST("/open-in-explorer", h.HandleOpenInExplorer)
v1.POST("/media-player/start", h.HandleStartDefaultMediaPlayer)
//
// AniList
//
v1Anilist := v1.Group("/anilist")
v1Anilist.GET("/collection", h.HandleGetAnimeCollection)
v1Anilist.POST("/collection", h.HandleGetAnimeCollection)
v1Anilist.GET("/collection/raw", h.HandleGetRawAnimeCollection)
v1Anilist.POST("/collection/raw", h.HandleGetRawAnimeCollection)
v1Anilist.GET("/media-details/:id", h.HandleGetAnilistAnimeDetails)
v1Anilist.GET("/studio-details/:id", h.HandleGetAnilistStudioDetails)
v1Anilist.POST("/list-entry", h.HandleEditAnilistListEntry)
v1Anilist.DELETE("/list-entry", h.HandleDeleteAnilistListEntry)
v1Anilist.POST("/list-anime", h.HandleAnilistListAnime)
v1Anilist.POST("/list-recent-anime", h.HandleAnilistListRecentAiringAnime)
v1Anilist.GET("/list-missed-sequels", h.HandleAnilistListMissedSequels)
v1Anilist.GET("/stats", h.HandleGetAniListStats)
//
// MAL
//
v1.POST("/mal/auth", h.HandleMALAuth)
v1.POST("/mal/logout", h.HandleMALLogout)
//
// Library
//
v1Library := v1.Group("/library")
v1Library.POST("/scan", h.HandleScanLocalFiles)
v1Library.DELETE("/empty-directories", h.HandleRemoveEmptyDirectories)
v1Library.GET("/local-files", h.HandleGetLocalFiles)
v1Library.POST("/local-files", h.HandleLocalFileBulkAction)
v1Library.PATCH("/local-files", h.HandleUpdateLocalFiles)
v1Library.DELETE("/local-files", h.HandleDeleteLocalFiles)
v1Library.GET("/local-files/dump", h.HandleDumpLocalFilesToFile)
v1Library.POST("/local-files/import", h.HandleImportLocalFiles)
v1Library.PATCH("/local-file", h.HandleUpdateLocalFileData)
v1Library.GET("/collection", h.HandleGetLibraryCollection)
v1Library.GET("/schedule", h.HandleGetAnimeCollectionSchedule)
v1Library.GET("/scan-summaries", h.HandleGetScanSummaries)
v1Library.GET("/missing-episodes", h.HandleGetMissingEpisodes)
v1Library.GET("/anime-entry/:id", h.HandleGetAnimeEntry)
v1Library.POST("/anime-entry/suggestions", h.HandleFetchAnimeEntrySuggestions)
v1Library.POST("/anime-entry/manual-match", h.HandleAnimeEntryManualMatch)
v1Library.PATCH("/anime-entry/bulk-action", h.HandleAnimeEntryBulkAction)
v1Library.POST("/anime-entry/open-in-explorer", h.HandleOpenAnimeEntryInExplorer)
v1Library.POST("/anime-entry/update-progress", h.HandleUpdateAnimeEntryProgress)
v1Library.POST("/anime-entry/update-repeat", h.HandleUpdateAnimeEntryRepeat)
v1Library.GET("/anime-entry/silence/:id", h.HandleGetAnimeEntrySilenceStatus)
v1Library.POST("/anime-entry/silence", h.HandleToggleAnimeEntrySilenceStatus)
v1Library.POST("/unknown-media", h.HandleAddUnknownMedia)
//
// Anime
//
v1.GET("/anime/episode-collection/:id", h.HandleGetAnimeEpisodeCollection)
//
// Torrent / Torrent Client
//
v1.POST("/torrent/search", h.HandleSearchTorrent)
v1.POST("/torrent-client/download", h.HandleTorrentClientDownload)
v1.GET("/torrent-client/list", h.HandleGetActiveTorrentList)
v1.POST("/torrent-client/action", h.HandleTorrentClientAction)
v1.POST("/torrent-client/rule-magnet", h.HandleTorrentClientAddMagnetFromRule)
//
// Download
//
v1.POST("/download-torrent-file", h.HandleDownloadTorrentFile)
//
// Updates
//
v1.GET("/latest-update", h.HandleGetLatestUpdate)
v1.GET("/changelog", h.HandleGetChangelog)
v1.POST("/install-update", h.HandleInstallLatestUpdate)
v1.POST("/download-release", h.HandleDownloadRelease)
//
// Theme
//
v1.GET("/theme", h.HandleGetTheme)
v1.PATCH("/theme", h.HandleUpdateTheme)
//
// Playback Manager
//
v1.POST("/playback-manager/sync-current-progress", h.HandlePlaybackSyncCurrentProgress)
v1.POST("/playback-manager/start-playlist", h.HandlePlaybackStartPlaylist)
v1.POST("/playback-manager/playlist-next", h.HandlePlaybackPlaylistNext)
v1.POST("/playback-manager/cancel-playlist", h.HandlePlaybackCancelCurrentPlaylist)
v1.POST("/playback-manager/next-episode", h.HandlePlaybackPlayNextEpisode)
v1.GET("/playback-manager/next-episode", h.HandlePlaybackGetNextEpisode)
v1.POST("/playback-manager/autoplay-next-episode", h.HandlePlaybackAutoPlayNextEpisode)
v1.POST("/playback-manager/play", h.HandlePlaybackPlayVideo)
v1.POST("/playback-manager/play-random", h.HandlePlaybackPlayRandomVideo)
//------------
v1.POST("/playback-manager/manual-tracking/start", h.HandlePlaybackStartManualTracking)
v1.POST("/playback-manager/manual-tracking/cancel", h.HandlePlaybackCancelManualTracking)
//
// Playlists
//
v1.GET("/playlists", h.HandleGetPlaylists)
v1.POST("/playlist", h.HandleCreatePlaylist)
v1.PATCH("/playlist", h.HandleUpdatePlaylist)
v1.DELETE("/playlist", h.HandleDeletePlaylist)
v1.GET("/playlist/episodes/:id/:progress", h.HandleGetPlaylistEpisodes)
//
// Onlinestream
//
v1.POST("/onlinestream/episode-source", h.HandleGetOnlineStreamEpisodeSource)
v1.POST("/onlinestream/episode-list", h.HandleGetOnlineStreamEpisodeList)
v1.DELETE("/onlinestream/cache", h.HandleOnlineStreamEmptyCache)
v1.POST("/onlinestream/search", h.HandleOnlinestreamManualSearch)
v1.POST("/onlinestream/manual-mapping", h.HandleOnlinestreamManualMapping)
v1.POST("/onlinestream/get-mapping", h.HandleGetOnlinestreamMapping)
v1.POST("/onlinestream/remove-mapping", h.HandleRemoveOnlinestreamMapping)
//
// Metadata Provider
//
v1.POST("/metadata-provider/filler", h.HandlePopulateFillerData)
v1.DELETE("/metadata-provider/filler", h.HandleRemoveFillerData)
//
// Manga
//
v1Manga := v1.Group("/manga")
v1Manga.POST("/anilist/collection", h.HandleGetAnilistMangaCollection)
v1Manga.GET("/anilist/collection/raw", h.HandleGetRawAnilistMangaCollection)
v1Manga.POST("/anilist/collection/raw", h.HandleGetRawAnilistMangaCollection)
v1Manga.POST("/anilist/list", h.HandleAnilistListManga)
v1Manga.GET("/collection", h.HandleGetMangaCollection)
v1Manga.GET("/latest-chapter-numbers", h.HandleGetMangaLatestChapterNumbersMap)
v1Manga.POST("/refetch-chapter-containers", h.HandleRefetchMangaChapterContainers)
v1Manga.GET("/entry/:id", h.HandleGetMangaEntry)
v1Manga.GET("/entry/:id/details", h.HandleGetMangaEntryDetails)
v1Manga.DELETE("/entry/cache", h.HandleEmptyMangaEntryCache)
v1Manga.POST("/chapters", h.HandleGetMangaEntryChapters)
v1Manga.POST("/pages", h.HandleGetMangaEntryPages)
v1Manga.POST("/update-progress", h.HandleUpdateMangaProgress)
v1Manga.GET("/downloaded-chapters/:id", h.HandleGetMangaEntryDownloadedChapters)
v1Manga.GET("/downloads", h.HandleGetMangaDownloadsList)
v1Manga.POST("/download-chapters", h.HandleDownloadMangaChapters)
v1Manga.POST("/download-data", h.HandleGetMangaDownloadData)
v1Manga.DELETE("/download-chapter", h.HandleDeleteMangaDownloadedChapters)
v1Manga.GET("/download-queue", h.HandleGetMangaDownloadQueue)
v1Manga.POST("/download-queue/start", h.HandleStartMangaDownloadQueue)
v1Manga.POST("/download-queue/stop", h.HandleStopMangaDownloadQueue)
v1Manga.DELETE("/download-queue", h.HandleClearAllChapterDownloadQueue)
v1Manga.POST("/download-queue/reset-errored", h.HandleResetErroredChapterDownloadQueue)
v1Manga.POST("/search", h.HandleMangaManualSearch)
v1Manga.POST("/manual-mapping", h.HandleMangaManualMapping)
v1Manga.POST("/get-mapping", h.HandleGetMangaMapping)
v1Manga.POST("/remove-mapping", h.HandleRemoveMangaMapping)
v1Manga.GET("/local-page/:path", h.HandleGetLocalMangaPage)
//
// File Cache
//
v1FileCache := v1.Group("/filecache")
v1FileCache.GET("/total-size", h.HandleGetFileCacheTotalSize)
v1FileCache.DELETE("/bucket", h.HandleRemoveFileCacheBucket)
v1FileCache.GET("/mediastream/videofiles/total-size", h.HandleGetFileCacheMediastreamVideoFilesTotalSize)
v1FileCache.DELETE("/mediastream/videofiles", h.HandleClearFileCacheMediastreamVideoFiles)
//
// Discord
//
v1Discord := v1.Group("/discord")
v1Discord.POST("/presence/manga", h.HandleSetDiscordMangaActivity)
v1Discord.POST("/presence/legacy-anime", h.HandleSetDiscordLegacyAnimeActivity)
v1Discord.POST("/presence/anime", h.HandleSetDiscordAnimeActivityWithProgress)
v1Discord.POST("/presence/anime-update", h.HandleUpdateDiscordAnimeActivityWithProgress)
v1Discord.POST("/presence/cancel", h.HandleCancelDiscordActivity)
//
// Media Stream
//
v1.GET("/mediastream/settings", h.HandleGetMediastreamSettings)
v1.PATCH("/mediastream/settings", h.HandleSaveMediastreamSettings)
v1.POST("/mediastream/request", h.HandleRequestMediastreamMediaContainer)
v1.POST("/mediastream/preload", h.HandlePreloadMediastreamMediaContainer)
// Transcode
v1.POST("/mediastream/shutdown-transcode", h.HandleMediastreamShutdownTranscodeStream)
v1.GET("/mediastream/transcode/*", h.HandleMediastreamTranscode)
v1.GET("/mediastream/subs/*", h.HandleMediastreamGetSubtitles)
v1.GET("/mediastream/att/*", h.HandleMediastreamGetAttachments)
v1.GET("/mediastream/direct", h.HandleMediastreamDirectPlay)
v1.HEAD("/mediastream/direct", h.HandleMediastreamDirectPlay)
v1.GET("/mediastream/file", h.HandleMediastreamFile)
//
// Direct Stream
//
v1.POST("/directstream/play/localfile", h.HandleDirectstreamPlayLocalFile)
v1.GET("/directstream/stream", echo.WrapHandler(h.HandleDirectstreamGetStream()))
v1.HEAD("/directstream/stream", echo.WrapHandler(h.HandleDirectstreamGetStream()))
v1.GET("/directstream/att/*", h.HandleDirectstreamGetAttachments)
//
// Torrent stream
//
v1.GET("/torrentstream/settings", h.HandleGetTorrentstreamSettings)
v1.PATCH("/torrentstream/settings", h.HandleSaveTorrentstreamSettings)
v1.POST("/torrentstream/start", h.HandleTorrentstreamStartStream)
v1.POST("/torrentstream/stop", h.HandleTorrentstreamStopStream)
v1.POST("/torrentstream/drop", h.HandleTorrentstreamDropTorrent)
v1.POST("/torrentstream/torrent-file-previews", h.HandleGetTorrentstreamTorrentFilePreviews)
v1.POST("/torrentstream/batch-history", h.HandleGetTorrentstreamBatchHistory)
v1.GET("/torrentstream/stream/*", h.HandleTorrentstreamServeStream)
//
// Extensions
//
v1Extensions := v1.Group("/extensions")
v1Extensions.POST("/playground/run", h.HandleRunExtensionPlaygroundCode)
v1Extensions.POST("/external/fetch", h.HandleFetchExternalExtensionData)
v1Extensions.POST("/external/install", h.HandleInstallExternalExtension)
v1Extensions.POST("/external/uninstall", h.HandleUninstallExternalExtension)
v1Extensions.POST("/external/edit-payload", h.HandleUpdateExtensionCode)
v1Extensions.POST("/external/reload", h.HandleReloadExternalExtensions)
v1Extensions.POST("/external/reload", h.HandleReloadExternalExtension)
v1Extensions.POST("/all", h.HandleGetAllExtensions)
v1Extensions.GET("/updates", h.HandleGetExtensionUpdateData)
v1Extensions.GET("/list", h.HandleListExtensionData)
v1Extensions.GET("/payload/:id", h.HandleGetExtensionPayload)
v1Extensions.GET("/list/development", h.HandleListDevelopmentModeExtensions)
v1Extensions.GET("/list/manga-provider", h.HandleListMangaProviderExtensions)
v1Extensions.GET("/list/onlinestream-provider", h.HandleListOnlinestreamProviderExtensions)
v1Extensions.GET("/list/anime-torrent-provider", h.HandleListAnimeTorrentProviderExtensions)
v1Extensions.GET("/user-config/:id", h.HandleGetExtensionUserConfig)
v1Extensions.POST("/user-config", h.HandleSaveExtensionUserConfig)
v1Extensions.GET("/marketplace", h.HandleGetMarketplaceExtensions)
v1Extensions.GET("/plugin-settings", h.HandleGetPluginSettings)
v1Extensions.POST("/plugin-settings/pinned-trays", h.HandleSetPluginSettingsPinnedTrays)
v1Extensions.POST("/plugin-permissions/grant", h.HandleGrantPluginPermissions)
//
// Continuity
//
v1Continuity := v1.Group("/continuity")
v1Continuity.PATCH("/item", h.HandleUpdateContinuityWatchHistoryItem)
v1Continuity.GET("/item/:id", h.HandleGetContinuityWatchHistoryItem)
v1Continuity.GET("/history", h.HandleGetContinuityWatchHistory)
//
// Sync
//
v1Local := v1.Group("/local")
v1Local.GET("/track", h.HandleLocalGetTrackedMediaItems)
v1Local.POST("/track", h.HandleLocalAddTrackedMedia)
v1Local.DELETE("/track", h.HandleLocalRemoveTrackedMedia)
v1Local.GET("/track/:id/:type", h.HandleLocalGetIsMediaTracked)
v1Local.POST("/local", h.HandleLocalSyncData)
v1Local.GET("/queue", h.HandleLocalGetSyncQueueState)
v1Local.POST("/anilist", h.HandleLocalSyncAnilistData)
v1Local.POST("/updated", h.HandleLocalSetHasLocalChanges)
v1Local.GET("/updated", h.HandleLocalGetHasLocalChanges)
v1Local.GET("/storage/size", h.HandleLocalGetLocalStorageSize)
v1Local.POST("/sync-simulated-to-anilist", h.HandleLocalSyncSimulatedDataToAnilist)
v1Local.POST("/offline", h.HandleSetOfflineMode)
//
// Debrid
//
v1.GET("/debrid/settings", h.HandleGetDebridSettings)
v1.PATCH("/debrid/settings", h.HandleSaveDebridSettings)
v1.POST("/debrid/torrents", h.HandleDebridAddTorrents)
v1.POST("/debrid/torrents/download", h.HandleDebridDownloadTorrent)
v1.POST("/debrid/torrents/cancel", h.HandleDebridCancelDownload)
v1.DELETE("/debrid/torrent", h.HandleDebridDeleteTorrent)
v1.GET("/debrid/torrents", h.HandleDebridGetTorrents)
v1.POST("/debrid/torrents/info", h.HandleDebridGetTorrentInfo)
v1.POST("/debrid/torrents/file-previews", h.HandleDebridGetTorrentFilePreviews)
v1.POST("/debrid/stream/start", h.HandleDebridStartStream)
v1.POST("/debrid/stream/cancel", h.HandleDebridCancelStream)
//
// Report
//
v1.POST("/report/issue", h.HandleSaveIssueReport)
v1.GET("/report/issue/download", h.HandleDownloadIssueReport)
//
// Nakama
//
v1Nakama := v1.Group("/nakama")
v1Nakama.GET("/ws", h.HandleNakamaWebSocket)
v1Nakama.POST("/message", h.HandleSendNakamaMessage)
v1Nakama.POST("/reconnect", h.HandleNakamaReconnectToHost)
v1Nakama.POST("/cleanup", h.HandleNakamaRemoveStaleConnections)
v1Nakama.GET("/host/anime/library", h.HandleGetNakamaAnimeLibrary)
v1Nakama.GET("/host/anime/library/collection", h.HandleGetNakamaAnimeLibraryCollection)
v1Nakama.GET("/host/anime/library/files/:id", h.HandleGetNakamaAnimeLibraryFiles)
v1Nakama.GET("/host/anime/library/files", h.HandleGetNakamaAnimeAllLibraryFiles)
v1Nakama.POST("/play", h.HandleNakamaPlayVideo)
v1Nakama.GET("/host/torrentstream/stream", h.HandleNakamaHostTorrentstreamServeStream)
v1Nakama.GET("/host/anime/library/stream", h.HandleNakamaHostAnimeLibraryServeStream)
v1Nakama.GET("/host/debridstream/stream", h.HandleNakamaHostDebridstreamServeStream)
v1Nakama.GET("/host/debridstream/url", h.HandleNakamaHostGetDebridstreamURL)
v1Nakama.GET("/stream", h.HandleNakamaProxyStream)
v1Nakama.POST("/watch-party/create", h.HandleNakamaCreateWatchParty)
v1Nakama.POST("/watch-party/join", h.HandleNakamaJoinWatchParty)
v1Nakama.POST("/watch-party/leave", h.HandleNakamaLeaveWatchParty)
}
func (h *Handler) JSON(c echo.Context, code int, i interface{}) error {
return c.JSON(code, i)
}
func (h *Handler) RespondWithData(c echo.Context, data interface{}) error {
return c.JSON(200, NewDataResponse(data))
}
func (h *Handler) RespondWithError(c echo.Context, err error) error {
return c.JSON(500, NewErrorResponse(err))
}
func headMethodMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Skip directstream route
if strings.Contains(c.Request().URL.Path, "/directstream/stream") {
return next(c)
}
if c.Request().Method == http.MethodHead {
// Set the method to GET temporarily to reuse the handler
c.Request().Method = http.MethodGet
defer func() {
c.Request().Method = http.MethodHead
}() // Restore method after
// Call the next handler and then clear the response body
if err := next(c); err != nil {
if err.Error() == echo.ErrMethodNotAllowed.Error() {
return c.NoContent(http.StatusOK)
}
return err
}
}
return next(c)
}
}