Browse Source

Make GET and POST requests to user avatar endpoints

master
Immanuel Onyeka 1 year ago
parent
commit
8c5c94da5d
4 changed files with 145 additions and 13 deletions
  1. +4
    -0
      assets/main.css
  2. +33
    -3
      components/app.vue
  3. +39
    -10
      components/settings.vue
  4. +69
    -0
      skouter.go

+ 4
- 0
assets/main.css View File

@@ -385,6 +385,10 @@ section.mi .row input[type=checkbox] {
position: absolute; position: absolute;
} }


label.error {
color: var(--text-important);
}

section.estimates .entry { section.estimates .entry {
padding: 10px 3px; padding: 10px 3px;
border-bottom: 2px solid var(--outline); border-bottom: 2px solid var(--outline);


+ 33
- 3
components/app.vue View File

@@ -26,7 +26,11 @@ v-else-if="active == 3"
@removeFeeTemp="(fee) => fees = fees.filter(f => f.id != fee.id)" @removeFeeTemp="(fee) => fees = fees.filter(f => f.id != fee.id)"
/> />


<settings :user="user" v-else-if="active == 4" />
<settings
:user="user"
:token="token"
@updateAvatar="updateAvatar"
v-else-if="active == 4" />
<sign-out :user="user" v-else-if="active == 5" /> <sign-out :user="user" v-else-if="active == 5" />
</template> </template>


@@ -97,12 +101,36 @@ function getUser() {
this.user = result[0] this.user = result[0]
if (this.user.avatar) return 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() { function getFees() {
const token = getCookie("skouter") const token = getCookie("skouter")
@@ -181,6 +209,8 @@ export default {
getUser, getUser,
getFees, getFees,
refreshToken, refreshToken,
updateAvatar,
getAvatar,
}, },
data() { data() {
return { return {


+ 39
- 10
components/settings.vue View File

@@ -6,9 +6,10 @@
<h3>Avatar</h3> <h3>Avatar</h3>
<canvas width="200" height="200" ref="canvas"></canvas> <canvas width="200" height="200" ref="canvas"></canvas>
<input type="file" <input type="file"
@change="e => changeAvatar(e.target.files[0])"
@change="e => uploadAvatar(e.target.files[0])"
/> />
<button>Upload</button> <button>Upload</button>
<label class="error">{{avatarError}}</label>
</section> </section>


<section class="form inputs"> <section class="form inputs">
@@ -56,14 +57,16 @@
</template> </template>


<script setup> <script setup>
import { ref, watch } from "vue"
import { ref, watch, onMounted } from "vue"
import Dialog from "./dialog.vue" import Dialog from "./dialog.vue"


let avatar = ref(null) let avatar = ref(null)
let ready = ref(false) let ready = ref(false)
let avatarChanged = ref(false) let avatarChanged = ref(false)
let avatarError = ref('')
const canvas = ref(null) const canvas = ref(null)
const props = defineProps(['user', 'token']) const props = defineProps(['user', 'token'])
const emit = defineEmits(['updateAvatar'])


function save() { function save() {
} }
@@ -73,25 +76,51 @@ function check() {
} }


function uploadAvatar(blob) { function uploadAvatar(blob) {

changeAvatar(blob)?.then(() => {
canvas.value.toBlob(b => {
fetch(`/api/user/avatar`,
{method: 'POST',
body: b,
headers: {
"Accept": "application/json",
"Authorization": `Bearer ${props.token}`,
},
}).then(resp => {
if (resp.ok) {emit('updateAvatar')}
})
})
})
canvas.value.toBlob(b => {
// uploadAvatar(b)
})
} }


function changeAvatar(blob) { function changeAvatar(blob) {
const validTypes = ['image/jpeg', 'image/png']
if (!validTypes.includes(blob.type)) {
avatarError.value = 'Image must be JPEG of PNG format'
return
}
avatarError.value = ''
avatar.value = blob avatar.value = blob
createImageBitmap(blob,
return createImageBitmap(blob,
{resizeWidth: 200, resizeHeight: 200, resizeQuality: 'medium'}). {resizeWidth: 200, resizeHeight: 200, resizeQuality: 'medium'}).
then((img) => { then((img) => {
avatar.value = img avatar.value = img
canvas.value.getContext("2d").drawImage(img, 0, 0, 200, 200) canvas.value.getContext("2d").drawImage(img, 0, 0, 200, 200)
canvas.value.toBlob(b => {
console.log(b)
})
}) })
} }




watch(props.user, () => { watch(props.user, () => {
if (!props.user.avatar) return
changeAvatar(props.user.avatar) changeAvatar(props.user.avatar)
})
}, {immediate: true})

</script> </script>

+ 69
- 0
skouter.go View File

@@ -17,6 +17,7 @@ import (
"errors" "errors"
"strings" "strings"
"math" "math"
"io"
// pdf "github.com/SebastiaanKlippert/go-wkhtmltopdf" // pdf "github.com/SebastiaanKlippert/go-wkhtmltopdf"
"github.com/golang-jwt/jwt/v4" "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) 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 ) { func queryBorrower(db *sql.DB, id int) ( Borrower, error ) {
var borrower Borrower var borrower Borrower
var query string var query string
@@ -1602,6 +1663,14 @@ func api(w http.ResponseWriter, r *http.Request) {
r.Method == http.MethodDelete && r.Method == http.MethodDelete &&
guard(r, 3): guard(r, 3):
deleteUser(w, db, r) 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) && case match(p, "/api/fees", &args) &&
r.Method == http.MethodGet && r.Method == http.MethodGet &&
guard(r, 1): guard(r, 1):


Loading…
Cancel
Save