mirror of
https://github.com/nextcloud/server.git
synced 2025-05-11 17:16:10 +00:00
feat(core): add setup cypress tests
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
This commit is contained in:
parent
2026dfd1b9
commit
0179cb4d8d
6 changed files with 451 additions and 54 deletions
core
lib/private
|
@ -133,8 +133,6 @@ class SetupController {
|
|||
if ($dbIsSet and $directoryIsSet and $adminAccountIsSet) {
|
||||
$post['install'] = 'true';
|
||||
}
|
||||
$post['dbIsSet'] = $dbIsSet;
|
||||
$post['directoryIsSet'] = $directoryIsSet;
|
||||
|
||||
return $post;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ export type DbType = 'sqlite' | 'mysql' | 'pgsql' | 'oci'
|
|||
export type SetupConfig = {
|
||||
adminlogin: string
|
||||
adminpass: string
|
||||
directory: string
|
||||
dbuser: string
|
||||
dbpass: string
|
||||
dbname: string
|
||||
|
@ -23,15 +24,8 @@ export type SetupConfig = {
|
|||
dbhost: string
|
||||
dbtype: DbType | ''
|
||||
|
||||
hasSQLite: boolean
|
||||
hasMySQL: boolean
|
||||
hasPostgreSQL: boolean
|
||||
hasOracle: boolean
|
||||
databases: Record<DbType, string>
|
||||
databases: Partial<Record<DbType, string>>
|
||||
|
||||
dbIsSet: boolean
|
||||
directory: string
|
||||
directoryIsSet: boolean
|
||||
hasAutoconfig: boolean
|
||||
htaccessWorking: boolean
|
||||
serverRoot: string
|
||||
|
|
|
@ -31,7 +31,7 @@ const isNextcloudUrl = (url) => {
|
|||
/**
|
||||
* Check if a user was logged in but is now logged-out.
|
||||
* If this is the case then the user will be forwarded to the login page.
|
||||
* @returns {Promise<void>}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function checkLoginStatus() {
|
||||
// skip if no logged in user
|
||||
|
@ -66,7 +66,7 @@ async function checkLoginStatus() {
|
|||
|
||||
/**
|
||||
* Clear all Browser storages connected to current origin.
|
||||
* @returns {Promise<void>}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
export async function wipeBrowserStorages() {
|
||||
try {
|
||||
|
|
369
core/src/views/Setup.cy.ts
Normal file
369
core/src/views/Setup.cy.ts
Normal file
|
@ -0,0 +1,369 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import type { SetupConfig, SetupLinks } from '../install'
|
||||
import SetupView from './Setup.vue'
|
||||
|
||||
import '../../css/guest.css'
|
||||
|
||||
const defaultConfig = Object.freeze({
|
||||
adminlogin: '',
|
||||
adminpass: '',
|
||||
dbuser: '',
|
||||
dbpass: '',
|
||||
dbname: '',
|
||||
dbtablespace: '',
|
||||
dbhost: '',
|
||||
dbtype: '',
|
||||
databases: {
|
||||
sqlite: 'SQLite',
|
||||
mysql: 'MySQL/MariaDB',
|
||||
pgsql: 'PostgreSQL',
|
||||
},
|
||||
directory: '',
|
||||
hasAutoconfig: false,
|
||||
htaccessWorking: true,
|
||||
serverRoot: '/var/www/html',
|
||||
errors: [],
|
||||
}) as SetupConfig
|
||||
|
||||
const links = {
|
||||
adminInstall: 'https://docs.nextcloud.com/server/32/go.php?to=admin-install',
|
||||
adminSourceInstall: 'https://docs.nextcloud.com/server/32/go.php?to=admin-source_install',
|
||||
adminDBConfiguration: 'https://docs.nextcloud.com/server/32/go.php?to=admin-db-configuration',
|
||||
} as SetupLinks
|
||||
|
||||
describe('Default setup page', () => {
|
||||
beforeEach(() => {
|
||||
cy.mockInitialState('core', 'links', links)
|
||||
})
|
||||
|
||||
afterEach(() => cy.unmockInitialState())
|
||||
|
||||
it('Renders default config', () => {
|
||||
cy.mockInitialState('core', 'config', defaultConfig)
|
||||
cy.mount(SetupView)
|
||||
|
||||
cy.get('[data-cy-setup-form]').scrollIntoView()
|
||||
cy.get('[data-cy-setup-form]').should('be.visible')
|
||||
|
||||
// Single note is the footer help
|
||||
cy.get('[data-cy-setup-form-note]')
|
||||
.should('have.length', 1)
|
||||
.should('be.visible')
|
||||
cy.get('[data-cy-setup-form-note]').should('contain', 'See the documentation')
|
||||
|
||||
// DB radio selectors
|
||||
cy.get('[data-cy-setup-form-field^="dbtype"]')
|
||||
.should('exist')
|
||||
.find('input')
|
||||
.should('be.checked')
|
||||
|
||||
cy.get('[data-cy-setup-form-field="dbtype-mysql"]').should('exist')
|
||||
cy.get('[data-cy-setup-form-field="dbtype-pgsql"]').should('exist')
|
||||
cy.get('[data-cy-setup-form-field="dbtype-oci"]').should('not.exist')
|
||||
|
||||
// Sqlite warning
|
||||
cy.get('[data-cy-setup-form-db-note="sqlite"]')
|
||||
.should('be.visible')
|
||||
|
||||
// admin login, password, data directory and 3 DB radio selectors
|
||||
cy.get('[data-cy-setup-form-field]')
|
||||
.should('be.visible')
|
||||
.should('have.length', 6)
|
||||
})
|
||||
|
||||
it('Renders single DB sqlite', () => {
|
||||
const config = {
|
||||
...defaultConfig,
|
||||
databases: {
|
||||
sqlite: 'SQLite',
|
||||
},
|
||||
}
|
||||
cy.mockInitialState('core', 'config', config)
|
||||
cy.mount(SetupView)
|
||||
|
||||
// No DB radio selectors if only sqlite
|
||||
cy.get('[data-cy-setup-form-field^="dbtype"]')
|
||||
.should('not.exist')
|
||||
|
||||
// Two warnings: sqlite and single db support
|
||||
cy.get('[data-cy-setup-form-db-note="sqlite"]')
|
||||
.should('be.visible')
|
||||
cy.get('[data-cy-setup-form-db-note="single-db"]')
|
||||
.should('be.visible')
|
||||
|
||||
// Admin login, password and data directory
|
||||
cy.get('[data-cy-setup-form-field]')
|
||||
.should('be.visible')
|
||||
.should('have.length', 3)
|
||||
})
|
||||
|
||||
it('Renders single DB mysql', () => {
|
||||
const config = {
|
||||
...defaultConfig,
|
||||
databases: {
|
||||
mysql: 'MySQL/MariaDB',
|
||||
},
|
||||
}
|
||||
cy.mockInitialState('core', 'config', config)
|
||||
cy.mount(SetupView)
|
||||
|
||||
// No DB radio selectors if only mysql
|
||||
cy.get('[data-cy-setup-form-field^="dbtype"]')
|
||||
.should('not.exist')
|
||||
|
||||
// Single db support warning
|
||||
cy.get('[data-cy-setup-form-db-note="single-db"]')
|
||||
.should('be.visible')
|
||||
.invoke('html')
|
||||
.should('contains', links.adminSourceInstall)
|
||||
|
||||
// No SQLite warning
|
||||
cy.get('[data-cy-setup-form-db-note="sqlite"]')
|
||||
.should('not.exist')
|
||||
|
||||
// Admin login, password, data directory, db user,
|
||||
// db password, db name and db host
|
||||
cy.get('[data-cy-setup-form-field]')
|
||||
.should('be.visible')
|
||||
.should('have.length', 7)
|
||||
})
|
||||
|
||||
it('Changes fields from sqlite to mysql then oci', () => {
|
||||
const config = {
|
||||
...defaultConfig,
|
||||
databases: {
|
||||
sqlite: 'SQLite',
|
||||
mysql: 'MySQL/MariaDB',
|
||||
pgsql: 'PostgreSQL',
|
||||
oci: 'Oracle',
|
||||
},
|
||||
}
|
||||
cy.mockInitialState('core', 'config', config)
|
||||
cy.mount(SetupView)
|
||||
|
||||
// SQLite selected
|
||||
cy.get('[data-cy-setup-form-field="dbtype-sqlite"]')
|
||||
.should('be.visible')
|
||||
.find('input')
|
||||
.should('be.checked')
|
||||
|
||||
// Admin login, password, data directory and 4 DB radio selectors
|
||||
cy.get('[data-cy-setup-form-field]')
|
||||
.should('be.visible')
|
||||
.should('have.length', 7)
|
||||
|
||||
// Change to MySQL
|
||||
cy.get('[data-cy-setup-form-field="dbtype-mysql"]').click()
|
||||
cy.get('[data-cy-setup-form-field="dbtype-mysql"] input').should('be.checked')
|
||||
|
||||
// Admin login, password, data directory, db user, db password,
|
||||
// db name, db host and 4 DB radio selectors
|
||||
cy.get('[data-cy-setup-form-field]')
|
||||
.should('be.visible')
|
||||
.should('have.length', 11)
|
||||
|
||||
// Change to Oracle
|
||||
cy.get('[data-cy-setup-form-field="dbtype-oci"]').click()
|
||||
cy.get('[data-cy-setup-form-field="dbtype-oci"] input').should('be.checked')
|
||||
|
||||
// Admin login, password, data directory, db user, db password,
|
||||
// db name, db table space, db host and 4 DB radio selectors
|
||||
cy.get('[data-cy-setup-form-field]')
|
||||
.should('be.visible')
|
||||
.should('have.length', 12)
|
||||
cy.get('[data-cy-setup-form-field="dbtablespace"]')
|
||||
.should('be.visible')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Setup page with errors and warning', () => {
|
||||
beforeEach(() => {
|
||||
cy.mockInitialState('core', 'links', links)
|
||||
})
|
||||
|
||||
afterEach(() => cy.unmockInitialState())
|
||||
|
||||
it('Renders error from backend', () => {
|
||||
const config = {
|
||||
...defaultConfig,
|
||||
errors: [
|
||||
{
|
||||
error: 'Error message',
|
||||
hint: 'Error hint',
|
||||
},
|
||||
],
|
||||
}
|
||||
cy.mockInitialState('core', 'config', config)
|
||||
cy.mount(SetupView)
|
||||
|
||||
// Error message and hint
|
||||
cy.get('[data-cy-setup-form-note="error"]')
|
||||
.should('be.visible')
|
||||
.should('have.length', 1)
|
||||
.should('contain', 'Error message')
|
||||
.should('contain', 'Error hint')
|
||||
})
|
||||
|
||||
it('Renders errors from backend', () => {
|
||||
const config = {
|
||||
...defaultConfig,
|
||||
errors: [
|
||||
'Error message 1',
|
||||
{
|
||||
error: 'Error message',
|
||||
hint: 'Error hint',
|
||||
},
|
||||
],
|
||||
}
|
||||
cy.mockInitialState('core', 'config', config)
|
||||
cy.mount(SetupView)
|
||||
|
||||
// Error message and hint
|
||||
cy.get('[data-cy-setup-form-note="error"]')
|
||||
.should('be.visible')
|
||||
.should('have.length', 2)
|
||||
cy.get('[data-cy-setup-form-note="error"]').eq(0)
|
||||
.should('contain', 'Error message 1')
|
||||
cy.get('[data-cy-setup-form-note="error"]').eq(1)
|
||||
.should('contain', 'Error message')
|
||||
.should('contain', 'Error hint')
|
||||
})
|
||||
|
||||
it('Renders all the submitted fields on error', () => {
|
||||
const config = {
|
||||
...defaultConfig,
|
||||
adminlogin: 'admin',
|
||||
adminpass: 'password',
|
||||
dbname: 'nextcloud',
|
||||
dbtype: 'mysql',
|
||||
dbuser: 'nextcloud',
|
||||
dbpass: 'password',
|
||||
dbhost: 'localhost',
|
||||
directory: '/var/www/html/nextcloud',
|
||||
} as SetupConfig
|
||||
cy.mockInitialState('core', 'config', config)
|
||||
cy.mount(SetupView)
|
||||
|
||||
cy.get('input[data-cy-setup-form-field="adminlogin"]')
|
||||
.should('have.value', 'admin')
|
||||
cy.get('input[data-cy-setup-form-field="adminpass"]')
|
||||
.should('have.value', 'password')
|
||||
cy.get('[data-cy-setup-form-field="dbtype-mysql"] input')
|
||||
.should('be.checked')
|
||||
cy.get('input[data-cy-setup-form-field="dbname"]')
|
||||
.should('have.value', 'nextcloud')
|
||||
cy.get('input[data-cy-setup-form-field="dbuser"]')
|
||||
.should('have.value', 'nextcloud')
|
||||
cy.get('input[data-cy-setup-form-field="dbpass"]')
|
||||
.should('have.value', 'password')
|
||||
cy.get('input[data-cy-setup-form-field="dbhost"]')
|
||||
.should('have.value', 'localhost')
|
||||
cy.get('input[data-cy-setup-form-field="directory"]')
|
||||
.should('have.value', '/var/www/html/nextcloud')
|
||||
})
|
||||
|
||||
it('Renders the htaccess warning', () => {
|
||||
const config = {
|
||||
...defaultConfig,
|
||||
htaccessWorking: false,
|
||||
}
|
||||
cy.mockInitialState('core', 'config', config)
|
||||
cy.mount(SetupView)
|
||||
|
||||
cy.get('[data-cy-setup-form-note="htaccess"]')
|
||||
.should('be.visible')
|
||||
.should('contain', 'Security warning')
|
||||
.invoke('html')
|
||||
.should('contains', links.adminInstall)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Setup page with autoconfig', () => {
|
||||
beforeEach(() => {
|
||||
cy.mockInitialState('core', 'links', links)
|
||||
})
|
||||
|
||||
afterEach(() => cy.unmockInitialState())
|
||||
|
||||
it('Renders autoconfig', () => {
|
||||
const config = {
|
||||
...defaultConfig,
|
||||
hasAutoconfig: true,
|
||||
dbname: 'nextcloud',
|
||||
dbtype: 'mysql',
|
||||
dbuser: 'nextcloud',
|
||||
dbpass: 'password',
|
||||
dbhost: 'localhost',
|
||||
directory: '/var/www/html/nextcloud',
|
||||
} as SetupConfig
|
||||
cy.mockInitialState('core', 'config', config)
|
||||
cy.mount(SetupView)
|
||||
|
||||
// Autoconfig info note
|
||||
cy.get('[data-cy-setup-form-note="autoconfig"]')
|
||||
.should('be.visible')
|
||||
.should('contain', 'Autoconfig file detected')
|
||||
|
||||
// Database and storage section is hidden as already set in autoconfig
|
||||
cy.get('[data-cy-setup-form-advanced-config]').should('be.visible')
|
||||
.invoke('attr', 'open')
|
||||
.should('equal', undefined)
|
||||
|
||||
// Oracle tablespace is hidden
|
||||
cy.get('[data-cy-setup-form-field="dbtablespace"]')
|
||||
.should('not.exist')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Submit a full form sends the data', () => {
|
||||
beforeEach(() => {
|
||||
cy.mockInitialState('core', 'links', links)
|
||||
})
|
||||
|
||||
afterEach(() => cy.unmockInitialState())
|
||||
|
||||
it('Submits a full form', () => {
|
||||
const config = {
|
||||
...defaultConfig,
|
||||
adminlogin: 'admin',
|
||||
adminpass: 'password',
|
||||
dbname: 'nextcloud',
|
||||
dbtype: 'mysql',
|
||||
dbuser: 'nextcloud',
|
||||
dbpass: 'password',
|
||||
dbhost: 'localhost',
|
||||
dbtablespace: 'tablespace',
|
||||
directory: '/var/www/html/nextcloud',
|
||||
} as SetupConfig
|
||||
|
||||
cy.intercept('POST', '**', {
|
||||
delay: 2000,
|
||||
}).as('setup')
|
||||
|
||||
cy.mockInitialState('core', 'config', config)
|
||||
cy.mount(SetupView)
|
||||
|
||||
// Not chaining breaks the test as the POST prevents the element from being retrieved twice
|
||||
// eslint-disable-next-line cypress/unsafe-to-chain-command
|
||||
cy.get('[data-cy-setup-form-submit]')
|
||||
.click()
|
||||
.invoke('attr', 'disabled')
|
||||
.should('equal', 'disabled', { timeout: 500 })
|
||||
|
||||
cy.wait('@setup')
|
||||
.its('request.body')
|
||||
.should('deep.equal', new URLSearchParams({
|
||||
adminlogin: 'admin',
|
||||
adminpass: 'password',
|
||||
directory: '/var/www/html/nextcloud',
|
||||
dbtype: 'mysql',
|
||||
dbuser: 'nextcloud',
|
||||
dbpass: 'password',
|
||||
dbname: 'nextcloud',
|
||||
dbhost: 'localhost',
|
||||
}).toString())
|
||||
})
|
||||
})
|
|
@ -7,11 +7,13 @@
|
|||
class="setup-form"
|
||||
:class="{ 'setup-form--loading': loading }"
|
||||
action=""
|
||||
data-cy-setup-form
|
||||
method="POST"
|
||||
@submit="onSubmit">
|
||||
<!-- Autoconfig info -->
|
||||
<NcNoteCard v-if="config.hasAutoconfig"
|
||||
:heading="t('core', 'Autoconfig file detected')"
|
||||
data-cy-setup-form-note="autoconfig"
|
||||
type="success">
|
||||
{{ t('core', 'The setup form below is pre-filled with the values from the config file.') }}
|
||||
</NcNoteCard>
|
||||
|
@ -19,6 +21,7 @@
|
|||
<!-- Htaccess warning -->
|
||||
<NcNoteCard v-if="config.htaccessWorking === false"
|
||||
:heading="t('core', 'Security warning')"
|
||||
data-cy-setup-form-note="htaccess"
|
||||
type="warning">
|
||||
<p v-html="htaccessWarning" />
|
||||
</NcNoteCard>
|
||||
|
@ -27,6 +30,7 @@
|
|||
<NcNoteCard v-for="(error, index) in errors"
|
||||
:key="index"
|
||||
:heading="error.heading"
|
||||
data-cy-setup-form-note="error"
|
||||
type="error">
|
||||
{{ error.message }}
|
||||
</NcNoteCard>
|
||||
|
@ -38,12 +42,14 @@
|
|||
<!-- Username -->
|
||||
<NcTextField v-model="config.adminlogin"
|
||||
:label="t('core', 'Administration account name')"
|
||||
data-cy-setup-form-field="adminlogin"
|
||||
name="adminlogin"
|
||||
required />
|
||||
|
||||
<!-- Password -->
|
||||
<NcPasswordField v-model="config.adminpass"
|
||||
:label="t('core', 'Administration account password')"
|
||||
data-cy-setup-form-field="adminpass"
|
||||
name="adminpass"
|
||||
required />
|
||||
|
||||
|
@ -54,18 +60,18 @@
|
|||
</fieldset>
|
||||
|
||||
<!-- Autoconfig toggle -->
|
||||
<details :open="!isValidAutoconfig">
|
||||
<summary>{{ t('core', 'Advanced settings') }}</summary>
|
||||
<details :open="!isValidAutoconfig" data-cy-setup-form-advanced-config>
|
||||
<summary>{{ t('core', 'Storage & database') }}</summary>
|
||||
|
||||
<!-- Data folder -->
|
||||
<fieldset class="setup-form__data-folder">
|
||||
<legend>{{ t('core', 'Data folder') }}</legend>
|
||||
<NcTextField v-model="config.directory"
|
||||
:label="t('core', 'Data folder')"
|
||||
:placeholder="config.serverRoot + '/data'"
|
||||
required
|
||||
autocomplete="off"
|
||||
autocapitalize="none"
|
||||
data-cy-setup-form-field="directory"
|
||||
name="directory"
|
||||
spellcheck="false" />
|
||||
</fieldset>
|
||||
|
@ -76,22 +82,22 @@
|
|||
|
||||
<!-- Database type select -->
|
||||
<fieldset class="setup-form__database-type">
|
||||
<legend>{{ t('core', 'Database type') }}</legend>
|
||||
<p v-if="Object.keys(config.databases).length > 1" class="setup-form__database-type-select">
|
||||
<p v-if="!firstAndOnlyDatabase" :class="`setup-form__database-type-select--${DBTypeGroupDirection}`" class="setup-form__database-type-select">
|
||||
<NcCheckboxRadioSwitch v-for="(name, db) in config.databases"
|
||||
:key="db"
|
||||
v-model="config.dbtype"
|
||||
:button-variant="true"
|
||||
:data-cy-setup-form-field="`dbtype-${db}`"
|
||||
:value="db"
|
||||
:button-variant-grouped="DBTypeGroupDirection"
|
||||
name="dbtype"
|
||||
button-variant-grouped="horizontal"
|
||||
type="radio">
|
||||
{{ name }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
</p>
|
||||
|
||||
<NcNoteCard v-else type="warning">
|
||||
{{ t('core', 'Only {db} is available.', { db: Object.values(config.databases).at(0) }) }}<br>
|
||||
<NcNoteCard v-else data-cy-setup-form-db-note="single-db" type="warning">
|
||||
{{ t('core', 'Only {firstAndOnlyDatabase} is available.', { firstAndOnlyDatabase }) }}<br>
|
||||
{{ t('core', 'Install and activate additional PHP modules to choose other database types.') }}<br>
|
||||
<a :href="links.adminSourceInstall" target="_blank" rel="noreferrer noopener">
|
||||
{{ t('core', 'For more details check out the documentation.') }} ↗
|
||||
|
@ -100,6 +106,7 @@
|
|||
|
||||
<NcNoteCard v-if="config.dbtype === 'sqlite'"
|
||||
:heading="t('core', 'Performance warning')"
|
||||
data-cy-setup-form-db-note="sqlite"
|
||||
type="warning">
|
||||
{{ t('core', 'You chose SQLite as database.') }}<br>
|
||||
{{ t('core', 'SQLite should only be used for minimal and development instances. For production we recommend a different database backend.') }}<br>
|
||||
|
@ -113,6 +120,7 @@
|
|||
:label="t('core', 'Database user')"
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
data-cy-setup-form-field="dbuser"
|
||||
name="dbuser"
|
||||
spellcheck="false"
|
||||
required />
|
||||
|
@ -121,6 +129,7 @@
|
|||
:label="t('core', 'Database password')"
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
data-cy-setup-form-field="dbpass"
|
||||
name="dbpass"
|
||||
spellcheck="false"
|
||||
required />
|
||||
|
@ -129,6 +138,7 @@
|
|||
:label="t('core', 'Database name')"
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
data-cy-setup-form-field="dbname"
|
||||
name="dbname"
|
||||
pattern="[0-9a-zA-Z\$_\-]+"
|
||||
spellcheck="false"
|
||||
|
@ -139,6 +149,7 @@
|
|||
:label="t('core', 'Database tablespace')"
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
data-cy-setup-form-field="dbtablespace"
|
||||
name="dbtablespace"
|
||||
spellcheck="false" />
|
||||
|
||||
|
@ -148,6 +159,7 @@
|
|||
:placeholder="t('core', 'localhost')"
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
data-cy-setup-form-field="dbhost"
|
||||
name="dbhost"
|
||||
spellcheck="false" />
|
||||
</fieldset>
|
||||
|
@ -161,6 +173,7 @@
|
|||
:loading="loading"
|
||||
:wide="true"
|
||||
alignment="center-reverse"
|
||||
data-cy-setup-form-submit
|
||||
native-type="submit"
|
||||
type="primary">
|
||||
<template #icon>
|
||||
|
@ -171,7 +184,7 @@
|
|||
</NcButton>
|
||||
|
||||
<!-- Help note -->
|
||||
<NcNoteCard type="info">
|
||||
<NcNoteCard data-cy-setup-form-note="help" type="info">
|
||||
{{ t('core', 'Need help?') }}
|
||||
<a target="_blank" rel="noreferrer noopener" :href="links.adminInstall">{{ t('core', 'See the documentation') }} ↗</a>
|
||||
</NcNoteCard>
|
||||
|
@ -194,9 +207,6 @@ import NcTextField from '@nextcloud/vue/components/NcTextField'
|
|||
|
||||
import IconArrowRight from 'vue-material-design-icons/ArrowRight.vue'
|
||||
|
||||
const config = loadState<SetupConfig>('core', 'config')
|
||||
const links = loadState<SetupLinks>('core', 'links')
|
||||
|
||||
enum PasswordStrength {
|
||||
VeryWeak,
|
||||
Weak,
|
||||
|
@ -206,6 +216,24 @@ enum PasswordStrength {
|
|||
ExtremelyStrong,
|
||||
}
|
||||
|
||||
const checkPasswordEntropy = (password: string = ''): PasswordStrength => {
|
||||
const uniqueCharacters = new Set(password)
|
||||
const entropy = parseInt(Math.log2(Math.pow(parseInt(uniqueCharacters.size.toString()), password.length)).toFixed(2))
|
||||
if (entropy < 16) {
|
||||
return PasswordStrength.VeryWeak
|
||||
} else if (entropy < 31) {
|
||||
return PasswordStrength.Weak
|
||||
} else if (entropy < 46) {
|
||||
return PasswordStrength.Moderate
|
||||
} else if (entropy < 61) {
|
||||
return PasswordStrength.Strong
|
||||
} else if (entropy < 76) {
|
||||
return PasswordStrength.VeryStrong
|
||||
}
|
||||
|
||||
return PasswordStrength.ExtremelyStrong
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Setup',
|
||||
|
||||
|
@ -221,14 +249,14 @@ export default defineComponent({
|
|||
|
||||
setup() {
|
||||
return {
|
||||
links,
|
||||
t,
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
config,
|
||||
config: {} as SetupConfig,
|
||||
links: {} as SetupLinks,
|
||||
isValidAutoconfig: false,
|
||||
loading: false,
|
||||
}
|
||||
|
@ -236,11 +264,11 @@ export default defineComponent({
|
|||
|
||||
computed: {
|
||||
passwordHelperText(): string {
|
||||
if (this.config.adminpass === '') {
|
||||
if (this.config?.adminpass === '') {
|
||||
return ''
|
||||
}
|
||||
|
||||
const passwordStrength = this.checkPasswordEntropy(this.config.adminpass)
|
||||
const passwordStrength = checkPasswordEntropy(this.config?.adminpass)
|
||||
switch (passwordStrength) {
|
||||
case PasswordStrength.VeryWeak:
|
||||
return t('core', 'Password is too weak')
|
||||
|
@ -259,21 +287,39 @@ export default defineComponent({
|
|||
return t('core', 'Unknown password strength')
|
||||
},
|
||||
passwordHelperType() {
|
||||
if (this.checkPasswordEntropy(this.config.adminpass) < PasswordStrength.Moderate) {
|
||||
if (checkPasswordEntropy(this.config?.adminpass) < PasswordStrength.Moderate) {
|
||||
return 'error'
|
||||
}
|
||||
if (this.checkPasswordEntropy(this.config.adminpass) < PasswordStrength.Strong) {
|
||||
if (checkPasswordEntropy(this.config?.adminpass) < PasswordStrength.Strong) {
|
||||
return 'warning'
|
||||
}
|
||||
return 'success'
|
||||
},
|
||||
|
||||
firstAndOnlyDatabase(): string|null {
|
||||
const dbNames = Object.values(this.config?.databases || {})
|
||||
if (dbNames.length === 1) {
|
||||
return dbNames[0]
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
|
||||
DBTypeGroupDirection() {
|
||||
const databases = Object.keys(this.config?.databases || {})
|
||||
// If we have more than 3 databases, we want to display them vertically
|
||||
if (databases.length > 3) {
|
||||
return 'vertical'
|
||||
}
|
||||
return 'horizontal'
|
||||
},
|
||||
|
||||
htaccessWarning(): string {
|
||||
// We use v-html, let's make sure we're safe
|
||||
const message = [
|
||||
t('core', 'Your data directory and files are probably accessible from the internet because the <code>.htaccess</code> file does not work.'),
|
||||
t('core', 'For information how to properly configure your server, please {linkStart}see the documentation{linkEnd}', {
|
||||
linkStart: '<a href="' + links.adminInstall + '" target="_blank" rel="noreferrer noopener">',
|
||||
linkStart: '<a href="' + this.links.adminInstall + '" target="_blank" rel="noreferrer noopener">',
|
||||
linkEnd: '</a>',
|
||||
}, { escape: false }),
|
||||
].join('<br>')
|
||||
|
@ -281,7 +327,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
errors() {
|
||||
return this.config.errors.map(error => {
|
||||
return (this.config?.errors || []).map(error => {
|
||||
if (typeof error === 'string') {
|
||||
return {
|
||||
heading: '',
|
||||
|
@ -305,7 +351,16 @@ export default defineComponent({
|
|||
},
|
||||
},
|
||||
|
||||
beforeMount() {
|
||||
// Needs to only read the state once we're mounted
|
||||
// for Cypress to be properly initialized.
|
||||
this.config = loadState<SetupConfig>('core', 'config')
|
||||
this.links = loadState<SetupLinks>('core', 'links')
|
||||
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// Set the first database type as default if none is set
|
||||
if (this.config.dbtype === '') {
|
||||
this.config.dbtype = Object.keys(this.config.databases).at(0) as DbType
|
||||
}
|
||||
|
@ -337,24 +392,6 @@ export default defineComponent({
|
|||
async onSubmit() {
|
||||
this.loading = true
|
||||
},
|
||||
|
||||
checkPasswordEntropy(password: string): PasswordStrength {
|
||||
const uniqueCharacters = new Set(password)
|
||||
const entropy = parseInt(Math.log2(Math.pow(parseInt(uniqueCharacters.size.toString()), password.length)).toFixed(2))
|
||||
if (entropy < 16) {
|
||||
return PasswordStrength.VeryWeak
|
||||
} else if (entropy < 31) {
|
||||
return PasswordStrength.Weak
|
||||
} else if (entropy < 46) {
|
||||
return PasswordStrength.Moderate
|
||||
} else if (entropy < 61) {
|
||||
return PasswordStrength.Strong
|
||||
} else if (entropy < 76) {
|
||||
return PasswordStrength.VeryStrong
|
||||
}
|
||||
|
||||
return PasswordStrength.ExtremelyStrong
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
@ -398,6 +435,9 @@ form {
|
|||
// Db select required styling
|
||||
.setup-form__database-type-select {
|
||||
display: flex;
|
||||
&--vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -205,10 +205,6 @@ class Setup {
|
|||
}
|
||||
|
||||
return [
|
||||
'hasSQLite' => isset($databases['sqlite']),
|
||||
'hasMySQL' => isset($databases['mysql']),
|
||||
'hasPostgreSQL' => isset($databases['pgsql']),
|
||||
'hasOracle' => isset($databases['oci']),
|
||||
'databases' => $databases,
|
||||
'directory' => $dataDir,
|
||||
'htaccessWorking' => $htAccessWorking,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue