Add endpoint to get status as json

This commit is contained in:
m15o 2021-11-24 07:30:19 +01:00
parent 087e5a2f4c
commit 003373834f
15 changed files with 221 additions and 101 deletions

2
Makefile Normal file
View file

@ -0,0 +1,2 @@
build:
CGO_ENABLED=0 GOOS=linux go build -o bin/statuscafe main.go

View file

@ -22,7 +22,6 @@ var TplCommonMap = map[string]string{
{{ template "content" . }}
</main>
<footer>
This is the footer
</footer>
</div>

View file

@ -64,21 +64,26 @@ func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http
}
h.initTpl()
// Index
router.HandleFunc("/", h.showIndexView).Methods(http.MethodGet)
router.HandleFunc("/login", h.showLoginView).Methods(http.MethodGet)
router.HandleFunc("/check-login", h.checkLogin).Methods(http.MethodPost)
router.HandleFunc("/register", h.handleRegister)
router.HandleFunc("/logout", h.logout).Methods(http.MethodGet)
//router.HandleFunc("/statuses/new", h.showNewStatusView).Methods(http.MethodGet)
router.HandleFunc("/status-new", h.showNewStatusView).Methods(http.MethodGet)
router.HandleFunc("/status-save", h.saveStatus).Methods(http.MethodPost)
router.HandleFunc("/statuses/{id}/edit", h.showEditStatusView).Methods(http.MethodGet)
router.HandleFunc("/statuses/{id}/update", h.updateStatus).Methods(http.MethodPost)
//router.HandleFunc("/statuses/{id}/remove", h.handleRemoveStatus)
//router.HandleFunc("/status-remove", h.handleRemoveStatus)
router.PathPrefix("/").Handler(http.FileServer(http.Dir(cfg.AssetsDir)))
router.HandleFunc("/users/{user}", h.showUserView).Methods(http.MethodGet)
router.HandleFunc("/users/{user}/status.json", h.showUserStatusView).Methods(http.MethodGet)
router.PathPrefix("/assets/").Handler(
http.StripPrefix("/assets/",
http.FileServer(
http.Dir(cfg.AssetsDir),
),
),
)
return router, nil
}

View file

@ -11,7 +11,16 @@ Are you sure you you want to delete the following status?
<input type="submit" value="Submit">
</form>
{{ end }}`,
"create_status": `{{ define "content" }}
"create_status": `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>status cafe</title>
<meta name="description" content="your friends' updates">
</head>
<body>
<main>
<h1>New status</h1>
{{ if .form.Error }}
<p>{{ .form.Error }}</p>
@ -19,13 +28,33 @@ Are you sure you you want to delete the following status?
{{ if .flash }}
<p>{{ .flash }}</p>
{{ end }}
<form action="/statuses/save" method="post">
<form action="/status-save?silent=1" method="post" name="update-status">
<div class="field">
<input type="text" name="content" placeholder="What's new?" required autofocus />
<textarea name="content" placeholder="What's new?" required style="width: 100%; box-sizing: border-box;" autofocus></textarea>
</div>
<br>
<input type="submit" value="Submit">
</form>
{{ end }}`,
</main>
</div>
<script>
document.forms["update-status"].onsubmit = function (event) {
event.preventDefault();
fetch("/status-save?silent=1", {
method: "POST",
body: new FormData(document.forms["update-status"])
})
.then(response => response.ok)
.then(submitted => {
if (submitted) {
window.close()
window.location = "/"
}
})
}
</script>
</body>
</html>`,
"edit_status": `{{ define "content" }}
<h1>Edit status</h1>
{{ if .form.Error }}
@ -55,17 +84,18 @@ Are you sure you you want to delete the following status?
{{ end }}
<form action="/status-save" method="post">
<div class="field">
<textarea name="content" placeholder="What's new?" required style="width: 100%; box-sizing: border-box;" autofocus></textarea>
<textarea name="content" placeholder="What's new?" required style="width: 100%; box-sizing: border-box; height: 100px;" autofocus></textarea>
</div>
<br>
<input type="submit" value="Submit">
<p>Drag to your bookmarks to update from anywhere: <a href="javascript:void(open('https://status.cafe/status-new','status.cafe','resizable,scrollbars,width=360,height=200'))">Set status</a></p>
</form>
</section>
<section>
<h2>Status stream</h2>
{{ range .statuses }}
<article class="status">
<div class="username"><a href="/{{ .User }}">{{ .User }}</a>, {{ .TimeAgo }}</div>
<div class="username"><a href="/users/{{ .User }}">{{ .User }}</a>, {{ .TimeAgo }}</div>
<p>{{ .Content }}</p>
</article>
{{ end }}
@ -111,5 +141,16 @@ Are you sure you you want to delete the following status?
<br>
<input type="submit" value="Submit">
</form>
{{ end }}`,
"user": `{{ define "content" }}
<section>
<h2>{{ .user }}</h2>
{{ range .statuses }}
<article class="status">
<div class="username">{{ .TimeAgo }}</div>
<p>{{ .Content }}</p>
</article>
{{ end }}
</section>
{{ end }}`,
}

View file

@ -17,7 +17,6 @@
{{ template "content" . }}
</main>
<footer>
This is the footer
</footer>
</div>

View file

@ -1,4 +1,13 @@
{{ define "content" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>status cafe</title>
<meta name="description" content="your friends' updates">
</head>
<body>
<main>
<h1>New status</h1>
{{ if .form.Error }}
<p>{{ .form.Error }}</p>
@ -6,10 +15,30 @@
{{ if .flash }}
<p>{{ .flash }}</p>
{{ end }}
<form action="/statuses/save" method="post">
<form action="/status-save?silent=1" method="post" name="update-status">
<div class="field">
<input type="text" name="content" placeholder="What's new?" required autofocus />
<textarea name="content" placeholder="What's new?" required style="width: 100%; box-sizing: border-box;" autofocus></textarea>
</div>
<br>
<input type="submit" value="Submit">
</form>
{{ end }}
</main>
</div>
<script>
document.forms["update-status"].onsubmit = function (event) {
event.preventDefault();
fetch("/status-save?silent=1", {
method: "POST",
body: new FormData(document.forms["update-status"])
})
.then(response => response.ok)
.then(submitted => {
if (submitted) {
window.close()
window.location = "/"
}
})
}
</script>
</body>
</html>

View file

@ -10,17 +10,18 @@
{{ end }}
<form action="/status-save" method="post">
<div class="field">
<textarea name="content" placeholder="What's new?" required style="width: 100%; box-sizing: border-box;" autofocus></textarea>
<textarea name="content" placeholder="What's new?" required style="width: 100%; box-sizing: border-box; height: 100px;" autofocus></textarea>
</div>
<br>
<input type="submit" value="Submit">
<p>Drag to your bookmarks to update from anywhere: <a href="javascript:void(open('https://status.cafe/status-new','status.cafe','resizable,scrollbars,width=360,height=200'))">Set status</a></p>
</form>
</section>
<section>
<h2>Status stream</h2>
{{ range .statuses }}
<article class="status">
<div class="username"><a href="/{{ .User }}">{{ .User }}</a>, {{ .TimeAgo }}</div>
<div class="username"><a href="/users/{{ .User }}">{{ .User }}</a>, {{ .TimeAgo }}</div>
<p>{{ .Content }}</p>
</article>
{{ end }}

View file

@ -0,0 +1,11 @@
{{ define "content" }}
<section>
<h2>{{ .user }}</h2>
{{ range .statuses }}
<article class="status">
<div class="username">{{ .TimeAgo }}</div>
<p>{{ .Content }}</p>
</article>
{{ end }}
</section>
{{ end }}

View file

@ -1,7 +1,6 @@
package handler
import (
"fmt"
"net/http"
)
@ -11,11 +10,6 @@ type Update struct {
}
func (h *Handler) showIndexView(w http.ResponseWriter, r *http.Request) {
user, err := h.getUser(r)
if err != nil {
unauthorized(w)
return
}
statuses, err := h.storage.LatestStatuses()
if err != nil {
serverError(w, err)
@ -31,7 +25,6 @@ func (h *Handler) showIndexView(w http.ResponseWriter, r *http.Request) {
flash = flashes[0].(string)
}
session.Save(r, w)
fmt.Println(user)
h.renderLayout(w, "index", map[string]interface{}{
"statuses": statuses,
"flash": flash,

View file

@ -112,6 +112,6 @@ func (h *Handler) register(w http.ResponseWriter, r *http.Request) {
serverError(w, err)
return
}
h.renderLayout(w, "index", nil, user.Name)
http.Redirect(w, r, "/", http.StatusFound)
}
}

View file

@ -3,22 +3,10 @@ package handler
import "net/http"
func (h *Handler) showNewStatusView(w http.ResponseWriter, r *http.Request) {
user, err := h.getUser(r)
_, err := h.getUser(r)
if err != nil {
unauthorized(w)
return
}
session, err := h.sess.Store.Get(r, "ichi")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
flash := ""
if flashes := session.Flashes(); len(flashes) > 0 {
flash = flashes[0].(string)
}
session.Save(r, w)
h.renderLayout(w, "create_status", map[string]interface{}{
"flash": flash,
}, user)
h.view("create_status").Execute(w, nil)
}

View file

@ -12,11 +12,9 @@ func RouteInt64Param(r *http.Request, param string) int64 {
if err != nil {
return 0
}
if value < 0 {
return 0
}
return value
}

View file

@ -28,6 +28,7 @@ func (h *Handler) saveStatus(w http.ResponseWriter, r *http.Request) {
serverError(w, err)
return
}
if r.URL.Query().Get("silent") != "1" {
session, err := h.sess.Store.Get(r, "ichi")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
@ -41,3 +42,4 @@ func (h *Handler) saveStatus(w http.ResponseWriter, r *http.Request) {
}
http.Redirect(w, r, "/", http.StatusFound)
}
}

52
web/handler/user_view.go Normal file
View file

@ -0,0 +1,52 @@
package handler
import (
"encoding/json"
"github.com/gorilla/mux"
"net/http"
)
func (h *Handler) showUserView(w http.ResponseWriter, r *http.Request) {
user := mux.Vars(r)["user"]
if !h.storage.UserExists(user) {
notFound(w)
return
}
statuses, err := h.storage.StatusByUsername(mux.Vars(r)["user"], 20, 0)
if err != nil {
serverError(w, err)
return
}
h.renderLayout(w, "user", map[string]interface{}{
"user": user,
"statuses": statuses,
}, "")
}
type statusjson struct {
Author string `json:"author"`
Content string `json:"content"`
TimeAgo string `json:"time_ago"`
}
func (h *Handler) showUserStatusView(w http.ResponseWriter, r *http.Request) {
user := mux.Vars(r)["user"]
if !h.storage.UserExists(user) {
notFound(w)
return
}
statuses, err := h.storage.StatusByUsername(mux.Vars(r)["user"], 1, 0)
if err != nil {
serverError(w, err)
return
}
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Content-Type", "application/json")
var res statusjson
if len(statuses) > 0 {
res.Author = statuses[0].User
res.Content = statuses[0].Content
res.TimeAgo = statuses[0].TimeAgo()
}
json.NewEncoder(w).Encode(res)
}

View file

@ -10,15 +10,15 @@ import (
"status/web/session"
)
func httpToHTTPSHandler() *http.ServeMux {
handleRedirect := func(w http.ResponseWriter, r *http.Request) {
newURI := "https://" + r.Host + r.URL.String()
http.Redirect(w, r, newURI, http.StatusFound)
}
mux := &http.ServeMux{}
mux.HandleFunc("/", handleRedirect)
return mux
}
//func httpToHTTPSHandler() *http.ServeMux {
// handleRedirect := func(w http.ResponseWriter, r *http.Request) {
// newURI := "https://" + r.Host + r.URL.String()
// http.Redirect(w, r, newURI, http.StatusFound)
// }
// mux := &http.ServeMux{}
// mux.HandleFunc("/", handleRedirect)
// return mux
//}
func Serve(data *storage.Storage, cfg *config.Config) error {
var err error
@ -27,21 +27,21 @@ func Serve(data *storage.Storage, cfg *config.Config) error {
if err != nil {
log.Fatal(err)
}
switch cfg.Env {
case "PROD":
go func() {
fmt.Printf("Starting HTTP server on :443\n")
err := http.ListenAndServeTLS(":443", cfg.CertFile, cfg.KeyFile, s)
if err != nil {
log.Fatalf("httpsSrv.ListendAndServeTLS() failed with %s", err)
}
}()
fmt.Printf("Starting HTTP to HTTPS server on :80\n")
err = http.ListenAndServe(":80", httpToHTTPSHandler())
break
default:
//switch cfg.Env {
//case "PROD":
// go func() {
// fmt.Printf("Starting HTTP server on :443\n")
// err := http.ListenAndServeTLS(":443", cfg.CertFile, cfg.KeyFile, s)
// if err != nil {
// log.Fatalf("httpsSrv.ListendAndServeTLS() failed with %s", err)
// }
// }()
// fmt.Printf("Starting HTTP to HTTPS server on :80\n")
// err = http.ListenAndServe(":80", httpToHTTPSHandler())
// break
//default:
fmt.Printf("Starting HTTP server on port 8000\n")
err = http.ListenAndServe(":8000", s)
}
//}
return err
}