Personal Git forge.

Code git clone ssh://git.poy.ooo:6969/~poyo/farga ZIP
app/app.go / 5KiB #9f6a746c
Raw
package appimport (	md "farga/app/middleware"	"farga/config"	"farga/templating"	"fmt"	"net/http"	"strings"	"github.com/gorilla/csrf"	"github.com/gorilla/handlers"	"github.com/gorilla/mux")type App struct {	Store   Store	Handler http.Handler	Config  config.Config}func New(store Store, config config.Config) *App {	fs := http.FileServer(http.Dir("./static")) // todo: embed static assets into binary.	r := mux.NewRouter()	r.StrictSlash(true) // treat "hello" and "hello/" the same	a := App{		Store:   store,		Handler: r,		Config:  config,	}	csrfMiddleware := csrf.Protect(		[]byte(config.CSRFKey),		csrf.Secure(strings.HasPrefix(config.PublicUrl, "https")),		csrf.SameSite(csrf.SameSiteLaxMode), // send cookies on top-level safe navigations, block on cross-site subrequests		csrf.Path("/"),                      // cookie path defaults to the request directory (e.g. /auth) otherwise		csrf.TrustedOrigins([]string{"localhost:3333", fmt.Sprintf("%s:%v", config.Domain, config.Port), config.Domain}),	)	r.Use(csrfMiddleware)	r.Use(md.GeneralAuth(a.Store))	r.Use(md.Messages)	r.Use(handlers.CompressHandler)	// normal website	r.HandleFunc("/", a.Index)	r.HandleFunc("/contributors", func(w http.ResponseWriter, r *http.Request) {		templating.Render(w, r, "base", "contributors")	})	r.HandleFunc("/discover", a.Discover)	r.PathPrefix("/static/").Handler(http.StripPrefix("/static", fs))	r.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {		w.Header().Set("Science-Status", "We do what we must, because we can.")		w.Write([]byte("OK - Still alive.")) // lol funny health check	})	r.Handle("/new", md.RequireAuth(md.ValidateForm[CreateRepoForm](a.NewRepo)))	r.Handle("/admin", md.RequireAdmin(http.HandlerFunc(a.AdminHandler)))	r.Handle("/gist", md.RequireAuth(md.ValidateForm[GistEditForm](a.NewGist))).Methods("GET", "POST")	r.HandleFunc("/gist/{id}", a.GetGist).Methods("GET")	r.Handle("/gist/{id}/delete", md.RequireAuth(http.HandlerFunc(a.DeleteGist))).Methods("POST")	r.Handle("/gist/{id}/edit", md.RequireAuth(md.ValidateForm[GistEditForm](a.EditGist))).Methods("GET", "POST")	// auth routes	auth := r.PathPrefix("/auth").Subrouter()	auth.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {		http.Redirect(w, r, "/auth/login", http.StatusSeeOther)	})	auth.Handle("/login", md.ExcludeAuth(md.ValidateForm[LoginForm](a.Login)))	auth.Handle("/register", md.ExcludeAuth(md.ValidateForm[RegisterForm](a.Register)))	auth.Handle("/register/{token}", md.ExcludeAuth(md.ValidateForm[RegisterForm](http.HandlerFunc(a.Register))))	auth.Handle("/logout", md.RequireAuth(md.ValidateForm[RedeemTokenForm](http.HandlerFunc(a.Logout))))	auth.Handle("/invite", md.ValidateForm[RedeemTokenForm](http.HandlerFunc(a.InviteHandler)))	auth.Handle("/hackatime", md.RequireAuth(http.HandlerFunc(a.HackatimeLogin)))	auth.Handle("/hackatime/callback", md.RequireAuth(http.HandlerFunc(a.HackatimeCallback)))	// config	userConfig := r.PathPrefix("/config").Subrouter()	userConfig.Use(md.RequireAuth)	userConfig.HandleFunc("/", a.UserConfigHandler)	userConfig.Handle("/default_branch", md.ValidateForm[BranchForm](a.branchHandler)).Methods("POST")	userConfig.Handle("/add_ssh_key", md.ValidateForm[AddSSHKeyForm](a.newSSHKeyHandler)).Methods("POST")	userConfig.Handle("/delete_ssh_key", md.ValidateForm[DeleteSSHKeyForm](a.deleteSSHKeyHandler)).Methods("POST")	// users	user := r.PathPrefix("/~{user}").Subrouter()	user.Use(md.UserHandle(a.Store))	user.HandleFunc("/", a.UserProfileHandler)	// repos	repo := r.PathPrefix("/~{user}/{repo}").Subrouter()	repo.Use(md.Repo(a.Store))	repo.HandleFunc("", func(w http.ResponseWriter, r *http.Request) {		if strings.HasSuffix(mux.Vars(r)["repo"], ".git") {			http.Error(w, "Repos are not accessible via HTTP and you should clone them using SSH.", http.StatusMethodNotAllowed)			return		}		withMode("tree", a.RepoHandler).ServeHTTP(w, r)	})	repo.HandleFunc("/issues", a.RepoIssuesIndex)	repo.Handle("/issues/report", md.ValidateForm[repoNewIssueForm](a.repoIssueReport))	repo.Handle("/issues/{issueID}", md.ValidateForm[createIssueCommentForm](a.repoIssueView))	issueClose := repo.PathPrefix("/issues").Subrouter()	issueClose.Use(md.RequireRepoOwnership(a.Store))	issueClose.HandleFunc("/{issueID}/close", a.repoIssueClose).Methods("POST")	repo.HandleFunc("/tree", withMode("tree", a.RepoHandler))	repo.HandleFunc("/tree/{branch}", withMode("tree", a.RepoHandler))	repo.HandleFunc("/tree/{branch}/{path:.+}", withMode("tree", a.RepoHandler))	repo.HandleFunc("/blob/{branch}/{path:.+}", withMode("blob", a.RepoHandler))	repo.HandleFunc("/raw/{branch}/{path:.+}", withMode("raw", a.RepoHandler))	repo.HandleFunc("/log", withMode("log", a.RepoHandler))	repo.HandleFunc("/log/{branch}", withMode("log", a.RepoHandler))	repo.HandleFunc("/zip", a.RepoZipDownload)	repo.HandleFunc("/zip/{branch}", a.RepoZipDownload)	repoConfig := repo.PathPrefix("/config").Subrouter()	repoConfig.Use(md.RequireRepoOwnership(a.Store))	repoConfig.HandleFunc("/", a.RepoConfigHandler).Methods("GET")	repoConfig.Handle("/hackatime", md.ValidateForm[RepoConfigHackatimeForm](a.RepoConfigHackatimeHandler)).Methods("POST")	repoConfig.Handle("/visibility", md.ValidateForm[RepoConfigVisibilityForm](a.RepoConfigVisibilityHandler)).Methods("POST")	repoConfig.Handle("/delete", md.ValidateForm[RepoConfigDeleteForm](a.RepoConfigDeleteHandler)).Methods("POST")	repoConfig.Handle("/issues", md.ValidateForm[RepoConfigIssuesForm](a.RepoConfigIssuesHandler)).Methods("POST")	return &a}