feat: Add ability to configure and test SMTP via UI
This commit is contained in:
@@ -21,14 +21,14 @@ export default function* coreSaga() {
|
||||
|
||||
yield take(ActionTypes.LOGOUT);
|
||||
|
||||
const oidcConfig = yield select(selectors.selectOidcConfig);
|
||||
const oidcBootstrap = yield select(selectors.selectOidcBootstrap);
|
||||
|
||||
if (oidcConfig && oidcConfig.endSessionUrl !== null) {
|
||||
if (oidcBootstrap && oidcBootstrap.endSessionUrl !== null) {
|
||||
const currentUser = yield select(selectors.selectCurrentUser);
|
||||
|
||||
if (!currentUser || currentUser.isSsoUser) {
|
||||
// Redirect the user to the IDP to log out.
|
||||
window.location.href = oidcConfig.endSessionUrl;
|
||||
window.location.href = oidcBootstrap.endSessionUrl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,11 @@ export function* fetchCore() {
|
||||
included: { notificationServices: notificationServices1 },
|
||||
} = yield call(request, api.getCurrentUser, true);
|
||||
|
||||
let config;
|
||||
let webhooks;
|
||||
|
||||
if (user.role === UserRoles.ADMIN) {
|
||||
({ item: config } = yield call(request, api.getConfig));
|
||||
({ items: webhooks } = yield call(request, api.getWebhooks));
|
||||
}
|
||||
|
||||
@@ -105,6 +108,7 @@ export function* fetchCore() {
|
||||
}
|
||||
|
||||
return {
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
webhooks,
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { call, put } from 'redux-saga/effects';
|
||||
|
||||
import request from '../request';
|
||||
import actions from '../../../actions';
|
||||
import api from '../../../api';
|
||||
|
||||
export function* updateConfig(data) {
|
||||
yield put(actions.updateConfig(data));
|
||||
|
||||
let config;
|
||||
try {
|
||||
({ item: config } = yield call(request, api.updateConfig, data));
|
||||
} catch (error) {
|
||||
yield put(actions.updateConfig.failure(error));
|
||||
return;
|
||||
}
|
||||
|
||||
yield put(actions.updateConfig.success(config));
|
||||
}
|
||||
|
||||
export function* handleConfigUpdate(config) {
|
||||
yield put(actions.handleConfigUpdate(config));
|
||||
}
|
||||
|
||||
export function* testSmtpConfig() {
|
||||
yield put(actions.testSmtpConfig());
|
||||
|
||||
let logs;
|
||||
try {
|
||||
({
|
||||
included: { logs },
|
||||
} = yield call(request, api.testSmtpConfig));
|
||||
} catch (error) {
|
||||
yield put(actions.testSmtpConfig.failure(error));
|
||||
}
|
||||
|
||||
yield put(actions.testSmtpConfig.success(logs));
|
||||
}
|
||||
|
||||
export default {
|
||||
updateConfig,
|
||||
handleConfigUpdate,
|
||||
testSmtpConfig,
|
||||
};
|
||||
@@ -14,11 +14,12 @@ import i18n from '../../../i18n';
|
||||
import { removeAccessToken } from '../../../utils/access-token-storage';
|
||||
|
||||
export function* initializeCore() {
|
||||
const { item: config } = yield call(request, api.getConfig); // TODO: handle error
|
||||
const { item: bootstrap } = yield call(request, api.getBootstrap); // TODO: handle error
|
||||
|
||||
yield put(actions.initializeCore.fetchConfig(config));
|
||||
yield put(actions.initializeCore.fetchBootstrap(bootstrap));
|
||||
|
||||
const {
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
webhooks,
|
||||
@@ -49,6 +50,7 @@ export function* initializeCore() {
|
||||
|
||||
yield put(
|
||||
actions.initializeCore(
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
webhooks,
|
||||
|
||||
@@ -7,6 +7,7 @@ import router from './router';
|
||||
import socket from './socket';
|
||||
import core from './core';
|
||||
import modals from './modals';
|
||||
import config from './config';
|
||||
import webhooks from './webhooks';
|
||||
import users from './users';
|
||||
import projects from './projects';
|
||||
@@ -34,6 +35,7 @@ export default {
|
||||
...socket,
|
||||
...core,
|
||||
...modals,
|
||||
...config,
|
||||
...webhooks,
|
||||
...users,
|
||||
...projects,
|
||||
|
||||
@@ -21,6 +21,7 @@ export function* handleSocketReconnect() {
|
||||
|
||||
yield put(actions.handleSocketReconnect.fetchCore(currentUserId, boardId));
|
||||
|
||||
let bootstrap;
|
||||
let config;
|
||||
let user;
|
||||
let board;
|
||||
@@ -47,7 +48,7 @@ export function* handleSocketReconnect() {
|
||||
let notificationServices;
|
||||
|
||||
try {
|
||||
({ item: config } = yield call(request, api.getConfig));
|
||||
({ item: bootstrap } = yield call(request, api.getBootstrap));
|
||||
|
||||
({
|
||||
user,
|
||||
@@ -80,6 +81,7 @@ export function* handleSocketReconnect() {
|
||||
|
||||
yield put(
|
||||
actions.handleSocketReconnect(
|
||||
bootstrap,
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
|
||||
@@ -68,6 +68,7 @@ export function* handleUserUpdate(user) {
|
||||
const currentUser = yield select(selectors.selectCurrentUser);
|
||||
const isCurrentUser = user.id === currentUser.id;
|
||||
|
||||
let bootstrap;
|
||||
let config;
|
||||
let board;
|
||||
let webhooks;
|
||||
@@ -102,6 +103,7 @@ export function* handleUserUpdate(user) {
|
||||
({ items: users1 } = yield call(request, api.getUsers));
|
||||
|
||||
if (user.role === UserRoles.ADMIN) {
|
||||
({ item: bootstrap } = yield call(request, api.getBootstrap));
|
||||
({ item: config } = yield call(request, api.getConfig));
|
||||
({ items: webhooks } = yield call(request, api.getWebhooks));
|
||||
|
||||
@@ -164,6 +166,7 @@ export function* handleUserUpdate(user) {
|
||||
user,
|
||||
projectIds,
|
||||
boardIds,
|
||||
bootstrap,
|
||||
config,
|
||||
board,
|
||||
webhooks,
|
||||
@@ -248,10 +251,10 @@ export function* updateUserPassword(id, data) {
|
||||
yield put(actions.updateUserPassword(id, data));
|
||||
|
||||
let user;
|
||||
let accessTokens;
|
||||
let accessToken;
|
||||
|
||||
try {
|
||||
({ item: user, included: { accessTokens } = {} } = yield call(
|
||||
({ item: user, included: { accessToken } = {} } = yield call(
|
||||
request,
|
||||
api.updateUserPassword,
|
||||
id,
|
||||
@@ -262,8 +265,6 @@ export function* updateUserPassword(id, data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const accessToken = accessTokens && accessTokens[0];
|
||||
|
||||
if (accessToken) {
|
||||
yield call(setAccessToken, accessToken);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
import { all, takeEvery } from 'redux-saga/effects';
|
||||
|
||||
import services from '../services';
|
||||
import EntryActionTypes from '../../../constants/EntryActionTypes';
|
||||
|
||||
export default function* configWatchers() {
|
||||
yield all([
|
||||
takeEvery(EntryActionTypes.CONFIG_UPDATE, ({ payload: { data } }) =>
|
||||
services.updateConfig(data),
|
||||
),
|
||||
takeEvery(EntryActionTypes.CONFIG_UPDATE_HANDLE, ({ payload: { config } }) =>
|
||||
services.handleConfigUpdate(config),
|
||||
),
|
||||
takeEvery(EntryActionTypes.SMTP_CONFIG_TEST, () => services.testSmtpConfig()),
|
||||
]);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import router from './router';
|
||||
import socket from './socket';
|
||||
import core from './core';
|
||||
import modals from './modals';
|
||||
import config from './config';
|
||||
import webhooks from './webhooks';
|
||||
import users from './users';
|
||||
import projects from './projects';
|
||||
@@ -34,6 +35,7 @@ export default [
|
||||
socket,
|
||||
core,
|
||||
modals,
|
||||
config,
|
||||
webhooks,
|
||||
users,
|
||||
projects,
|
||||
|
||||
@@ -29,6 +29,18 @@ const createSocketEventsChannel = () =>
|
||||
emit(entryActions.handleConfigUpdate(item));
|
||||
};
|
||||
|
||||
const handleWebhookCreate = ({ item }) => {
|
||||
emit(entryActions.handleWebhookCreate(item));
|
||||
};
|
||||
|
||||
const handleWebhookUpdate = ({ item }) => {
|
||||
emit(entryActions.handleWebhookUpdate(item));
|
||||
};
|
||||
|
||||
const handleWebhookDelete = ({ item }) => {
|
||||
emit(entryActions.handleWebhookDelete(item));
|
||||
};
|
||||
|
||||
const handleUserCreate = ({ item }) => {
|
||||
emit(entryActions.handleUserCreate(item));
|
||||
};
|
||||
@@ -280,6 +292,10 @@ const createSocketEventsChannel = () =>
|
||||
|
||||
socket.on('configUpdate', handleConfigUpdate);
|
||||
|
||||
socket.on('webhookCreate', handleWebhookCreate);
|
||||
socket.on('webhookUpdate', handleWebhookUpdate);
|
||||
socket.on('webhookDelete', handleWebhookDelete);
|
||||
|
||||
socket.on('userCreate', handleUserCreate);
|
||||
socket.on('userUpdate', handleUserUpdate);
|
||||
socket.on('userDelete', handleUserDelete);
|
||||
@@ -370,6 +386,10 @@ const createSocketEventsChannel = () =>
|
||||
|
||||
socket.off('configUpdate', handleConfigUpdate);
|
||||
|
||||
socket.off('webhookCreate', handleWebhookCreate);
|
||||
socket.off('webhookUpdate', handleWebhookUpdate);
|
||||
socket.off('webhookDelete', handleWebhookDelete);
|
||||
|
||||
socket.off('userCreate', handleUserCreate);
|
||||
socket.off('userUpdate', handleUserUpdate);
|
||||
socket.off('userDelete', handleUserDelete);
|
||||
|
||||
@@ -16,9 +16,9 @@ import Paths from '../../../constants/Paths';
|
||||
import AccessTokenSteps from '../../../constants/AccessTokenSteps';
|
||||
|
||||
export function* initializeLogin() {
|
||||
const { item: config } = yield call(api.getConfig); // TODO: handle error
|
||||
const { item: bootstrap } = yield call(api.getBootstrap); // TODO: handle error
|
||||
|
||||
yield put(actions.initializeLogin(config));
|
||||
yield put(actions.initializeLogin(bootstrap));
|
||||
}
|
||||
|
||||
export function* authenticate(data) {
|
||||
@@ -42,7 +42,7 @@ export function* authenticate(data) {
|
||||
}
|
||||
|
||||
export function* authenticateWithOidc() {
|
||||
const oidcConfig = yield select(selectors.selectOidcConfig);
|
||||
const oidcBootstrap = yield select(selectors.selectOidcBootstrap);
|
||||
|
||||
const state = nanoid();
|
||||
window.localStorage.setItem('oidc-state', state);
|
||||
@@ -50,7 +50,7 @@ export function* authenticateWithOidc() {
|
||||
const nonce = nanoid();
|
||||
window.localStorage.setItem('oidc-nonce', nonce);
|
||||
|
||||
let redirectUrl = `${oidcConfig.authorizationUrl}`;
|
||||
let redirectUrl = `${oidcBootstrap.authorizationUrl}`;
|
||||
redirectUrl += `&state=${encodeURIComponent(state)}`;
|
||||
redirectUrl += `&nonce=${encodeURIComponent(nonce)}`;
|
||||
|
||||
|
||||
@@ -49,9 +49,9 @@ export function* handleLocationChange() {
|
||||
|
||||
switch (pathsMatch.pattern.path) {
|
||||
case Paths.LOGIN: {
|
||||
const oidcConfig = yield select(selectors.selectOidcConfig);
|
||||
const oidcBootstrap = yield select(selectors.selectOidcBootstrap);
|
||||
|
||||
if (oidcConfig) {
|
||||
if (oidcBootstrap) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
|
||||
if (params.has('authenticateWithOidc')) {
|
||||
|
||||
Reference in New Issue
Block a user