From 8c5c94da5d25ac5b3d435d83f17f6092cfb6c015 Mon Sep 17 00:00:00 2001 From: Immanuel Onyeka Date: Thu, 3 Aug 2023 18:45:28 -0400 Subject: [PATCH] Make GET and POST requests to user avatar endpoints --- assets/main.css | 4 +++ components/app.vue | 36 +++++++++++++++++++-- components/settings.vue | 49 +++++++++++++++++++++++------ skouter.go | 69 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 13 deletions(-) diff --git a/assets/main.css b/assets/main.css index 14ac617..021e600 100644 --- a/assets/main.css +++ b/assets/main.css @@ -385,6 +385,10 @@ section.mi .row input[type=checkbox] { position: absolute; } +label.error { + color: var(--text-important); +} + section.estimates .entry { padding: 10px 3px; border-bottom: 2px solid var(--outline); diff --git a/components/app.vue b/components/app.vue index 2a99736..9b8e70d 100644 --- a/components/app.vue +++ b/components/app.vue @@ -26,7 +26,11 @@ v-else-if="active == 3" @removeFeeTemp="(fee) => fees = fees.filter(f => f.id != fee.id)" /> - + @@ -97,12 +101,36 @@ function getUser() { this.user = result[0] if (this.user.avatar) return - fetch("/assets/image/empty-avatar.jpg").then(r => r.blob()). - then(b => this.user.avatar = b) + return getAvatar(token) + }).then(b => { + const validTypes = ['image/jpeg', 'image/png'] + + if (!validTypes.includes(b.type) || b.size <= 1) { + fetch("/assets/image/empty-avatar.jpg"). + then(r => r.blob()).then( a => this.user.avatar = a ) + return + } + + this.user.avatar = b }) } +function getAvatar(t) { + return fetch("/api/user/avatar", + {method: 'GET', + headers: { + "Accept": "application/json", + "Authorization": `Bearer ${t || this.token}`, + } + }).then(r => r.blob()) +} + +function updateAvatar() { + const token = getCookie("skouter") + getAvatar(token).then(b => this.user.avatar = b) +} + function getFees() { const token = getCookie("skouter") @@ -181,6 +209,8 @@ export default { getUser, getFees, refreshToken, + updateAvatar, + getAvatar, }, data() { return { diff --git a/components/settings.vue b/components/settings.vue index d9eba03..b296cdd 100644 --- a/components/settings.vue +++ b/components/settings.vue @@ -6,9 +6,10 @@

Avatar

+
@@ -56,14 +57,16 @@ diff --git a/skouter.go b/skouter.go index 73a9095..5e6f3e8 100644 --- a/skouter.go +++ b/skouter.go @@ -17,6 +17,7 @@ import ( "errors" "strings" "math" + "io" // pdf "github.com/SebastiaanKlippert/go-wkhtmltopdf" "github.com/golang-jwt/jwt/v4" ) @@ -959,6 +960,66 @@ func createUser(w http.ResponseWriter, db *sql.DB, r *http.Request) { json.NewEncoder(w).Encode(user) } +func fetchAvatar(db *sql.DB, user int) ( []byte, error ) { + var img []byte + var query string + var err error + + query = `SELECT + avatar + FROM user WHERE user.id = ? + ` + row := db.QueryRow(query, user) + err = row.Scan(&img) + + if err != nil { + return img, err + } + + return img, nil +} + + +func insertAvatar(db *sql.DB, user int, img []byte) error { + query := `UPDATE user + SET avatar = ? + WHERE id = ? + ` + _, err := db.Exec(query, img, user) + if err != nil { + return err + } + + return nil +} + +func setAvatar(w http.ResponseWriter, db *sql.DB, r *http.Request) { + var validTypes []string = []string{"image/png", "image/jpeg"} + var isValidType bool + + claims, err := getClaims(r) + if err != nil { http.Error(w, "Invalid token.", 422); return } + img, err := io.ReadAll(r.Body) + if err != nil { http.Error(w, "Invalid file.", 422); return } + for _, v := range validTypes { + if v == http.DetectContentType(img) { isValidType = true } + } + if !isValidType { http.Error(w, "Invalid file type.", 422); return } + + err = insertAvatar(db, claims.Id, img) + if err != nil { http.Error(w, "Could not insert.", 500); return } +} + +func getAvatar(w http.ResponseWriter, db *sql.DB, r *http.Request) { + claims, err := getClaims(r) + if err != nil { http.Error(w, "Invalid token.", 422); return } + img, err := fetchAvatar(db, claims.Id) + if err != nil { http.Error(w, "Could not retrieve.", 500); return } + + w.Header().Set("Content-Type", http.DetectContentType(img)) + w.Write(img) +} + func queryBorrower(db *sql.DB, id int) ( Borrower, error ) { var borrower Borrower var query string @@ -1602,6 +1663,14 @@ func api(w http.ResponseWriter, r *http.Request) { r.Method == http.MethodDelete && guard(r, 3): deleteUser(w, db, r) + case match(p, "/api/user/avatar", &args) && + r.Method == http.MethodGet && + guard(r, 1): + getAvatar(w, db, r) + case match(p, "/api/user/avatar", &args) && + r.Method == http.MethodPost && + guard(r, 1): + setAvatar(w, db, r) case match(p, "/api/fees", &args) && r.Method == http.MethodGet && guard(r, 1):