feat(admin): migrate locale + login + storage views to vue 3 composable

parent 0c522c17
# Based of https://github.com/microsoft/vscode-dev-containers/blob/main/containers/javascript-node/.devcontainer/base.Dockerfile
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 16, 14, 12, 16-bullseye, 14-bullseye, 12-bullseye, 16-buster, 14-buster, 12-buster
ARG VARIANT=16-bullseye
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}
ARG VARIANT=18-bullseye
FROM node:${VARIANT}
# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>
ENV DEBIAN_FRONTEND=noninteractive
# [Optional] Uncomment if you want to install an additional version of node using nvm
# ARG EXTRA_NODE_VERSION=10
# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"
# Copy library scripts to execute
ADD https://raw.githubusercontent.com/microsoft/vscode-dev-containers/main/containers/javascript-node/.devcontainer/library-scripts/common-debian.sh /tmp/library-scripts/
ADD https://raw.githubusercontent.com/microsoft/vscode-dev-containers/main/containers/javascript-node/.devcontainer/library-scripts/node-debian.sh /tmp/library-scripts/
ADD https://raw.githubusercontent.com/microsoft/vscode-dev-containers/main/containers/javascript-node/.devcontainer/library-scripts/meta.env /tmp/library-scripts/
# [Optional] Uncomment if you want to install more global node modules
# RUN su node -c "npm install -g <your-package-list-here>"
# [Option] Install zsh
ARG INSTALL_ZSH="true"
# [Option] Upgrade OS packages to their latest versions
ARG UPGRADE_PACKAGES="true"
EXPOSE 3000
# Install needed packages, yarn, nvm and setup non-root user. Use a separate RUN statement to add your own dependencies.
ARG USERNAME=node
ARG USER_UID=1000
ARG USER_GID=$USER_UID
ARG NPM_GLOBAL=/usr/local/share/npm-global
ENV NVM_DIR=/usr/local/share/nvm
ENV NVM_SYMLINK_CURRENT=true \
PATH=${NPM_GLOBAL}/bin:${NVM_DIR}/current/bin:${PATH}
RUN apt-get update \
# Remove imagemagick due to https://security-tracker.debian.org/tracker/CVE-2019-10131
&& apt-get purge -y imagemagick imagemagick-6-common \
# Install common packages, non-root user, update yarn and install nvm
&& bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" "true" "true" \
# Install yarn, nvm
&& rm -rf /opt/yarn-* /usr/local/bin/yarn /usr/local/bin/yarnpkg \
&& bash /tmp/library-scripts/node-debian.sh "${NVM_DIR}" "none" "${USERNAME}" \
# Configure global npm install location, use group to adapt to UID/GID changes
&& if ! cat /etc/group | grep -e "^npm:" > /dev/null 2>&1; then groupadd -r npm; fi \
&& usermod -a -G npm ${USERNAME} \
&& umask 0002 \
&& mkdir -p ${NPM_GLOBAL} \
&& touch /usr/local/etc/npmrc \
&& chown ${USERNAME}:npm ${NPM_GLOBAL} /usr/local/etc/npmrc \
&& chmod g+s ${NPM_GLOBAL} \
&& npm config -g set prefix ${NPM_GLOBAL} \
&& sudo -u ${USERNAME} npm config -g set prefix ${NPM_GLOBAL} \
# Install eslint
&& su ${USERNAME} -c "umask 0002 && npm install -g eslint" \
&& npm cache clean --force > /dev/null 2>&1 \
# Install python-is-python3 on bullseye to prevent node-gyp regressions
&& . /etc/os-release \
&& if [ "${VERSION_CODENAME}" = "bullseye" ]; then apt-get -y install --no-install-recommends python-is-python3; fi \
# Clean up
&& apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* /root/.gnupg /tmp/library-scripts
ENV DEBIAN_FRONTEND=noninteractive
EXPOSE 3000
# Add Docker Source
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
......@@ -33,6 +70,7 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && apt-get install -
git \
gnupg2 \
nano \
netcat \
pandoc \
unzip \
wget
......@@ -48,7 +86,7 @@ ENV npm_config_fund false
RUN sed -i 's/#force_color_prompt=/force_color_prompt=/' /root/.bashrc
# Fetch wait-for utility
ADD https://raw.githubusercontent.com/eficode/wait-for/v2.1.3/wait-for /usr/local/bin/
ADD https://raw.githubusercontent.com/eficode/wait-for/v2.2.3/wait-for /usr/local/bin/
RUN chmod +rx /usr/local/bin/wait-for
# Copy the startup file
......
......@@ -2,7 +2,11 @@
cd /workspace
echo "Disabling git info in terminal..."
git config codespaces-theme.hide-status 1
git config oh-my-zsh.hide-info 1
echo "Waiting for DB container to come online..."
/usr/local/bin/wait-for localhost:5432 -- echo "DB ready"
echo "Ready!"
......@@ -32,10 +32,10 @@
"arcanis.vscode-zipfs",
"dbaeumer.vscode-eslint",
"eamodio.gitlens",
"johnsoncodehk.volar",
"Vue.volar",
"oderwat.indent-rainbow",
"redhat.vscode-yaml",
"visualstudioexptteam.vscodeintellicode",
"VisualStudioExptTeam.vscodeintellicode",
"editorconfig.editorconfig",
"lokalise.i18n-ally",
"mrmlnc.vscode-duplicate",
......
......@@ -6,10 +6,10 @@ services:
context: .
dockerfile: Dockerfile
args:
# Update 'VARIANT' to pick an LTS version of Node.js: 16, 14, 12.
# Update 'VARIANT' to pick an LTS version of Node.js: 18, 16, 14, 12.
# Append -bullseye or -buster to pin to an OS version.
# Use -bullseye variants on local arm64/Apple Silicon.
VARIANT: 16-bullseye
VARIANT: 18-bullseye
volumes:
- ..:/workspace
......
const _ = require('lodash')
const cfgHelper = require('../helpers/config')
const Promise = require('bluebird')
const fs = require('fs-extra')
const path = require('path')
......@@ -11,71 +8,13 @@ module.exports = {
channel: 'BETA',
version: WIKI.version,
releaseDate: WIKI.releaseDate,
minimumVersionRequired: '2.0.0-beta.0',
minimumNodeRequired: '10.12.0'
minimumVersionRequired: '3.0.0-beta.0',
minimumNodeRequired: '18.0.0'
},
init() {
// Clear content cache
fs.emptyDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'cache'))
return this
},
/**
* Upgrade from WIKI.js 1.x - MongoDB database
*
* @param {Object} opts Options object
*/
async upgradeFromMongo (opts) {
WIKI.logger.info('Upgrading from MongoDB...')
let mongo = require('mongodb').MongoClient
let parsedMongoConStr = cfgHelper.parseConfigValue(opts.mongoCnStr)
return new Promise((resolve, reject) => {
// Connect to MongoDB
mongo.connect(parsedMongoConStr, {
autoReconnect: false,
reconnectTries: 2,
reconnectInterval: 1000,
connectTimeoutMS: 5000,
socketTimeoutMS: 5000
}, async (err, db) => {
try {
if (err !== null) { throw err }
let users = db.collection('users')
// Check if users table is populated
let userCount = await users.count()
if (userCount < 2) {
throw new Error('MongoDB Upgrade: Users table is empty!')
}
// Import all users
let userData = await users.find({
email: {
$not: 'guest'
}
}).toArray()
await WIKI.models.User.bulkCreate(_.map(userData, usr => {
return {
email: usr.email,
name: usr.name || 'Imported User',
password: usr.password || '',
provider: usr.provider || 'local',
providerId: usr.providerId || '',
role: 'user',
createdAt: usr.createdAt
}
}))
resolve(true)
} catch (errc) {
reject(errc)
}
db.close()
})
})
}
}
......@@ -8,8 +8,8 @@ const { nanoid } = require('nanoid')
const { DateTime } = require('luxon')
const semver = require('semver')
if (!semver.satisfies(process.version, '>=16')) {
console.error('ERROR: Node.js 16.x or later required!')
if (!semver.satisfies(process.version, '>=18')) {
console.error('ERROR: Node.js 18.x or later required!')
process.exit(1)
}
......
......@@ -11,8 +11,8 @@
"lint": "eslint --ext .js,.vue ./"
},
"dependencies": {
"@apollo/client": "3.6.1",
"@codemirror/autocomplete": "0.20.0",
"@apollo/client": "3.6.4",
"@codemirror/autocomplete": "0.20.1",
"@codemirror/basic-setup": "0.20.0",
"@codemirror/closebrackets": "0.19.2",
"@codemirror/commands": "0.20.0",
......@@ -25,15 +25,15 @@
"@codemirror/lang-html": "0.20.0",
"@codemirror/lang-javascript": "0.20.0",
"@codemirror/lang-json": "0.20.0",
"@codemirror/lang-markdown": "0.20.0",
"@codemirror/lang-markdown": "0.20.1",
"@codemirror/matchbrackets": "0.19.4",
"@codemirror/search": "0.20.1",
"@codemirror/state": "0.20.0",
"@codemirror/tooltip": "0.19.16",
"@codemirror/view": "0.20.3",
"@codemirror/view": "0.20.6",
"@lezer/common": "0.16.0",
"@quasar/extras": "1.13.6",
"@tiptap/core": "2.0.0-beta.175",
"@quasar/extras": "1.14.0",
"@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",
"@tiptap/extension-color": "2.0.0-beta.9",
......@@ -44,9 +44,9 @@
"@tiptap/extension-highlight": "2.0.0-beta.33",
"@tiptap/extension-history": "2.0.0-beta.21",
"@tiptap/extension-image": "2.0.0-beta.27",
"@tiptap/extension-mention": "2.0.0-beta.96",
"@tiptap/extension-mention": "2.0.0-beta.97",
"@tiptap/extension-placeholder": "2.0.0-beta.48",
"@tiptap/extension-table": "2.0.0-beta.48",
"@tiptap/extension-table": "2.0.0-beta.49",
"@tiptap/extension-table-cell": "2.0.0-beta.20",
"@tiptap/extension-table-header": "2.0.0-beta.22",
"@tiptap/extension-table-row": "2.0.0-beta.19",
......@@ -55,38 +55,38 @@
"@tiptap/extension-text-align": "2.0.0-beta.29",
"@tiptap/extension-text-style": "2.0.0-beta.23",
"@tiptap/extension-typography": "2.0.0-beta.20",
"@tiptap/starter-kit": "2.0.0-beta.184",
"@tiptap/starter-kit": "2.0.0-beta.185",
"@tiptap/vue-3": "2.0.0-beta.91",
"@vue/apollo-option": "4.0.0-alpha.16",
"@vue/apollo-option": "4.0.0-alpha.17",
"apollo-upload-client": "17.0.0",
"browser-fs-access": "0.29.4",
"clipboard": "2.0.10",
"browser-fs-access": "0.29.5",
"clipboard": "2.0.11",
"filesize": "8.0.7",
"filesize-parser": "1.5.0",
"graphql": "16.4.0",
"graphql": "16.5.0",
"graphql-tag": "2.12.6",
"js-cookie": "3.0.1",
"jwt-decode": "3.1.2",
"lodash": "4.17.21",
"luxon": "2.3.2",
"pinia": "2.0.13",
"luxon": "2.4.0",
"pinia": "2.0.14",
"pug": "3.0.2",
"quasar": "2.6.6",
"quasar": "2.7.0",
"tippy.js": "6.3.7",
"uuid": "8.3.2",
"v-network-graph": "0.5.13",
"v-network-graph": "0.5.16",
"vue": "3.2.31",
"vue-i18n": "9.1.9",
"vue-router": "4.0.14",
"vue-i18n": "9.1.10",
"vue-router": "4.0.15",
"vuedraggable": "4.1.0",
"zxcvbn": "4.4.2"
},
"devDependencies": {
"@intlify/vite-plugin-vue-i18n": "3.4.0",
"@quasar/app-vite": "1.0.0-beta.14",
"@quasar/app-vite": "1.0.0",
"@types/lodash": "4.14.182",
"autoprefixer": "10.4.5",
"eslint": "8.14.0",
"autoprefixer": "10.4.7",
"eslint": "8.16.0",
"eslint-config-standard": "17.0.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-n": "15.2.0",
......
<template lang="pug">
q-dialog(ref='dialog', @hide='onDialogHide')
q-dialog(ref='dialogRef', @hide='onDialogHide')
q-card(style='min-width: 850px;')
q-card-section.card-header
q-icon(name='img:/_assets/icons/fluent-down.svg', left, size='sm')
span {{$t(`admin.locale.downloadTitle`)}}
span {{t(`admin.locale.downloadTitle`)}}
q-card-section.q-pa-none
q-table.no-border-radius(
:data='locales'
:data='state.locales'
:columns='headers'
row-name='code'
flat
hide-bottom
:rows-per-page-options='[0]'
:loading='loading > 0'
:loading='state.loading > 0'
)
template(v-slot:body-cell-code='props')
q-td(:props='props')
......@@ -81,90 +81,101 @@ q-dialog(ref='dialog', @hide='onDialogHide')
q-space
q-btn.acrylic-btn(
flat
:label='$t(`common.actions.close`)'
:label='t(`common.actions.close`)'
color='grey'
padding='xs md'
@click='hide'
@click='onDialogCancel'
)
q-inner-loading(:showing='loading')
q-inner-loading(:showing='state.loading > 0')
q-spinner(color='accent', size='lg')
</template>
<script>
// import gql from 'graphql-tag'
// import cloneDeep from 'lodash/cloneDeep'
export default {
emits: ['ok', 'hide'],
data () {
return {
locales: [],
loading: 0
}
<script setup>
import { useI18n } from 'vue-i18n'
import { useDialogPluginComponent, useQuasar } from 'quasar'
import { reactive, ref } from 'vue'
import { useAdminStore } from '../stores/admin'
// EMITS
defineEmits([
...useDialogPluginComponent.emits
])
// QUASAR
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
const $q = useQuasar()
// STORES
const adminStore = useAdminStore()
// I18N
const { t } = useI18n()
// DATA
const state = reactive({
locales: [],
loading: 0
})
const headers = [
{
label: t('admin.locale.code'),
align: 'left',
field: 'code',
name: 'code',
sortable: true,
style: 'width: 90px'
},
{
label: t('admin.locale.name'),
align: 'left',
field: 'name',
name: 'name',
sortable: true
},
{
label: t('admin.locale.nativeName'),
align: 'left',
field: 'nativeName',
name: 'nativeName',
sortable: true
},
computed: {
headers () {
return [
{
label: this.$t('admin.locale.code'),
align: 'left',
field: 'code',
name: 'code',
sortable: true,
style: 'width: 90px'
},
{
label: this.$t('admin.locale.name'),
align: 'left',
field: 'name',
name: 'name',
sortable: true
},
{
label: this.$t('admin.locale.nativeName'),
align: 'left',
field: 'nativeName',
name: 'nativeName',
sortable: true
},
{
label: this.$t('admin.locale.rtl'),
align: 'center',
field: 'isRTL',
name: 'isRTL',
sortable: false,
style: 'width: 10px'
},
{
label: this.$t('admin.locale.availability'),
align: 'center',
field: 'availability',
name: 'availability',
sortable: false,
style: 'width: 120px'
},
{
label: this.$t('admin.locale.download'),
align: 'center',
field: 'isInstalled',
name: 'isInstalled',
sortable: false,
style: 'width: 100px'
}
]
}
{
label: t('admin.locale.rtl'),
align: 'center',
field: 'isRTL',
name: 'isRTL',
sortable: false,
style: 'width: 10px'
},
methods: {
show () {
this.$refs.dialog.show()
},
hide () {
this.$refs.dialog.hide()
},
onDialogHide () {
this.$emit('hide')
}
{
label: t('admin.locale.availability'),
align: 'center',
field: 'availability',
name: 'availability',
sortable: false,
style: 'width: 120px'
},
{
label: t('admin.locale.download'),
align: 'center',
field: 'isInstalled',
name: 'isInstalled',
sortable: false,
style: 'width: 100px'
}
]
// METHODS
async function download (lc) {
}
</script>
......@@ -16,7 +16,7 @@ q-page.admin-flags
type='a'
)
q-btn.q-mr-sm.acrylic-btn(
icon='fa-solid fa-rotate'
icon='las la-redo-alt'
flat
color='secondary'
:loading='loading > 0'
......
......@@ -11,7 +11,7 @@ q-page.admin-flags
icon='las la-question-circle'
flat
color='grey'
href='https://docs.requarks.io/admin/flags'
href='https://docs.js.wiki/admin/flags'
target='_blank'
type='a'
)
......
......@@ -16,7 +16,7 @@ q-page.admin-general
type='a'
)
q-btn.q-mr-sm.acrylic-btn(
icon='fa-solid fa-rotate'
icon='las la-redo-alt'
flat
color='secondary'
:loading='state.loading > 0'
......@@ -385,7 +385,6 @@ import { onMounted, reactive, watch } from 'vue'
import { useAdminStore } from 'src/stores/admin'
import { useSiteStore } from 'src/stores/site'
import { useDataStore } from 'src/stores/data'
import { useRoute, useRouter } from 'vue-router'
// QUASAR
......@@ -397,11 +396,6 @@ const adminStore = useAdminStore()
const siteStore = useSiteStore()
const dataStore = useDataStore()
// ROUTER
const router = useRouter()
const route = useRoute()
// I18N
const { t } = useI18n()
......
......@@ -16,7 +16,7 @@ q-page.admin-locale
target='_blank'
)
q-btn.q-mr-sm.acrylic-btn(
icon='fa-solid fa-rotate'
icon='las la-redo-alt'
flat
color='secondary'
@click='refresh'
......@@ -100,7 +100,7 @@ q-page.admin-locale
<script setup>
import { useMeta, useQuasar } from 'quasar'
import { useI18n } from 'vue-i18n'
import { defineAsyncComponent, nextTick, onMounted, reactive, ref, watch } from 'vue'
import { nextTick, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useAdminStore } from '../stores/admin'
......
......@@ -16,7 +16,7 @@ q-page.admin-system
type='a'
)
q-btn.q-mr-sm.acrylic-btn(
icon='fa-solid fa-rotate'
icon='las la-redo-alt'
flat
color='secondary'
:loading='state.loading > 0'
......@@ -234,26 +234,10 @@ import { useMeta, useQuasar } from 'quasar'
import { computed, onMounted, reactive, ref, watch } from 'vue'
import ClipboardJS from 'clipboard'
import { useAdminStore } from 'src/stores/admin'
import { useSiteStore } from 'src/stores/site'
import { useDataStore } from 'src/stores/data'
import { useRoute, useRouter } from 'vue-router'
// QUASAR
const $q = useQuasar()
// STORES
const adminStore = useAdminStore()
const siteStore = useSiteStore()
const dataStore = useDataStore()
// ROUTER
const router = useRouter()
const route = useRoute()
// I18N
const { t } = useI18n()
......
......@@ -30,20 +30,20 @@ const routes = [
{ path: '', redirect: '/_admin/dashboard' },
{ path: 'dashboard', component: () => import('../pages/AdminDashboard.vue') },
{ path: 'sites', component: () => import('../pages/AdminSites.vue') },
// // -> Site
// -> Site
{ path: ':siteid/general', component: () => import('../pages/AdminGeneral.vue') },
{ path: ':siteid/editors', component: () => import('../pages/AdminEditors.vue') },
// { path: ':siteid/locale', component: () => import('../pages/AdminLocale.vue') },
// { path: ':siteid/login', component: () => import('../pages/AdminLogin.vue') },
{ path: ':siteid/locale', component: () => import('../pages/AdminLocale.vue') },
{ path: ':siteid/login', component: () => import('../pages/AdminLogin.vue') },
// { path: ':siteid/navigation', component: () => import('../pages/AdminNavigation.vue') },
// { path: ':siteid/storage/:id?', component: () => import('../pages/AdminStorage.vue') },
{ path: ':siteid/storage/:id?', component: () => import('../pages/AdminStorage.vue') },
// { path: ':siteid/rendering', component: () => import('../pages/AdminRendering.vue') },
// { path: ':siteid/theme', component: () => import('../pages/AdminTheme.vue') },
// // -> Users
// -> Users
// { path: 'auth', component: () => import('../pages/AdminAuth.vue') },
// { path: 'groups/:id?/:section?', component: () => import('../pages/AdminGroups.vue') },
// { path: 'users/:id?/:section?', component: () => import('../pages/AdminUsers.vue') },
// // -> System
// -> System
// { path: 'api', component: () => import('../pages/AdminApi.vue') },
// { path: 'extensions', component: () => import('../pages/AdminExtensions.vue') },
// { path: 'mail', component: () => import('../pages/AdminMail.vue') },
......
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