special pages workflow changed
Some checks are pending
Run tests / test-runner (push) Waiting to run

This commit is contained in:
Jan Prochazka 2024-10-21 17:36:46 +02:00
parent 967615b6e5
commit 32c7919885
14 changed files with 339 additions and 109 deletions

View File

@ -36,8 +36,9 @@ function authMiddleware(req, res, next) {
'/auth/login',
'/auth/redirect',
'/stream',
'storage/get-connections-for-login-page',
'auth/get-providers',
'/storage/get-connections-for-login-page',
'/storage/set-admin-password',
'/auth/get-providers',
'/connections/dblogin-web',
'/connections/dblogin-app',
'/connections/dblogin-auth',
@ -69,6 +70,7 @@ function authMiddleware(req, res, next) {
return next();
} catch (err) {
if (skipAuth) {
req.isInvalidToken = true;
return next();
}

View File

@ -60,6 +60,14 @@ module.exports = {
const checkedLicense = storageConnectionError ? null : await checkLicense();
const isLicenseValid = checkedLicense?.status == 'ok';
const logoutUrl = storageConnectionError ? null : await authProvider.getLogoutUrl();
const adminConfig = storageConnectionError ? null : await storage.readConfig({ group: 'admin' });
const isAdminPasswordMissing = !!(
process.env.STORAGE_DATABASE &&
!process.env.ADMIN_PASSWORD &&
!process.env.BASIC_AUTH &&
!adminConfig?.adminPasswordState
);
return {
runAsPortal: !!connections.portalConnections,
@ -87,6 +95,9 @@ module.exports = {
!process.env.BASIC_AUTH &&
checkedLicense?.type == 'premium'
),
isAdminPasswordMissing,
isInvalidToken: req.isInvalidToken,
adminPasswordState: adminConfig?.adminPasswordState,
storageDatabase: process.env.STORAGE_DATABASE,
logsFilePath: getLogsFilePath(),
connectionsFilePath: path.join(datadir(), 'connections.jsonl'),

View File

@ -20,5 +20,10 @@ module.exports = {
getStorageConnectionError() {
return null;
}
},
readConfig_meta: true,
async readConfig({ group }) {
return {};
},
};

View File

@ -2,7 +2,17 @@ const fs = require('fs');
const template = fs.readFileSync('./index.html.tpl', 'utf-8');
for (const page of ['', 'not-logged', 'error', 'admin-login', 'login', 'admin', 'license']) {
for (const page of [
'',
'not-logged',
'error',
'admin-login',
'login',
'admin',
'license',
'set-admin-password',
'redirect',
]) {
const text = template.replace(/{{page}}/g, page);
fs.writeFileSync(`public/${page || 'index'}.html`, text);
}

View File

@ -38,7 +38,7 @@
// console.log('************** LOADING API');
const config = await getConfig();
await handleAuthOnStartup(config, isAdminPage);
await handleAuthOnStartup(config);
const connections = await apiCall('connections/list');
const settings = await getSettings();

View File

@ -11,6 +11,8 @@
import getElectron from './utility/getElectron';
import { openWebLink } from './utility/exportFileTools';
import SpecialPageLayout from './widgets/SpecialPageLayout.svelte';
import hasPermission from './utility/hasPermission';
import ErrorInfo from './elements/ErrorInfo.svelte';
const config = useConfig();
const values = writable({ amoid: null, databaseServer: null });
@ -25,89 +27,95 @@
errorMessage = 'Your license is expired';
expiredMessageSet = true;
}
// $: console.log('CONFIG', $config);
</script>
<FormProviderCore {values}>
<SpecialPageLayout>
<div class="heading">License</div>
<FormTextAreaField label="Enter your license key" name="licenseKey" rows={5} />
{#if getElectron() || ($config?.storageDatabase && hasPermission('admin/license'))}
<div class="heading">License</div>
<FormTextAreaField label="Enter your license key" name="licenseKey" rows={5} />
<div class="submit">
<FormSubmit
value="Save license"
on:click={async e => {
sessionStorage.setItem('continueTrialConfirmed', '1');
const { licenseKey } = e.detail;
const resp = await apiCall('config/save-license-key', { licenseKey });
if (resp?.status == 'ok') {
internalRedirectTo('/index.html');
} else {
errorMessage = resp?.errorMessage || 'Error saving license key';
}
}}
/>
</div>
{#if !isExpired && trialDaysLeft == null}
<div class="submit">
<FormStyledButton
value="Start 30-day trial"
<FormSubmit
value="Save license"
on:click={async e => {
errorMessage = '';
const license = await apiCall('config/start-trial');
if (license?.status == 'ok') {
sessionStorage.setItem('continueTrialConfirmed', '1');
sessionStorage.setItem('continueTrialConfirmed', '1');
const { licenseKey } = e.detail;
const resp = await apiCall('config/save-license-key', { licenseKey });
if (resp?.status == 'ok') {
internalRedirectTo('/index.html');
} else {
errorMessage = license?.errorMessage || 'Error starting trial';
errorMessage = resp?.errorMessage || 'Error saving license key';
}
}}
/>
</div>
{/if}
{#if trialDaysLeft > 0}
{#if !isExpired && trialDaysLeft == null}
<div class="submit">
<FormStyledButton
value="Start 30-day trial"
on:click={async e => {
errorMessage = '';
const license = await apiCall('config/start-trial');
if (license?.status == 'ok') {
sessionStorage.setItem('continueTrialConfirmed', '1');
internalRedirectTo('/index.html');
} else {
errorMessage = license?.errorMessage || 'Error starting trial';
}
}}
/>
</div>
{/if}
{#if trialDaysLeft > 0}
<div class="submit">
<FormStyledButton
value={`Continue trial (${trialDaysLeft} days left)`}
on:click={async e => {
sessionStorage.setItem('continueTrialConfirmed', '1');
internalRedirectTo('/index.html');
}}
/>
</div>
{/if}
<div class="submit">
<FormStyledButton
value={`Continue trial (${trialDaysLeft} days left)`}
value="Purchase DbGate Premium"
on:click={async e => {
sessionStorage.setItem('continueTrialConfirmed', '1');
internalRedirectTo('/index.html');
openWebLink(
`https://auth.dbgate.eu/create-checkout-session-simple?source=trial-${isExpired ? 'expired' : (trialDaysLeft ?? 'no')}`
);
}}
/>
</div>
{/if}
<div class="submit">
<FormStyledButton
value="Purchase DbGate Premium"
on:click={async e => {
openWebLink(
`https://auth.dbgate.eu/create-checkout-session-simple?source=trial-${isExpired ? 'expired' : (trialDaysLeft ?? 'no')}`
);
}}
/>
</div>
{#if getElectron()}
<div class="submit">
<FormStyledButton
value="Exit"
on:click={e => {
getElectron().send('quit-app');
}}
/>
</div>
{/if}
{#if getElectron()}
<div class="submit">
<FormStyledButton
value="Exit"
on:click={e => {
getElectron().send('quit-app');
}}
/>
{#if errorMessage}
<div class="error">{errorMessage}</div>
{/if}
<div class="purchase-info">
For more info about DbGate licensing, you could visit <Link href="https://dbgate.eu/">dbgate.eu</Link> web or contact
us at <Link href="mailto:sales@dbgate.eu">sales@dbgate.eu</Link>
</div>
{:else}
<ErrorInfo message="License for DbGate is not valid. Please contact administrator." />
{/if}
{#if errorMessage}
<div class="error">{errorMessage}</div>
{/if}
<div class="purchase-info">
For more info about DbGate licensing, you could visit <Link href="https://dbgate.eu/">dbgate.eu</Link> web or contact
us at <Link href="mailto:sales@dbgate.eu">sales@dbgate.eu</Link>
</div>
</SpecialPageLayout>
</FormProviderCore>

View File

@ -0,0 +1,3 @@
<script lang='ts'>
</script>

View File

@ -0,0 +1,79 @@
<script lang="ts">
import { writable } from 'svelte/store';
import FormCheckboxField from './forms/FormCheckboxField.svelte';
import FormPasswordField from './forms/FormPasswordField.svelte';
import FormSubmit from './forms/FormSubmit.svelte';
import SpecialPageLayout from './widgets/SpecialPageLayout.svelte';
import FormProviderCore from './forms/FormProviderCore.svelte';
import { apiCall } from './utility/api';
import { useConfig } from './utility/metadataLoaders';
import ErrorInfo from './elements/ErrorInfo.svelte';
import { internalRedirectTo } from './clientAuth';
const values = writable({ denyUseAdminPassword: false });
const config = useConfig();
let error;
</script>
<SpecialPageLayout>
<FormProviderCore {values}>
<div class="heading">Set admin password</div>
<div class="text">
Please set password for DbGate administrator account. If you lose this paassword, you can change it later in
DbGate internal database, in table config.
</div>
<FormCheckboxField label="Don't use admin password" name="denyUseAdminPassword" />
{#if $values?.denyUseAdminPassword}
<div class="text">
You have selected to not use admin password. You can change this setting later in DbGate internal database, in
table config. Please assign to some regular user admin role, to be able to perform admin tasks.
</div>
{:else}
{#if $config?.adminPasswordState == 'set'}
<FormPasswordField label="Current password" name="oldPassword" autocomplete="current-password" saveOnInput />
{/if}
<FormPasswordField label="New password" name="newPassword" autocomplete="current-password" saveOnInput />
<FormPasswordField label="Repeat password" name="repeatPassword" autocomplete="current-password" saveOnInput />
{/if}
{#if error}
<ErrorInfo message={error} />
{/if}
<div class="submit">
<FormSubmit
value="Set password"
on:click={async e => {
const resp = await apiCall('storage/set-admin-password', e.detail);
if (resp?.status == 'error') {
error = resp?.errorMessage;
return;
}
internalRedirectTo('/admin.html');
}}
/>
</div>
</FormProviderCore>
</SpecialPageLayout>
<style>
.heading {
text-align: center;
margin: 1em;
font-size: xx-large;
}
.submit {
margin: var(--dim-large-form-margin);
display: flex;
}
.text {
margin-left: var(--dim-large-form-margin);
margin-right: var(--dim-large-form-margin);
}
</style>

View File

@ -116,48 +116,120 @@ export function handleOauthCallback() {
return false;
}
export async function handleAuthOnStartup(config, isAdminPage = false) {
if (config.configurationError) {
internalRedirectTo(`/error.html`);
return;
}
export async function handleAuthOnStartup(config) {
const page = window['dbgate_page'];
if (!config.isLicenseValid) {
if (config.storageDatabase || getElectron()) {
internalRedirectTo(`/license.html`);
} else {
function checkConfigError() {
if (config.configurationError) {
internalRedirectTo(`/error.html`);
return true;
}
}
if (
config.trialDaysLeft != null &&
config.trialDaysLeft <= 14 &&
!sessionStorage.getItem('continueTrialConfirmed') &&
getElectron()
) {
internalRedirectTo(`/license.html`);
function checkInvalidLicense() {
if (!config.isLicenseValid) {
if (config.storageDatabase || getElectron()) {
internalRedirectTo(`/license.html`);
} else {
internalRedirectTo(`/error.html`);
}
return true;
}
}
if (getAuthCategory(config) == 'admin') {
if (localStorage.getItem('adminAccessToken')) {
return;
function checkTrialDaysLeft() {
if (
config.trialDaysLeft != null &&
config.trialDaysLeft <= 14 &&
!sessionStorage.getItem('continueTrialConfirmed') &&
getElectron()
) {
internalRedirectTo(`/license.html`);
return true;
}
}
function checkLoggedUser() {
if (getAuthCategory(config) == 'admin') {
if (!config.isInvalidToken && localStorage.getItem('adminAccessToken')) {
return false;
}
redirectToAdminLogin();
return true;
}
redirectToAdminLogin();
return;
if (getAuthCategory(config) == 'token') {
if (!config.isInvalidToken && localStorage.getItem('accessToken')) {
return false;
}
redirectToLogin(config);
return true;
}
}
// if (config.oauth) {
// console.log('OAUTH callback URL:', location.origin + location.pathname);
function checkAdminPasswordSet() {
if (config.isAdminPasswordMissing) {
internalRedirectTo(`/set-admin-password.html`);
return true;
}
}
if (page == 'error') return;
if (checkConfigError()) return;
if (page == 'set-admin-password') return;
if (checkAdminPasswordSet()) return;
if (page == 'login' || page == 'admin-login' || page == 'not-logged') return;
if (checkLoggedUser()) return;
if (page == 'license') return;
if (checkTrialDaysLeft()) return;
if (checkInvalidLicense()) return;
// if (config.configurationError) {
// internalRedirectTo(`/error.html`);
// return;
// }
if (getAuthCategory(config) == 'token') {
if (localStorage.getItem('accessToken')) {
return;
}
redirectToLogin(config);
}
// if (!config.isLicenseValid) {
// if (config.storageDatabase || getElectron()) {
// internalRedirectTo(`/license.html`);
// } else {
// internalRedirectTo(`/error.html`);
// }
// }
// if (
// config.trialDaysLeft != null &&
// config.trialDaysLeft <= 14 &&
// !sessionStorage.getItem('continueTrialConfirmed') &&
// getElectron()
// ) {
// internalRedirectTo(`/license.html`);
// }
// if (getAuthCategory(config) == 'admin') {
// if (localStorage.getItem('adminAccessToken')) {
// return;
// }
// redirectToAdminLogin();
// return;
// }
// // if (config.oauth) {
// // console.log('OAUTH callback URL:', location.origin + location.pathname);
// // }
// if (getAuthCategory(config) == 'token') {
// if (localStorage.getItem('accessToken')) {
// return;
// }
// redirectToLogin(config);
// }
}
export async function redirectToAdminLogin() {

View File

@ -36,7 +36,7 @@ import runCommand from './runCommand';
import { openWebLink } from '../utility/exportFileTools';
import { getSettings } from '../utility/metadataLoaders';
import { isMac, switchCurrentDatabase } from '../utility/common';
import { doLogout, internalRedirectTo } from '../clientAuth';
import { doLogout } from '../clientAuth';
import { disconnectServerConnection } from '../appobj/ConnectionAppObject.svelte';
import UploadErrorModal from '../modals/UploadErrorModal.svelte';
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';

View File

@ -8,6 +8,8 @@ import LoginPage from './LoginPage.svelte';
import NotLoggedPage from './NotLoggedPage.svelte';
import ErrorPage from './ErrorPage.svelte';
import EnterLicensePage from './EnterLicensePage.svelte';
import SetAdminPasswordPage from './SetAdminPasswordPage.svelte';
import RedirectPage from './RedirectPage.svelte';
const isOauthCallback = handleOauthCallback();
@ -43,11 +45,20 @@ function createApp() {
isAdminPage: true,
},
});
case 'redirect':
return new RedirectPage({
target: document.body,
});
case 'not-logged':
return new NotLoggedPage({
target: document.body,
props: {},
});
case 'set-admin-password':
return new SetAdminPasswordPage({
target: document.body,
props: {},
});
case 'admin':
return new App({
target: document.body,

View File

@ -4,7 +4,7 @@ import { writable } from 'svelte/store';
import getElectron from './getElectron';
// import socket from './socket';
import { showSnackbarError } from '../utility/snackbar';
import { isOauthCallback, redirectToAdminLogin, redirectToLogin } from '../clientAuth';
import { handleAuthOnStartup, isOauthCallback, redirectToAdminLogin, redirectToLogin } from '../clientAuth';
import { showModal } from '../modals/modalTools';
import DatabaseLoginModal, { isDatabaseLoginVisible } from '../modals/DatabaseLoginModal.svelte';
import _ from 'lodash';
@ -144,17 +144,23 @@ export function transformApiArgsInv(args) {
});
}
export async function apiCall(route: string, args: {} = undefined) {
export async function apiCall(
route: string,
args: {} = undefined,
options: { skipDisableChecks: boolean } = undefined
) {
if (apiLogging) {
console.log('>>> API CALL', route, args);
}
if (apiDisabled) {
console.log('API disabled!!', route);
return;
}
if (disabledOnOauth && route != 'auth/oauth-token') {
console.log('API disabled because oauth callback!!', route);
return;
if (!options?.skipDisableChecks) {
if (apiDisabled) {
console.log('API disabled!!', route);
return;
}
if (disabledOnOauth && route != 'auth/oauth-token') {
console.log('API disabled because oauth callback!!', route);
return;
}
}
args = transformApiArgs(args);
@ -180,12 +186,15 @@ export async function apiCall(route: string, args: {} = undefined) {
disableApi();
console.log('Disabling API', route);
if (page != 'login' && page != 'admin-login' && page != 'not-logged') {
// unauthorized
if (page == 'admin') {
redirectToAdminLogin();
} else {
redirectToLogin();
}
const config = await apiCall('config/get', {}, { skipDisableChecks: true });
await handleAuthOnStartup(config);
// // unauthorized
// if (page == 'admin') {
// redirectToAdminLogin();
// } else {
// redirectToLogin();
// }
}
return;
}

View File

@ -18,3 +18,7 @@ export function subscribePermissionCompiler() {
// console.log('COMPILED PERMS', compiled);
});
}
export function setConfigForPermissions(config) {
compiled = compilePermissions(config?.permissions || {});
}

View File

@ -1,9 +1,25 @@
<script lang="ts">
import { onMount } from 'svelte';
import { getConfig } from '../utility/metadataLoaders';
import { handleAuthOnStartup } from '../clientAuth';
import { setConfigForPermissions } from '../utility/hasPermission';
async function loadApi() {
try {
const config = await getConfig();
setConfigForPermissions(config);
await handleAuthOnStartup(config);
} catch (e) {
console.log('Error calling API, trying again in 1s');
setTimeout(loadApi, 1000);
}
}
onMount(() => {
const removed = document.getElementById('starting_dbgate_zero');
if (removed) removed.remove();
loadApi();
});
</script>