From be70109c43359c1c2793ef20ac43e3250c64d67e Mon Sep 17 00:00:00 2001 From: mstar Date: Sat, 26 Oct 2024 11:42:51 +0200 Subject: [PATCH] Serverside stuff. Mostly shuffling things around --- .gitignore | 2 +- authentication-flow.md | 4 +- config/config.go | 6 + main.go | 9 +- server/apiFrontendAccounts.go | 1 + server/apiFrontendNotes.go | 5 + server/apiFrontendRouter.go | 8 + server/apiRouter.go | 286 ++++++++++++++++++++++++++++++++++ server/frontend.go | 14 -- server/frontendRouter.go | 22 +++ server/healthAndMetrics.go | 9 ++ server/middlewares.go | 15 ++ server/server.go | 17 +- server/utils.go | 33 ++++ storage/emote.go | 12 ++ storage/notes.go | 5 +- storage/remoteServerInfo.go | 19 +-- storage/roles.go | 2 +- storage/storage.go | 79 +++++++++- storage/user.go | 36 ++--- 20 files changed, 513 insertions(+), 71 deletions(-) create mode 100644 server/apiFrontendAccounts.go create mode 100644 server/apiFrontendNotes.go create mode 100644 server/apiFrontendRouter.go create mode 100644 server/apiRouter.go delete mode 100644 server/frontend.go create mode 100644 server/frontendRouter.go create mode 100644 server/utils.go create mode 100644 storage/emote.go diff --git a/.gitignore b/.gitignore index 1ae9a46..168c9d0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ db.sqlite /frontend/ config.toml .air.toml -*/**/.null_* +*/.null_* # --- Section ember frontend --- diff --git a/authentication-flow.md b/authentication-flow.md index e7c4224..4346e74 100644 --- a/authentication-flow.md +++ b/authentication-flow.md @@ -17,8 +17,8 @@ 3. Get webauthn options from response 4. Get passkey response 5. Send response to Server -6. Server checks and replies with session token -7. Frontend uses session token for authorisation of all requests afterwards +6. Server checks and gives ok or fail. + Ok also sets a server only cookie with an access token ## api diff --git a/config/config.go b/config/config.go index e53b696..f16914a 100644 --- a/config/config.go +++ b/config/config.go @@ -82,6 +82,11 @@ type ConfigMail struct { TemplateOverwriteDirectory *string `toml:"template_overwrite_directory,omitempty"` } +type ConfigSelf struct { + ServerActorDisplayName string `toml:"server_actor_display_name"` + ServerDisplayName string `toml:"server_display_name"` +} + type Config struct { General ConfigGeneral `toml:"general"` SSL ConfigSSL `toml:"ssl"` @@ -89,6 +94,7 @@ type Config struct { Webauthn ConfigWebAuthn `toml:"webauthn"` Storage ConfigStorage `toml:"storage"` Mail ConfigMail `toml:"mail"` + Self ConfigSelf `toml:"self"` } var GlobalConfig Config diff --git a/main.go b/main.go index 084d8c5..a39ac7f 100644 --- a/main.go +++ b/main.go @@ -36,11 +36,6 @@ func main() { Str("config-file", *flagConfigFile). Msg("Failed to read config and couldn't write default") } - // res, err := ap.GetAccountWebfinger("@aufricus_athudath@activitypub.academy") - // log.Info(). - // Err(err). - // Any("webfinger", res). - // Msg("Webfinger request result for @aufricus_athudath@activitypub.academy") storageCache, err := cache.NewCache( config.GlobalConfig.Storage.MaxInMemoryCacheSize, config.GlobalConfig.Storage.RedisUrl, @@ -79,8 +74,8 @@ func main() { server := server.NewServer( store, pkey, - util.NewFSWrapper(reactiveFS, "frontend-reactive/dist/", true), - util.NewFSWrapper(nojsFS, "frontend-noscript/", true), + util.NewFSWrapper(reactiveFS, "frontend-reactive/dist/", false), + util.NewFSWrapper(nojsFS, "frontend-noscript/", false), ) server.Start(":8000") // TODO: Set up media server diff --git a/server/apiFrontendAccounts.go b/server/apiFrontendAccounts.go new file mode 100644 index 0000000..abb4e43 --- /dev/null +++ b/server/apiFrontendAccounts.go @@ -0,0 +1 @@ +package server diff --git a/server/apiFrontendNotes.go b/server/apiFrontendNotes.go new file mode 100644 index 0000000..9f225ce --- /dev/null +++ b/server/apiFrontendNotes.go @@ -0,0 +1,5 @@ +package server + +import "net/http" + +func getLinstromNote(w http.ResponseWriter, r *http.Request) {} diff --git a/server/apiFrontendRouter.go b/server/apiFrontendRouter.go new file mode 100644 index 0000000..4bbca6b --- /dev/null +++ b/server/apiFrontendRouter.go @@ -0,0 +1,8 @@ +package server + +import "net/http" + +func setupApiFrontendRouter() http.Handler { + router := http.NewServeMux() + return router +} diff --git a/server/apiRouter.go b/server/apiRouter.go new file mode 100644 index 0000000..74bd5fe --- /dev/null +++ b/server/apiRouter.go @@ -0,0 +1,286 @@ +package server + +import "net/http" + +// Mounted at /api +func setupApiRouter() http.Handler { + router := http.NewServeMux() + + // Section MastoApi + router.HandleFunc("GET /oauth/authorize", placeholderEndpoint) + router.HandleFunc("POST /oauth/token", placeholderEndpoint) + router.HandleFunc("POST /oauth/revoke", placeholderEndpoint) + router.HandleFunc("GET /.well-known/oauth-authorization-server", placeholderEndpoint) + + router.HandleFunc("POST /v1/apps", placeholderEndpoint) + router.HandleFunc("GET /v1/apps/verify_credentials", placeholderEndpoint) + router.HandleFunc("POST /v1/emails/confirmations", placeholderEndpoint) + router.HandleFunc("POST /v1/accounts", placeholderEndpoint) + router.HandleFunc("GET /v1/accounts/verify_credentials", placeholderEndpoint) + router.HandleFunc("PATCH /v1/accounts/update_credentials", placeholderEndpoint) + router.HandleFunc("GET /v1/accounts/{id}", placeholderEndpoint) + router.HandleFunc("GET /v1/accounts", placeholderEndpoint) + router.HandleFunc("GET /v1/accounts/{id}/statuses", placeholderEndpoint) + router.HandleFunc("GET /v1/accounts/{id}/followers", placeholderEndpoint) + router.HandleFunc("GET /v1/accounts/{id}/following", placeholderEndpoint) + router.HandleFunc("GET /v1/accounts/{id}/featured_tags", placeholderEndpoint) + router.HandleFunc("GET /v1/accounts/{id}/lists", placeholderEndpoint) + router.HandleFunc("POST /v1/accounts/{id}/follow", placeholderEndpoint) + router.HandleFunc("POST /v1/accounts/{id}/unfollow", placeholderEndpoint) + router.HandleFunc("POST /v1/accounts/{id}/remove_from_followers", placeholderEndpoint) + router.HandleFunc("POST /v1/accounts/{id}/block", placeholderEndpoint) + router.HandleFunc("POST /v1/accounts/{id}/unblock", placeholderEndpoint) + router.HandleFunc("POST /v1/accounts/{id}/mute", placeholderEndpoint) + router.HandleFunc("POST /v1/accounts/{id}/unmute", placeholderEndpoint) + router.HandleFunc("POST /v1/accounts/{id}/pin", placeholderEndpoint) + router.HandleFunc("POST /v1/accounts/{id}/unpin", placeholderEndpoint) + router.HandleFunc("POST /v1/accounts/{id}/note", placeholderEndpoint) + router.HandleFunc("GET /v1/accounts/relationships", placeholderEndpoint) + router.HandleFunc("GET /v1/accounts/familiar_followers", placeholderEndpoint) + router.HandleFunc("GET /v1/accounts/search", placeholderEndpoint) + router.HandleFunc("GET /v1/accounts/lookup", placeholderEndpoint) + router.HandleFunc("GET /v1/bookmarks", placeholderEndpoint) + router.HandleFunc("GET /v1/favourites", placeholderEndpoint) + router.HandleFunc("GET /v1/mutes", placeholderEndpoint) + router.HandleFunc("GET /v1/blocks", placeholderEndpoint) + router.HandleFunc("GET /v1/domain_blocks", placeholderEndpoint) + router.HandleFunc("POST /v1/domain_blocks", placeholderEndpoint) + router.HandleFunc("DELETE /v1/domain_blocks", placeholderEndpoint) + router.HandleFunc("GET /v2/filters", placeholderEndpoint) + router.HandleFunc("GET /v2/filters/{id}", placeholderEndpoint) + router.HandleFunc("POST /v2/filters", placeholderEndpoint) + router.HandleFunc("PUT /v2/filters/{id}", placeholderEndpoint) + router.HandleFunc("DELETE /v2/filters/{id}", placeholderEndpoint) + router.HandleFunc("GET /v2/filters/:filter_id/keywords", placeholderEndpoint) + router.HandleFunc("POST /v2/filters/:filter_id/keywords", placeholderEndpoint) + router.HandleFunc("GET /v2/filters/keywords/{id}", placeholderEndpoint) + router.HandleFunc("PUT /v2/filters/keywords/{id}", placeholderEndpoint) + router.HandleFunc("DELETE /v2/filters/keywords/{id}", placeholderEndpoint) + router.HandleFunc("GET /v2/filters/:filter_id/statuses", placeholderEndpoint) + router.HandleFunc("POST /v2/filters/:filter_id/statuses", placeholderEndpoint) + router.HandleFunc("GET /v2/filters/statuses/{id}", placeholderEndpoint) + router.HandleFunc("DELETE /v2/filters/statuses/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/reports", placeholderEndpoint) + router.HandleFunc("GET /v1/follow_requests", placeholderEndpoint) + router.HandleFunc("POST /v1/follow_requests/:account_id/authorize", placeholderEndpoint) + router.HandleFunc("POST /v1/follow_requests/:account_id/reject", placeholderEndpoint) + router.HandleFunc("GET /v1/endorsements", placeholderEndpoint) + router.HandleFunc("GET /v1/featured_tags", placeholderEndpoint) + router.HandleFunc("POST /v1/featured_tags", placeholderEndpoint) + router.HandleFunc("DELETE /v1/featured_tags/{id}", placeholderEndpoint) + router.HandleFunc("GET /v1/featured_tags/suggestions", placeholderEndpoint) + router.HandleFunc("GET /v1/preferences", placeholderEndpoint) + router.HandleFunc("GET /v1/followed_tags", placeholderEndpoint) + router.HandleFunc("GET /v2/suggestions", placeholderEndpoint) + router.HandleFunc("DELETE /v1/suggestions/:account_id", placeholderEndpoint) + router.HandleFunc("GET /v1/tags/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/tags/{id}/follow", placeholderEndpoint) + router.HandleFunc("POST /v1/tags/{id}/unfollow", placeholderEndpoint) + router.HandleFunc("DELETE /v1/profile/avatar", placeholderEndpoint) + router.HandleFunc("DELETE /v1/profile/header", placeholderEndpoint) + router.HandleFunc("POST /v1/statuses", placeholderEndpoint) + router.HandleFunc("GET /v1/statuses/{id}", placeholderEndpoint) + router.HandleFunc("GET /v1/statuses", placeholderEndpoint) + router.HandleFunc("DELETE /v1/statuses/{id}", placeholderEndpoint) + router.HandleFunc("GET /v1/statuses/{id}/context", placeholderEndpoint) + router.HandleFunc("POST /v1/statuses/{id}/translate", placeholderEndpoint) + router.HandleFunc("GET /v1/statuses/{id}/reblogged_by", placeholderEndpoint) + router.HandleFunc("GET /v1/statuses/{id}/favourited_by", placeholderEndpoint) + router.HandleFunc("POST /v1/statuses/{id}/favourite", placeholderEndpoint) + router.HandleFunc("POST /v1/statuses/{id}/unfavourite", placeholderEndpoint) + router.HandleFunc("POST /v1/statuses/{id}/reblog", placeholderEndpoint) + router.HandleFunc("POST /v1/statuses/{id}/unreblog", placeholderEndpoint) + router.HandleFunc("POST /v1/statuses/{id}/bookmark", placeholderEndpoint) + router.HandleFunc("POST /v1/statuses/{id}/unbookmark", placeholderEndpoint) + router.HandleFunc("POST /v1/statuses/{id}/mute", placeholderEndpoint) + router.HandleFunc("POST /v1/statuses/{id}/unmute", placeholderEndpoint) + router.HandleFunc("POST /v1/statuses/{id}/pin", placeholderEndpoint) + router.HandleFunc("POST /v1/statuses/{id}/unpin", placeholderEndpoint) + router.HandleFunc("PUT /v1/statuses/{id}", placeholderEndpoint) + router.HandleFunc("GET /v1/statuses/{id}/history", placeholderEndpoint) + router.HandleFunc("GET /v1/statuses/{id}/source", placeholderEndpoint) + router.HandleFunc("POST /v2/media", placeholderEndpoint) + router.HandleFunc("GET /v1/media/{id}", placeholderEndpoint) + router.HandleFunc("PUT /v1/media/{id}", placeholderEndpoint) + router.HandleFunc("GET /v1/polls/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/polls/{id}/votes", placeholderEndpoint) + router.HandleFunc("GET /v1/scheduled_statuses", placeholderEndpoint) + router.HandleFunc("GET /v1/scheduled_statuses/{id}", placeholderEndpoint) + router.HandleFunc("PUT /v1/scheduled_statuses/{id}", placeholderEndpoint) + router.HandleFunc("DELETE /v1/scheduled_statuses/{id}", placeholderEndpoint) + router.HandleFunc("GET /v1/timelines/public", placeholderEndpoint) + router.HandleFunc("GET /v1/timelines/tag/:hashtag", placeholderEndpoint) + router.HandleFunc("GET /v1/timelines/home", placeholderEndpoint) + router.HandleFunc("GET /v1/timelines/link", placeholderEndpoint) // ?url=:url + router.HandleFunc("GET /v1/timelines/list/:list_id", placeholderEndpoint) + router.HandleFunc("GET /v1/conversations", placeholderEndpoint) + router.HandleFunc("DELETE /v1/conversations/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/conversations/{id}/read", placeholderEndpoint) + router.HandleFunc("GET /v1/lists", placeholderEndpoint) + router.HandleFunc("GET /v1/lists/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/lists", placeholderEndpoint) + router.HandleFunc("PUT /v1/lists/{id}", placeholderEndpoint) + router.HandleFunc("DELETE /v1/lists/{id}", placeholderEndpoint) + router.HandleFunc("GET /v1/lists/{id}/accounts", placeholderEndpoint) + router.HandleFunc("POST /v1/lists/{id}/accounts", placeholderEndpoint) + router.HandleFunc("DELETE /v1/lists/{id}/accounts", placeholderEndpoint) + router.HandleFunc("GET /v1/markers", placeholderEndpoint) + router.HandleFunc("POST /v1/markers", placeholderEndpoint) + router.HandleFunc("GET /v1/streaming/health", placeholderEndpoint) + router.HandleFunc("GET /v1/streaming/user", placeholderEndpoint) + router.HandleFunc("GET /v1/streaming/user/notification", placeholderEndpoint) + router.HandleFunc("GET /v1/streaming/public", placeholderEndpoint) + router.HandleFunc("GET /v1/streaming/public/local", placeholderEndpoint) + router.HandleFunc("GET /v1/streaming/public/remote", placeholderEndpoint) + router.HandleFunc("GET /v1/streaming/hashtag", placeholderEndpoint) + router.HandleFunc("GET /v1/streaming/hashtag/local", placeholderEndpoint) + router.HandleFunc("GET /v1/streaming/list", placeholderEndpoint) + router.HandleFunc("GET /v1/streaming/direct", placeholderEndpoint) + router.HandleFunc("GET /v2/notifications", placeholderEndpoint) + router.HandleFunc("GET /v2/notifications/:group_key", placeholderEndpoint) + router.HandleFunc("POST /v2/notifications/:group_key/dismiss", placeholderEndpoint) + router.HandleFunc("GET /v2/notifications/:group_key/accounts", placeholderEndpoint) + router.HandleFunc("GET /v2/notifications/unread_count", placeholderEndpoint) + router.HandleFunc("GET /v1/notifications", placeholderEndpoint) + router.HandleFunc("GET /v1/notifications/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/notifications/clear", placeholderEndpoint) + router.HandleFunc("POST /v1/notifications/{id}/dismiss", placeholderEndpoint) + router.HandleFunc("GET /v1/notifications/unread_count", placeholderEndpoint) + router.HandleFunc("GET /v2/notifications/policy", placeholderEndpoint) + router.HandleFunc("PATCH /v2/notifications/policy", placeholderEndpoint) + router.HandleFunc("GET /v1/notifications/requests", placeholderEndpoint) + router.HandleFunc("GET /v1/notifications/requests/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/notifications/requests/{id}/accept", placeholderEndpoint) + router.HandleFunc("POST /v1/notifications/requests/{id}/dismiss", placeholderEndpoint) + router.HandleFunc("POST /v1/notifications/requests/accept", placeholderEndpoint) + router.HandleFunc("POST /v1/notifications/requests/dismiss", placeholderEndpoint) + router.HandleFunc("GET /v1/notifications/requests/merged", placeholderEndpoint) + router.HandleFunc("POST /v1/push/subscription", placeholderEndpoint) + router.HandleFunc("GET /v1/push/subscription", placeholderEndpoint) + router.HandleFunc("PUT /v1/push/subscription", placeholderEndpoint) + router.HandleFunc("DELETE /v1/push/subscription", placeholderEndpoint) + router.HandleFunc("GET /v2/search", placeholderEndpoint) + router.HandleFunc("GET /v2/instance", placeholderEndpoint) + router.HandleFunc("GET /v1/instance/peers", placeholderEndpoint) + router.HandleFunc("GET /v1/instance/activity", placeholderEndpoint) + router.HandleFunc("GET /v1/instance/rules", placeholderEndpoint) + router.HandleFunc("GET /v1/instance/domain_blocks", placeholderEndpoint) + router.HandleFunc("GET /v1/instance/extended_description", placeholderEndpoint) + router.HandleFunc("GET /v1/instance/translation_languages", placeholderEndpoint) + router.HandleFunc("GET /v1/trends/tags", placeholderEndpoint) + router.HandleFunc("GET /v1/trends/statuses", placeholderEndpoint) + router.HandleFunc("GET /v1/trends/links", placeholderEndpoint) + router.HandleFunc("GET /v1/directory", placeholderEndpoint) + router.HandleFunc("GET /v1/custom_emojis", placeholderEndpoint) + router.HandleFunc("GET /v1/announcements", placeholderEndpoint) + router.HandleFunc("POST /v1/announcements/{id}/dismiss", placeholderEndpoint) + router.HandleFunc("PUT /v1/announcements/{id}/reactions/:name", placeholderEndpoint) + router.HandleFunc("DELETE /v1/announcements/{id}/reactions/:name", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/accounts", placeholderEndpoint) + router.HandleFunc("GET /v2/admin/accounts", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/accounts/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/accounts/{id}/approve", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/accounts/{id}/reject", placeholderEndpoint) + router.HandleFunc("DELETE /v1/admin/accounts/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/accounts/{id}/action", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/accounts/{id}/enable", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/accounts/{id}/unsilence", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/accounts/{id}/unsuspend", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/accounts/{id}/unsensitive", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/canonical_email_blocks", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/canonical_email_blocks/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/canonical_email_blocks/test", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/canonical_email_blocks", placeholderEndpoint) + router.HandleFunc("DELETE /v1/admin/canonical_email_blocks/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/dimensions", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/domain_allows", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/domain_allows/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/domain_allows", placeholderEndpoint) + router.HandleFunc("DELETE /v1/admin/domain_allows/{id}", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/domain_blocks", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/domain_blocks/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/domain_blocks", placeholderEndpoint) + router.HandleFunc("PUT /v1/admin/domain_blocks/{id}", placeholderEndpoint) + router.HandleFunc("DELETE /v1/admin/domain_blocks/{id}", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/email_domain_blocks", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/email_domain_blocks/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/email_domain_blocks", placeholderEndpoint) + router.HandleFunc("DELETE /v1/admin/email_domain_blocks/{id}", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/ip_blocks", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/ip_blocks/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/ip_blocks", placeholderEndpoint) + router.HandleFunc("PUT /v1/admin/ip_blocks/{id}", placeholderEndpoint) + router.HandleFunc("DELETE /v1/admin/ip_blocks/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/measures", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/reports", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/reports/{id}", placeholderEndpoint) + router.HandleFunc("PUT /v1/admin/reports/{id}", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/reports/{id}/assign_to_self", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/reports/{id}/unassign", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/reports/{id}/resolve", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/reports/{id}/reopen", placeholderEndpoint) + router.HandleFunc("POST /v1/admin/retention", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/trends/links", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/trends/statuses", placeholderEndpoint) + router.HandleFunc("GET /v1/admin/trends/tags", placeholderEndpoint) + router.HandleFunc("GET /oembed", placeholderEndpoint) + + router.HandleFunc( + "GET /v1/accounts/{id}/identity_proofs", + placeholderEndpoint, + ) // Deprecated + router.HandleFunc( + "GET /v1/filters", + placeholderEndpoint, + ) // Deprecated + router.HandleFunc( + "GET /v1/filters/{id}", + placeholderEndpoint, + ) // Deprecated + router.HandleFunc( + "POST /v1/filters", + placeholderEndpoint, + ) // Deprecated + router.HandleFunc( + "PUT /v1/filters/{id}", + placeholderEndpoint, + ) // Deprecated + router.HandleFunc( + "DELETE /v1/filters/{id}", + placeholderEndpoint, + ) // Deprecated + router.HandleFunc( + "GET /v1/suggestions", + placeholderEndpoint, + ) // Deprecated + router.HandleFunc( + "GET /v1/statuses/{id}/card", + placeholderEndpoint, + ) // Deprecated + router.HandleFunc( + "POST /v1/media", + placeholderEndpoint, + ) // Deprecated + router.HandleFunc( + "GET /v1/timelines/direct", + placeholderEndpoint, + ) // Deprecated + router.HandleFunc( + "POST /v1/notifications/dismiss", + placeholderEndpoint, + ) // Removed + router.HandleFunc( + "GET /v1/search", + placeholderEndpoint, + ) // Removed + router.HandleFunc( + "GET /v1/instance", + placeholderEndpoint, + ) // Deprecated + router.HandleFunc( + "GET /proofs", + placeholderEndpoint, + ) // Removed + + return router +} diff --git a/server/frontend.go b/server/frontend.go deleted file mode 100644 index 990822a..0000000 --- a/server/frontend.go +++ /dev/null @@ -1,14 +0,0 @@ -package server - -import ( - "io/fs" - "net/http" -) - -func setupFrontendRouter(interactiveFs, noscriptFs fs.FS) http.Handler { - router := http.NewServeMux() - router.Handle("/noscript/", http.StripPrefix("/noscript", http.FileServerFS(noscriptFs))) - router.Handle("/", http.FileServerFS(interactiveFs)) - - return router -} diff --git a/server/frontendRouter.go b/server/frontendRouter.go new file mode 100644 index 0000000..e7aa33c --- /dev/null +++ b/server/frontendRouter.go @@ -0,0 +1,22 @@ +package server + +import ( + "io/fs" + "net/http" +) + +// Mounted at / +func setupFrontendRouter(interactiveFs, noscriptFs fs.FS) http.Handler { + router := http.NewServeMux() + router.Handle("/noscript/", http.StripPrefix("/noscript", http.FileServerFS(noscriptFs))) + router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + http.ServeFileFS(w, r, interactiveFs, "index.html") + }) + router.Handle("/assets/", http.FileServerFS(interactiveFs)) + router.HandleFunc( + "/robots.txt", + func(w http.ResponseWriter, r *http.Request) { http.ServeFileFS(w, r, interactiveFs, "robots.txt") }, + ) + + return router +} diff --git a/server/healthAndMetrics.go b/server/healthAndMetrics.go index d9c52e8..63fc35f 100644 --- a/server/healthAndMetrics.go +++ b/server/healthAndMetrics.go @@ -12,8 +12,10 @@ import ( "gitlab.com/mstarongitlab/goutils/other" ) +// Mounted at /profiling func setupProfilingHandler() http.Handler { router := http.NewServeMux() + router.HandleFunc("/", profilingRootHandler) router.HandleFunc("GET /current-goroutines", metricActiveGoroutinesHandler) router.HandleFunc("GET /memory", metricMemoryStatsHandler) router.HandleFunc("GET /pprof/cpu", pprof.Profile) @@ -28,6 +30,13 @@ func isAliveHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "yup") } +func profilingRootHandler(w http.ResponseWriter, r *http.Request) { + fmt.Fprint( + w, + "Endpoints: /, /{memory,current-goroutines}, /pprof/{cpu,memory,goroutines,blockers}", + ) +} + func metricActiveGoroutinesHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "{\"goroutines\": %d}", runtime.NumGoroutine()) } diff --git a/server/middlewares.go b/server/middlewares.go index 798214f..5efc928 100644 --- a/server/middlewares.go +++ b/server/middlewares.go @@ -4,11 +4,13 @@ import ( "context" "net/http" "slices" + "strings" "time" "github.com/rs/zerolog/hlog" "github.com/rs/zerolog/log" "gitlab.com/mstarongitlab/goutils/other" + "gitlab.com/mstarongitlab/linstrom/config" ) type HandlerBuilder func(http.Handler) http.Handler @@ -38,6 +40,9 @@ func LoggingMiddleware(handler http.Handler) http.Handler { return ChainMiddlewares(handler, hlog.NewHandler(log.Logger), hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) { + if strings.HasPrefix(r.URL.Path, "/assets") { + return + } hlog.FromRequest(r).Info(). Str("method", r.Method). Stringer("url", r.URL). @@ -85,3 +90,13 @@ func passkeyIdToAccountIdTransformerMiddleware(handler http.Handler) http.Handle handler.ServeHTTP(w, r) }) } + +func profilingAuthenticationMiddleware(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.FormValue("password") != config.GlobalConfig.Admin.ProfilingPassword { + other.HttpErr(w, HttpErrIdNotAuthenticated, "Bad password", http.StatusUnauthorized) + return + } + handler.ServeHTTP(w, r) + }) +} diff --git a/server/server.go b/server/server.go index 47f7882..6d4d669 100644 --- a/server/server.go +++ b/server/server.go @@ -7,7 +7,6 @@ import ( "github.com/mstarongithub/passkey" "github.com/rs/zerolog/log" "gitlab.com/mstarongitlab/goutils/other" - "gitlab.com/mstarongitlab/linstrom/config" "gitlab.com/mstarongitlab/linstrom/storage" ) @@ -33,17 +32,13 @@ func buildRootHandler(pkey *passkey.Passkey, reactiveFS, staticFS fs.FS) http.Ha mux.Handle("/", setupFrontendRouter(reactiveFS, staticFS)) mux.Handle("/pk/", http.StripPrefix("/pk", http.FileServer(http.Dir("pk-auth")))) mux.HandleFunc("/alive", isAliveHandler) + mux.Handle("/api/", http.StripPrefix("/api", setupApiRouter())) - profilingHandler := setupProfilingHandler() - mux.Handle("/profiling/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Only allow access to profiling if a password is provided and it matches the one in the config - if r.FormValue("password") != config.GlobalConfig.Admin.ProfilingPassword { - // Specifically reply with a plain 404 - http.Error(w, "", 404) - return - } - profilingHandler.ServeHTTP(w, r) - })) + mux.Handle( + "/profiling/", + http.StripPrefix("/profiling", profilingAuthenticationMiddleware(setupProfilingHandler())), + ) + // temporary until proper route structure exists mux.Handle( "/authonly/", pkey.Auth( diff --git a/server/utils.go b/server/utils.go new file mode 100644 index 0000000..161debc --- /dev/null +++ b/server/utils.go @@ -0,0 +1,33 @@ +package server + +import ( + "net/http" + + "github.com/rs/zerolog/hlog" + "gitlab.com/mstarongitlab/goutils/other" + "gitlab.com/mstarongitlab/linstrom/storage" +) + +func placeholderEndpoint(w http.ResponseWriter, r *http.Request) { + hlog.FromRequest(r).Error().Stringer("url", r.URL).Msg("Placeholder endpoint accessed") + other.HttpErr( + w, + HttpErrIdPlaceholder, + "Endpoint not implemented yet, this is a placeholder", + http.StatusInternalServerError, + ) +} + +func getStorageFromRequest(w http.ResponseWriter, r *http.Request) *storage.Storage { + store, ok := r.Context().Value(ContextKeyStorage).(*storage.Storage) + if !ok { + other.HttpErr( + w, + HttpErrIdMissingContextValue, + "Missing storage in context", + http.StatusInternalServerError, + ) + return nil + } + return store +} diff --git a/storage/emote.go b/storage/emote.go new file mode 100644 index 0000000..53242bd --- /dev/null +++ b/storage/emote.go @@ -0,0 +1,12 @@ +package storage + +import "gorm.io/gorm" + +type Emote struct { + gorm.Model + // Metadata MediaMetadata // `gorm:"foreignKey:MetadataId"` + MetadataId string + Name string + // Server RemoteServer // `gorm:"foreignKey:ServerId;references:ID"` + ServerId string +} diff --git a/storage/notes.go b/storage/notes.go index cc01f5a..d66c11d 100644 --- a/storage/notes.go +++ b/storage/notes.go @@ -22,8 +22,9 @@ type Note struct { // Soft delete means that this entry still exists in the db, but gorm won't include it anymore unless specifically told to // If not null, this entry is marked as deleted DeletedAt gorm.DeletedAt `gorm:"index"` - Creator string // Id of the author in the db, not the handle - Remote bool // Whether the note is originally a remote one and just "cached" + // Creator Account // `gorm:"foreignKey:CreatorId;references:ID"` // Account that created the post + CreatorId string + Remote bool // Whether the note is originally a remote one and just "cached" // Raw content of the note. So without additional formatting applied // Might already have formatting applied beforehand from the origin server RawContent string diff --git a/storage/remoteServerInfo.go b/storage/remoteServerInfo.go index d28812b..61ba85f 100644 --- a/storage/remoteServerInfo.go +++ b/storage/remoteServerInfo.go @@ -1,30 +1,21 @@ package storage import ( - "time" - "gorm.io/gorm" ) type RemoteServer struct { - ID string `gorm:"primarykey"` // ID is also server url - CreatedAt time.Time // When this entry was created - UpdatedAt time.Time // When this entry was last updated - // When this entry was deleted (for soft deletions) - // Soft delete means that this entry still exists in the db, but gorm won't include it anymore unless specifically told to - // If not null, this entry is marked as deleted - DeletedAt gorm.DeletedAt `gorm:"index"` + gorm.Model ServerType RemoteServerType // What software the server is running. Useful for formatting + Domain string // `gorm:"primaryKey"` // Domain the server exists under. Additional primary key Name string // What the server wants to be known as (usually same as url) Icon string // ID of a media file IsSelf bool // Whether this server is yours truly } func (s *Storage) FindRemoteServer(url string) (*RemoteServer, error) { - server := RemoteServer{ - ID: url, - } - err := s.db.First(&server).Error + server := RemoteServer{} + err := s.db.Where("domain = ?").First(&server).Error switch err { case nil: return &server, nil @@ -63,7 +54,7 @@ func (s *Storage) NewRemoteServer( return nil, err } server := RemoteServer{ - ID: url, + Domain: url, Name: displayName, Icon: icon, ServerType: serverType, diff --git a/storage/roles.go b/storage/roles.go index 19eb990..f75edfc 100644 --- a/storage/roles.go +++ b/storage/roles.go @@ -22,7 +22,7 @@ type Role struct { gorm.Model // Name of the role - Name string + Name string `gorm:"primaryKey"` // Priority of the role // Lower priority gets applied first and thus overwritten by higher priority ones diff --git a/storage/storage.go b/storage/storage.go index d210a56..4eb8ea5 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -6,12 +6,21 @@ package storage import ( + "crypto/ed25519" + "fmt" + "github.com/rs/zerolog/log" + "gitlab.com/mstarongitlab/linstrom/config" "gitlab.com/mstarongitlab/linstrom/storage/cache" "gorm.io/driver/postgres" "gorm.io/gorm" ) +// Always keep a reference of the server's own RemoteServer entry here +// Removes the need to perform a db request every time a new local anything +// is created +var serverSelf RemoteServer + // Storage is responsible for all database, cache and media related actions // and serves as the lowest layer of the cake type Storage struct { @@ -36,10 +45,78 @@ func NewStorage(dbUrl string, cache *cache.Cache) (*Storage, error) { InboundJob{}, OutboundJob{}, AccessToken{}, + Emote{}, ) if err != nil { + return nil, fmt.Errorf("failed to apply migrations: %w", err) + } + + s := &Storage{db, cache} + + if err = s.insertSelfFromConfig(); err != nil { return nil, err } - return &Storage{db, cache}, nil + return s, nil +} + +func (s *Storage) insertSelfFromConfig() error { + const ServerActorId = "self" + + var err error + + // Insert server info + serverData := RemoteServer{} + err = s.db.Where("id = 1"). + Attrs(RemoteServer{ + Domain: config.GlobalConfig.General.GetFullDomain(), + }). + Assign(RemoteServer{ + IsSelf: true, + Name: config.GlobalConfig.Self.ServerDisplayName, + // Icon: "", // TODO: Set to server icon media + }).FirstOrCreate(&serverData).Error + if err != nil { + return err + } + // Set module specific global var + serverSelf = serverData + + // Insert server actor + serverActor := Account{} + serverActorPublicKey, serverActorPrivateKey, err := ed25519.GenerateKey(nil) + if err != nil { + return err + } + err = s.db.Where(Account{ID: ServerActorId}). + // Values to always (re)set after launch + Assign(Account{ + DisplayName: config.GlobalConfig.Self.ServerActorDisplayName, + // Server: serverData, + ServerId: serverData.ID, + // CustomFields: []uint{}, + Description: "Server actor of a Linstrom server", + // Tags: []string{}, + IsBot: true, + // Followers: []string{}, + // Follows: []string{}, + // Icon: "", // TODO: Replace with reference to server icon + // Background: "", // TODO: Replace with reference to background media + // Banner: "", // TODO: Replace with reference to banner media + Indexable: false, + RestrictedFollow: false, + IdentifiesAs: []Being{}, + Gender: []string{}, + Roles: []string{}, // TODO: Add server actor role once created + }). + // Values that'll only be set on first creation + Attrs(Account{ + PublicKey: serverActorPublicKey, + PrivateKey: serverActorPrivateKey, + }). + FirstOrCreate(&serverActor).Error + if err != nil { + return err + } + return nil } diff --git a/storage/user.go b/storage/user.go index 4e28242..fbb9a48 100644 --- a/storage/user.go +++ b/storage/user.go @@ -31,21 +31,21 @@ type Account struct { // When this entry was deleted (for soft deletions) // Soft delete means that this entry still exists in the db, but gorm won't include it anymore unless specifically told to // If not null, this entry is marked as deleted - DeletedAt gorm.DeletedAt `gorm:"index"` - Remote bool // Whether the account is a local or remote one - Server string // The url of the server this account is from - DisplayName string // The display name of the user. Can be different from the handle - CustomFields []uint `gorm:"serializer:json"` // IDs to the custom fields a user has - Description string // The description of a user account - Tags []string `gorm:"serializer:json"` // Hashtags - IsBot bool // Whether to mark this account as a script controlled one - Follows []string `gorm:"serializer:json"` // List of handles this account follows - Followers []string `gorm:"serializer:json"` // List of handles that follow this account - Icon string // ID of a media file used as icon - Background string // ID of a media file used as background image - Banner string // ID of a media file used as banner - Indexable bool // Whether this account can be found by crawlers - PublicKey []byte // The public key of the account + DeletedAt gorm.DeletedAt `gorm:"index"` + // Server RemoteServer // `gorm:"foreignKey:ServerId;references:ID"` // The server this user is from + ServerId uint // Id of the server this user is from, needed for including RemoteServer + DisplayName string // The display name of the user. Can be different from the handle + CustomFields []uint `gorm:"serializer:json"` // IDs to the custom fields a user has + Description string // The description of a user account + Tags []string `gorm:"serializer:json"` // Hashtags + IsBot bool // Whether to mark this account as a script controlled one + Follows []string `gorm:"serializer:json"` // List of handles this account follows + Followers []string `gorm:"serializer:json"` // List of handles that follow this account + Icon string // ID of a media file used as icon + Background string // ID of a media file used as background image + Banner string // ID of a media file used as banner + Indexable bool // Whether this account can be found by crawlers + PublicKey []byte // The public key of the account // Whether this account restricts following // If true, the owner must approve of a follow request first RestrictedFollow bool @@ -346,8 +346,8 @@ func (s *Storage) NewLocalAccount(handle string) (*Account, error) { return nil, err } acc.Username = handle - acc.Server = config.GlobalConfig.General.GetFullDomain() - acc.Remote = false + // acc.Server = serverSelf + acc.ServerId = serverSelf.ID acc.DisplayName = handle publicKey, privateKey, err := ed25519.GenerateKey(nil) @@ -419,7 +419,7 @@ func (s *Storage) GetOrCreateUser(userID string) passkey.User { Str("account-handle", userID). Msg("Looking for or creating account for passkey stuff") acc := &Account{} - res := s.db.Where(Account{Username: userID, Server: config.GlobalConfig.General.GetFullDomain()}). + res := s.db.Where(Account{Username: userID, ServerId: serverSelf.ID}). First(acc) if errors.Is(res.Error, gorm.ErrRecordNotFound) { log.Debug().Str("account-handle", userID)