Fix, placeholder image, api

Added a placeholder image, code style
Fixed metadata missing issue
More work on api
This commit is contained in:
Melody Becker 2024-11-06 16:58:57 +01:00
parent 529d106351
commit 0e036b152b
8 changed files with 192 additions and 94 deletions

21
Code-Style.md Normal file
View file

@ -0,0 +1,21 @@
# Code style guide
## Go
- Use `gofmt`
- `jsonapi` fields must use dasharised names
(eg `some-field` instead of `some_field` or `someField`)
- Function, struct and variable names must describe their purpose adequatly.
Making them longer for that purpose is fine
- Follow [https://go.dev/doc/effective_go]
(which also happens to be convenient place to start learning Go)
- Always use zerolog for console output. In http handlers,
use `hlog.FromRequest` to get a logger instance prepared with a bunch of metadata
- As Linstrom is both intended for active use as well as providing a learning resource,
all functions and structs must be documented
## JS/TS
<!-- TODO: Fill this out -->
- Biome should do most of the work

BIN
duck.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View file

@ -27,6 +27,9 @@ var reactiveFS embed.FS
//go:embed frontend-noscript //go:embed frontend-noscript
var nojsFS embed.FS var nojsFS embed.FS
//go:embed duck.webp
var placeholderFile string
func main() { func main() {
setLogger() setLogger()
setLogLevel() setLogLevel()
@ -76,6 +79,7 @@ func main() {
pkey, pkey,
util.NewFSWrapper(reactiveFS, "frontend-reactive/dist/", false), util.NewFSWrapper(reactiveFS, "frontend-reactive/dist/", false),
util.NewFSWrapper(nojsFS, "frontend-noscript/", false), util.NewFSWrapper(nojsFS, "frontend-noscript/", false),
&placeholderFile,
) )
server.Start(":8000") server.Start(":8000")
// TODO: Set up media server // TODO: Set up media server

View file

@ -5,6 +5,7 @@ import "net/http"
func setupLinstromApiRouter() http.Handler { func setupLinstromApiRouter() http.Handler {
router := http.NewServeMux() router := http.NewServeMux()
router.Handle("/v1/", http.StripPrefix("/v1", setupLinstromApiV1Router())) router.Handle("/v1/", http.StripPrefix("/v1", setupLinstromApiV1Router()))
router.Handle("/s2s/v1/", http.StripPrefix("/s2s/v1", setupLinstromS2SApiV1Router()))
return router return router
} }
@ -93,3 +94,10 @@ func setupLinstromApiV1Router() http.Handler {
return router return router
} }
func setupLinstromS2SApiV1Router() http.Handler {
router := http.NewServeMux()
// TODO: Figure out a decent server to server API definition
router.HandleFunc("/", placeholderEndpoint)
return router
}

View file

@ -165,7 +165,37 @@ func linstromUpdateAccount(w http.ResponseWriter, r *http.Request) {
log.Error().Err(err).Msg("Failed to marshal and write updated account") log.Error().Err(err).Msg("Failed to marshal and write updated account")
} }
} }
func linstromDeleteAccount(w http.ResponseWriter, r *http.Request) {} func linstromDeleteAccount(w http.ResponseWriter, r *http.Request) {
actorId, _ := ActorIdFromRequest(r)
log := hlog.FromRequest(r)
store := StorageFromRequest(r)
targetAccountId := AccountIdFromRequest(r)
if targetAccountId != actorId {
other.HttpErr(w, HttpErrIdNotAuthenticated, "Action forbidden", http.StatusForbidden)
return
}
acc, err := store.FindAccountById(targetAccountId)
if err != nil {
log.Error().Err(err).Str("account-id", actorId).Msg("Failed to get account for deletion")
other.HttpErr(
w,
HttpErrIdDbFailure,
"Failed to get account from db",
http.StatusInternalServerError,
)
return
}
// allRoles, err := store.FindRolesByNames(acc.Roles)
// collapsedRole := storage.CollapseRolesIntoOne(allRoles...)
// TODO: Start job of sending out deletion messages to all federated servers
// Clean up related data first
// User role
// Custom fields
_ = acc
store.DeleteAccount(actorId)
}
func linstromIsFollowingAccount(w http.ResponseWriter, r *http.Request) {} func linstromIsFollowingAccount(w http.ResponseWriter, r *http.Request) {}
func linstromFollowAccount(w http.ResponseWriter, r *http.Request) {} func linstromFollowAccount(w http.ResponseWriter, r *http.Request) {}

View file

@ -23,9 +23,22 @@ func convertAccountStorageToLinstrom(
if err != nil { if err != nil {
return nil, fmt.Errorf("icon: %w", err) return nil, fmt.Errorf("icon: %w", err)
} }
storageBanner, err := store.GetMediaMetadataById(acc.Banner) var apiBanner *linstromMediaMetadata
if err != nil { if acc.Banner != nil {
return nil, fmt.Errorf("banner: %w", err) storageBanner, err := store.GetMediaMetadataById(*acc.Banner)
if err != nil {
return nil, fmt.Errorf("banner: %w", err)
}
apiBanner = convertMediaMetadataStorageToLinstrom(storageBanner)
}
var apiBackground *linstromMediaMetadata
if acc.Background != nil {
storageBackground, err := store.GetMediaMetadataById(*acc.Background)
if err != nil {
return nil, fmt.Errorf("banner: %w", err)
}
apiBackground = convertMediaMetadataStorageToLinstrom(storageBackground)
} }
storageFields, err := store.FindMultipleUserFieldsById(acc.CustomFields) storageFields, err := store.FindMultipleUserFieldsById(acc.CustomFields)
if err != nil { if err != nil {
@ -50,7 +63,11 @@ func convertAccountStorageToLinstrom(
IsBot: acc.IsBot, IsBot: acc.IsBot,
Description: acc.Description, Description: acc.Description,
Icon: convertMediaMetadataStorageToLinstrom(storageIcon), Icon: convertMediaMetadataStorageToLinstrom(storageIcon),
Banner: convertMediaMetadataStorageToLinstrom(storageBanner), IconId: acc.Icon,
Banner: apiBanner,
BannerId: acc.Banner,
Background: apiBackground,
BackgroundId: acc.Background,
FollowerIds: acc.Followers, FollowerIds: acc.Followers,
FollowingIds: acc.Follows, FollowingIds: acc.Follows,
Indexable: acc.Indexable, Indexable: acc.Indexable,

View file

@ -16,85 +16,89 @@ var (
type linstromNote struct { type linstromNote struct {
Id string `jsonapi:"primary,notes"` Id string `jsonapi:"primary,notes"`
RawContent string `jsonapi:"attr,content"` RawContent string `jsonapi:"attr,content"`
OriginServer *linstromOriginServer `jsonapi:"relation,origin_server"` OriginServer *linstromOriginServer `jsonapi:"relation,origin-server"`
OriginServerId int `jsonapi:"attr,origin_server_id"` OriginServerId int `jsonapi:"attr,origin-server-id"`
ReactionCount string `jsonapi:"attr,reaction_count"` ReactionCount string `jsonapi:"attr,reaction-count"`
CreatedAt time.Time `jsonapi:"attr,created_at"` CreatedAt time.Time `jsonapi:"attr,created-at"`
UpdatedAt *time.Time `jsonapi:"attr,updated_at,omitempty"` UpdatedAt *time.Time `jsonapi:"attr,updated-at,omitempty"`
Author *linstromAccount `jsonapi:"relation,author"` Author *linstromAccount `jsonapi:"relation,author"`
AuthorId string `jsonapi:"attr,author_id"` AuthorId string `jsonapi:"attr,author-id"`
ContentWarning *string `jsonapi:"attr,content_warning,omitempty"` ContentWarning *string `jsonapi:"attr,content-warning,omitempty"`
InReplyToId *string `jsonapi:"attr,in_reply_to_id,omitempty"` InReplyToId *string `jsonapi:"attr,in-reply-to-id,omitempty"`
QuotesId *string `jsonapi:"attr,quotes_id,omitempty"` QuotesId *string `jsonapi:"attr,quotes-id,omitempty"`
EmoteIds []string `jsonapi:"attr,emotes,omitempty"` EmoteIds []string `jsonapi:"attr,emotes,omitempty"`
Attachments []*linstromMediaMetadata `jsonapi:"relation,attachments,omitempty"` Attachments []*linstromMediaMetadata `jsonapi:"relation,attachments,omitempty"`
AttachmentIds []string `jsonapi:"attr,attachment_ids"` AttachmentIds []string `jsonapi:"attr,attachment-ids"`
AccessLevel uint8 `jsonapi:"attr,access_level"` AccessLevel uint8 `jsonapi:"attr,access-level"`
Pings []*linstromAccount `jsonapi:"relation,pings,omitempty"` Pings []*linstromAccount `jsonapi:"relation,pings,omitempty"`
PingIds []string `jsonapi:"attr,ping_ids,omitempty"` PingIds []string `jsonapi:"attr,ping-ids,omitempty"`
} }
type linstromOriginServer struct { type linstromOriginServer struct {
Id uint `jsonapi:"primary,origins"` Id uint `jsonapi:"primary,origins"`
CreatedAt time.Time `jsonapi:"attr,created_at"` CreatedAt time.Time `jsonapi:"attr,created-at"`
UpdatedAt *time.Time `jsonapi:"attr,updated_at,omitempty"` UpdatedAt *time.Time `jsonapi:"attr,updated-at,omitempty"`
ServerType string `jsonapi:"attr,server_type"` // one of "Linstrom", "Mastodon", "Plemora", "Misskey" or "Wafrn" ServerType string `jsonapi:"attr,server-type"` // one of "Linstrom", "Mastodon", "Plemora", "Misskey" or "Wafrn"
Domain string `jsonapi:"attr,domain"` Domain string `jsonapi:"attr,domain"`
DisplayName string `jsonapi:"attr,display_name"` DisplayName string `jsonapi:"attr,display-name"`
Icon *linstromMediaMetadata `jsonapi:"relation,icon"` Icon *linstromMediaMetadata `jsonapi:"relation,icon"`
IsSelf bool `jsonapi:"attr,is_self"` IsSelf bool `jsonapi:"attr,is-self"`
} }
type linstromMediaMetadata struct { type linstromMediaMetadata struct {
Id string `jsonapi:"primary,medias"` Id string `jsonapi:"primary,media"`
CreatedAt time.Time `jsonapi:"attr,created_at"` CreatedAt time.Time `jsonapi:"attr,created-at"`
UpdatedAt *time.Time `jsonapi:"attr,updated_at,omitempty"` UpdatedAt *time.Time `jsonapi:"attr,updated-at,omitempty"`
IsRemote bool `jsonapi:"attr,is_remote"` IsRemote bool `jsonapi:"attr,is-remote"`
Url string `jsonapi:"attr,url"` Url string `jsonapi:"attr,url"`
MimeType string `jsonapi:"attr,mime_type"` MimeType string `jsonapi:"attr,mime-type"`
Name string `jsonapi:"attr,name"` Name string `jsonapi:"attr,name"`
AltText string `jsonapi:"attr,alt_text"` AltText string `jsonapi:"attr,alt-text"`
Blurred bool `jsonapi:"attr,blurred"` Blurred bool `jsonapi:"attr,blurred"`
} }
type linstromAccount struct { type linstromAccount struct {
Id string `jsonapi:"primary,accounts"` Id string `jsonapi:"primary,accounts"`
CreatedAt time.Time `jsonapi:"attr,created_at"` CreatedAt time.Time `jsonapi:"attr,created-at"`
UpdatedAt *time.Time `jsonapi:"attr,updated_at,omitempty"` UpdatedAt *time.Time `jsonapi:"attr,updated-at,omitempty"`
Username string `jsonapi:"attr,username"` Username string `jsonapi:"attr,username"`
OriginServer *linstromOriginServer `jsonapi:"relation,origin_server"` OriginServer *linstromOriginServer `jsonapi:"relation,origin-server"`
OriginServerId int `jsonapi:"attr,origin_server_id"` OriginServerId int `jsonapi:"attr,origin-server-id"`
DisplayName string `jsonapi:"attr,display_name"` DisplayName string `jsonapi:"attr,display-name"`
CustomFields []*linstromCustomAccountField `jsonapi:"relation,custom_fields"` CustomFields []*linstromCustomAccountField `jsonapi:"relation,custom-fields"`
CustomFieldIds []uint `jsonapi:"attr,custom_field_ids"` CustomFieldIds []uint `jsonapi:"attr,custom-field-ids"`
IsBot bool `jsonapi:"attr,is_bot"` IsBot bool `jsonapi:"attr,is-bot"`
Description string `jsonapi:"attr,description"` Description string `jsonapi:"attr,description"`
Icon *linstromMediaMetadata `jsonapi:"relation,icon"` Icon *linstromMediaMetadata `jsonapi:"relation,icon"`
IconId string `jsonapi:"attr,icon-id"`
Banner *linstromMediaMetadata `jsonapi:"relation,banner"` Banner *linstromMediaMetadata `jsonapi:"relation,banner"`
FollowerIds []string `jsonapi:"attr,follows_ids"` BannerId *string `jsonapi:"attr,banner-id"`
FollowingIds []string `jsonapi:"attr,following_ids"` Background *linstromMediaMetadata `jsonapi:"relation,background"`
BackgroundId *string `jsonapi:"attr,background-id"`
FollowerIds []string `jsonapi:"attr,follows-ids"`
FollowingIds []string `jsonapi:"attr,following-ids"`
Indexable bool `jsonapi:"attr,indexable"` Indexable bool `jsonapi:"attr,indexable"`
RestrictedFollow bool `jsonapi:"attr,restricted_follow"` RestrictedFollow bool `jsonapi:"attr,restricted-follow"`
IdentifiesAs []string `jsonapi:"attr,identifies_as"` IdentifiesAs []string `jsonapi:"attr,identifies-as"`
Pronouns []string `jsonapi:"attr,pronouns"` Pronouns []string `jsonapi:"attr,pronouns"`
Roles []string `jsonapi:"attr,roles"` Roles []string `jsonapi:"attr,roles"`
} }
type linstromCustomAccountField struct { type linstromCustomAccountField struct {
Id uint `jsonapi:"primary,custom_account_fields"` Id uint `jsonapi:"primary,custom-account-fields"`
CreatedAt time.Time `jsonapi:"attr,created_at"` CreatedAt time.Time `jsonapi:"attr,created-at"`
UpdatedAt *time.Time `jsonapi:"attr,updated_at,omitempty"` UpdatedAt *time.Time `jsonapi:"attr,updated-at,omitempty"`
Key string `jsonapi:"attr,key"` Key string `jsonapi:"attr,key"`
Value string `jsonapi:"attr,value"` Value string `jsonapi:"attr,value"`
Verified *bool `jsonapi:"attr,verified,omitempty"` Verified *bool `jsonapi:"attr,verified,omitempty"`
BelongsToId string `jsonapi:"attr,belongs_to_id"` BelongsToId string `jsonapi:"attr,belongs-to-id"`
} }
// Role is essentially just a carbon copy of storage/roles.go // Role is essentially just a carbon copy of storage/roles.go
type linstromRole struct { type linstromRole struct {
Id uint `jsonapi:"primary,roles"` Id uint `jsonapi:"primary,roles"`
CreatedAt time.Time `jsonapi:"attr,created_at"` CreatedAt time.Time `jsonapi:"attr,created-at"`
UpdatedAt *time.Time `jsonapi:"attr,updated_at,omitempty"` UpdatedAt *time.Time `jsonapi:"attr,updated-at,omitempty"`
// Name of the role // Name of the role
Name string `jsonapi:"attr,name"` Name string `jsonapi:"attr,name"`
@ -107,35 +111,35 @@ type linstromRole struct {
Priority uint `jsonapi:"attr,priority"` Priority uint `jsonapi:"attr,priority"`
// Whether this role is for a for a single user only (like custom, per user permissions in Discord) // Whether this role is for a for a single user only (like custom, per user permissions in Discord)
// If yes, Name will be the id of the user in question // If yes, Name will be the id of the user in question
IsUserRole bool `jsonapi:"attr,is_user_role"` IsUserRole bool `jsonapi:"attr,is-user-role"`
// Whether this role is one built into Linstrom from the start or not // Whether this role is one built into Linstrom from the start or not
// Note: Built-in roles can't be modified // Note: Built-in roles can't be modified
IsBuiltIn bool `jsonapi:"attr,is_builtin"` IsBuiltIn bool `jsonapi:"attr,is-builtin"`
// --- User permissions --- // --- User permissions ---
CanSendMedia *bool `jsonapi:"attr,can_send_media"` CanSendMedia *bool `jsonapi:"attr,can-send-media"`
CanSendCustomEmotes *bool `jsonapi:"attr,can_send_custom_emotes"` CanSendCustomEmotes *bool `jsonapi:"attr,can-send-custom-emotes"`
CanSendCustomReactions *bool `jsonapi:"attr,can_send_custom_reactions"` CanSendCustomReactions *bool `jsonapi:"attr,can-send-custom-reactions"`
CanSendPublicNotes *bool `jsonapi:"attr,can_send_public_notes"` CanSendPublicNotes *bool `jsonapi:"attr,can-send-public-notes"`
CanSendLocalNotes *bool `jsonapi:"attr,can_send_local_notes"` CanSendLocalNotes *bool `jsonapi:"attr,can-send-local-notes"`
CanSendFollowerOnlyNotes *bool `jsonapi:"attr,can_send_follower_only_notes"` CanSendFollowerOnlyNotes *bool `jsonapi:"attr,can-send-follower-only-notes"`
CanSendPrivateNotes *bool `jsonapi:"attr,can_send_private_notes"` CanSendPrivateNotes *bool `jsonapi:"attr,can-send-private-notes"`
CanSendReplies *bool `jsonapi:"attr,can_send_replies"` CanSendReplies *bool `jsonapi:"attr,can-send-replies"`
CanQuote *bool `jsonapi:"attr,can_quote"` CanQuote *bool `jsonapi:"attr,can-quote"`
CanBoost *bool `jsonapi:"attr,can_boost"` CanBoost *bool `jsonapi:"attr,can-boost"`
CanIncludeLinks *bool `jsonapi:"attr,can_include_links"` CanIncludeLinks *bool `jsonapi:"attr,can-include-links"`
CanIncludeSurvey *bool `jsonapi:"attr,can_include_survey"` CanIncludeSurvey *bool `jsonapi:"attr,can-include-survey"`
CanChangeDisplayName *bool `jsonapi:"attr,can_change_display_name"` CanChangeDisplayName *bool `jsonapi:"attr,can-change-display-name"`
BlockedUsers []string `jsonapi:"attr,blocked_users"` BlockedUsers []string `jsonapi:"attr,blocked-users"`
CanSubmitReports *bool `jsonapi:"attr,can_submit_reports"` CanSubmitReports *bool `jsonapi:"attr,can-submit-reports"`
CanLogin *bool `jsonapi:"attr,can_login"` CanLogin *bool `jsonapi:"attr,can-login"`
CanMentionOthers *bool `jsonapi:"attr,can_mention_others"` CanMentionOthers *bool `jsonapi:"attr,can-mention-others"`
HasMentionCountLimit *bool `jsonapi:"attr,has_mention_count_limit"` HasMentionCountLimit *bool `jsonapi:"attr,has-mention-count-limit"`
MentionLimit *uint32 `jsonapi:"attr,mention_count_limit"` MentionLimit *uint32 `jsonapi:"attr,mention-count-limit"`
// CanViewBoosts *bool // CanViewBoosts *bool
// CanViewQuotes *bool // CanViewQuotes *bool
@ -143,32 +147,32 @@ type linstromRole struct {
// CanViewCustomEmotes *bool // CanViewCustomEmotes *bool
// --- Automod --- // --- Automod ---
AutoNsfwMedia *bool `jsonapi:"attr,auto_nsfw_media"` AutoNsfwMedia *bool `jsonapi:"attr,auto-nsfw-media"`
AutoCwPosts *bool `jsonapi:"attr,auto_cw_posts"` AutoCwPosts *bool `jsonapi:"attr,auto-cw-posts"`
AutoCwPostsText *string `jsonapi:"attr,auto_cw_posts_text"` AutoCwPostsText *string `jsonapi:"attr,auto-cw-posts-text"`
ScanCreatedPublicNotes *bool `jsonapi:"attr,scan_created_public_notes"` ScanCreatedPublicNotes *bool `jsonapi:"attr,scan-created-public-notes"`
ScanCreatedLocalNotes *bool `jsonapi:"attr,scan_created_local_notes"` ScanCreatedLocalNotes *bool `jsonapi:"attr,scan-created-local-notes"`
ScanCreatedFollowerOnlyNotes *bool `jsonapi:"attr,scan_created_follower_only_notes"` ScanCreatedFollowerOnlyNotes *bool `jsonapi:"attr,scan-created-follower-only-notes"`
ScanCreatedPrivateNotes *bool `jsonapi:"attr,scan_created_private_notes"` ScanCreatedPrivateNotes *bool `jsonapi:"attr,scan-created-private-notes"`
DisallowInteractionsWith []string `jsonapi:"attr,disallow_interactions_with"` DisallowInteractionsWith []string `jsonapi:"attr,disallow-interactions-with"`
WithholdNotesForManualApproval *bool `jsonapi:"attr,withhold_notes_for_manual_approval"` WithholdNotesForManualApproval *bool `jsonapi:"attr,withhold-notes-for-manual-approval"`
WithholdNotesBasedOnRegex *bool `jsonapi:"attr,withhold_notes_based_on_regex"` WithholdNotesBasedOnRegex *bool `jsonapi:"attr,withhold-notes-based-on-regex"`
WithholdNotesRegexes []string `jsonapi:"attr,withhold_notes_regexes"` WithholdNotesRegexes []string `jsonapi:"attr,withhold-notes-regexes"`
// --- Admin perms --- // --- Admin perms ---
// If set, counts as all permissions being set as given and all restrictions being disabled // If set, counts as all permissions being set as given and all restrictions being disabled
FullAdmin *bool `jsonapi:"attr,full_admin"` FullAdmin *bool `jsonapi:"attr,full-admin"`
CanAffectOtherAdmins *bool `jsonapi:"attr,can_affect_other_admins"` CanAffectOtherAdmins *bool `jsonapi:"attr,can-affect-other-admins"`
CanDeleteNotes *bool `jsonapi:"attr,can_delete_notes"` CanDeleteNotes *bool `jsonapi:"attr,can-delete-notes"`
CanConfirmWithheldNotes *bool `jsonapi:"attr,can_confirm_withheld_notes"` CanConfirmWithheldNotes *bool `jsonapi:"attr,can-confirm-withheld-notes"`
CanAssignRoles *bool `jsonapi:"attr,can_assign_roles"` CanAssignRoles *bool `jsonapi:"attr,can-assign-roles"`
CanSupressInteractionsBetweenUsers *bool `jsonapi:"attr,can_supress_interactions_between_users"` CanSupressInteractionsBetweenUsers *bool `jsonapi:"attr,can-supress-interactions-between-users"`
CanOverwriteDisplayNames *bool `jsonapi:"attr,can_overwrite_display_names"` CanOverwriteDisplayNames *bool `jsonapi:"attr,can-overwrite-display-names"`
CanManageCustomEmotes *bool `jsonapi:"attr,can_manage_custom_emotes"` CanManageCustomEmotes *bool `jsonapi:"attr,can-manage-custom-emotes"`
CanViewDeletedNotes *bool `jsonapi:"attr,can_view_deleted_notes"` CanViewDeletedNotes *bool `jsonapi:"attr,can-view-deleted-notes"`
CanRecoverDeletedNotes *bool `jsonapi:"attr,can_recover_deleted_notes"` CanRecoverDeletedNotes *bool `jsonapi:"attr,can-recover-deleted-notes"`
CanManageAvatarDecorations *bool `jsonapi:"attr,can_manage_avatar_decorations"` CanManageAvatarDecorations *bool `jsonapi:"attr,can-manage-avatar-decorations"`
CanManageAds *bool `jsonapi:"attr,can_manage_ads"` CanManageAds *bool `jsonapi:"attr,can-manage-ads"`
CanSendAnnouncements *bool `jsonapi:"attr,can_send_announcements"` CanSendAnnouncements *bool `jsonapi:"attr,can-send-announcements"`
} }

View file

@ -1,6 +1,7 @@
package server package server
import ( import (
"fmt"
"io/fs" "io/fs"
"net/http" "net/http"
@ -15,8 +16,13 @@ type Server struct {
router http.Handler router http.Handler
} }
func NewServer(store *storage.Storage, pkey *passkey.Passkey, reactiveFS, staticFS fs.FS) *Server { func NewServer(
handler := buildRootHandler(pkey, reactiveFS, staticFS) store *storage.Storage,
pkey *passkey.Passkey,
reactiveFS, staticFS fs.FS,
placeholderFile *string,
) *Server {
handler := buildRootHandler(pkey, reactiveFS, staticFS, placeholderFile)
handler = ChainMiddlewares(handler, LoggingMiddleware, ContextValsMiddleware(map[any]any{ handler = ChainMiddlewares(handler, LoggingMiddleware, ContextValsMiddleware(map[any]any{
ContextKeyStorage: store, ContextKeyStorage: store,
})) }))
@ -26,7 +32,11 @@ func NewServer(store *storage.Storage, pkey *passkey.Passkey, reactiveFS, static
} }
} }
func buildRootHandler(pkey *passkey.Passkey, reactiveFS, staticFS fs.FS) http.Handler { func buildRootHandler(
pkey *passkey.Passkey,
reactiveFS, staticFS fs.FS,
placeholderFile *string,
) http.Handler {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle( mux.Handle(
"/webauthn/", "/webauthn/",
@ -61,6 +71,10 @@ func buildRootHandler(pkey *passkey.Passkey, reactiveFS, staticFS fs.FS) http.Ha
)(ChainMiddlewares(setupTestEndpoints(), passkeyIdToAccountIdTransformerMiddleware)), )(ChainMiddlewares(setupTestEndpoints(), passkeyIdToAccountIdTransformerMiddleware)),
) )
mux.HandleFunc("/placeholder-file", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, placeholderFile)
})
return mux return mux
} }