package main import ( "errors" "fmt" "io/ioutil" "log" "net/http" "os" "regexp" "strings" "text/template" "time" "github.com/google/uuid" "gopkg.in/yaml.v3" ) // Config types // ------------ type ServerConfig struct { Name string Homepage string } type PanelConfig struct { Host string WebRoot string Port int LogPath string } type AuthConfig struct { GiteaURL string ClientID string ClientSecret string AuthorizedUsers []string } type Config struct { Server ServerConfig Panel PanelConfig Auth AuthConfig } var config = new(Config) var redirecturi string var requesturl string func loadConfig() (err error) { configfile, err := ioutil.ReadFile("config.yml") if err != nil { return err } err = yaml.Unmarshal(configfile, &config) // todo: check for bad data !! var re = regexp.MustCompile(`(((ftp|http|https):\/\/)|(\/)|(..\/))(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?`) tocheck := [2]string{config.Panel.Host, config.Auth.GiteaURL} for _, val := range tocheck { if len(re.FindStringIndex(val)) == 0 { log.Fatalf(`"%s" is not a valid url.`, val) } } if config.Panel.Port > 65535 { log.Fatalf("port is too big (max 65535)") } // init oauth bits redirecturi = fmt.Sprintf("%s/login/endpoint", config.Panel.Host) requesturl = fmt.Sprintf("%s/login/oauth/authorize?client_id=%s&redirect_uri=%s&response_type=code&state=STATE", strings.Trim(config.Auth.GiteaURL, " "), strings.Trim(config.Auth.ClientID, " "), strings.Trim(redirecturi, " ")) return err } func openLogFile(logfile string) { if logfile != "" { lf, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640) if err != nil { log.Fatal("OpenLogfile: os.OpenFile:", err) } log.SetOutput(lf) } } func logRequest(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Printf("%s %s %s\n", r.RemoteAddr, r.Method, r.URL) handler.ServeHTTP(w, r) }) } func main() { err := loadConfig() if err != nil { log.Fatalf("couldn't load config: %s", err) } openLogFile(config.Panel.LogPath) log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) http.HandleFunc("/login/", loginHandler) http.Handle("/resources/", http.StripPrefix("/resources/", http.FileServer(http.Dir("./resources")))) http.HandleFunc("/", handler) fmt.Printf("listening on %v\n", config.Panel.Port) fmt.Printf("Logging to %v\n", config.Panel.LogPath) err = http.ListenAndServe(fmt.Sprintf(":%d", config.Panel.Port), logRequest(http.DefaultServeMux)) if err != nil { log.Fatal(err) } } func exists(path string) (bool, error) { _, err := os.Stat(path) if err == nil { return true, nil } if os.IsNotExist(err) { return false, nil } return false, err } func handler(w http.ResponseWriter, r *http.Request) { path := r.URL.Path loc := fmt.Sprintf("templates%s.html", path) // var re = regexp.MustCompile(`\.(svg|jpg|jpeg|png|webp|ico|css|js)$`) // if re.MatchString(path) { // http.StripPrefix("/resources/", // http.FileServer(http.Dir("./resources"))) // return // } var tmpl string anonPath := regexp.MustCompile(`^/(signup|login|hello)$`) m := anonPath.FindStringSubmatch(path) if m != nil { userid, err := r.Cookie("user-id") if errors.As(err, &http.ErrNoCookie) { http.Redirect(w, r, "/hello", http.StatusFound) } fmt.Print(userid) } switch path { case "/": tmpl = "templates/home.html" t, _ := template.ParseFiles(tmpl) t.Execute(w, config) default: exists, err := exists(loc) if err != nil { log.Fatal(err) } if !exists { http.NotFound(w, r) return } tmpl = fmt.Sprintf("templates%s.html", path) t, _ := template.ParseFiles(tmpl) t.Execute(w, config) } } func loginHandler(w http.ResponseWriter, r *http.Request) { userid, err := r.Cookie("user-id") fmt.Print(userid, err) if errors.As(err, &http.ErrNoCookie) { useridbody := uuid.New() expiration := time.Now().Add(365 * 24 * time.Hour) cookie := http.Cookie{Name: "user-id", Value: useridbody.String(), Expires: expiration} http.SetCookie(w, &cookie) // todo: store in db } switch strings.TrimRight(r.URL.Path, "/") { case "/login": http.Redirect(w, r, requesturl, http.StatusFound) case "/login/endpoint": loginEndpoint(w, r) } } func loginEndpoint(w http.ResponseWriter, r *http.Request) { token := r.FormValue("code") fmt.Print(token) }