mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-24 21:46:22 -04:00
#74 social login - admin panel
This commit is contained in:
parent
248f597f46
commit
508a27dcf7
@ -28,9 +28,16 @@
|
||||
{{s.el.username}}
|
||||
</td>
|
||||
<td>
|
||||
<a :href="`mailto:${s.el.email}`" target="_blank" rel="noopener">
|
||||
{{s.el.email}}
|
||||
</a>
|
||||
<p>
|
||||
<a :href="`mailto:${s.el.email}`" target="_blank" rel="noopener">
|
||||
{{s.el.email}}
|
||||
</a>
|
||||
</p>
|
||||
<ul v-if="s.el.socialConnections.length" class="list-inline">
|
||||
<li v-for="conn in s.el.socialConnections" class="list-inline-item">
|
||||
<Icon :v="socialProviders[conn].icon || conn" set="b"/>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<span :class="['badge', s.el.roles === 'admin' ? 'badge-primary' : 'badge-light']">
|
||||
@ -53,8 +60,12 @@
|
||||
|
||||
<script>
|
||||
import {head} from "../src/helpers";
|
||||
import {socialProviders} from "../src/data";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return { socialProviders }
|
||||
},
|
||||
async asyncData({ app, store }) {
|
||||
if (!store.state.user || store.state.user.roles !== 'admin') {
|
||||
return {};
|
||||
|
@ -1,15 +1,13 @@
|
||||
import {gravatar} from "../src/helpers";
|
||||
import {gravatar, now} 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})
|
||||
AND (validUntil IS NULL OR validUntil > ${now()})
|
||||
`)
|
||||
if (auth) {
|
||||
return JSON.parse(auth.payload).avatar;
|
||||
|
@ -24,7 +24,7 @@ app.use(async function (req, res, next) {
|
||||
next();
|
||||
});
|
||||
|
||||
router.use(grant.express()(require('./social').default));
|
||||
router.use(grant.express()(require('./social').config));
|
||||
|
||||
app.use(require('./routes/banner').default);
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Router } from 'express';
|
||||
import SQL from 'sql-template-strings';
|
||||
import avatar from '../avatar';
|
||||
import {config as socialLoginConfig} from "../social";
|
||||
import {now} from "../../src/helpers";
|
||||
|
||||
const router = Router();
|
||||
|
||||
@ -16,6 +18,12 @@ router.get('/admin/users', async (req, res) => {
|
||||
ORDER BY u.id DESC
|
||||
`);
|
||||
|
||||
const authenticators = await req.db.all(SQL`
|
||||
SELECT userId, type FROM authenticators
|
||||
WHERE type IN (`.append(Object.keys(socialLoginConfig).map(k => `'${k}'`).join(',')).append(SQL`)
|
||||
AND (validUntil IS NULL OR validUntil > ${now()})
|
||||
`));
|
||||
|
||||
const groupedUsers = {};
|
||||
for (let user of users) {
|
||||
if (groupedUsers[user.id] === undefined) {
|
||||
@ -24,12 +32,17 @@ router.get('/admin/users', async (req, res) => {
|
||||
locale: undefined,
|
||||
profiles: user.locale ? [user.locale] : [],
|
||||
avatar: await avatar(req.db, user),
|
||||
socialConnections: [],
|
||||
}
|
||||
} else {
|
||||
groupedUsers[user.id].profiles.push(user.locale);
|
||||
}
|
||||
}
|
||||
|
||||
for (let auth of authenticators) {
|
||||
groupedUsers[auth.userId].socialConnections.push(auth.type);
|
||||
}
|
||||
|
||||
return res.json(groupedUsers);
|
||||
});
|
||||
|
||||
|
@ -1,14 +1,13 @@
|
||||
import { Router } from 'express';
|
||||
import SQL from 'sql-template-strings';
|
||||
import {ulid} from "ulid";
|
||||
import {buildDict, makeId} from "../../src/helpers";
|
||||
import {buildDict, makeId, now} from "../../src/helpers";
|
||||
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);
|
||||
import { config as socialLoginConfig, handlers as socialLoginHandlers } from '../social';
|
||||
|
||||
const USERNAME_CHARS = 'A-Za-zĄĆĘŁŃÓŚŻŹąćęłńóśżź0-9._-';
|
||||
|
||||
@ -21,7 +20,7 @@ const saveAuthenticator = async (db, type, user, payload, validForMinutes = null
|
||||
${user ? user.id : null},
|
||||
${type},
|
||||
${JSON.stringify(payload)},
|
||||
${validForMinutes ? (now + validForMinutes * 60) : null}
|
||||
${validForMinutes ? (now() + validForMinutes * 60) : null}
|
||||
)`);
|
||||
return id;
|
||||
}
|
||||
@ -30,7 +29,7 @@ const findAuthenticator = async (db, id, type) => {
|
||||
const authenticator = await db.get(SQL`SELECT * FROM authenticators
|
||||
WHERE id = ${id}
|
||||
AND type = ${type}
|
||||
AND (validUntil IS NULL OR validUntil > ${now})
|
||||
AND (validUntil IS NULL OR validUntil > ${now()})
|
||||
`);
|
||||
|
||||
if (authenticator) {
|
||||
@ -42,7 +41,7 @@ const findAuthenticator = async (db, id, type) => {
|
||||
|
||||
const invalidateAuthenticator = async (db, id) => {
|
||||
await db.get(SQL`UPDATE authenticators
|
||||
SET validUntil = ${now}
|
||||
SET validUntil = ${now()}
|
||||
WHERE id = ${id}
|
||||
`);
|
||||
}
|
||||
@ -246,39 +245,6 @@ router.post('/user/delete', async (req, res) => {
|
||||
return res.json(true);
|
||||
});
|
||||
|
||||
const socialLoginHandlers = {
|
||||
twitter(r) {
|
||||
return {
|
||||
id: r.profile.id_str,
|
||||
email: r.profile.email,
|
||||
name: r.profile.screen_name,
|
||||
avatar: r.profile.profile_image_url_https.replace('_normal', '_400x400'),
|
||||
access_token: r.access_token,
|
||||
access_secret: r.access_secret,
|
||||
}
|
||||
},
|
||||
facebook(r) {
|
||||
return {
|
||||
id: r.profile.id,
|
||||
email: r.profile.email,
|
||||
name: r.profile.name,
|
||||
avatar: r.profile.picture.data.url,
|
||||
access_token: r.access_token,
|
||||
access_secret: r.access_secret,
|
||||
}
|
||||
},
|
||||
google(r) {
|
||||
return {
|
||||
id: r.profile.sub,
|
||||
email: r.profile.email_verified !== false ? r.profile.email : undefined,
|
||||
name: r.profile.email,
|
||||
avatar: r.profile.picture,
|
||||
access_token: r.access_token,
|
||||
access_secret: r.access_secret,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
router.get('/user/social/:provider', async (req, res) => {
|
||||
const payload = socialLoginHandlers[req.params.provider](req.session.grant.response)
|
||||
|
||||
@ -286,7 +252,7 @@ router.get('/user/social/:provider', async (req, res) => {
|
||||
SELECT * FROM authenticators
|
||||
WHERE type = ${req.params.provider}
|
||||
AND payload LIKE ${'{"id":"' + payload.id + '"%'}
|
||||
AND (validUntil IS NULL OR validUntil > ${now})
|
||||
AND (validUntil IS NULL OR validUntil > ${now()})
|
||||
`)
|
||||
|
||||
const user = auth ? await req.db.get(SQL`
|
||||
@ -319,9 +285,9 @@ router.get('/user/social-connections', async (req, res) => {
|
||||
|
||||
const authenticators = await req.db.all(SQL`
|
||||
SELECT type, payload FROM authenticators
|
||||
WHERE type IN (`.append(Object.keys(socialLoginHandlers).map(k => `'${k}'`).join(',')).append(SQL`)
|
||||
WHERE type IN (`.append(Object.keys(socialLoginConfig).map(k => `'${k}'`).join(',')).append(SQL`)
|
||||
AND userId = ${req.user.id}
|
||||
AND (validUntil IS NULL OR validUntil > ${now})
|
||||
AND (validUntil IS NULL OR validUntil > ${now()})
|
||||
`));
|
||||
|
||||
return res.json(buildDict(function* () {
|
||||
@ -340,7 +306,7 @@ router.post('/user/social-connection/:provider/disconnect', async (req, res) =>
|
||||
SELECT id FROM authenticators
|
||||
WHERE type = ${req.params.provider}
|
||||
AND userId = ${req.user.id}
|
||||
AND (validUntil IS NULL OR validUntil > ${now})
|
||||
AND (validUntil IS NULL OR validUntil > ${now()})
|
||||
`)
|
||||
|
||||
await invalidateAuthenticator(req.db, auth.id)
|
||||
|
@ -1,4 +1,4 @@
|
||||
export default {
|
||||
export const config = {
|
||||
defaults: {
|
||||
origin: process.env.BASE_URL,
|
||||
transport: 'session',
|
||||
@ -25,3 +25,36 @@ export default {
|
||||
callback: '/api/user/social/google',
|
||||
}
|
||||
}
|
||||
|
||||
export const handlers = {
|
||||
twitter(r) {
|
||||
return {
|
||||
id: r.profile.id_str,
|
||||
email: r.profile.email,
|
||||
name: r.profile.screen_name,
|
||||
avatar: r.profile.profile_image_url_https.replace('_normal', '_400x400'),
|
||||
access_token: r.access_token,
|
||||
access_secret: r.access_secret,
|
||||
}
|
||||
},
|
||||
facebook(r) {
|
||||
return {
|
||||
id: r.profile.id,
|
||||
email: r.profile.email,
|
||||
name: r.profile.name,
|
||||
avatar: r.profile.picture.data.url,
|
||||
access_token: r.access_token,
|
||||
access_secret: r.access_secret,
|
||||
}
|
||||
},
|
||||
google(r) {
|
||||
return {
|
||||
id: r.profile.sub,
|
||||
email: r.profile.email_verified !== false ? r.profile.email : undefined,
|
||||
name: r.profile.email,
|
||||
avatar: r.profile.picture,
|
||||
access_token: r.access_token,
|
||||
access_secret: r.access_secret,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -118,3 +118,7 @@ export const curry = function (func) {
|
||||
export const capitalise = function (word) {
|
||||
return word.substring(0, 1).toUpperCase() + word.substring(1);
|
||||
}
|
||||
|
||||
export const now = function () {
|
||||
return Math.floor(Date.now() / 1000);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user