mirror of
https://github.com/plankanban/planka.git
synced 2025-07-18 20:59:44 +02:00
parent
7273b33768
commit
4fe77c305c
15 changed files with 130 additions and 42 deletions
|
@ -47,9 +47,11 @@ initializeCore.fetchConfig = (config) => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const logout = () => ({
|
const logout = (invalidateAccessToken) => ({
|
||||||
type: ActionTypes.LOGOUT,
|
type: ActionTypes.LOGOUT,
|
||||||
payload: {},
|
payload: {
|
||||||
|
invalidateAccessToken,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
logout.invalidateAccessToken = () => ({
|
logout.invalidateAccessToken = () => ({
|
||||||
|
|
|
@ -38,7 +38,7 @@ const handleCardUpdate = (card) => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const moveCard = (id, listId, index = 0) => ({
|
const moveCard = (id, listId, index) => ({
|
||||||
type: EntryActionTypes.CARD_MOVE,
|
type: EntryActionTypes.CARD_MOVE,
|
||||||
payload: {
|
payload: {
|
||||||
id,
|
id,
|
||||||
|
@ -47,7 +47,7 @@ const moveCard = (id, listId, index = 0) => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const moveCurrentCard = (listId, index = 0) => ({
|
const moveCurrentCard = (listId, index) => ({
|
||||||
type: EntryActionTypes.CURRENT_CARD_MOVE,
|
type: EntryActionTypes.CURRENT_CARD_MOVE,
|
||||||
payload: {
|
payload: {
|
||||||
listId,
|
listId,
|
||||||
|
@ -55,7 +55,7 @@ const moveCurrentCard = (listId, index = 0) => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const transferCard = (id, boardId, listId, index = 0) => ({
|
const transferCard = (id, boardId, listId, index) => ({
|
||||||
type: EntryActionTypes.CARD_TRANSFER,
|
type: EntryActionTypes.CARD_TRANSFER,
|
||||||
payload: {
|
payload: {
|
||||||
id,
|
id,
|
||||||
|
@ -65,7 +65,7 @@ const transferCard = (id, boardId, listId, index = 0) => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const transferCurrentCard = (boardId, listId, index = 0) => ({
|
const transferCurrentCard = (boardId, listId, index) => ({
|
||||||
type: EntryActionTypes.CURRENT_CARD_TRANSFER,
|
type: EntryActionTypes.CURRENT_CARD_TRANSFER,
|
||||||
payload: {
|
payload: {
|
||||||
boardId,
|
boardId,
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import EntryActionTypes from '../constants/EntryActionTypes';
|
import EntryActionTypes from '../constants/EntryActionTypes';
|
||||||
|
|
||||||
const logout = () => ({
|
const logout = (invalidateAccessToken) => ({
|
||||||
type: EntryActionTypes.LOGOUT,
|
type: EntryActionTypes.LOGOUT,
|
||||||
payload: {},
|
payload: {
|
||||||
|
invalidateAccessToken,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { call, fork, join, put, select, take } from 'redux-saga/effects';
|
import { call, fork, join, put, select, take } from 'redux-saga/effects';
|
||||||
|
|
||||||
import selectors from '../../selectors';
|
import selectors from '../../selectors';
|
||||||
import actions from '../../actions';
|
import entryActions from '../../entry-actions';
|
||||||
import { removeAccessToken } from '../../utils/access-token-storage';
|
|
||||||
import ErrorCodes from '../../constants/ErrorCodes';
|
import ErrorCodes from '../../constants/ErrorCodes';
|
||||||
|
|
||||||
let lastRequestTask;
|
let lastRequestTask;
|
||||||
|
@ -22,8 +21,7 @@ function* queueRequest(method, ...args) {
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === ErrorCodes.UNAUTHORIZED) {
|
if (error.code === ErrorCodes.UNAUTHORIZED) {
|
||||||
yield call(removeAccessToken);
|
yield put(entryActions.logout(false));
|
||||||
yield put(actions.logout()); // TODO: next url
|
|
||||||
yield take();
|
yield take();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ export function* handleCardUpdate(card) {
|
||||||
yield put(actions.handleCardUpdate(card));
|
yield put(actions.handleCardUpdate(card));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* moveCard(id, listId, index) {
|
export function* moveCard(id, listId, index = 0) {
|
||||||
const position = yield select(selectors.selectNextCardPosition, listId, index, id);
|
const position = yield select(selectors.selectNextCardPosition, listId, index, id);
|
||||||
|
|
||||||
yield call(updateCard, id, {
|
yield call(updateCard, id, {
|
||||||
|
@ -101,7 +101,7 @@ export function* moveCurrentCard(listId, index) {
|
||||||
yield call(moveCard, cardId, listId, index);
|
yield call(moveCard, cardId, listId, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* transferCard(id, boardId, listId, index) {
|
export function* transferCard(id, boardId, listId, index = 0) {
|
||||||
const { cardId: currentCardId, boardId: currentBoardId } = yield select(selectors.selectPath);
|
const { cardId: currentCardId, boardId: currentBoardId } = yield select(selectors.selectPath);
|
||||||
const position = yield select(selectors.selectNextCardPosition, listId, index, id);
|
const position = yield select(selectors.selectNextCardPosition, listId, index, id);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { call, put, select, take } from 'redux-saga/effects';
|
import { call, put, select } from 'redux-saga/effects';
|
||||||
|
|
||||||
import request from '../request';
|
import request from '../request';
|
||||||
import requests from '../requests';
|
import requests from '../requests';
|
||||||
|
@ -84,8 +84,7 @@ export function* logout(invalidateAccessToken = true) {
|
||||||
} catch (error) {} // eslint-disable-line no-empty
|
} catch (error) {} // eslint-disable-line no-empty
|
||||||
}
|
}
|
||||||
|
|
||||||
yield put(actions.logout());
|
yield put(actions.logout()); // TODO: next url
|
||||||
yield take();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import { call, put, select, take } from 'redux-saga/effects';
|
import { call, put, select, take } from 'redux-saga/effects';
|
||||||
import { push } from '../../../lib/redux-router';
|
import { push } from '../../../lib/redux-router';
|
||||||
|
|
||||||
|
import { logout } from './core';
|
||||||
import request from '../request';
|
import request from '../request';
|
||||||
import selectors from '../../../selectors';
|
import selectors from '../../../selectors';
|
||||||
import actions from '../../../actions';
|
import actions from '../../../actions';
|
||||||
import api from '../../../api';
|
import api from '../../../api';
|
||||||
|
import { getAccessToken } from '../../../utils/access-token-storage';
|
||||||
import ActionTypes from '../../../constants/ActionTypes';
|
import ActionTypes from '../../../constants/ActionTypes';
|
||||||
import Paths from '../../../constants/Paths';
|
import Paths from '../../../constants/Paths';
|
||||||
|
|
||||||
|
@ -25,6 +27,13 @@ export function* goToCard(cardId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* handleLocationChange() {
|
export function* handleLocationChange() {
|
||||||
|
const accessToken = yield call(getAccessToken);
|
||||||
|
|
||||||
|
if (!accessToken) {
|
||||||
|
yield call(logout, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const pathsMatch = yield select(selectors.selectPathsMatch);
|
const pathsMatch = yield select(selectors.selectPathsMatch);
|
||||||
|
|
||||||
if (!pathsMatch) {
|
if (!pathsMatch) {
|
||||||
|
|
|
@ -218,6 +218,7 @@ export function* handleUserDelete(user) {
|
||||||
|
|
||||||
if (user.id === currentUserId) {
|
if (user.id === currentUserId) {
|
||||||
yield call(logout, false);
|
yield call(logout, false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield put(actions.handleUserDelete(user));
|
yield put(actions.handleUserDelete(user));
|
||||||
|
|
|
@ -4,5 +4,9 @@ import services from '../services';
|
||||||
import EntryActionTypes from '../../../constants/EntryActionTypes';
|
import EntryActionTypes from '../../../constants/EntryActionTypes';
|
||||||
|
|
||||||
export default function* coreWatchers() {
|
export default function* coreWatchers() {
|
||||||
yield all([takeEvery(EntryActionTypes.LOGOUT, () => services.logout())]);
|
yield all([
|
||||||
|
takeEvery(EntryActionTypes.LOGOUT, ({ payload: { invalidateAccessToken } }) =>
|
||||||
|
services.logout(invalidateAccessToken),
|
||||||
|
),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,10 @@ const createSocketEventsChannel = () =>
|
||||||
emit(entryActions.handleSocketReconnect());
|
emit(entryActions.handleSocketReconnect());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
emit(entryActions.logout(false));
|
||||||
|
};
|
||||||
|
|
||||||
const handleUserCreate = api.makeHandleUserCreate(({ item }) => {
|
const handleUserCreate = api.makeHandleUserCreate(({ item }) => {
|
||||||
emit(entryActions.handleUserCreate(item));
|
emit(entryActions.handleUserCreate(item));
|
||||||
});
|
});
|
||||||
|
@ -171,6 +175,8 @@ const createSocketEventsChannel = () =>
|
||||||
socket.on('disconnect', handleDisconnect);
|
socket.on('disconnect', handleDisconnect);
|
||||||
socket.on('reconnect', handleReconnect);
|
socket.on('reconnect', handleReconnect);
|
||||||
|
|
||||||
|
socket.on('logout', handleLogout);
|
||||||
|
|
||||||
socket.on('userCreate', handleUserCreate);
|
socket.on('userCreate', handleUserCreate);
|
||||||
socket.on('userUpdate', handleUserUpdate);
|
socket.on('userUpdate', handleUserUpdate);
|
||||||
socket.on('userDelete', handleUserDelete);
|
socket.on('userDelete', handleUserDelete);
|
||||||
|
@ -227,6 +233,8 @@ const createSocketEventsChannel = () =>
|
||||||
socket.off('disconnect', handleDisconnect);
|
socket.off('disconnect', handleDisconnect);
|
||||||
socket.off('reconnect', handleReconnect);
|
socket.off('reconnect', handleReconnect);
|
||||||
|
|
||||||
|
socket.off('logout', handleLogout);
|
||||||
|
|
||||||
socket.off('userCreate', handleUserCreate);
|
socket.off('userCreate', handleUserCreate);
|
||||||
socket.off('userUpdate', handleUserUpdate);
|
socket.off('userUpdate', handleUserUpdate);
|
||||||
socket.off('userDelete', handleUserDelete);
|
socket.off('userDelete', handleUserDelete);
|
||||||
|
|
|
@ -9,6 +9,10 @@ module.exports = {
|
||||||
deletedAt: new Date().toISOString(),
|
deletedAt: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.req.isSocket) {
|
||||||
|
sails.sockets.leaveAll(`@accessToken:${accessToken}`);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
item: accessToken,
|
item: accessToken,
|
||||||
};
|
};
|
||||||
|
|
|
@ -61,6 +61,7 @@ module.exports = function defineCurrentUserHook(sails) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (req.isSocket) {
|
if (req.isSocket) {
|
||||||
|
sails.sockets.join(req, `@accessToken:${accessToken}`);
|
||||||
sails.sockets.join(req, `@user:${currentUser.id}`);
|
sails.sockets.join(req, `@user:${currentUser.id}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
const openidClient = require('openid-client');
|
const openidClient = require('openid-client');
|
||||||
|
|
||||||
module.exports = function oidcServiceHook(sails) {
|
/**
|
||||||
|
* oidc hook
|
||||||
|
*
|
||||||
|
* @description :: A hook definition. Extends Sails by adding shadow routes, implicit actions,
|
||||||
|
* and/or initialization logic.
|
||||||
|
* @docs :: https://sailsjs.com/docs/concepts/extending-sails/hooks
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function defineOidcHook(sails) {
|
||||||
let client = null;
|
let client = null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -9,17 +17,20 @@ module.exports = function oidcServiceHook(sails) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
if (sails.config.custom.oidcIssuer) {
|
if (!sails.config.custom.oidcIssuer) {
|
||||||
const issuer = await openidClient.Issuer.discover(sails.config.custom.oidcIssuer);
|
return;
|
||||||
|
|
||||||
client = new issuer.Client({
|
|
||||||
client_id: sails.config.custom.oidcClientId,
|
|
||||||
client_secret: sails.config.custom.oidcClientSecret,
|
|
||||||
redirect_uris: [sails.config.custom.oidcRedirectUri],
|
|
||||||
response_types: ['code'],
|
|
||||||
});
|
|
||||||
sails.log.info('OIDC hook has been loaded successfully');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sails.log.info('Initializing custom hook (`oidc`)');
|
||||||
|
|
||||||
|
const issuer = await openidClient.Issuer.discover(sails.config.custom.oidcIssuer);
|
||||||
|
|
||||||
|
client = new issuer.Client({
|
||||||
|
client_id: sails.config.custom.oidcClientId,
|
||||||
|
client_secret: sails.config.custom.oidcClientSecret,
|
||||||
|
redirect_uris: [sails.config.custom.oidcRedirectUri],
|
||||||
|
response_types: ['code'],
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getClient() {
|
getClient() {
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
const nodemailer = require('nodemailer');
|
const nodemailer = require('nodemailer');
|
||||||
|
|
||||||
module.exports = function smtpServiceHook(sails) {
|
/**
|
||||||
|
* smtp hook
|
||||||
|
*
|
||||||
|
* @description :: A hook definition. Extends Sails by adding shadow routes, implicit actions,
|
||||||
|
* and/or initialization logic.
|
||||||
|
* @docs :: https://sailsjs.com/docs/concepts/extending-sails/hooks
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function defineSmtpHook(sails) {
|
||||||
let transporter = null;
|
let transporter = null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -9,19 +17,22 @@ module.exports = function smtpServiceHook(sails) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
if (sails.config.custom.smtpHost) {
|
if (!sails.config.custom.smtpHost) {
|
||||||
transporter = nodemailer.createTransport({
|
return;
|
||||||
pool: true,
|
|
||||||
host: sails.config.custom.smtpHost,
|
|
||||||
port: sails.config.custom.smtpPort,
|
|
||||||
secure: sails.config.custom.smtpSecure,
|
|
||||||
auth: sails.config.custom.smtpUser && {
|
|
||||||
user: sails.config.custom.smtpUser,
|
|
||||||
pass: sails.config.custom.smtpPassword,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
sails.log.info('SMTP hook has been loaded successfully');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sails.log.info('Initializing custom hook (`smtp`)');
|
||||||
|
|
||||||
|
transporter = nodemailer.createTransport({
|
||||||
|
pool: true,
|
||||||
|
host: sails.config.custom.smtpHost,
|
||||||
|
port: sails.config.custom.smtpPort,
|
||||||
|
secure: sails.config.custom.smtpSecure,
|
||||||
|
auth: sails.config.custom.smtpUser && {
|
||||||
|
user: sails.config.custom.smtpUser,
|
||||||
|
pass: sails.config.custom.smtpPassword,
|
||||||
|
},
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getTransporter() {
|
getTransporter() {
|
||||||
|
|
38
server/api/hooks/watcher/index.js
Normal file
38
server/api/hooks/watcher/index.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
* watcher hook
|
||||||
|
*
|
||||||
|
* @description :: A hook definition. Extends Sails by adding shadow routes, implicit actions,
|
||||||
|
* and/or initialization logic.
|
||||||
|
* @docs :: https://sailsjs.com/docs/concepts/extending-sails/hooks
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function defineWatcherHook(sails) {
|
||||||
|
const checkSocketConnectionsToLogout = () => {
|
||||||
|
Object.keys(sails.io.sockets.adapter.rooms).forEach((room) => {
|
||||||
|
if (!room.startsWith('@accessToken:')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessToken = room.split(':')[1];
|
||||||
|
|
||||||
|
try {
|
||||||
|
sails.helpers.utils.verifyToken(accessToken);
|
||||||
|
} catch (error) {
|
||||||
|
sails.sockets.broadcast(room, 'logout');
|
||||||
|
sails.sockets.leaveAll(room);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Runs when this Sails app loads/lifts.
|
||||||
|
*/
|
||||||
|
|
||||||
|
async initialize() {
|
||||||
|
sails.log.info('Initializing custom hook (`watcher`)');
|
||||||
|
|
||||||
|
setInterval(checkSocketConnectionsToLogout, 60 * 1000);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
Loading…
Add table
Add a link
Reference in a new issue