123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- package main
- import (
- "encoding/json"
- "errors"
- "fmt"
- "html/template"
- "log"
- "net/http"
- "strconv"
- "strings"
- "github.com/Masterminds/sprig/v3"
- )
- const (
- App = "contactsapp"
- SecretKey = "hypermedia rocks"
- )
- func MakeTemplate(names ...string) *template.Template {
- names = append([]string{"layout"}, names...)
- paths := make([]string, len(names))
- for i, name := range names {
- paths[i] = fmt.Sprintf("./templates/%s.gohtml", name)
- }
- tpl := template.Must(template.New("layout.html").
- Funcs(sprig.FuncMap()).
- Funcs(template.FuncMap{
- "get_flashed_messages": func() []string {
- return []string{"some message"}
- },
- }).
- ParseFiles(paths...),
- )
- return tpl
- }
- /*
- @app.route("/contacts")
- def contacts():
- */
- func RouteContacts(w http.ResponseWriter, r *http.Request) {
- search := r.URL.Query().Get("q")
- page, err := strconv.Atoi(r.URL.Query().Get("page"))
- if err != nil || page == 0 {
- page = 1
- }
- var contactsSet []Contact
- if search != "" {
- contactsSet = (&Contact{}).Search(search)
- if r.Header.Get("HX-Trigger") == "search" {
- MakeTemplate("rows").Execute(w, anyMap{"contacts": contactsSet})
- return
- }
- } else {
- contactsSet = (&Contact{}).All(page)
- }
- MakeTemplate(
- "index",
- "archive_ui",
- "rows",
- ).Execute(w, anyMap{
- "contacts": contactsSet,
- "search": search,
- "page": page,
- "archiver": NewArchiver(),
- })
- }
- var (
- /*
- @app.route("/")
- def index():
- */
- RouteFront = http.RedirectHandler("/contacts", http.StatusFound)
- /*
- @app.route("/contacts/archive", methods=["POST"])
- def start_archive():
- */
- MakeStartArchive = func(templates *template.Template) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- archiver := NewArchiver()
- archiver.Run()
- templates.ExecuteTemplate(w, "archive_ui.html", anyMap{"archiver": archiver})
- }
- }
- /*
- @app.route("/contacts/archive", methods=["GET"])
- def archive_status():
- */
- MakeArchiveStatus = func(templates *template.Template) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- archiver := NewArchiver()
- templates.ExecuteTemplate(w, "archive_ui.html", anyMap{"archiver": archiver})
- }
- }
- /*
- @app.route("/contacts/archive/file", methods=["GET"])
- def archive_content():
- */
- ArchiveContent http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
- archiver := NewArchiver()
- fileName := archiver.ArchiveFile()
- w.Header().Set("Content-Disposition", fmt.Sprintf(`attachement; filename="%s"`, fileName))
- http.ServeFile(w, r, fileName)
- }
- /*
- @app.route("/contacts/archive", methods=["DELETE"])
- def reset_archive():
- */
- MakeResetArchive = func(templates *template.Template) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- archiver := NewArchiver()
- archiver.Reset()
- templates.ExecuteTemplate(w, "archive_ui.html", anyMap{"archiver": archiver})
- }
- }
- /*
- @app.route("/contacts/count")
- def contacts_count():
- */
- ContactsCount http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
- count := (&Contact{}).Count()
- _, _ = fmt.Fprintf(w, "(%d total Contacts)", count)
- }
- /*
- @app.route("/contacts/new", methods=['GET'])
- def contacts_new_get():
- */
- MakeContactsNewGet = func(templates *template.Template) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- templates.ExecuteTemplate(w, "new.html", anyMap{"contact": NewContact(nil)})
- }
- }
- /*
- @app.route("/contacts/new", methods=['POST'])
- def contacts_new():
- */
- MakeContactsNew = func(templates *template.Template) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- c := NewContact(anyMap{
- "first": r.FormValue("first_name"),
- "last": r.FormValue("last_name"),
- "email": r.FormValue("phone"),
- "phone": r.FormValue("email"),
- })
- if c.Save() {
- flash("Created New Contact!")
- http.Redirect(w, r, "/contacts", http.StatusFound)
- return
- } else {
- templates.ExecuteTemplate(w, "new.html", anyMap{"contact": c})
- }
- }
- }
- /*
- @app.route("/contacts/<contact_id>")
- def contacts_view(contact_id=0):
- */
- MakeContactsView = func(templates *template.Template) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- contact_id, _ := strconv.Atoi(r.PathValue("contact_id"))
- contact := (&Contact{}).Find(uint64(contact_id))
- templates.ExecuteTemplate(w, "show.html", anyMap{"contact": contact})
- }
- }
- /*
- @app.route("/contacts/<contact_id>/edit", methods=["GET"])
- def contacts_edit_get(contact_id=0):
- */
- MakeContactsEditGet = func(templates *template.Template) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- contact_id, _ := strconv.Atoi(r.PathValue("contact_id"))
- contact := (&Contact{}).Find(uint64(contact_id))
- MakeTemplate(
- "edit",
- ).
- Execute(w, anyMap{"contact": contact})
- }
- }
- /*
- @app.route("/contacts/<contact_id>/edit", methods=["POST"])
- def contacts_edit_post(contact_id=0):
- */
- MakeContactsEditPost = func(templates *template.Template) http.HandlerFunc {
- return func(w http.ResponseWriter, request *http.Request) {
- contact_id, _ := strconv.Atoi(request.PathValue("contact_id"))
- c := (&Contact{}).Find(uint64(contact_id))
- c.Update(
- request.FormValue("first_name"),
- request.FormValue("last_name"),
- request.FormValue("phone"),
- request.FormValue("email"),
- )
- if c.Save() {
- flash("Updated Contact!")
- http.Redirect(w, request, fmt.Sprintf("/contacts/%d", contact_id), http.StatusFound)
- return
- } else {
- templates.ExecuteTemplate(w, "edit.html", anyMap{"contact": c})
- }
- }
- }
- /*
- @app.route("/contacts/<contact_id>/email", methods=["GET"])
- def contacts_email_get(contact_id=0):
- */
- ContactsEmailGet http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
- contact_id, _ := strconv.Atoi(r.PathValue("contact_id"))
- c := (&Contact{}).Find(uint64(contact_id))
- c.Email = r.FormValue("email")
- c.validate()
- // FIXME find what this is really expected to do in the original Python.
- for _, err := range c.Errors {
- err := err.Error()
- if strings.Contains(err, "email") {
- fmt.Fprintln(w, err)
- }
- }
- }
- /*
- @app.route("/contacts/<contact_id>", methods=["DELETE"])
- def contacts_delete(contact_id=0):
- */
- ContactsDelete http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
- contact_id, _ := strconv.Atoi(r.PathValue("contact_id"))
- contact := (&Contact{}).Find(uint64(contact_id))
- contact.Delete()
- if r.Header.Get("HX-Trigger") == "delete-btn" {
- flash("Deleted Contact!")
- http.Redirect(w, r, "/contacts", http.StatusSeeOther)
- return
- } else {
- return
- }
- }
- /*
- @app.route("/contacts/", methods=["DELETE"])
- def contacts_delete_all():
- */
- MakeContactsDeleteAll = func(templates *template.Template) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- sContactIDs := strings.Split(r.FormValue("selected_contact_ids"), ",")
- for _, sContactID := range sContactIDs {
- if contactID, err := strconv.Atoi(sContactID); contactID > 0 && err == nil {
- contact := (&Contact{}).Find(uint64(contactID))
- contact.Delete()
- }
- }
- flash("Deleted contacts!")
- contacts_set := (&Contact{}).All(1)
- templates.ExecuteTemplate(w, "index.html", anyMap{"contacts": contacts_set})
- }
- }
- /*
- # ===========================================================
- # JSON Data API
- # ===========================================================
- */
- /*
- @app.route("/api/v1/contacts", methods=["GET"])
- def json_contacts():
- */
- JSONContacts http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
- contacts_set := (&Contact{}).All(1)
- enc := json.NewEncoder(w)
- if err := enc.Encode(anyMap{"contacts": contacts_set}); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- }
- /*
- @app.route("/api/v1/contacts", methods=["POST"])
- def json_contacts_new():
- */
- JSONContactsNew http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
- c := NewContact(anyMap{
- "first": r.FormValue("first_°name"),
- "last": r.FormValue("last_name"),
- "phone": r.FormValue("phone"),
- "email": r.FormValue("email"),
- })
- enc := json.NewEncoder(w)
- if c.Save() {
- if err := enc.Encode(c); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- } else {
- w.WriteHeader(http.StatusBadRequest)
- if err := enc.Encode(anyMap{"errors": c.Errors}); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- }
- }
- /*
- @app.route("/api/v1/contacts/<contact_id>", methods=["GET"])
- def json_contacts_view(contact_id=0):
- */
- JSONContactsEdit http.HandlerFunc = func(w http.ResponseWriter, request *http.Request) {
- contact_id, _ := strconv.Atoi(request.PathValue("contact_id"))
- c := (&Contact{}).Find(uint64(contact_id))
- c.Update(
- request.FormValue("first_name"),
- request.FormValue("last_name"),
- request.FormValue("phone"),
- request.FormValue("email"),
- )
- enc := json.NewEncoder(w)
- if c.Save() {
- if err := enc.Encode(c); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- } else {
- w.WriteHeader(http.StatusBadRequest)
- if err := enc.Encode(anyMap{"errors": c.Errors}); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- }
- }
- /*
- @app.route("/api/v1/contacts/<contact_id>", methods=["DELETE"])
- def json_contacts_delete(contact_id=0):
- */
- JSONContactsDelete http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
- contact_id, _ := strconv.Atoi(r.PathValue("contact_id"))
- contact := (&Contact{}).Find(uint64(contact_id))
- contact.Delete()
- enc := json.NewEncoder(w)
- _ = enc.Encode(anyMap{"success": true})
- }
- )
- /*
- *
- TODO implement me
- */
- func flash(message string) {
- }
- func setupRoutes(mux *http.ServeMux, templates *template.Template) {
- mux.Handle("/", RouteFront)
- mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
- mux.HandleFunc("/contacts", RouteContacts)
- mux.Handle("POST /contacts/archive", MakeStartArchive(templates))
- mux.Handle("GET /contacts/archive", MakeArchiveStatus(templates))
- mux.Handle("GET /contacts/archive/file", ArchiveContent)
- mux.Handle("DELETE /contacts/archive", MakeResetArchive(templates))
- mux.Handle("GET /contacts/count", ContactsCount)
- mux.Handle("GET /contacts/new", MakeContactsNewGet(templates))
- mux.Handle("POST /contacts/new", MakeContactsNew(templates))
- mux.Handle("GET /contacts/{contact_id}", MakeContactsView(templates))
- mux.Handle("GET /contacts/{contact_id}/edit", MakeContactsEditGet(templates))
- mux.Handle("POST /contacts/{contact_id}/edit", MakeContactsEditPost(templates))
- mux.Handle("GET /contacts/{contact_id}/email", ContactsEmailGet)
- mux.Handle("DELETE /contacts/{contact_id}", ContactsDelete)
- mux.Handle("DELETE /contacts/", MakeContactsDeleteAll(templates))
- mux.Handle("GET /api/v1/contacts", JSONContacts)
- mux.Handle("POST /api/v1/contacts", JSONContactsNew)
- mux.Handle("GET /api/v1/contacts/{contact_id}", JSONContactsEdit)
- mux.Handle("DELETE /api/v1/contacts/{contact_id}", JSONContactsDelete)
- }
- func main() {
- mux := http.NewServeMux()
- templates := template.Must(
- template.
- New("htmx-app").
- Funcs(sprig.FuncMap()). // Arithmetic, etc.
- Funcs(template.FuncMap{
- "get_flashed_messages": func() string { return "some message" },
- }).
- ParseGlob("./templates/*.gohtml"),
- )
- setupRoutes(mux, templates)
- (&Contact{}).LoadDB()
- if err := http.ListenAndServe(":8080", mux); err != nil {
- if !errors.Is(err, http.ErrServerClosed) {
- log.Printf("Server error: %v\n", err)
- return
- }
- }
- log.Println("Server shutdown")
- }
|