Serverside stuff. Mostly shuffling things around

This commit is contained in:
Melody Becker 2024-10-26 11:42:51 +02:00
parent 90667d96c7
commit be70109c43
20 changed files with 513 additions and 71 deletions

2
.gitignore vendored
View file

@ -6,7 +6,7 @@ db.sqlite
/frontend/ /frontend/
config.toml config.toml
.air.toml .air.toml
*/**/.null_* */.null_*
# --- Section ember frontend --- # --- Section ember frontend ---

View file

@ -17,8 +17,8 @@
3. Get webauthn options from response 3. Get webauthn options from response
4. Get passkey response 4. Get passkey response
5. Send response to Server 5. Send response to Server
6. Server checks and replies with session token 6. Server checks and gives ok or fail.
7. Frontend uses session token for authorisation of all requests afterwards Ok also sets a server only cookie with an access token
## api ## api

View file

@ -82,6 +82,11 @@ type ConfigMail struct {
TemplateOverwriteDirectory *string `toml:"template_overwrite_directory,omitempty"` 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 { type Config struct {
General ConfigGeneral `toml:"general"` General ConfigGeneral `toml:"general"`
SSL ConfigSSL `toml:"ssl"` SSL ConfigSSL `toml:"ssl"`
@ -89,6 +94,7 @@ type Config struct {
Webauthn ConfigWebAuthn `toml:"webauthn"` Webauthn ConfigWebAuthn `toml:"webauthn"`
Storage ConfigStorage `toml:"storage"` Storage ConfigStorage `toml:"storage"`
Mail ConfigMail `toml:"mail"` Mail ConfigMail `toml:"mail"`
Self ConfigSelf `toml:"self"`
} }
var GlobalConfig Config var GlobalConfig Config

View file

@ -36,11 +36,6 @@ func main() {
Str("config-file", *flagConfigFile). Str("config-file", *flagConfigFile).
Msg("Failed to read config and couldn't write default") 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( storageCache, err := cache.NewCache(
config.GlobalConfig.Storage.MaxInMemoryCacheSize, config.GlobalConfig.Storage.MaxInMemoryCacheSize,
config.GlobalConfig.Storage.RedisUrl, config.GlobalConfig.Storage.RedisUrl,
@ -79,8 +74,8 @@ func main() {
server := server.NewServer( server := server.NewServer(
store, store,
pkey, pkey,
util.NewFSWrapper(reactiveFS, "frontend-reactive/dist/", true), util.NewFSWrapper(reactiveFS, "frontend-reactive/dist/", false),
util.NewFSWrapper(nojsFS, "frontend-noscript/", true), util.NewFSWrapper(nojsFS, "frontend-noscript/", false),
) )
server.Start(":8000") server.Start(":8000")
// TODO: Set up media server // TODO: Set up media server

View file

@ -0,0 +1 @@
package server

View file

@ -0,0 +1,5 @@
package server
import "net/http"
func getLinstromNote(w http.ResponseWriter, r *http.Request) {}

View file

@ -0,0 +1,8 @@
package server
import "net/http"
func setupApiFrontendRouter() http.Handler {
router := http.NewServeMux()
return router
}

286
server/apiRouter.go Normal file
View file

@ -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
}

View file

@ -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
}

22
server/frontendRouter.go Normal file
View file

@ -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
}

View file

@ -12,8 +12,10 @@ import (
"gitlab.com/mstarongitlab/goutils/other" "gitlab.com/mstarongitlab/goutils/other"
) )
// Mounted at /profiling
func setupProfilingHandler() http.Handler { func setupProfilingHandler() http.Handler {
router := http.NewServeMux() router := http.NewServeMux()
router.HandleFunc("/", profilingRootHandler)
router.HandleFunc("GET /current-goroutines", metricActiveGoroutinesHandler) router.HandleFunc("GET /current-goroutines", metricActiveGoroutinesHandler)
router.HandleFunc("GET /memory", metricMemoryStatsHandler) router.HandleFunc("GET /memory", metricMemoryStatsHandler)
router.HandleFunc("GET /pprof/cpu", pprof.Profile) router.HandleFunc("GET /pprof/cpu", pprof.Profile)
@ -28,6 +30,13 @@ func isAliveHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "yup") 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) { func metricActiveGoroutinesHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "{\"goroutines\": %d}", runtime.NumGoroutine()) fmt.Fprintf(w, "{\"goroutines\": %d}", runtime.NumGoroutine())
} }

View file

@ -4,11 +4,13 @@ import (
"context" "context"
"net/http" "net/http"
"slices" "slices"
"strings"
"time" "time"
"github.com/rs/zerolog/hlog" "github.com/rs/zerolog/hlog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"gitlab.com/mstarongitlab/goutils/other" "gitlab.com/mstarongitlab/goutils/other"
"gitlab.com/mstarongitlab/linstrom/config"
) )
type HandlerBuilder func(http.Handler) http.Handler type HandlerBuilder func(http.Handler) http.Handler
@ -38,6 +40,9 @@ func LoggingMiddleware(handler http.Handler) http.Handler {
return ChainMiddlewares(handler, return ChainMiddlewares(handler,
hlog.NewHandler(log.Logger), hlog.NewHandler(log.Logger),
hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) { hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
if strings.HasPrefix(r.URL.Path, "/assets") {
return
}
hlog.FromRequest(r).Info(). hlog.FromRequest(r).Info().
Str("method", r.Method). Str("method", r.Method).
Stringer("url", r.URL). Stringer("url", r.URL).
@ -85,3 +90,13 @@ func passkeyIdToAccountIdTransformerMiddleware(handler http.Handler) http.Handle
handler.ServeHTTP(w, r) 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)
})
}

View file

@ -7,7 +7,6 @@ import (
"github.com/mstarongithub/passkey" "github.com/mstarongithub/passkey"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"gitlab.com/mstarongitlab/goutils/other" "gitlab.com/mstarongitlab/goutils/other"
"gitlab.com/mstarongitlab/linstrom/config"
"gitlab.com/mstarongitlab/linstrom/storage" "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("/", setupFrontendRouter(reactiveFS, staticFS))
mux.Handle("/pk/", http.StripPrefix("/pk", http.FileServer(http.Dir("pk-auth")))) mux.Handle("/pk/", http.StripPrefix("/pk", http.FileServer(http.Dir("pk-auth"))))
mux.HandleFunc("/alive", isAliveHandler) mux.HandleFunc("/alive", isAliveHandler)
mux.Handle("/api/", http.StripPrefix("/api", setupApiRouter()))
profilingHandler := setupProfilingHandler() mux.Handle(
mux.Handle("/profiling/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { "/profiling/",
// Only allow access to profiling if a password is provided and it matches the one in the config http.StripPrefix("/profiling", profilingAuthenticationMiddleware(setupProfilingHandler())),
if r.FormValue("password") != config.GlobalConfig.Admin.ProfilingPassword { )
// Specifically reply with a plain 404 // temporary until proper route structure exists
http.Error(w, "", 404)
return
}
profilingHandler.ServeHTTP(w, r)
}))
mux.Handle( mux.Handle(
"/authonly/", "/authonly/",
pkey.Auth( pkey.Auth(

33
server/utils.go Normal file
View file

@ -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
}

12
storage/emote.go Normal file
View file

@ -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
}

View file

@ -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 // 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 // If not null, this entry is marked as deleted
DeletedAt gorm.DeletedAt `gorm:"index"` DeletedAt gorm.DeletedAt `gorm:"index"`
Creator string // Id of the author in the db, not the handle // Creator Account // `gorm:"foreignKey:CreatorId;references:ID"` // Account that created the post
Remote bool // Whether the note is originally a remote one and just "cached" 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 // Raw content of the note. So without additional formatting applied
// Might already have formatting applied beforehand from the origin server // Might already have formatting applied beforehand from the origin server
RawContent string RawContent string

View file

@ -1,30 +1,21 @@
package storage package storage
import ( import (
"time"
"gorm.io/gorm" "gorm.io/gorm"
) )
type RemoteServer struct { type RemoteServer struct {
ID string `gorm:"primarykey"` // ID is also server url gorm.Model
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"`
ServerType RemoteServerType // What software the server is running. Useful for formatting 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) Name string // What the server wants to be known as (usually same as url)
Icon string // ID of a media file Icon string // ID of a media file
IsSelf bool // Whether this server is yours truly IsSelf bool // Whether this server is yours truly
} }
func (s *Storage) FindRemoteServer(url string) (*RemoteServer, error) { func (s *Storage) FindRemoteServer(url string) (*RemoteServer, error) {
server := RemoteServer{ server := RemoteServer{}
ID: url, err := s.db.Where("domain = ?").First(&server).Error
}
err := s.db.First(&server).Error
switch err { switch err {
case nil: case nil:
return &server, nil return &server, nil
@ -63,7 +54,7 @@ func (s *Storage) NewRemoteServer(
return nil, err return nil, err
} }
server := RemoteServer{ server := RemoteServer{
ID: url, Domain: url,
Name: displayName, Name: displayName,
Icon: icon, Icon: icon,
ServerType: serverType, ServerType: serverType,

View file

@ -22,7 +22,7 @@ type Role struct {
gorm.Model gorm.Model
// Name of the role // Name of the role
Name string Name string `gorm:"primaryKey"`
// Priority of the role // Priority of the role
// Lower priority gets applied first and thus overwritten by higher priority ones // Lower priority gets applied first and thus overwritten by higher priority ones

View file

@ -6,12 +6,21 @@
package storage package storage
import ( import (
"crypto/ed25519"
"fmt"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"gitlab.com/mstarongitlab/linstrom/config"
"gitlab.com/mstarongitlab/linstrom/storage/cache" "gitlab.com/mstarongitlab/linstrom/storage/cache"
"gorm.io/driver/postgres" "gorm.io/driver/postgres"
"gorm.io/gorm" "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 // Storage is responsible for all database, cache and media related actions
// and serves as the lowest layer of the cake // and serves as the lowest layer of the cake
type Storage struct { type Storage struct {
@ -36,10 +45,78 @@ func NewStorage(dbUrl string, cache *cache.Cache) (*Storage, error) {
InboundJob{}, InboundJob{},
OutboundJob{}, OutboundJob{},
AccessToken{}, AccessToken{},
Emote{},
) )
if err != nil { 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 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
} }

View file

@ -31,21 +31,21 @@ type Account struct {
// When this entry was deleted (for soft deletions) // 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 // 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 // If not null, this entry is marked as deleted
DeletedAt gorm.DeletedAt `gorm:"index"` DeletedAt gorm.DeletedAt `gorm:"index"`
Remote bool // Whether the account is a local or remote one // Server RemoteServer // `gorm:"foreignKey:ServerId;references:ID"` // The server this user is from
Server string // The url of the server this account 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 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 CustomFields []uint `gorm:"serializer:json"` // IDs to the custom fields a user has
Description string // The description of a user account Description string // The description of a user account
Tags []string `gorm:"serializer:json"` // Hashtags Tags []string `gorm:"serializer:json"` // Hashtags
IsBot bool // Whether to mark this account as a script controlled one IsBot bool // Whether to mark this account as a script controlled one
Follows []string `gorm:"serializer:json"` // List of handles this account follows Follows []string `gorm:"serializer:json"` // List of handles this account follows
Followers []string `gorm:"serializer:json"` // List of handles that follow this account Followers []string `gorm:"serializer:json"` // List of handles that follow this account
Icon string // ID of a media file used as icon Icon string // ID of a media file used as icon
Background string // ID of a media file used as background image Background string // ID of a media file used as background image
Banner string // ID of a media file used as banner Banner string // ID of a media file used as banner
Indexable bool // Whether this account can be found by crawlers Indexable bool // Whether this account can be found by crawlers
PublicKey []byte // The public key of the account PublicKey []byte // The public key of the account
// Whether this account restricts following // Whether this account restricts following
// If true, the owner must approve of a follow request first // If true, the owner must approve of a follow request first
RestrictedFollow bool RestrictedFollow bool
@ -346,8 +346,8 @@ func (s *Storage) NewLocalAccount(handle string) (*Account, error) {
return nil, err return nil, err
} }
acc.Username = handle acc.Username = handle
acc.Server = config.GlobalConfig.General.GetFullDomain() // acc.Server = serverSelf
acc.Remote = false acc.ServerId = serverSelf.ID
acc.DisplayName = handle acc.DisplayName = handle
publicKey, privateKey, err := ed25519.GenerateKey(nil) publicKey, privateKey, err := ed25519.GenerateKey(nil)
@ -419,7 +419,7 @@ func (s *Storage) GetOrCreateUser(userID string) passkey.User {
Str("account-handle", userID). Str("account-handle", userID).
Msg("Looking for or creating account for passkey stuff") Msg("Looking for or creating account for passkey stuff")
acc := &Account{} 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) First(acc)
if errors.Is(res.Error, gorm.ErrRecordNotFound) { if errors.Is(res.Error, gorm.ErrRecordNotFound) {
log.Debug().Str("account-handle", userID) log.Debug().Str("account-handle", userID)