feat: login screen UI + server code cleanup

parent 1a720c91
......@@ -8,7 +8,7 @@ trim_trailing_whitespace = true
end_of_line = lf
insert_final_newline = true
[*.{jade,pug,md}]
[*.{pug,md}]
trim_trailing_whitespace = false
[Makefile]
......
......@@ -47,7 +47,7 @@ store.commit('user/REFRESH_AUTH')
// Initialize Apollo Client (GraphQL)
// ====================================
const graphQLEndpoint = window.location.protocol + '//' + window.location.host + '/graphql'
const graphQLEndpoint = window.location.protocol + '//' + window.location.host + '/_graphql'
const graphQLLink = ApolloLink.from([
new ErrorLink(({ graphQLErrors, networkError }) => {
......
/* eslint-disable import/first */
import Vue from 'vue'
import Vuetify from 'vuetify/lib'
import boot from './modules/boot'
/* eslint-enable import/first */
window.WIKI = null
window.boot = boot
Vue.use(Vuetify)
Vue.component('setup', () => import(/* webpackMode: "eager" */ './components/setup.vue'))
let bootstrap = () => {
window.WIKI = new Vue({
el: '#root',
vuetify: new Vuetify()
})
}
window.boot.onDOMReady(bootstrap)
......@@ -662,9 +662,13 @@ export default {
apollo: {
strategies: {
query: gql`
{
authentication {
activeStrategies(enabledOnly: true) {
query loginFetchSiteStrategies(
$siteId: UUID
) {
authStrategies(
siteId: $siteId
enabledOnly: true
) {
key
strategy {
key
......@@ -679,9 +683,13 @@ export default {
selfRegistration
}
}
}
`,
update: (data) => _.sortBy(data.authentication.activeStrategies, ['order']),
variables () {
return {
siteId: siteConfig.id
}
},
update: (data) => _.sortBy(data.authStrategies, ['order']),
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'login-strategies-refresh')
}
......
require('./scss/legacy.scss')
require('./scss/fonts/default.scss')
window.WIKI = null
require('core-js/stable')
require('regenerator-runtime/runtime')
/* eslint-disable no-unused-expressions */
require('./scss/app.scss')
import(/* webpackChunkName: "mdi" */ '@mdi/font/css/materialdesignicons.css')
require('./helpers/compatibility.js')
require('./client-setup.js')
......@@ -60,7 +60,7 @@ const init = {
},
async reload() {
console.warn(chalk.yellow('--- Gracefully stopping server...'))
await global.WIKI.kernel.shutdown()
await global.WIKI.kernel.shutdown(true)
console.warn(chalk.yellow('--- Purging node modules cache...'))
......
......@@ -29,6 +29,7 @@ html(lang=siteConfig.lang)
//- Site Properties
script.
var siteId = "!{siteId}"
var siteConfig = !{JSON.stringify(siteConfig)}
var siteLangs = !{JSON.stringify(langs)}
......
doctype html
html
head
meta(http-equiv='X-UA-Compatible', content='IE=edge')
meta(charset='UTF-8')
meta(name='viewport', content='user-scalable=yes, width=device-width, initial-scale=1, maximum-scale=5')
meta(name='theme-color', content='#1976d2')
meta(name='msapplication-TileColor', content='#1976d2')
meta(name='msapplication-TileImage', content='/_assets/favicons/mstile-150x150.png')
title Wiki.js Setup
//- Favicon
link(rel='apple-touch-icon', sizes='180x180', href='/_assets/favicons/apple-touch-icon.png')
link(rel='icon', type='image/png', sizes='192x192', href='/_assets/favicons/android-chrome-192x192.png')
link(rel='icon', type='image/png', sizes='32x32', href='/_assets/favicons/favicon-32x32.png')
link(rel='icon', type='image/png', sizes='16x16', href='/_assets/favicons/favicon-16x16.png')
link(rel='mask-icon', href='/_assets/favicons/safari-pinned-tab.svg', color='#1976d2')
link(rel='manifest', href='/_assets/manifest.json')
//- Site Lang
script.
var siteConfig = !{JSON.stringify({ title: config.title })}
//- Dev Mode Warning
if devMode
script.
siteConfig.devMode = true
//- CSS
<% for (var index in htmlWebpackPlugin.files.css) { %>
link(
type='text/css'
rel='stylesheet'
href='<%= htmlWebpackPlugin.files.css[index] %>'
)
<% } %>
//- JS
<% for (var index in htmlWebpackPlugin.files.js) { %>
script(
type='text/javascript'
src='<%= htmlWebpackPlugin.files.js[index] %>'
)
<% } %>
body
#root
setup(telemetry-id=telemetryClientID, wiki-version=packageObj.version)
......@@ -24,9 +24,7 @@ fs.emptyDirSync(path.join(process.cwd(), 'assets-legacy'))
module.exports = {
mode: 'development',
entry: {
app: ['./client/index-app.js', 'webpack-hot-middleware/client'],
legacy: ['./client/index-legacy.js', 'webpack-hot-middleware/client'],
setup: ['./client/index-setup.js', 'webpack-hot-middleware/client']
app: ['./client/index-app.js', 'webpack-hot-middleware/client']
},
output: {
path: path.join(process.cwd(), 'assets-legacy'),
......@@ -197,25 +195,10 @@ module.exports = {
]
}),
new HtmlWebpackPlugin({
template: 'dev/templates/master.pug',
filename: '../server/views/master.pug',
template: 'dev/templates/base.pug',
filename: '../server/views/base.pug',
hash: false,
inject: false,
excludeChunks: ['setup', 'legacy']
}),
new HtmlWebpackPlugin({
template: 'dev/templates/legacy.pug',
filename: '../server/views/legacy/master.pug',
hash: false,
inject: false,
excludeChunks: ['setup', 'app']
}),
new HtmlWebpackPlugin({
template: 'dev/templates/setup.pug',
filename: '../server/views/setup.pug',
hash: false,
inject: false,
excludeChunks: ['app', 'legacy']
inject: false
}),
new HtmlWebpackPugPlugin(),
new WebpackBarPlugin({
......
......@@ -29,9 +29,7 @@ fs.emptyDirSync(path.join(process.cwd(), 'assets-legacy'))
module.exports = {
mode: 'production',
entry: {
app: './client/index-app.js',
legacy: './client/index-legacy.js',
setup: './client/index-setup.js'
app: './client/index-app.js'
},
output: {
path: path.join(process.cwd(), 'assets-legacy'),
......@@ -208,25 +206,10 @@ module.exports = {
chunkFilename: 'css/[name].[chunkhash].css'
}),
new HtmlWebpackPlugin({
template: 'dev/templates/master.pug',
filename: '../server/views/master.pug',
template: 'dev/templates/base.pug',
filename: '../server/views/base.pug',
hash: false,
inject: false,
excludeChunks: ['setup', 'legacy']
}),
new HtmlWebpackPlugin({
template: 'dev/templates/legacy.pug',
filename: '../server/views/legacy/master.pug',
hash: false,
inject: false,
excludeChunks: ['setup', 'app']
}),
new HtmlWebpackPlugin({
template: 'dev/templates/setup.pug',
filename: '../server/views/setup.pug',
hash: false,
inject: false,
excludeChunks: ['app', 'legacy']
inject: false
}),
new HtmlWebpackPugPlugin(),
new ScriptExtHtmlWebpackPlugin({
......
......@@ -8,7 +8,7 @@
"scripts": {
"start": "node server",
"dev": "nodemon server",
"legacy:dev": "node dev",
"legacy:dev": "NODE_OPTIONS=--openssl-legacy-provider node dev",
"legacy:build": "NODE_OPTIONS=--openssl-legacy-provider webpack --profile --config dev/webpack/webpack.prod.js",
"test": "eslint --format codeframe --ext .js,.vue . && pug-lint server/views && jest",
"cypress:open": "cypress open"
......
......@@ -24,14 +24,13 @@ module.exports = {
process.exit(1)
}
this.bootMaster()
this.bootWeb()
},
/**
* Pre-Master Boot Sequence
* Pre-Web Boot Sequence
*/
async preBootMaster() {
async preBootWeb() {
try {
await this.initTelemetry()
WIKI.sideloader = await require('./sideloader').init()
WIKI.cache = require('./cache').init()
WIKI.scheduler = require('./scheduler').init()
......@@ -48,22 +47,22 @@ module.exports = {
}
},
/**
* Boot Master Process
* Boot Web Process
*/
async bootMaster() {
async bootWeb() {
try {
await this.preBootMaster()
await require('../master')()
this.postBootMaster()
await this.preBootWeb()
await require('../web')()
this.postBootWeb()
} catch (err) {
WIKI.logger.error(err)
process.exit(1)
}
},
/**
* Post-Master Boot Sequence
* Post-Web Boot Sequence
*/
async postBootMaster() {
async postBootWeb() {
await WIKI.models.analytics.refreshProvidersFromDisk()
await WIKI.models.authentication.refreshStrategiesFromDisk()
await WIKI.models.commentProviders.refreshProvidersFromDisk()
......@@ -74,30 +73,16 @@ module.exports = {
await WIKI.auth.activateStrategies()
await WIKI.models.commentProviders.initProvider()
await WIKI.models.sites.reloadCache()
await WIKI.models.storage.initTargets()
// WIKI.scheduler.start()
await WIKI.models.subscribeToNotifications()
},
/**
* Init Telemetry
*/
async initTelemetry() {
require('./telemetry').init()
process.on('unhandledRejection', (err) => {
WIKI.logger.warn(err)
WIKI.telemetry.sendError(err)
})
process.on('uncaughtException', (err) => {
WIKI.logger.warn(err)
WIKI.telemetry.sendError(err)
})
},
/**
* Graceful shutdown
*/
async shutdown () {
async shutdown (devMode = false) {
if (WIKI.servers) {
await WIKI.servers.stopServers()
}
......@@ -113,6 +98,8 @@ module.exports = {
if (WIKI.asar) {
await WIKI.asar.unload()
}
if (!devMode) {
process.exit(0)
}
}
}
......@@ -11,7 +11,7 @@ module.exports = {
minimumVersionRequired: '3.0.0-beta.0',
minimumNodeRequired: '18.0.0'
},
init() {
init () {
// Clear content cache
fs.emptyDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'cache'))
......
const _ = require('lodash')
const { createApolloFetch } = require('apollo-fetch')
const { v4: uuid } = require('uuid')
const os = require('os')
const fs = require('fs-extra')
/* global WIKI */
module.exports = {
enabled: false,
init() {
WIKI.telemetry = this
if (_.get(WIKI.config, 'telemetry.isEnabled', false) === true && WIKI.config.offline !== true) {
this.enabled = true
this.sendInstanceEvent('STARTUP')
}
},
sendError(err) {
// TODO
},
sendEvent(eventCategory, eventAction, eventLabel) {
// TODO
},
async sendInstanceEvent(eventType) {
if (WIKI.devMode || !this.enabled) { return }
try {
const apollo = createApolloFetch({
uri: WIKI.config.graphEndpoint
})
// Platform detection
let platform = 'LINUX'
let isDockerized = false
let osname = `${os.type()} ${os.release()}`
switch (os.platform()) {
case 'win32':
platform = 'WINDOWS'
break
case 'darwin':
platform = 'MACOS'
break
default:
platform = 'LINUX'
isDockerized = await fs.pathExists('/.dockerenv')
if (isDockerized) {
osname = 'Docker'
}
break
}
// DB Version detection
let dbVersion = 'Unknown'
switch (WIKI.config.db.type) {
case 'mariadb':
case 'mysql':
const resultMYSQL = await WIKI.models.knex.raw('SELECT VERSION() as version;')
dbVersion = _.get(resultMYSQL, '[0][0].version', 'Unknown')
break
case 'mssql':
const resultMSSQL = await WIKI.models.knex.raw('SELECT @@VERSION as version;')
dbVersion = _.get(resultMSSQL, '[0].version', 'Unknown')
break
case 'postgres':
dbVersion = _.get(WIKI.models, 'knex.client.version', 'Unknown')
break
case 'sqlite':
dbVersion = _.get(WIKI.models, 'knex.client.driver.VERSION', 'Unknown')
break
}
let arch = os.arch().toUpperCase()
if (['ARM', 'ARM64', 'X32', 'X64'].indexOf(arch) < 0) {
arch = 'OTHER'
}
// Send Event
const respStrings = await apollo({
query: `mutation (
$version: String!
$platform: TelemetryPlatform!
$os: String!
$architecture: TelemetryArchitecture!
$dbType: TelemetryDBType!
$dbVersion: String!
$nodeVersion: String!
$cpuCores: Int!
$ramMBytes: Int!,
$clientId: String!,
$event: TelemetryInstanceEvent!
) {
telemetry {
instance(
version: $version
platform: $platform
os: $os
architecture: $architecture
dbType: $dbType
dbVersion: $dbVersion
nodeVersion: $nodeVersion
cpuCores: $cpuCores
ramMBytes: $ramMBytes
clientId: $clientId
event: $event
) {
responseResult {
succeeded
errorCode
slug
message
}
}
}
}`,
variables: {
version: WIKI.version,
platform,
os: osname,
architecture: arch,
dbType: WIKI.config.db.type.toUpperCase(),
dbVersion,
nodeVersion: process.version.substr(1),
cpuCores: os.cpus().length,
ramMBytes: Math.round(os.totalmem() / 1024 / 1024),
clientId: WIKI.config.telemetry.clientId,
event: eventType
}
})
const telemetryResponse = _.get(respStrings, 'data.telemetry.instance.responseResult', { succeeded: false, message: 'Unexpected Error' })
if (!telemetryResponse.succeeded) {
WIKI.logger.warn('Failed to send instance telemetry: ' + telemetryResponse.message)
} else {
WIKI.logger.info('Telemetry is active: [ OK ]')
}
} catch (err) {
WIKI.logger.warn(err)
}
},
generateClientId() {
_.set(WIKI.config, 'telemetry.clientId', uuid())
return WIKI.config.telemetry.clientId
}
}
......@@ -32,6 +32,7 @@ module.exports = {
// }), ['title', 'key'])
return _.sortBy(WIKI.storage.defs.map(md => {
const dbTarget = dbTargets.find(tg => tg.module === md.key)
console.info(md.actions)
return {
id: dbTarget?.id ?? uuid(),
isEnabled: dbTarget?.isEnabled ?? false,
......@@ -62,12 +63,12 @@ module.exports = {
setup: {
handler: md?.setup?.handler,
state: dbTarget?.state?.setup ?? 'notconfigured',
values: md.setup?.handler
? _.transform(md.setup.defaultValues,
values: md.setup?.handler ?
_.transform(md.setup.defaultValues,
(r, v, k) => {
r[k] = dbTarget?.config?.[k] ?? v
}, {})
: {}
}, {}) :
{}
},
config: _.transform(md.props, (r, v, k) => {
const cfValue = dbTarget?.config?.[k] ?? v.default
......
......@@ -8,6 +8,7 @@ extend type Query {
apiState: Boolean
authStrategies(
siteId: UUID
enabledOnly: Boolean
): [AuthenticationStrategy]
}
......
......@@ -22,6 +22,8 @@ let WIKI = {
Error: require('./helpers/error'),
configSvc: require('./core/config'),
kernel: require('./core/kernel'),
sites: {},
sitesMappings: {},
startedAt: DateTime.utc(),
storage: {
defs: [],
......
......@@ -28,6 +28,28 @@ module.exports = class Site extends Model {
return ['config']
}
static async getSiteByHostname ({ hostname, forceReload = false }) {
if (forceReload) {
await WIKI.models.sites.reloadCache()
}
const siteId = WIKI.sitesMappings[hostname] || WIKI.sitesMappings['*']
if (siteId) {
return WIKI.sites[siteId]
}
return null
}
static async reloadCache () {
WIKI.logger.info('Reloading site configurations...')
const sites = await WIKI.models.sites.query().orderBy('id')
WIKI.sites = _.keyBy(sites, 'id')
WIKI.sitesMappings = {}
for (const site of sites) {
WIKI.sitesMappings[site.hostname] = site.id
}
WIKI.logger.info(`Loaded ${sites.length} site configurations [ OK ]`)
}
static async createSite (hostname, config) {
const newSite = await WIKI.models.sites.query().insertAndFetch({
hostname,
......
name: Default
author: requarks.io
site: https://wiki.requarks.io/
version: 1.0.0
requirements:
minimum: '>= 2.0.0'
maximum: '< 3.0.0'
props:
accentColor:
type: String
title: Accent Color
hint: Color used in the sidebar navigation and other elements.
order: 1
default: blue darken-2
control: color-material
tocPosition:
type: String
title: Table of Contents Position
hint: Select whether the table of contents is shown on the left, right or not at all.
order: 2
default: left
enum:
- left
- right
- hidden
This diff was suppressed by a .gitattributes entry.
extends master.pug
extends base.pug
block body
#root
......
doctype html
html
html(lang=siteConfig.lang)
head
meta(http-equiv='X-UA-Compatible', content='IE=edge')
meta(charset='UTF-8')
......@@ -21,12 +21,23 @@ html
//- Favicon
link(rel='apple-touch-icon', sizes='180x180', href='/_assets/favicons/apple-touch-icon.png')
link(rel='icon', type='image/png', sizes='192x192', href='/_assets/favicons/android-icon-192x192.png')
link(rel='icon', type='image/png', sizes='192x192', href='/_assets/favicons/android-chrome-192x192.png')
link(rel='icon', type='image/png', sizes='32x32', href='/_assets/favicons/favicon-32x32.png')
link(rel='icon', type='image/png', sizes='16x16', href='/_assets/favicons/favicon-16x16.png')
link(rel='mask-icon', href='/_assets/favicons/safari-pinned-tab.svg', color='#1976d2')
link(rel='manifest', href='/_assets/manifest.json')
//- Site Properties
script.
var siteId = "!{siteId}"
var siteConfig = !{JSON.stringify(siteConfig)}
var siteLangs = !{JSON.stringify(langs)}
//- Dev Mode Warning
if devMode
script.
siteConfig.devMode = true
//- Icon Set
if config.theming.iconset === 'fa'
link(
......@@ -42,26 +53,24 @@ html
)
//- CSS
<% for (var index in htmlWebpackPlugin.files.css) { %>
link(
type='text/css'
rel='stylesheet'
href='<%= htmlWebpackPlugin.files.css[index] %>'
)
<% } %>
//- JS
script(
crossorigin='anonymous'
src='https://polyfill.io/v3/polyfill.min.js?features=EventSource'
type='text/javascript'
src='/_assets-legacy/js/runtime.js'
)
//- JS
<% for (var index in htmlWebpackPlugin.files.js) { %>
script(
type='text/javascript'
src='<%= htmlWebpackPlugin.files.js[index] %>'
src='/_assets-legacy/js/app.js'
)
<% } %>
!= analyticsCode.head
......
extends master.pug
extends base.pug
block head
if injectCode.css
......
extends master.pug
extends base.pug
block body
#root.is-fullscreen
......
extends master.pug
extends base.pug
block head
......
extends master.pug
block body
#root
.login-deprecated!= t('outdatedBrowserWarning', { modernBrowser: '<a href="https://bestvpn.org/outdatedbrowser/en" rel="nofollow">' + t('modernBrowser') + '</a>', interpolation: { escapeValue: false } })
.login
.login-dialog
if err
.login-error= err.message
form(method='post', action='/login')
h1= config.title
select(name='strategy')
each str in formStrategies
option(value=str.key, selected)= str.title
input(type='text', name='user', placeholder=t('auth:fields.emailUser'))
input(type='password', name='pass', placeholder=t('auth:fields.password'))
button(type='submit')= t('auth:actions.login')
if socialStrategies.length
.login-social
h2= t('auth:orLoginUsingStrategy')
each str in socialStrategies
a.login-social-icon(href='/login/' + str.key, class=str.color)
!= str.icon
extends master.pug
block head
if injectCode.css
style(type='text/css')!= injectCode.css
if injectCode.head
!= injectCode.head
block body
#root
.header
span.header-title= siteConfig.title
span.header-deprecated!= t('outdatedBrowserWarning', { modernBrowser: '<a href="https://bestvpn.org/outdatedbrowser/en" rel="nofollow">' + t('modernBrowser') + '</a>', interpolation: { escapeValue: false } })
span.header-login
if !isAuthenticated
a(href='/login', title='Login')
i.mdi.mdi-account-circle
else
a(href='/logout', title='Logout')
i.mdi.mdi-logout
.main
.sidebar
each navItem in sidebar
if navItem.k === 'link'
a.sidebar-link(href=navItem.t)
i.mdi(class=navItem.c)
span= navItem.l
else if navItem.k === 'divider'
.sidebar-divider
else if navItem.k === 'header'
.sidebar-title= navItem.l
.main-container
.page-header
.page-header-left
h1= page.title
h2= page.description
//- .page-header-right
//- .page-header-right-title Last edited by
//- .page-header-right-author= page.authorName
//- .page-header-right-updated= page.updatedAt
.page-contents.v-content
.contents
div!= page.render
if page.toc.length
.toc
.toc-title= t('page.toc')
each tocItem, tocIdx in page.toc
a.toc-tile(href=tocItem.anchor)
i.mdi.mdi-chevron-right
span= tocItem.title
if tocIdx < page.toc.length - 1 || tocItem.children.length
.toc-divider
each tocSubItem in tocItem.children
a.toc-tile.inset(href=tocSubItem.anchor)
i.mdi.mdi-chevron-right
span= tocSubItem.title
if tocIdx < page.toc.length - 1
.toc-divider.inset
if injectCode.body
!= injectCode.body
extends master.pug
extends base.pug
block body
#root.is-fullscreen
......
extends master.pug
extends base.pug
block body
#root.is-fullscreen
......
extends master.pug
extends base.pug
block body
#root.is-fullscreen
......
extends master.pug
extends base.pug
block head
if injectCode.css
......
extends master.pug
extends base.pug
block body
#root
......
extends master.pug
extends base.pug
block body
#root.is-fullscreen
......
extends master.pug
extends base.pug
block head
......
extends master.pug
extends base.pug
block body
#root
......
extends master.pug
extends base.pug
block body
#root.is-fullscreen
......
extends master.pug
extends base.pug
block body
#root.is-fullscreen
......
......@@ -149,15 +149,20 @@ module.exports = async () => {
// ----------------------------------------
app.use(async (req, res, next) => {
const currentSite = await WIKI.models.sites.getSiteByHostname({ hostname: req.hostname })
if (!currentSite) {
return res.status(404).send('Site Not Found')
}
res.locals.siteConfig = {
title: WIKI.config.title,
theme: WIKI.config.theming.theme,
darkMode: WIKI.config.theming.darkMode,
lang: WIKI.config.lang.code,
rtl: WIKI.config.lang.rtl,
company: WIKI.config.company,
contentLicense: WIKI.config.contentLicense,
logoUrl: WIKI.config.logoUrl
id: currentSite.id,
title: currentSite.config.title,
darkMode: currentSite.config.theme.dark,
lang: currentSite.config.locale,
rtl: false, // TODO: handle RTL
company: currentSite.config.company,
contentLicense: currentSite.config.contentLicense,
logoUrl: currentSite.config.logoUrl
}
res.locals.langs = await WIKI.models.locales.getNavLocales({ cache: true })
res.locals.analyticsCode = await WIKI.models.analytics.getCode({ cache: true })
......
......@@ -68,6 +68,7 @@ module.exports = {
'prefer-promise-reject-errors': 'off',
'no-unused-vars': 'off',
'vue/multi-word-component-names': 'off',
// allow debugger during development only
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
......
......@@ -8,6 +8,9 @@
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
<title>Wiki.js</title>
<!--preload-links-->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Rubik:wght@300..900&display=swap" rel="stylesheet">
<style type="text/css">
@keyframes initspinner {
to { transform: rotate(360deg); }
......
......@@ -11,28 +11,28 @@
"lint": "eslint --ext .js,.vue ./"
},
"dependencies": {
"@apollo/client": "3.6.8",
"@apollo/client": "3.6.9",
"@codemirror/autocomplete": "6.0.2",
"@codemirror/basic-setup": "0.20.0",
"@codemirror/closebrackets": "0.19.2",
"@codemirror/commands": "6.0.0",
"@codemirror/commands": "6.0.1",
"@codemirror/comment": "0.19.1",
"@codemirror/fold": "0.19.4",
"@codemirror/gutter": "0.19.9",
"@codemirror/highlight": "0.19.8",
"@codemirror/history": "0.19.2",
"@codemirror/lang-css": "6.0.0",
"@codemirror/lang-html": "6.0.0",
"@codemirror/lang-javascript": "6.0.0",
"@codemirror/lang-html": "6.1.0",
"@codemirror/lang-javascript": "6.0.1",
"@codemirror/lang-json": "6.0.0",
"@codemirror/lang-markdown": "6.0.0",
"@codemirror/matchbrackets": "0.19.4",
"@codemirror/search": "6.0.0",
"@codemirror/state": "6.0.1",
"@codemirror/tooltip": "0.19.16",
"@codemirror/view": "6.0.1",
"@codemirror/view": "6.0.2",
"@lezer/common": "1.0.0",
"@quasar/extras": "1.14.0",
"@quasar/extras": "1.14.2",
"@tiptap/core": "2.0.0-beta.176",
"@tiptap/extension-code-block": "2.0.0-beta.37",
"@tiptap/extension-code-block-lowlight": "2.0.0-beta.68",
......@@ -59,24 +59,23 @@
"@tiptap/vue-3": "2.0.0-beta.91",
"@vue/apollo-option": "4.0.0-alpha.17",
"apollo-upload-client": "17.0.0",
"browser-fs-access": "0.29.6",
"browser-fs-access": "0.30.2",
"clipboard": "2.0.11",
"codemirror": "6.0.0",
"filesize": "9.0.9",
"codemirror": "6.0.1",
"filesize": "9.0.11",
"filesize-parser": "1.5.0",
"graphql": "16.5.0",
"graphql-tag": "2.12.6",
"js-cookie": "3.0.1",
"jwt-decode": "3.1.2",
"lodash": "4.17.21",
"lodash-es": "4.17.21",
"luxon": "2.4.0",
"pinia": "2.0.14",
"pug": "3.0.2",
"quasar": "2.7.3",
"quasar": "2.7.4",
"tippy.js": "6.3.7",
"uuid": "8.3.2",
"v-network-graph": "0.5.19",
"v-network-graph": "0.6.3",
"vue": "3.2.37",
"vue-codemirror": "6.0.0",
"vue-i18n": "9.1.10",
......@@ -86,10 +85,10 @@
},
"devDependencies": {
"@intlify/vite-plugin-vue-i18n": "3.4.0",
"@quasar/app-vite": "1.0.2",
"@quasar/app-vite": "1.0.4",
"@types/lodash": "4.14.182",
"autoprefixer": "10.4.7",
"eslint": "8.18.0",
"browserlist": "latest",
"eslint": "8.19.0",
"eslint-config-standard": "17.0.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-n": "15.2.3",
......
......@@ -63,7 +63,7 @@ module.exports = configure(function (/* ctx */) {
vueRouterMode: 'history', // available values: 'hash', 'history'
// vueRouterBase,
// vueDevtools,
vueOptionsAPI: true,
// vueOptionsAPI: true,
rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
......
<template>
<router-view />
<template lang="pug">
router-view
</template>
<script>
import { defineComponent, nextTick, onMounted } from 'vue'
<script setup>
import { nextTick, onMounted } from 'vue'
import { useSiteStore } from 'src/stores/site'
export default defineComponent({
name: 'App',
setup () {
onMounted(() => {
/* global siteConfig */
// STORES
const siteStore = useSiteStore()
// INIT SITE STORE
if (typeof siteConfig !== 'undefined') {
siteStore.$patch({
id: siteConfig.id,
title: siteConfig.title
})
} else {
siteStore.loadSite(window.location.hostname)
}
// MOUNTED
onMounted(async () => {
nextTick(() => {
document.querySelector('.init-loading').remove()
})
})
}
})
</script>
......@@ -494,9 +494,8 @@ q-layout(view='hHh lpR fFf', container)
<script setup>
import gql from 'graphql-tag'
import { DateTime } from 'luxon'
import cloneDeep from 'lodash/cloneDeep'
import some from 'lodash/some'
import { v4 as uuid } from 'uuid'
import { cloneDeep, some } from 'lodash-es'
import { fileOpen } from 'browser-fs-access'
import { useI18n } from 'vue-i18n'
......
......@@ -119,7 +119,7 @@ q-card.icon-picker(flat, style='width: 400px;')
</template>
<script>
import find from 'lodash/find'
import { find } from 'lodash-es'
export default {
props: {
......
......@@ -161,8 +161,7 @@ q-card.page-datatmpl-dialog(style='width: 1100px; max-width: 1100px;')
<script>
import { get, sync } from 'vuex-pathify'
import { v4 as uuid } from 'uuid'
import cloneDeep from 'lodash/cloneDeep'
import sortBy from 'lodash/sortBy'
import { cloneDeep, sortBy } from 'lodash-es'
import draggable from 'vuedraggable'
export default {
......
......@@ -114,8 +114,7 @@ q-card.page-relation-dialog(style='width: 500px;')
<script>
import { v4 as uuid } from 'uuid'
import find from 'lodash/find'
import cloneDeep from 'lodash/cloneDeep'
import { cloneDeep, find } from 'lodash-es'
import IconPickerDialog from './IconPickerDialog.vue'
......
......@@ -30,7 +30,7 @@ q-dialog(ref='dialogRef', @hide='onDialogHide')
<script setup>
import gql from 'graphql-tag'
import cloneDeep from 'lodash/cloneDeep'
import { cloneDeep } from 'lodash-es'
import { useI18n } from 'vue-i18n'
import { useDialogPluginComponent, useQuasar } from 'quasar'
import { reactive, ref } from 'vue'
......
......@@ -69,7 +69,7 @@ q-dialog(ref='dialogRef', @hide='onDialogHide')
<script setup>
import gql from 'graphql-tag'
import sampleSize from 'lodash/sampleSize'
import { sampleSize } from 'lodash-es'
import zxcvbn from 'zxcvbn'
import { useI18n } from 'vue-i18n'
......
......@@ -161,9 +161,8 @@ q-dialog(ref='dialogRef', @hide='onDialogHide')
<script setup>
import gql from 'graphql-tag'
import sampleSize from 'lodash/sampleSize'
import { cloneDeep, sampleSize } from 'lodash-es'
import zxcvbn from 'zxcvbn'
import cloneDeep from 'lodash/cloneDeep'
import { useI18n } from 'vue-i18n'
import { useDialogPluginComponent, useQuasar } from 'quasar'
import { computed, onMounted, reactive, ref } from 'vue'
......
......@@ -488,12 +488,7 @@ q-layout(view='hHh lpR fFf', container)
<script setup>
import gql from 'graphql-tag'
import cloneDeep from 'lodash/cloneDeep'
import some from 'lodash/some'
import find from 'lodash/find'
import findKey from 'lodash/findKey'
import _get from 'lodash/get'
import map from 'lodash/map'
import { cloneDeep, find, findKey, map, some } from 'lodash-es'
import { DateTime } from 'luxon'
import { useI18n } from 'vue-i18n'
......
......@@ -194,7 +194,7 @@ q-dialog(ref='dialogRef', @hide='onDialogHide')
<script setup>
import gql from 'graphql-tag'
import cloneDeep from 'lodash/cloneDeep'
import { cloneDeep } from 'lodash-es'
import { useI18n } from 'vue-i18n'
import { useDialogPluginComponent, useQuasar } from 'quasar'
import { computed, onMounted, reactive, ref } from 'vue'
......
......@@ -33,7 +33,7 @@ body::-webkit-scrollbar-thumb {
}
.font-poppins {
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-family: $font-poppins;
}
@font-face {
......@@ -117,7 +117,7 @@ body::-webkit-scrollbar-thumb {
// BUTTONS
// ------------------------------------------------------------------
body.desktop .acrylic-btn {
#app .acrylic-btn {
.q-focus-helper {
background-color: currentColor;
opacity: .1;
......
......@@ -30,3 +30,7 @@ $dark-6: #070a0d;
$dark-5: #0d1117;
$dark-4: #161b22;
$dark-3: #1e232a;
// -- FONTS --
$font-poppins: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
......@@ -300,7 +300,7 @@
"admin.login.logoutRedirect": "Logout Redirect",
"admin.login.logoutRedirectHint": "Optionally redirect the user to a specific page when he/she logouts. This can be overridden at the group level.",
"admin.login.providers": "Login Providers",
"admin.login.providersVisbleWarning": "Note that you can always temporarily show all hidden providers by adding ?all to the url. This is useful to login as local admin while hiding it from normal users.",
"admin.login.providersVisbleWarning": "Note that you can always temporarily show all hidden providers by adding ?all=1 to the url. This is useful to login as local admin while hiding it from normal users.",
"admin.login.subtitle": "Configure the login user experience of your wiki site",
"admin.login.title": "Login",
"admin.login.welcomeRedirect": "First-time Login Redirect",
......@@ -671,7 +671,7 @@
"admin.theme.bodyHtmlInjectionHint": "HTML code to be injected just before the closing body tag.",
"admin.theme.codeInjection": "Code Injection",
"admin.theme.cssOverride": "CSS Override",
"admin.theme.cssOverrideHint": "CSS code to inject after system default CSS. Consider using custom themes if you have a large amount of css code. Injecting too much CSS code will result in poor page load performance! CSS will automatically be minified.",
"admin.theme.cssOverrideHint": "CSS code to inject after system default CSS. Injecting too much CSS code can result in poor page load performance! CSS will automatically be minified.",
"admin.theme.cssOverrideWarning": "{caution} When adding styles for page content, you must scope them to the {cssClass} class. Omitting this could break the layout of the editor!",
"admin.theme.cssOverrideWarningCaution": "CAUTION:",
"admin.theme.darkMode": "Dark Mode",
......
......@@ -79,7 +79,7 @@ q-layout.admin(view='hHh Lpr lff')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-comments.svg')
q-item-section {{ t('admin.comments.title') }}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/editors`', v-ripple, active-class='bg-primary text-white')
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/editors`', v-ripple, active-class='bg-primary text-white', disabled)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-cashbook.svg')
q-item-section {{ t('admin.editors.title') }}
......
......@@ -4,45 +4,78 @@ q-layout(view='hHh lpr lff')
router-view
</template>
<script>
<script setup>
export default {
name: 'AuthLayout',
data () {
return {
bgUrl: '_assets/bg/login-v3.jpg'
// bgUrl: 'https://docs.requarks.io/_assets/img/splash/1.jpg'
}
}
}
</script>
<style lang="scss">
.auth {
background-color: #FFF;
background-size: cover;
background-position: center center;
height: 100vh;
display: flex;
font-family: 'Rubik', sans-serif;
@at-root .body--dark & {
background-color: $dark-6;
}
&-content {
flex: 1 0 100%;
width: 100%;
max-width: 500px;
padding: 3rem 4rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
align-items: stretch;
&-box {
background-color: rgba(255,255,255,.25);
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
padding: 32px;
border-radius: 8px;
width: 500px;
max-width: 95vw;
@media (max-width: $breakpoint-xs-max) {
padding: 1rem 2rem;
max-width: 100vw;
}
}
@at-root .no-backdropfilter & {
background-color: rgba(255,255,255,.95);
&-logo {
img {
height: 72px;
}
}
&-site-title {
font-size: 1.875rem;
line-height: 2.25rem;
font-weight: 700;
margin: 0;
color: $blue-grey-9;
@at-root .body--dark & {
color: $blue-grey-1;
}
}
&-strategies {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(45%, 1fr));
gap: 10px;
}
&-bg {
flex: 1;
flex-basis: 0;
position: relative;
height: 100vh;
overflow: hidden;
@media (max-width: $breakpoint-md-max) {
margin-left: 0;
img {
position: relative;
width: 100%;
height: 100%;
object-fit: cover;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: 0;
padding: 0;
}
}
}
......
......@@ -32,23 +32,30 @@ q-page.admin-navigation
)
q-separator(inset)
.row.q-pa-md.q-col-gutter-md
.col-6
//- v-container(fluid, grid-list-lg)
//- v-layout(row wrap)
//- v-flex(xs12)
//- .admin-header
//- img.animated.fadeInUp(src='/_assets/svg/icon-triangle-arrow.svg', alt='Navigation', style='width: 80px;')
//- .admin-header-title
//- .headline.primary--text.animated.fadeInLeft {{$t('navigation.title')}}
//- .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{$t('navigation.subtitle')}}
//- v-spacer
//- v-btn.animated.fadeInDown.wait-p3s(icon, outlined, color='grey', href='https://docs.requarks.io/navigation', target='_blank')
//- v-icon mdi-help-circle
//- v-btn.mx-3.animated.fadeInDown.wait-p2s.mr-3(icon, outlined, color='grey', @click='refresh')
//- v-icon mdi-refresh
//- v-btn.animated.fadeInDown(color='success', depressed, @click='save', large)
//- v-icon(left) mdi-check
//- span {{$t('common.actions.apply')}}
.col-auto
q-card.q-mt-sm {{t('admin.navigation.mode')}}
q-card.bg-dark.q-mt-sm
q-list(
style='min-width: 350px;'
padding
dark
)
q-item
q-item-section
q-select(
dark
outlined
option-value='value'
option-label='text'
emit-value
map-options
dense
options-dense
:label='t(`admin.navigation.mode`)'
:aria-label='t(`admin.navigation.mode`)'
)
//- v-container.pa-0.mt-3(fluid, grid-list-lg)
//- v-row(dense)
//- v-col(cols='3')
......
......@@ -165,14 +165,7 @@ q-page.admin-mail
</template>
<script>
import cloneDeep from 'lodash/cloneDeep'
import concat from 'lodash/concat'
import filter from 'lodash/filter'
import find from 'lodash/find'
import findIndex from 'lodash/findIndex'
import reduce from 'lodash/reduce'
import reverse from 'lodash/reverse'
import sortBy from 'lodash/sortBy'
import { cloneDeep, concat, filter, find, findIndex, reduce, reverse, sortBy } from 'lodash-es'
import { DepGraph } from 'dependency-graph'
import gql from 'graphql-tag'
......
......@@ -8,13 +8,13 @@ const routes = [
// { path: 'n/:editor?', component: () => import('../pages/Index.vue') }
// ]
// },
// {
// path: '/login',
// component: () => import('../layouts/AuthLayout.vue'),
// children: [
// { path: '', component: () => import('../pages/Login.vue') }
// ]
// },
{
path: '/login',
component: () => import('../layouts/AuthLayout.vue'),
children: [
{ path: '', component: () => import('../pages/Login.vue') }
]
},
// {
// path: '/p',
// component: () => import('../layouts/ProfileLayout.vue'),
......
import { defineStore } from 'pinia'
import gql from 'graphql-tag'
import cloneDeep from 'lodash/cloneDeep'
import { cloneDeep } from 'lodash-es'
/* global APOLLO_CLIENT */
......
import { defineStore } from 'pinia'
import gql from 'graphql-tag'
import clone from 'lodash/clone'
import { clone } from 'lodash-es'
export const useSiteStore = defineStore('site', {
state: () => ({
......
This diff was suppressed by a .gitattributes entry.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment