#88 select avatar source

This commit is contained in:
Andrea Vos 2020-11-02 21:12:15 +01:00
parent a248bf8667
commit 81d8212786
7 changed files with 86 additions and 39 deletions

View File

@ -6,7 +6,13 @@
<p class="mb-0">
<Avatar :user="$user()"/>
</p>
<p>
<p v-if="$user().avatarSource" class="mt-3">
Gravatar:
<a href="#" @click.prevent="setAvatar(null)">
<Avatar :user="$user()" grav dsize="2rem"/>
</a>
</p>
<p v-else>
<a href="https://gravatar.com" target="_blank" rel="noopener" class="small">
<Icon v="external-link"/>
<T>user.avatar.change</T>
@ -73,7 +79,8 @@
</template>
<ul v-if="socialConnections !== undefined" class="list-group">
<li v-for="(providerOptions, provider) in socialProviders" :key="provider" :class="['list-group-item', 'en' === config.locale ? 'profile-current' : '']">
<SocialConnection :provider="provider" :providerOptions="providerOptions" :connection="socialConnections[provider]" @disconnected="socialConnections[provider] = undefined"/>
<SocialConnection :provider="provider" :providerOptions="providerOptions" :connection="socialConnections[provider]"
@disconnected="socialConnections[provider] = undefined" @setAvatar="setAvatar"/>
</li>
</ul>
</Loading>
@ -183,6 +190,12 @@
this.logout();
},
async setAvatar(source) {
const response = await this.$axios.$post(`/user/set-avatar`, {source});
this.$store.commit('setToken', response.token);
this.$cookies.set('token', this.$store.state.token);
},
},
}
</script>

View File

@ -1,5 +1,6 @@
<template>
<img :src="src || gravatar" alt="" class="rounded-circle" :style="`width: 100%;max-width: ${dsize};max-height: ${dsize};`"/>
<img :src="grav ? gravatar : (src || user.avatar || gravatar)" alt="" class="rounded-circle"
:style="`width: 100%;max-width: ${dsize};max-height: ${dsize};`"/>
</template>
<script>
@ -8,9 +9,10 @@
export default {
props: {
user: { required: true },
src: {},
size: { 'default': 128 },
dsize: { 'default': '6rem' },
src: {},
grav: { type: Boolean },
},
data() {
return {

View File

@ -12,7 +12,9 @@
</span>
<span v-else class="text-center">
<span class="mr-3">
<Avatar :src="connection.avatar" :user="$user()" dsize="2rem"/>
<a href="#" @click.prevent="$emit('setAvatar', provider)">
<Avatar :src="connection.avatar" :user="$user()" dsize="2rem"/>
</a>
{{connection.name}}
</span>
<br class="d-md-none"/>

20
server/avatar.js Normal file
View File

@ -0,0 +1,20 @@
import {gravatar} from "../src/helpers";
import SQL from "sql-template-strings";
const now = Math.floor(Date.now() / 1000);
export default async (db, user) => {
if (user.avatarSource) {
const auth = await db.get(SQL`
SELECT payload FROM authenticators
WHERE type = ${user.avatarSource}
AND userId = ${user.id}
AND (validUntil IS NULL OR validUntil > ${now})
`)
if (auth) {
return JSON.parse(auth.payload).avatar;
}
}
return gravatar(user);
}

View File

@ -1,5 +1,6 @@
import { Router } from 'express';
import SQL from 'sql-template-strings';
import avatar from '../avatar';
const router = Router();
@ -9,7 +10,7 @@ router.get('/admin/users', async (req, res) => {
}
const users = await req.db.all(SQL`
SELECT u.id, u.username, u.email, u.roles, p.locale
SELECT u.id, u.username, u.email, u.roles, u.avatarSource, p.locale
FROM users u
LEFT JOIN profiles p ON p.userId = u.id
ORDER BY u.id DESC
@ -22,6 +23,7 @@ router.get('/admin/users', async (req, res) => {
...user,
locale: undefined,
profiles: user.locale ? [user.locale] : [],
avatar: await avatar(req.db, user),
}
} else {
groupedUsers[user.id].profiles.push(user.locale);

View File

@ -3,6 +3,7 @@ import SQL from 'sql-template-strings';
import md5 from "js-md5";
import {buildDict} from "../../src/helpers";
import {ulid} from "ulid";
import avatar from "../avatar";
const calcAge = birthday => {
if (!birthday) {
@ -21,41 +22,33 @@ const calcAge = birthday => {
return parseInt(Math.floor(diff / 1000 / 60 / 60 / 24 / 365.25));
}
const buildProfile = profile => {
return {
id: profile.id,
userId: profile.userId,
username: profile.username,
emailHash: md5(profile.email),
names: JSON.parse(profile.names),
pronouns: JSON.parse(profile.pronouns),
description: profile.description,
age: calcAge(profile.birthday),
links: JSON.parse(profile.links),
flags: JSON.parse(profile.flags),
words: JSON.parse(profile.words),
};
};
const fetchProfiles = async (db, username, self) => {
const profiles = await db.all(SQL`
SELECT profiles.*, users.username, users.email FROM profiles LEFT JOIN users on users.id == profiles.userId
SELECT profiles.*, users.id, users.username, users.email, users.avatarSource FROM profiles LEFT JOIN users on users.id == profiles.userId
WHERE users.username = ${username}
AND profiles.active = 1
ORDER BY profiles.locale
`);
return buildDict(function* () {
for (let profile of profiles) {
yield [
profile.locale,
{
...buildProfile(profile),
birthday: self ? profile.birthday : undefined,
}
];
}
});
const p = {}
for (let profile of profiles) {
p[profile.locale] = {
id: profile.id,
userId: profile.userId,
username: profile.username,
emailHash: md5(profile.email),
names: JSON.parse(profile.names),
pronouns: JSON.parse(profile.pronouns),
description: profile.description,
age: calcAge(profile.birthday),
links: JSON.parse(profile.links),
flags: JSON.parse(profile.flags),
words: JSON.parse(profile.words),
avatar: await avatar(db, profile),
birthday: self ? profile.birthday : undefined,
};
}
return p;
};
const router = Router();

View File

@ -6,6 +6,7 @@ import translations from "../translations";
import jwt from "../../src/jwt";
import mailer from "../../src/mailer";
import config from '../config';
import avatar from '../avatar';
const now = Math.floor(Date.now() / 1000);
@ -63,7 +64,7 @@ const defaultUsername = async (db, email) => {
}
}
const fetchOrCreateUser = async (db, user) => {
const fetchOrCreateUser = async (db, user, avatarSource = null) => {
let dbUser = await db.get(SQL`SELECT * FROM users WHERE email = ${normalise(user.email)}`);
if (!dbUser) {
dbUser = {
@ -71,12 +72,14 @@ const fetchOrCreateUser = async (db, user) => {
username: await defaultUsername(db, user.name || user.email),
email: normalise(user.email),
roles: 'user',
avatarSource: null,
avatarSource: avatarSource,
}
await db.get(SQL`INSERT INTO users(id, username, email, roles, avatarSource)
VALUES (${dbUser.id}, ${dbUser.username}, ${dbUser.email}, ${dbUser.roles}, ${dbUser.avatarSource})`)
}
dbUser.avatar = await avatar(db, dbUser);
return dbUser;
}
@ -255,7 +258,6 @@ const socialLoginHandlers = {
}
},
facebook(r) {
console.log(r);
return {
id: r.profile.id,
email: r.profile.email,
@ -266,7 +268,6 @@ const socialLoginHandlers = {
}
},
google(r) {
console.log(r);
return {
id: r.profile.sub,
email: r.profile.email_verified !== false ? r.profile.email : undefined,
@ -296,7 +297,7 @@ router.get('/user/social/:provider', async (req, res) => {
const dbUser = await fetchOrCreateUser(req.db, user || {
email: payload.email || `${payload.id}@${req.params.provider}.oauth`,
name: payload.name,
});
}, req.params.provider);
const token = jwt.sign({
...dbUser,
@ -347,4 +348,18 @@ router.post('/user/social-connection/:provider/disconnect', async (req, res) =>
return res.json('ok');
});
router.post('/user/set-avatar', async (req, res) => {
if (!req.user) {
return res.status(401).json({error: 'Unauthorised'});
}
await req.db.get(SQL`
UPDATE users
SET avatarSource = ${req.body.source || null}
WHERE id = ${req.user.id}
`)
return res.json({token: await issueAuthentication(req.db, req.user)});
});
export default router;