diff --git a/build/webpack.base.config.js b/build/webpack.base.config.js index 360f1df..180ae23 100644 --- a/build/webpack.base.config.js +++ b/build/webpack.base.config.js @@ -21,10 +21,6 @@ module.exports = { test: /\.vue$/, loader: 'vue-loader', }, - { - resourceQuery: /^\?vue/, - use: path.resolve(__dirname, 'includer.js') - }, { test: /\.js$/, loader: 'babel-loader', diff --git a/build/webpack.dev.config.js b/build/webpack.dev.config.js index f27ee9b..8e0c014 100644 --- a/build/webpack.dev.config.js +++ b/build/webpack.dev.config.js @@ -1,5 +1,6 @@ const path = require('path'); const webpack = require('webpack'); +const pckg = require('../package.json'); const { merge } = require('webpack-merge'); const baseWpConfig = require('./webpack.base.config'); @@ -8,7 +9,7 @@ baseWpConfig.entry.unshift('webpack-hot-middleware/client'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); -const publicDir = path.resolve(__dirname, '../server/public'); +const publicDir = path.resolve(__dirname, `../server/.${pckg.name}/public`); const clientDir = path.resolve(__dirname, '../client'); module.exports = merge(baseWpConfig, { diff --git a/client/assets/robots.txt b/client/assets/robots.txt new file mode 100644 index 0000000..4d6e74a --- /dev/null +++ b/client/assets/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: /# diff --git a/client/components/App.vue b/client/components/App.vue new file mode 100644 index 0000000..88de668 --- /dev/null +++ b/client/components/App.vue @@ -0,0 +1,141 @@ + + + + + + + diff --git a/client/components/Search/Search.vue b/client/components/Search/Search.vue new file mode 100644 index 0000000..3c664f3 --- /dev/null +++ b/client/components/Search/Search.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/client/components/fonts/web-default.ttf b/client/components/fonts/web-default.ttf new file mode 100644 index 0000000..4a3a5e6 Binary files /dev/null and b/client/components/fonts/web-default.ttf differ diff --git a/client/components/fonts/web-default.woff b/client/components/fonts/web-default.woff new file mode 100644 index 0000000..b2ac07b Binary files /dev/null and b/client/components/fonts/web-default.woff differ diff --git a/client/components/share/Dialog.vue b/client/components/share/Dialog.vue new file mode 100644 index 0000000..7a93bac --- /dev/null +++ b/client/components/share/Dialog.vue @@ -0,0 +1,80 @@ + + + + + \ No newline at end of file diff --git a/client/components/share/Notify.vue b/client/components/share/Notify.vue new file mode 100644 index 0000000..92c3759 --- /dev/null +++ b/client/components/share/Notify.vue @@ -0,0 +1,58 @@ + + + diff --git a/client/components/share/StdDialog.vue b/client/components/share/StdDialog.vue new file mode 100644 index 0000000..384585d --- /dev/null +++ b/client/components/share/StdDialog.vue @@ -0,0 +1,361 @@ + + + + + \ No newline at end of file diff --git a/client/components/vueComponent.js b/client/components/vueComponent.js new file mode 100644 index 0000000..67f8c2e --- /dev/null +++ b/client/components/vueComponent.js @@ -0,0 +1,52 @@ +import { defineComponent } from 'vue'; +import _ from 'lodash'; + +export default function(componentClass) { + const comp = {}; + const obj = new componentClass(); + + //data, options, props + const data = {}; + for (const prop of Object.getOwnPropertyNames(obj)) { + if (['_options', '_props'].includes(prop)) {//meta props + if (prop === '_options') { + const options = obj[prop]; + for (const optName of ['components', 'watch', 'emits']) { + if (options[optName]) { + comp[optName] = options[optName]; + } + } + } else if (prop === '_props') { + comp['props'] = obj[prop]; + } + } else {//usual prop + data[prop] = obj[prop]; + } + } + comp.data = () => _.cloneDeep(data); + + //methods + const classProto = Object.getPrototypeOf(obj); + const classMethods = Object.getOwnPropertyNames(classProto); + const methods = {}; + const computed = {}; + for (const method of classMethods) { + const desc = Object.getOwnPropertyDescriptor(classProto, method); + if (desc.get) {//has getter, computed + computed[method] = {get: desc.get}; + if (desc.set) + computed[method].set = desc.set; + } else if ( ['beforeCreate', 'created', 'beforeMount', 'mounted', 'beforeUpdate', 'updated', 'activated',//life cycle hooks + 'deactivated', 'beforeUnmount', 'unmounted', 'errorCaptured', 'renderTracked', 'renderTriggered',//life cycle hooks + 'setup'].includes(method) ) { + comp[method] = obj[method]; + } else if (method !== 'constructor') {//usual + methods[method] = obj[method]; + } + } + comp.methods = methods; + comp.computed = computed; + + //console.log(comp); + return defineComponent(comp); +} diff --git a/client/index.html.template b/client/index.html.template new file mode 100644 index 0000000..7f9a5e9 --- /dev/null +++ b/client/index.html.template @@ -0,0 +1,11 @@ + + + + + + + + +
+ + diff --git a/client/main.js b/client/main.js new file mode 100644 index 0000000..a00b092 --- /dev/null +++ b/client/main.js @@ -0,0 +1,16 @@ +import { createApp } from 'vue'; + +import router from './router'; +import store from './store'; +import q from './quasar'; + +import App from './components/App.vue'; + +const app = createApp(App); + +app.use(router); +app.use(store); +app.use(q.quasar, q.options); +q.init(); + +app.mount('#app'); diff --git a/client/quasar.js b/client/quasar.js new file mode 100644 index 0000000..0702203 --- /dev/null +++ b/client/quasar.js @@ -0,0 +1,97 @@ +import 'quasar/dist/quasar.css'; + +import Quasar from 'quasar/src/vue-plugin.js'; +//config +const config = {}; + +//components +//import {QLayout} from 'quasar/src/components/layout'; +//import {QPageContainer, QPage} from 'quasar/src/components/page'; +//import {QDrawer} from 'quasar/src/components/drawer'; + +//import {QCircularProgress} from 'quasar/src/components/circular-progress'; +import {QInput} from 'quasar/src/components/input'; +import {QBtn} from 'quasar/src/components/btn'; +//import {QBtnGroup} from 'quasar/src/components/btn-group'; +//import {QBtnToggle} from 'quasar/src/components/btn-toggle'; +import {QIcon} from 'quasar/src/components/icon'; +//import {QSlider} from 'quasar/src/components/slider'; +//import {QTabs, QTab} from 'quasar/src/components/tabs'; +//import {QTabPanels, QTabPanel} from 'quasar/src/components/tab-panels'; +//import {QSeparator} from 'quasar/src/components/separator'; +//import {QList} from 'quasar/src/components/item'; +//import {QItem, QItemSection, QItemLabel} from 'quasar/src/components/item'; +//import {QTooltip} from 'quasar/src/components/tooltip'; +//import {QSpinner} from 'quasar/src/components/spinner'; +//import {QTable, QTh, QTr, QTd} from 'quasar/src/components/table'; +//import {QCheckbox} from 'quasar/src/components/checkbox'; +//import {QSelect} from 'quasar/src/components/select'; +//import {QColor} from 'quasar/src/components/color'; +//import {QPopupProxy} from 'quasar/src/components/popup-proxy'; +import {QDialog} from 'quasar/src/components/dialog'; +//import {QChip} from 'quasar/src/components/chip'; +//import {QTree} from 'quasar/src/components/tree'; +//import {QVirtualScroll} from 'quasar/src/components/virtual-scroll'; + +//import {QExpansionItem} from 'quasar/src/components/expansion-item'; + +const components = { + //QLayout, + //QPageContainer, QPage, + //QDrawer, + + //QCircularProgress, + QInput, + QBtn, + //QBtnGroup, + //QBtnToggle, + QIcon, + //QSlider, + //QTabs, QTab, + //QTabPanels, QTabPanel, + //QSeparator, + //QList, + //QItem, QItemSection, QItemLabel, + //QTooltip, + //QSpinner, + //QTable, QTh, QTr, QTd, + //QCheckbox, + //QSelect, + //QColor, + //QPopupProxy, + QDialog, + //QChip, + //QTree, + //QExpansionItem, + //QVirtualScroll, +}; + +//directives +//import Ripple from 'quasar/src/directives/Ripple'; +import ClosePopup from 'quasar/src/directives/ClosePopup'; + +const directives = {/*Ripple, */ClosePopup}; + +//plugins +//import AppFullscreen from 'quasar/src/plugins/AppFullscreen'; +//import Notify from 'quasar/src/plugins/Notify'; + +const plugins = { + //AppFullscreen, + //Notify, +}; + +//icons +//import '@quasar/extras/fontawesome-v5/fontawesome-v5.css'; +//import fontawesomeV5 from 'quasar/icon-set/fontawesome-v5.js' + +import '@quasar/extras/line-awesome/line-awesome.css'; +import lineAwesome from 'quasar/icon-set/line-awesome.js' + +export default { + quasar: Quasar, + options: { config, components, directives, plugins }, + init: () => { + Quasar.iconSet.set(lineAwesome); +} +}; \ No newline at end of file diff --git a/client/router.js b/client/router.js new file mode 100644 index 0000000..3fb73bb --- /dev/null +++ b/client/router.js @@ -0,0 +1,38 @@ +import { createRouter, createWebHashHistory } from 'vue-router'; +import _ from 'lodash'; + +const Search = () => import('./components/Search/Search.vue'); + +const myRoutes = [ + ['/', Search], + ['/:pathMatch(.*)*', null, null, '/'], +]; + +let routes = {}; + +for (let route of myRoutes) { + const [path, component, name, redirect] = route; + let cleanRoute = _.pickBy({path, component, name, redirect}, _.identity); + + let parts = cleanRoute.path.split('~'); + let f = routes; + for (let part of parts) { + const curRoute = _.assign({}, cleanRoute, { path: part }); + + if (!f.children) + f.children = []; + let r = f.children; + + f = _.find(r, {path: part}); + if (!f) { + r.push(curRoute); + f = curRoute; + } + } +} +routes = routes.children; + +export default createRouter({ + history: createWebHashHistory(), + routes +}); diff --git a/client/share/utils.js b/client/share/utils.js new file mode 100644 index 0000000..3fe79a0 --- /dev/null +++ b/client/share/utils.js @@ -0,0 +1,30 @@ +//import _ from 'lodash'; + +export function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +export function keyEventToCode(event) { + let result = []; + let code = event.code; + + const modCode = code.substring(0, 3); + if (event.metaKey && modCode != 'Met') + result.push('Meta'); + if (event.ctrlKey && modCode != 'Con') + result.push('Ctrl'); + if (event.shiftKey && modCode != 'Shi') + result.push('Shift'); + if (event.altKey && modCode != 'Alt') + result.push('Alt'); + + if (modCode == 'Dig') { + code = code.substring(5, 6); + } else if (modCode == 'Key') { + code = code.substring(3, 4); + } + result.push(code); + + return result.join('+'); +} + diff --git a/client/store/index.js b/client/store/index.js new file mode 100644 index 0000000..06cace6 --- /dev/null +++ b/client/store/index.js @@ -0,0 +1,15 @@ +import { createStore } from 'vuex'; +import VuexPersistence from 'vuex-persist'; + +import root from './root.js'; + +const debug = process.env.NODE_ENV !== 'production'; + +const vuexLocal = new VuexPersistence(); + +export default createStore(Object.assign({}, root, { + modules: { + }, + strict: debug, + plugins: [vuexLocal.plugin] +})); diff --git a/client/store/root.js b/client/store/root.js new file mode 100644 index 0000000..1cd73ad --- /dev/null +++ b/client/store/root.js @@ -0,0 +1,25 @@ +// initial state +const state = { + apiError: null, +}; + +// getters +const getters = {}; + +// actions +const actions = {}; + +// mutations +const mutations = { + setApiError(state, value) { + state.apiError = value; + }, +}; + +export default { + namespaced: true, + state, + getters, + actions, + mutations +}; diff --git a/server/config/base.js b/server/config/base.js index 72d1c28..644f024 100644 --- a/server/config/base.js +++ b/server/config/base.js @@ -2,7 +2,7 @@ const path = require('path'); const pckg = require('../../package.json'); const execDir = path.resolve(__dirname, '..'); -const dataDir = `${execDir}/.${pckg.name}/data`; +const dataDir = `${execDir}/.${pckg.name}`; module.exports = { branch: 'unknown', diff --git a/server/config/production.js b/server/config/production.js index fd4a516..e649161 100644 --- a/server/config/production.js +++ b/server/config/production.js @@ -3,7 +3,7 @@ const pckg = require('../../package.json'); const base = require('./base'); const execDir = path.dirname(process.execPath); -const dataDir = `${execDir}/.${pckg.name}/data`; +const dataDir = `${execDir}/.${pckg.name}`; module.exports = Object.assign({}, base, { branch: 'production', diff --git a/server/dev.js b/server/dev.js index 1ae2dfb..d652348 100644 --- a/server/dev.js +++ b/server/dev.js @@ -20,7 +20,7 @@ function webpackDevMiddleware(app) { function logQueries(app) { app.use(function(req, res, next) { const start = Date.now(); - log(`${req.method} ${req.originalUrl} ${JSON.stringify(req.body).substr(0, 4000)}`); + log(`${req.method} ${req.originalUrl} ${JSON.stringify(req.body ? req.body : '').substr(0, 4000)}`); //log(`${JSON.stringify(req.headers, null, 2)}`) res.once('finish', () => { log(`${Date.now() - start}ms`); diff --git a/server/index.js b/server/index.js index d9473e4..e02a7ef 100644 --- a/server/index.js +++ b/server/index.js @@ -105,6 +105,17 @@ async function main() { function initStatic(app, config) {// eslint-disable-line //загрузка файлов в /files //TODO + + app.use(express.static(config.publicDir, { + maxAge: '30d', + + /*setHeaders: (res, filePath) => { + if (path.dirname(filePath) == filesDir) { + res.set('Content-Type', 'application/xml'); + res.set('Content-Encoding', 'gzip'); + } + },*/ + })); } (async() => {