diff --git a/.gitignore b/.gitignore index 56e4652f9fd0ebcd2030a07c6f3bbcb50d3bf56a..9780d9b6aec7b3df855ee7ad2bf51fd82cc70075 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .idea secrets.env -data/ \ No newline at end of file +app \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..35d70790c2ec55d48990a6129b6e4f19cc3defdf --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM golang:alpine + +WORKDIR /app + +ADD templates /app/templates +ADD app /app/app + +EXPOSE 8080 + +ENTRYPOINT ["/app/app"] \ No newline at end of file diff --git a/api/send.go b/api/send.go new file mode 100644 index 0000000000000000000000000000000000000000..eb5e8fae70b49e6d792f0a8156d962da8ceb659c --- /dev/null +++ b/api/send.go @@ -0,0 +1,59 @@ +package api + +import ( + "encoding/json" + "fmt" + "git.sch.bme.hu/mikewashere/voxfrontend/db" + "git.sch.bme.hu/mikewashere/voxfrontend/languages" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +func SendHandler(w http.ResponseWriter, r *http.Request) { + b, e := ioutil.ReadAll(r.Body) + if e != nil { + _, _ = w.Write([]byte(e.Error())) + return + } + + var msg db.Message + e = json.Unmarshal(b, &msg) + if e != nil { + _, _ = w.Write([]byte(e.Error())) + return + } + + msg.Lang = languages.Voice(strings.TrimSpace(string(msg.Lang))) + + u := db.GetUser(r) + msg.SchAcc = u.SchAcc + u.Lang = msg.Lang + + resp, e := http.Get(fmt.Sprintf("http://vox.internal/uplink/duma/%s?%s", msg.Lang.ESpeak(), url.QueryEscape(msg.Message))) + if e != nil { + _, _ = w.Write([]byte(e.Error())) + return + } + b, e = ioutil.ReadAll(resp.Body) + if e != nil { + _, _ = w.Write([]byte(e.Error())) + return + } + + e = db.SaveMsg(&msg) + if e != nil { + _, _ = w.Write([]byte(e.Error())) + return + } + + go func() { + e := db.UpdateUser(&u) + if e != nil { + fmt.Println(e) + } + }() + + _, _ = w.Write(b) +} diff --git a/cookies/data.go b/db/data.go similarity index 52% rename from cookies/data.go rename to db/data.go index 424c094daaa077006b1e16a09300777ec9989502..f62739d211badb30a256a5ad6612e1dba4cd2d5b 100644 --- a/cookies/data.go +++ b/db/data.go @@ -1,4 +1,4 @@ -package cookies +package db import ( "fmt" @@ -6,6 +6,8 @@ import ( "github.com/go-pg/pg/v10" "github.com/go-pg/pg/v10/orm" "net/http" + "os" + "strings" "time" ) @@ -25,17 +27,39 @@ type Cookie struct { } type Message struct { - Id int64 `pg:"id,pk"` - Sent time.Time `pg:"sent,default:now()"` - Message string `pg:"message"` - SchAcc string `pg:"user_schacc"` - User *User `pg:"rel:has-one"` + Id int64 `pg:"id,pk"` + Sent time.Time `pg:"sent,default:now()"` + Message string `pg:"message"` + SchAcc string `pg:"user_schacc"` + Lang languages.Voice `pg:"lang,default:'ðŸ‡ðŸ‡º'"` + User *User `pg:"rel:has-one"` } var db = pg.Connect(&pg.Options{ - Addr: "localhost:5432", - User: "postgres", - Password: "postgres", + Addr: func() string { + host := os.Getenv("POSTGRES") + if host == "" { + host = "localhost" + } + if !strings.Contains(host, ":") { + host += ":5432" + } + return host + }(), + User: func() string { + us := os.Getenv("POSTGRES_USER") + if us == "" { + us = "postgres" + } + return us + }(), + Password: func() string { + pw := os.Getenv("POSTGRES_PASS") + if pw == "" { + pw = "postgres" + } + return pw + }(), }) func init() { @@ -56,6 +80,17 @@ func init() { } } +func GetLastMessages() ([]Message, error) { + var msgs []Message + + err := db.Model(&msgs).Relation("User").Order("sent DESC").Limit(10).Select() + if err != nil { + return nil, err + } + + return msgs, nil +} + func GetUser(r *http.Request) User { c, e := r.Cookie(cookiename) if e != nil { @@ -74,6 +109,36 @@ func GetUser(r *http.Request) User { return *cook.User } +func Logout(w http.ResponseWriter, r *http.Request) { + c, e := r.Cookie(cookiename) + if e != nil { + return + } + + cook := &Cookie{ + Id: c.Value, + } + + _, e = db.Model(cook).WherePK().Delete() + + http.Redirect(w, r, "/", http.StatusFound) +} + +func SwitchTheme(w http.ResponseWriter, r *http.Request) { + u := GetUser(r) + u.Dark = !u.Dark + e := UpdateUser(&u) + if e != nil { + fmt.Println(e) + } + http.Redirect(w, r, "/", http.StatusFound) +} + +func UpdateUser(u *User) error { + _, e := db.Model(u).WherePK().Update() + return e +} + func SaveUser(w http.ResponseWriter, name, schacc string) error { u := &User{ SchAcc: schacc, @@ -107,3 +172,8 @@ func SaveUser(w http.ResponseWriter, name, schacc string) error { return nil } + +func SaveMsg(msg *Message) error { + _, e := db.Model(msg).Insert(msg) + return e +} diff --git a/languages/languages.go b/languages/languages.go index 4be2030e31a9ca133cb2762313d9845f0a42f3d3..9a01f031e07fb8bb960d21cb67f1e2b0f208c2af 100644 --- a/languages/languages.go +++ b/languages/languages.go @@ -15,6 +15,7 @@ var voices = map[Voice]string{ "ðŸ‡ðŸ‡º": "hu", "🇬🇧": "en", "🇩🇪": "de", + "🤫": "whisper", } var Voices = make([]Voice, 0, len(voices)) diff --git a/main.go b/main.go index 25f1fd205ae3b070380270aa532fcb350a2e1df7..8a9c0b6140c951e483cd61b86e85664cf36663e0 100644 --- a/main.go +++ b/main.go @@ -3,7 +3,8 @@ package main import ( "fmt" "git.sch.bme.hu/mikewashere/authsch" - "git.sch.bme.hu/mikewashere/voxfrontend/cookies" + "git.sch.bme.hu/mikewashere/voxfrontend/api" + "git.sch.bme.hu/mikewashere/voxfrontend/db" "git.sch.bme.hu/mikewashere/voxfrontend/mainpage" "net/http" "os" @@ -45,7 +46,7 @@ func authSuccess(details *authsch.AccDetails, w http.ResponseWriter, r *http.Req return } - e := cookies.SaveUser(w, details.DisplayName, details.LinkedAccounts.SchAcc) + e := db.SaveUser(w, details.DisplayName, details.LinkedAccounts.SchAcc) if e != nil { fmt.Println(e) } @@ -61,6 +62,9 @@ func main() { mux.Handle("/", mainpage.CreateHandler(auth)) mux.Handle("/login/", auth.GetLoginHandler(authSuccess, authError)) + mux.HandleFunc("/send/", api.SendHandler) + mux.HandleFunc("/logout/", db.Logout) + mux.HandleFunc("/themeswitch/", db.SwitchTheme) e := http.ListenAndServe(":8080", mux) if e != nil { diff --git a/mainpage/handler.go b/mainpage/handler.go index 4ad2e2206a1fec7d03a6b8356aa5d044c6fd06e9..20451bd6df3dff4746ffda5bc8e4902741c828e6 100644 --- a/mainpage/handler.go +++ b/mainpage/handler.go @@ -3,7 +3,7 @@ package mainpage import ( "fmt" "git.sch.bme.hu/mikewashere/authsch" - "git.sch.bme.hu/mikewashere/voxfrontend/cookies" + "git.sch.bme.hu/mikewashere/voxfrontend/db" "git.sch.bme.hu/mikewashere/voxfrontend/languages" "html/template" "net/http" @@ -12,15 +12,16 @@ import ( var tmpl *template.Template type indexData struct { - User cookies.User + User db.User LoginURL string Langs []languages.Voice DefLang languages.Voice + Msgs []db.Message } func init() { var e error - tmpl, e = template.ParseFiles("mainpage/index.template.html") + tmpl, e = template.ParseFiles("templates/index.template.html") if e != nil { panic(e) } @@ -29,12 +30,18 @@ func init() { func CreateHandler(client authsch.Client) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { data := indexData{ - User: cookies.GetUser(r), + User: db.GetUser(r), LoginURL: client.GetAuthURL(), Langs: languages.Voices, DefLang: languages.DefaultVoice, } + var err error + data.Msgs, err = db.GetLastMessages() + if err != nil { + fmt.Println(err) + } + e := tmpl.Execute(w, data) if e != nil { fmt.Println(e) diff --git a/mainpage/index.template.html b/mainpage/index.template.html deleted file mode 100644 index 43da48874d09068f62558a30dc4b4d42fef75f93..0000000000000000000000000000000000000000 --- a/mainpage/index.template.html +++ /dev/null @@ -1,64 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="UTF-8"> - <title>Vox</title> - <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> - <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous"> -</head> -<body class="{{ if .User.Dark }} bg-dark {{ end }}"> - - <nav class="navbar navbar-expand-lg {{ if .User.Dark }} navbar-dark {{ else }} navbar-light {{ end }} bg-primary"> - <a class="navbar-brand" href="#">Vox</a> - <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation"> - <span class="navbar-toggler-icon"></span> - </button> - <div class="collapse navbar-collapse" id="navbarNavAltMarkup"> - <div class="navbar-nav mr-auto"></div> - <div class="navbar-nav"> - {{ if .User.SchAcc }} - <li class="nav-item dropdown"> - <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> - {{ .User.SchAcc }} - </a> - <div class="dropdown-menu {{ if .User.Dark }} bg-dark {{ end }}" style="width: auto; min-width: auto" aria-labelledby="navbarDropdown"> - <a class="nav-link" href="/logout" style="padding: 1em; width: auto">Log out</a> - </div> - </li> - {{ else }} - <a class="nav-link" href="{{ .LoginURL }}">Log in</a> - {{ end }} - </div> - </div> - </nav> - - <div class="container" style="padding: 2em"> - {{ if .User.SchAcc }} - <div class="input-group mb-3"> - <div class="input-group-prepend"> - <button class="btn btn-info dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> - {{ .User.Lang }} - </button> - <div class="dropdown-menu {{ if .User.Dark }} bg-dark text-white {{ end }}"> - {{ range .Langs }} - <a class="dropdown-item {{ if $.User.Dark }} text-white {{ end }}" href="#">{{ . }} ({{ .ESpeak }})</a> - {{ end }} - </div> - </div> - <input type="text" class="form-control {{ if .User.Dark }} bg-secondary text-white {{ end }}" aria-label="Vox message"> - <div class="input-group-append"> - <button class="btn btn-primary" type="button" id="button-addon2">Küldés!</button> - </div> - </div> - {{ else }} - <h1> - Be kell jelentkezned, ahhoz, hogy használhasd Vox-ot. - </h1> - {{ end }} - </div> - - <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> - <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script> - <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script> -</body> -</html> \ No newline at end of file diff --git a/templates/index.template.html b/templates/index.template.html new file mode 100644 index 0000000000000000000000000000000000000000..e3258ad54a73cfbd1b41ea8000495815a3146f2b --- /dev/null +++ b/templates/index.template.html @@ -0,0 +1,166 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Vox</title> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous"> +</head> +<body class="{{ if .User.Dark }} bg-dark {{ end }}"> + + <div id="overlay"> + <h2 class="text-white" style="margin-top: 50vh; width: 100%; text-align: center"> + <div class="spinner-border text-primary" role="status"> + <span class="sr-only">Loading...</span> + </div> + Vox éppen beszél... + </h2> + </div> + <style> + #overlay { + visibility: hidden; + opacity: 0; + transition: visibility 0s, opacity 0.5s linear; + + backdrop-filter: blur(10px); + background-color: rgba(0, 0, 0, 0.5); + position: fixed; + left: 0; + z-index: 999; + top: 0; + width: 100%; + height: 100%; + } + </style> + + <nav class="navbar navbar-expand-lg {{ if .User.Dark }} navbar-dark {{ else }} navbar-light {{ end }} bg-primary"> + <a class="navbar-brand" href="#">Vox</a> + <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation"> + <span class="navbar-toggler-icon"></span> + </button> + <div class="collapse navbar-collapse" id="navbarNavAltMarkup"> + <div class="navbar-nav mr-auto"> + <a class="nav-link" href="/themeswitch/">Témaváltás</a> + </div> + <div class="navbar-nav"> + {{ if .User.SchAcc }} + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + {{ .User.SchAcc }} + </a> + <div class="dropdown-menu {{ if .User.Dark }} bg-dark {{ end }}" style="width: auto; min-width: auto" aria-labelledby="navbarDropdown"> + <a class="nav-link" href="/logout" style="padding: 1em; width: auto">Log out</a> + </div> + </li> + {{ else }} + <a class="nav-link" href="{{ .LoginURL }}">Log in</a> + {{ end }} + </div> + </div> + </nav> + + <div class="container" style="padding: 2em"> + {{ if .User.SchAcc }} + <div class="input-group mb-3"> + <div class="input-group-prepend"> + <button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="langbutton"> + {{ .User.Lang }} + </button> + <script> + const btn = document.getElementById("langbutton"); + function changeLanguage(lang) { + btn.innerText = lang; + } + </script> + <div class="dropdown-menu {{ if .User.Dark }} bg-dark text-white {{ end }}"> + {{ range .Langs }} + <a class="dropdown-item {{ if $.User.Dark }} text-white {{ end }}" onclick="changeLanguage({{ . }})">{{ . }} ({{ .ESpeak }})</a> + {{ end }} + </div> + </div> + <input id="msg" type="text" class="form-control {{ if .User.Dark }} bg-secondary text-white {{ end }}" aria-label="Vox message"> + <div class="input-group-append"> + <button class="btn btn-primary" type="button" id="send">Küldés!</button> + </div> + <script> + const lang = document.getElementById("langbutton"); + const msg = document.getElementById("msg"); + const overlay = document.getElementById("overlay"); + + function send() { + const obj = { + Message: msg.value, + Lang: lang.innerText, + }; + + + overlay.style.visibility = "visible"; + overlay.style.opacity = "1"; + + + const xhr = new XMLHttpRequest(); + xhr.open("POST", "/send/", true); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.send(JSON.stringify(obj)); + xhr.onload = () => { + window.location.reload(); + } + } + + msg.addEventListener("keyup", (event) => { + if (event.keyCode === 13) { + event.preventDefault(); + send(); + } + }); + document.getElementById('send').onclick = send; + </script> + </div> + {{ if .Msgs }} + <div class="container"> + <div class="card"> + <div class="card-body" {{ if .User.Dark }} style="background: #545b62" {{ end }}> + <table class="table {{ if .User.Dark }} text-white {{ end }}"> + <thead> + <tr> + <th scope="row">IdÅ‘</th> + <th scope="row">Üzenet</th> + <th scope="row">KüldÅ‘</th> + <th scope="row">Nyelv</th> + </tr> + </thead> + {{ range .Msgs }} + <tr> + <td> + {{ .Sent.Format "Jan 2 15:04" }} + </td> + <td> + {{ .Message }} + </td> + <td> + {{ .SchAcc }} + </td> + <td> + {{ .Lang }} + </td> + </tr> + {{ end }} + </table> + </div> + </div> + </div> + {{ end }} + {{ else }} + <h1> + Be kell jelentkezned, ahhoz, hogy használhasd Vox-ot. + </h1> + {{ end }} + </div> + + + + <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> + <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script> + <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script> +</body> +</html> \ No newline at end of file