small pixel drawing of a pufferfish vore

bleh
Jes Olson j3s@c3f.net
Sun, 12 Mar 2023 04:46:58 -0700
commit

d07519db817474cdef5034279dc79b864d229961

parent

772c5ba06e2959f28cf0e77133a1a2c03510e9a0

8 files changed, 233 insertions(+), 0 deletions(-)

jump to
A .gitignore

@@ -0,0 +1,1 @@

+feeds.gay
M go.modgo.mod

@@ -1,3 +1,17 @@

module git.j3s.sh/feeds.gay go 1.20 + +require github.com/glebarez/go-sqlite v1.21.0 + +require ( + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230126093431-47fa9a501578 // indirect + golang.org/x/sys v0.4.0 // indirect + modernc.org/libc v1.22.2 // indirect + modernc.org/mathutil v1.5.0 // indirect + modernc.org/memory v1.5.0 // indirect + modernc.org/sqlite v1.20.4 // indirect +)
A go.sum

@@ -0,0 +1,23 @@

+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/glebarez/go-sqlite v1.21.0 h1:b8MHPtBagkSD2gntImZPsG3o3QEXgMDxguW/GLUonHQ= +github.com/glebarez/go-sqlite v1.21.0/go.mod h1:GodsA6yGSa3eKbvpr7dS+JaqazzVfMcjIXvx6KHhW/c= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/remyoudompheng/bigfft v0.0.0-20230126093431-47fa9a501578 h1:VstopitMQi3hZP0fzvnsLmzXZdQGc4bEcgu24cp+d4M= +github.com/remyoudompheng/bigfft v0.0.0-20230126093431-47fa9a501578/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +modernc.org/libc v1.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0= +modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= +modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds= +modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/sqlite v1.20.4 h1:J8+m2trkN+KKoE7jglyHYYYiaq5xmz2HoHJIiBlRzbE= +modernc.org/sqlite v1.20.4/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A=
A http.go

@@ -0,0 +1,31 @@

+package main + +import ( + "net/http" + "strings" +) + +func internalServerError(w http.ResponseWriter, details string) { + status := "oopsie woopsie, uwu\n" + status += "we made a fucky wucky!!\n\n" + status += details + http.Error(w, status, http.StatusInternalServerError) +} + +// methodAllowed takes an http w/r, and returns true if the +// http requests method is in teh allowedMethods list. +// if methodNotAllowed returns false, it has already +// written a request & it's on the caller to close it. +func methodAllowed(w http.ResponseWriter, r *http.Request, allowedMethods ...string) bool { + allowed := false + for _, m := range allowedMethods { + if m == r.Method { + allowed = true + } + } + if allowed == false { + w.Header().Set("Allow", strings.Join(allowedMethods, ", ")) + http.Error(w, "405 method not allowed", http.StatusMethodNotAllowed) + } + return allowed +}
M main.gomain.go

@@ -1,1 +1,17 @@

package main + +import ( + "log" + "net/http" +) + +func main() { + s := New() + log.Println("listening on http://localhost:5544") + mux := http.NewServeMux() + mux.HandleFunc("/", s.rootHandler) + mux.HandleFunc("/login", s.loginHandler) + mux.HandleFunc("/logout", s.logoutHandler) + mux.HandleFunc("/register", s.registerHandler) + s.Start(":5544", mux) +}
A readme

@@ -0,0 +1,61 @@

+todo + + [ ] GET / + logged out: + describe what feeds.gay is & why it's cool, tell ppl to sign up + discover (list of top20 most popular feeds on feeds.gay) + logged in: + discover (list of top20 most popular feeds on feeds.gay) + [ ] GET /login + form (existing account): username & password + form (new account): username & password + built & maintained by jes + [ ] POST /login + success: + add session cookie + update last_login + redirect to /{username} + fail: + log an existing user in, add session cookie, redirect to /{username} + login failure, redirect to /login + [ ] POST /logout + delete session cookie if exists + redirect to / + [ ] POST /register + success: + add session cookie + redirect to / + fail: + update + add new user & hashed password to database + register failure: redirect to /login + [ ] GET /{username} + display pretty feeds for a given user + there is a button to view the raw feed list (plaintext) + if logged in: there is an edit button + [ ] GET /{username}/feeds + put your feeds here, one per line: + <text box with pre-populated list of feeds from {username}> + display a text box pre-populated with the list of feeds that make up {username} + if you don't know what feeds to use, check out /discover for ideas + button: validate + + if validated, display locked list of feeds with button: submit + [ ] POST /{username}/feeds + subscribe to all of the feeds in your textbox + + maybe: + rate limiting for logins & registrations + + background: + fetch feeds every ~90m + + headers + unauth'd: login + auth'd: my feeds | login + + sql + user (id, username, password, created_at) + feed (id, url, fetch_error, created_at, created_by) + subscribe (id, user, feed, created_at, created_by) +
A site.go

@@ -0,0 +1,71 @@

+package main + +import ( + "database/sql" + "fmt" + "log" + "net/http" + + "git.j3s.sh/feeds.gay/sqlite" +) + +type Site struct { + db *sql.DB +} + +// New returns a fully populated & ready for action Site +func New() *Site { + s := Site{ + db: sqlite.SetupAndOpen("feeds.gay.db"), + } + return &s +} + +func (s *Site) Start(addr string, mux *http.ServeMux) { + log.Fatal(http.ListenAndServe(addr, mux)) +} + +func (s *Site) rootHandler(w http.ResponseWriter, r *http.Request) { + if !methodAllowed(w, r, "GET") { + return + } + // The "/" pattern matches everything, so we need to check + // that we're at the root here. + if r.URL.Path != "/" { + http.NotFound(w, r) + return + } + fmt.Fprintf(w, "feeds.gay is dope & you should like it\n") +} + +func (s *Site) loginHandler(w http.ResponseWriter, r *http.Request) { + if !methodAllowed(w, r, "GET", "POST") { + return + } + if r.Method == "GET" { + // if logged out: + fmt.Fprintf(w, "display login forms\n") + // if logged in: + fmt.Fprintf(w, "you are already logged in :D\n") + } + if r.Method == "POST" { + fmt.Fprintf(w, "cmon POST\n") + } +} + +func (s *Site) logoutHandler(w http.ResponseWriter, r *http.Request) { + if !methodAllowed(w, r, "POST") { + return + } + // TODO: delete session cookie + http.Redirect(w, r, "/", http.StatusSeeOther) +} + +func (s *Site) registerHandler(w http.ResponseWriter, r *http.Request) { + if !methodAllowed(w, r, "POST") { + return + } + // TODO: create user in database + // TODO: add session cookie + http.Redirect(w, r, "/", http.StatusSeeOther) +}
A sqlite/sql.go

@@ -0,0 +1,16 @@

+package sqlite + +import ( + "database/sql" + "log" + + _ "github.com/glebarez/go-sqlite" +) + +func SetupAndOpen(path string) *sql.DB { + db, err := sql.Open("sqlite", path) + if err != nil { + log.Fatal(err) + } + return db +}