add invite to space feature
This commit is contained in:
parent
109821d0a0
commit
4d6e6799a0
10 changed files with 407 additions and 8 deletions
|
|
@ -15,11 +15,12 @@ import (
|
|||
)
|
||||
|
||||
type authHandler struct {
|
||||
authService *service.AuthService
|
||||
authService *service.AuthService
|
||||
inviteService *service.InviteService
|
||||
}
|
||||
|
||||
func NewAuthHandler(authService *service.AuthService) *authHandler {
|
||||
return &authHandler{authService: authService}
|
||||
func NewAuthHandler(authService *service.AuthService, inviteService *service.InviteService) *authHandler {
|
||||
return &authHandler{authService: authService, inviteService: inviteService}
|
||||
}
|
||||
|
||||
func (h *authHandler) AuthPage(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
@ -88,6 +89,28 @@ func (h *authHandler) VerifyMagicLink(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
h.authService.SetJWTCookie(w, jwtToken, time.Now().Add(7*24*time.Hour))
|
||||
|
||||
// Check for pending invite
|
||||
inviteCookie, err := r.Cookie("pending_invite")
|
||||
if err == nil && inviteCookie.Value != "" {
|
||||
spaceID, err := h.inviteService.AcceptInvite(inviteCookie.Value, user.ID)
|
||||
if err != nil {
|
||||
slog.Error("failed to process pending invite", "error", err, "token", inviteCookie.Value)
|
||||
// Don't fail the login, just maybe notify user?
|
||||
} else {
|
||||
slog.Info("accepted pending invite", "user_id", user.ID, "space_id", spaceID)
|
||||
// Clear cookie
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "pending_invite",
|
||||
Value: "",
|
||||
Path: "/",
|
||||
MaxAge: -1,
|
||||
HttpOnly: true,
|
||||
})
|
||||
// If we want to redirect to the space immediately, we can.
|
||||
// But check onboarding first.
|
||||
}
|
||||
}
|
||||
|
||||
needsOnboarding, err := h.authService.NeedsOnboarding(user.ID)
|
||||
if err != nil {
|
||||
slog.Warn("failed to check onboarding status", "error", err, "user_id", user.ID)
|
||||
|
|
|
|||
|
|
@ -20,14 +20,16 @@ type SpaceHandler struct {
|
|||
tagService *service.TagService
|
||||
listService *service.ShoppingListService
|
||||
expenseService *service.ExpenseService
|
||||
inviteService *service.InviteService
|
||||
}
|
||||
|
||||
func NewSpaceHandler(ss *service.SpaceService, ts *service.TagService, sls *service.ShoppingListService, es *service.ExpenseService) *SpaceHandler {
|
||||
func NewSpaceHandler(ss *service.SpaceService, ts *service.TagService, sls *service.ShoppingListService, es *service.ExpenseService, is *service.InviteService) *SpaceHandler {
|
||||
return &SpaceHandler{
|
||||
spaceService: ss,
|
||||
tagService: ts,
|
||||
listService: sls,
|
||||
expenseService: es,
|
||||
inviteService: is,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -353,3 +355,57 @@ func (h *SpaceHandler) CreateExpense(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
ui.Render(w, r, pages.ExpenseCreatedResponse(newExpense, balance))
|
||||
}
|
||||
|
||||
func (h *SpaceHandler) CreateInvite(w http.ResponseWriter, r *http.Request) {
|
||||
spaceID := r.PathValue("spaceID")
|
||||
user := ctxkeys.User(r.Context())
|
||||
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "Bad Request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
email := r.FormValue("email")
|
||||
if email == "" {
|
||||
http.Error(w, "Email is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
_, err := h.inviteService.CreateInvite(spaceID, user.ID, email)
|
||||
if err != nil {
|
||||
slog.Error("failed to create invite", "error", err, "space_id", spaceID)
|
||||
http.Error(w, "Failed to create invite", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Return a nice UI response (toast or list update)
|
||||
w.Write([]byte("Invitation sent!"))
|
||||
}
|
||||
|
||||
func (h *SpaceHandler) JoinSpace(w http.ResponseWriter, r *http.Request) {
|
||||
token := r.PathValue("token")
|
||||
user := ctxkeys.User(r.Context())
|
||||
|
||||
if user != nil {
|
||||
spaceID, err := h.inviteService.AcceptInvite(token, user.ID)
|
||||
if err != nil {
|
||||
slog.Error("failed to accept invite", "error", err, "token", token)
|
||||
http.Error(w, "Failed to join space: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/app/spaces/"+spaceID, http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
// Not logged in: set cookie and redirect to auth
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "pending_invite",
|
||||
Value: token,
|
||||
Path: "/",
|
||||
Expires: time.Now().Add(1 * time.Hour),
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
http.Redirect(w, r, "/auth?invite=true", http.StatusTemporaryRedirect)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue