Something went wrong. Try again.
Lightweight and easily self-hostable Git forge.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181package app
import ( "errors" md "farga/app/middleware" db "farga/db" "farga/db/types" "farga/lib" "farga/templating" "net/http" "strings"
"github.com/google/uuid" "github.com/gorilla/mux")
type RegisterForm struct { Name string `form:"name" validate:"required,min=2,max=40"` Handle string `form:"handle" validate:"required,min=3,max=30"` Email string `form:"email" validate:"required,email"` Password string `form:"password" validate:"required,min=8"` InviteToken string `form:"invite-token"`}
type LoginForm struct { Email string `form:"email" validate:"required,email"` Password string `form:"password" validate:"required,min=8"`}
func (a *App) Login(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": templating.Render(w, r, "base", "login", map[string]any{ "AllowSignups": a.Config.AllowSignups, }) case "POST": form, _ := md.Form[LoginForm](r) user, err := a.Store.LoginUser(form.Email, form.Password) if err != nil && (errors.Is(err, db.UserNotFound) || errors.Is(err, db.IncorrectPassword)) { // user not found or incorrect password http.Redirect(w, r, "/auth/login?err=InvalidCredentials", http.StatusSeeOther) return }
session, err := a.Store.NewSession(&user.ID)
if err != nil { http.Error(w, "Something went wrong while creating your session.", http.StatusInternalServerError) return }
lib.SetCookie(w, "session", session.ID.String()) http.Redirect(w, r, "/?msg=AuthSuccess", http.StatusSeeOther) }}
func (a *App) Register(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) switch r.Method { case "GET": inviteToken := vars["token"]
// if signups are disallowed, but the user has an inviteToken if inviteToken != "" { invite, ok := a.Store.ValidateInviteToken(inviteToken) if ok { // allow user to register templating.Render(w, r, "base", "register", map[string]any{ "HandleRegex": lib.HandleRegex.String(), "Invite": invite, }) } else { // token is invvalid/expired templating.Render(w, r, "base", "invite/invalid_token") } } else if a.Config.AllowSignups { // if signups are allowed templating.Render(w, r, "base", "register", map[string]any{ "HandleRegex": lib.HandleRegex.String(), }) } else { // if neither, just reject them. templating.Render(w, r, "base", "invite_only") } case "POST": form, ok := md.Form[RegisterForm](r) if !ok { http.Error(w, "Internal error.", http.StatusInternalServerError) return }
if !a.Config.AllowSignups && form.InviteToken == "" { http.Redirect(w, r, "/auth/register?err=Forbidden", http.StatusSeeOther) return }
var invite types.Invite if form.InviteToken != "" { invite, ok = a.Store.ValidateInviteToken(form.InviteToken) if !ok { templating.Render(w, r, "base", "invite/invalid_token") return } }
if !lib.ValidateHandle(form.Handle) { http.Redirect(w, r, "?err=UserInvalidHandle", http.StatusSeeOther) return }
if !lib.ValidateUsername(form.Name) { http.Redirect(w, r, "?err=UserInvalidHandle", http.StatusSeeOther) return }
password, err := lib.GenerateHash(form.Password) if err != nil { http.Error(w, "Something went wrong while generating your password's hash. Please try again!", 500) return }
user := types.User{ Name: strings.TrimSpace(form.Name), Handle: strings.TrimSpace(form.Handle), Email: strings.TrimSpace(form.Email), PasswordHash: password, Config: types.UserConfig{ GitDefaultBranch: lib.GitDefaultBranch, }, }
// set to admin if invite token says so. if invite.ID != uuid.Nil && invite.Role == uint(types.RoleAdmin) { user.Role = types.RoleAdmin } else { user.Role = types.RoleUser }
if form.InviteToken != "" { err = a.Store.NewUserWithInvite(&user, form.InviteToken) } else { err = a.Store.NewUser(&user) }
if err != nil { if errors.Is(err, db.ErrInvalidInvite) { templating.Render(w, r, "base", "invite/invalid_token") return } constrainedField := strings.Replace(err.Error(), "UNIQUE constraint failed: users.", "That ", 1) + " has already been claimed." http.Error(w, constrainedField, 500) return }
session, err := a.Store.NewSession(&user.ID)
if err != nil { http.Error(w, "Something went wrong while creating your session.", http.StatusInternalServerError) return }
lib.SetCookie(w, "session", session.ID.String()) http.Redirect(w, r, "/?msg=AuthSuccess", http.StatusSeeOther) }}
func (a *App) Logout(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": templating.Render(w, r, "base", "logout") case "POST": session := r.Context().Value(lib.SessionKey).(string)
err := a.Store.DeleteSession(session) if err != nil { http.Error(w, "Something went wrong while creating your session.", http.StatusInternalServerError) return }
lib.DeleteCookie(w, "session") http.Redirect(w, r, "/auth/login?msg=LogoutSuccess", http.StatusFound) }}