Fixed restructuring, removed some legacy shit
This commit is contained in:
parent
4014783dbf
commit
eb9ffde724
16 changed files with 157 additions and 293 deletions
1
base/.npmrc
Normal file
1
base/.npmrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package-lock=false
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { FormidableHandler } from 'flaska'
|
||||||
import { parseFiles } from '../file/util.mjs'
|
import { parseFiles } from '../file/util.mjs'
|
||||||
import { parseArticles, parseArticle } from './util.mjs'
|
import { parseArticles, parseArticle } from './util.mjs'
|
||||||
import { upload } from '../media/upload.mjs'
|
import { upload } from '../media/upload.mjs'
|
||||||
|
@ -10,6 +11,17 @@ export default class ArticleRoutes {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register(server) {
|
||||||
|
server.flaska.get('/api/articles/:path', this.getArticle.bind(this))
|
||||||
|
server.flaska.get('/api/auth/articles', server.authenticate(), this.auth_getAllArticles.bind(this))
|
||||||
|
server.flaska.get('/api/auth/articles/:id', server.authenticate(), this.auth_getSingleArticle.bind(this))
|
||||||
|
server.flaska.put('/api/auth/articles/:id', [
|
||||||
|
server.authenticate(),
|
||||||
|
server.formidable({ maxFileSize: 20 * 1024 * 1024, }),
|
||||||
|
], this.auth_updateCreateSingleArticle.bind(this))
|
||||||
|
server.flaska.delete('/api/auth/articles/:id', server.authenticate(), this.auth_removeSingleArticle.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
/** GET: /api/articles/[path] */
|
/** GET: /api/articles/[path] */
|
||||||
async getArticle(ctx) {
|
async getArticle(ctx) {
|
||||||
let res = await ctx.db.safeCallProc('article_get_single', [ctx.params.path])
|
let res = await ctx.db.safeCallProc('article_get_single', [ctx.params.path])
|
||||||
|
|
|
@ -11,6 +11,10 @@ export default class AuthenticationRoutes {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register(server) {
|
||||||
|
server.flaska.post('/api/authentication/login', server.jsonHandler(), this.login.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
/** GET: /api/authentication/login */
|
/** GET: /api/authentication/login */
|
||||||
async login(ctx) {
|
async login(ctx) {
|
||||||
let res = await ctx.db.safeCallProc('auth_login', [
|
let res = await ctx.db.safeCallProc('auth_login', [
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import nconf from 'nconf-lite'
|
import nconf from 'nconf-lite'
|
||||||
import { readFileSync } from 'fs'
|
|
||||||
|
|
||||||
// Helper method for global usage.
|
// Helper method for global usage.
|
||||||
nconf.inTest = () => nconf.get('NODE_ENV') === 'test'
|
nconf.inTest = () => nconf.get('NODE_ENV') === 'test'
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
|
|
||||||
// taken from isobject npm library
|
|
||||||
function isObject(val) {
|
|
||||||
return val != null && typeof val === 'object' && Array.isArray(val) === false
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function defaults(options, def) {
|
|
||||||
let out = { }
|
|
||||||
|
|
||||||
if (options) {
|
|
||||||
Object.keys(options || {}).forEach(key => {
|
|
||||||
out[key] = options[key]
|
|
||||||
|
|
||||||
if (Array.isArray(out[key])) {
|
|
||||||
out[key] = out[key].map(item => {
|
|
||||||
if (isObject(item)) return defaults(item)
|
|
||||||
return item
|
|
||||||
})
|
|
||||||
} else if (out[key] && typeof out[key] === 'object') {
|
|
||||||
out[key] = defaults(options[key], def && def[key])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (def) {
|
|
||||||
Object.keys(def).forEach(function(key) {
|
|
||||||
if (typeof out[key] === 'undefined') {
|
|
||||||
out[key] = def[key]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
9
base/package.json
Normal file
9
base/package.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"dot": "^2.0.0-beta.1",
|
||||||
|
"flaska": "^1.3.0",
|
||||||
|
"formidable": "^1.2.6",
|
||||||
|
"msnodesqlv8": "^2.4.7",
|
||||||
|
"nconf-lite": "^1.0.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,19 @@ export default class PageRoutes {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register(server) {
|
||||||
|
server.flaska.get('/api/pagetree', this.getPageTree.bind(this))
|
||||||
|
server.flaska.get('/api/frontpage', this.getPage.bind(this))
|
||||||
|
server.flaska.get('/api/pages/:path', this.getPage.bind(this))
|
||||||
|
server.flaska.get('/api/auth/pages', server.authenticate(), this.auth_getAllPages.bind(this))
|
||||||
|
server.flaska.get('/api/auth/pages/:id', server.authenticate(), this.auth_getSinglePage.bind(this))
|
||||||
|
server.flaska.put('/api/auth/pages/:id', [
|
||||||
|
server.authenticate(),
|
||||||
|
server.formidable({ maxFileSize: 20 * 1024 * 1024, }),
|
||||||
|
], this.auth_updateCreateSinglePage.bind(this))
|
||||||
|
server.flaska.delete('/api/auth/pages/:id', server.authenticate(), this.auth_removeSinglePage.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
/** GET: /api/pagetree */
|
/** GET: /api/pagetree */
|
||||||
async getPageTree(ctx, onlyReturn = false) {
|
async getPageTree(ctx, onlyReturn = false) {
|
||||||
let res = await ctx.db.safeCallProc('pages_get_tree', [])
|
let res = await ctx.db.safeCallProc('pages_get_tree', [])
|
||||||
|
|
|
@ -1,124 +0,0 @@
|
||||||
import _ from 'lodash'
|
|
||||||
import config from '../config.mjs'
|
|
||||||
|
|
||||||
function limit(value, min, max, fallback) {
|
|
||||||
let out = parseInt(value, 10)
|
|
||||||
|
|
||||||
if (!out) {
|
|
||||||
out = fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out < min) {
|
|
||||||
out = min
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out > max) {
|
|
||||||
out = max
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parsePagination(ctx) {
|
|
||||||
let out = {
|
|
||||||
perPage: limit(ctx.query.perPage, 1, 1500, 1250),
|
|
||||||
page: limit(ctx.query.page, 1, Number.MAX_SAFE_INTEGER, 1),
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.keys(ctx.query).forEach(item => {
|
|
||||||
if (item.startsWith('perPage.')) {
|
|
||||||
let name = item.substring(8)
|
|
||||||
out[name] = {
|
|
||||||
perPage: limit(ctx.query[`perPage.${name}`], 1, 1500, 1250),
|
|
||||||
page: limit(ctx.query[`page.${name}`], 1, Number.MAX_SAFE_INTEGER, 1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parseFilter(ctx) {
|
|
||||||
let where
|
|
||||||
let whereNot
|
|
||||||
|
|
||||||
where = _.omitBy(ctx.query, test => test[0] === '!')
|
|
||||||
|
|
||||||
whereNot = _.pickBy(ctx.query, test => test[0] === '!')
|
|
||||||
whereNot = _.transform(
|
|
||||||
whereNot,
|
|
||||||
(result, value, key) => (result[key] = value.slice(1))
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
where: pick => _.pick(where, pick),
|
|
||||||
whereNot: pick => _.pick(whereNot, pick),
|
|
||||||
includes: (ctx.query.includes && ctx.query.includes.split(',')) || [],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generateLinks(ctx, total) {
|
|
||||||
let out = []
|
|
||||||
|
|
||||||
let base = _(ctx.query)
|
|
||||||
.omit(['page'])
|
|
||||||
.transform((res, val, key) => res.push(`${key}=${val}`), [])
|
|
||||||
.value()
|
|
||||||
|
|
||||||
if (!ctx.query.perPage) {
|
|
||||||
base.push(`perPage=${ctx.state.pagination.perPage}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// let protocol = ctx.protocol
|
|
||||||
|
|
||||||
// if (config.get('frontend:url').startsWith('https')) {
|
|
||||||
// protocol = 'https'
|
|
||||||
// }
|
|
||||||
|
|
||||||
let proto = 'http'
|
|
||||||
|
|
||||||
if (config.get('frontend:url').startsWith('https')) {
|
|
||||||
proto = 'https'
|
|
||||||
}
|
|
||||||
|
|
||||||
let first = new URL(ctx.path, proto + '://' + ctx.host).toString()
|
|
||||||
|
|
||||||
first += `?${base.join('&')}`
|
|
||||||
|
|
||||||
// Add the current page first
|
|
||||||
out.push({
|
|
||||||
rel: 'current',
|
|
||||||
title: `Page ${ctx.query.page || 1}`,
|
|
||||||
url: `${first}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Then add any previous pages if we can
|
|
||||||
if (ctx.state.pagination.page > 1) {
|
|
||||||
out.push({
|
|
||||||
rel: 'previous',
|
|
||||||
title: 'Previous',
|
|
||||||
url: `${first}&page=${ctx.state.pagination.page - 1}`,
|
|
||||||
})
|
|
||||||
out.push({
|
|
||||||
rel: 'first',
|
|
||||||
title: 'First',
|
|
||||||
url: `${first}&page=1`,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then add any next pages if we can
|
|
||||||
if ((ctx.state.pagination.perPage * (ctx.state.pagination.page - 1)) + ctx.state.pagination.perPage < total) {
|
|
||||||
out.push({
|
|
||||||
rel: 'next',
|
|
||||||
title: 'Next',
|
|
||||||
url: `${first}&page=${ctx.state.pagination.page + 1}`,
|
|
||||||
})
|
|
||||||
out.push({
|
|
||||||
rel: 'last',
|
|
||||||
title: 'Last',
|
|
||||||
url: `${first}&page=${Math.ceil(total / ctx.state.pagination.perPage)}`,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
import format from 'format-link-header'
|
|
||||||
|
|
||||||
import * as pagination from './helpers.mjs'
|
|
||||||
|
|
||||||
export default class ParserMiddleware {
|
|
||||||
constructor(opts = {}) {
|
|
||||||
Object.assign(this, {
|
|
||||||
pagination: opts.pagination || pagination,
|
|
||||||
format: opts.format || format,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
contextParser() {
|
|
||||||
return (ctx) => {
|
|
||||||
ctx.state.pagination = this.pagination.parsePagination(ctx)
|
|
||||||
ctx.state.filter = this.pagination.parseFilter(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
generateLinks() {
|
|
||||||
return async (ctx, next) => {
|
|
||||||
await next()
|
|
||||||
|
|
||||||
if (ctx.state.pagination.total > 0) {
|
|
||||||
ctx.set('Link', this.format(this.pagination.generateLinks(ctx, ctx.state.pagination.total)))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.state.pagination.total != null) {
|
|
||||||
ctx.set('pagination_total', ctx.state.pagination.total)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -21,6 +21,10 @@ export default class ServeHandler {
|
||||||
// console.log(indexFile.toString())
|
// console.log(indexFile.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register(server) {
|
||||||
|
server.flaska.get('/::file', this.serve.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
/** GET: /::file */
|
/** GET: /::file */
|
||||||
serve(ctx) {
|
serve(ctx) {
|
||||||
if (ctx.params.file.startsWith('api/')) {
|
if (ctx.params.file.startsWith('api/')) {
|
||||||
|
|
101
base/server.mjs
101
base/server.mjs
|
@ -4,39 +4,65 @@ import formidable from 'formidable'
|
||||||
import { initPool } from './db.mjs'
|
import { initPool } from './db.mjs'
|
||||||
import config from './config.mjs'
|
import config from './config.mjs'
|
||||||
import PageRoutes from './page/routes.mjs'
|
import PageRoutes from './page/routes.mjs'
|
||||||
import ServeHandler from './serve.mjs'
|
|
||||||
import ArticleRoutes from './article/routes.mjs'
|
import ArticleRoutes from './article/routes.mjs'
|
||||||
import AuthenticationRoutes from './authentication/routes.mjs'
|
import AuthenticationRoutes from './authentication/routes.mjs'
|
||||||
import { authenticate } from './authentication/security.mjs'
|
import { authenticate } from './authentication/security.mjs'
|
||||||
|
|
||||||
export function run(http, port, core) {
|
export default class Server {
|
||||||
let localUtil = new core.sc.Util(import.meta.url)
|
constructor(http, port, core, opts = {}) {
|
||||||
|
Object.assign(this, opts)
|
||||||
|
this.http = http
|
||||||
|
this.port = port
|
||||||
|
this.core = core
|
||||||
|
|
||||||
|
this.authenticate = authenticate
|
||||||
|
this.formidable = FormidableHandler.bind(this, formidable)
|
||||||
|
this.jsonHandler = JsonHandler
|
||||||
|
this.routes = [
|
||||||
|
new PageRoutes(),
|
||||||
|
new ArticleRoutes(),
|
||||||
|
new AuthenticationRoutes(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
getRouteInstance(type) {
|
||||||
|
for (let route of this.routes) {
|
||||||
|
if (route instanceof type) {
|
||||||
|
return route
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addCustomRoutes() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
runCreateServer() {
|
||||||
// Create our server
|
// Create our server
|
||||||
const flaska = new Flaska({
|
this.flaska = new Flaska({
|
||||||
appendHeaders: {
|
appendHeaders: {
|
||||||
'Content-Security-Policy': `default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'`,
|
'Content-Security-Policy': `default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'`,
|
||||||
},
|
},
|
||||||
log: core.log,
|
log: this.core.log,
|
||||||
nonce: ['script-src'],
|
nonce: ['script-src'],
|
||||||
nonceCacheLength: 50,
|
nonceCacheLength: 50,
|
||||||
}, http)
|
}, this.http)
|
||||||
|
|
||||||
// Create our database pool
|
// Create our database pool
|
||||||
let pool = initPool(core, config.get('mssql'))
|
let pool = this.runCreateDatabase()
|
||||||
|
|
||||||
// configure our server
|
// configure our server
|
||||||
if (config.get('NODE_ENV') === 'development') {
|
if (config.get('NODE_ENV') === 'development') {
|
||||||
flaska.devMode()
|
this.flaska.devMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
flaska.before(function(ctx) {
|
this.flaska.before(function(ctx) {
|
||||||
ctx.state.started = new Date().getTime()
|
ctx.state.started = new Date().getTime()
|
||||||
ctx.db = pool
|
ctx.db = pool
|
||||||
})
|
})
|
||||||
flaska.before(QueryHandler())
|
this.flaska.before(QueryHandler())
|
||||||
|
|
||||||
flaska.after(function(ctx) {
|
this.flaska.after(function(ctx) {
|
||||||
let ended = new Date().getTime()
|
let ended = new Date().getTime()
|
||||||
var requestTime = ended - ctx.state.started
|
var requestTime = ended - ctx.state.started
|
||||||
|
|
||||||
|
@ -55,42 +81,29 @@ export function run(http, port, core) {
|
||||||
status: ctx.status,
|
status: ctx.status,
|
||||||
}, `<-- ${status}${ctx.method} ${ctx.url}`)
|
}, `<-- ${status}${ctx.method} ${ctx.url}`)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const page = new PageRoutes()
|
runRegisterRoutes() {
|
||||||
flaska.get('/api/pagetree', page.getPageTree.bind(page))
|
for (let route of this.routes) {
|
||||||
flaska.get('/api/frontpage', page.getPage.bind(page))
|
route.register(this)
|
||||||
flaska.get('/api/pages/:path', page.getPage.bind(page))
|
}
|
||||||
flaska.get('/api/auth/pages', authenticate(), page.auth_getAllPages.bind(page))
|
}
|
||||||
flaska.get('/api/auth/pages/:id', authenticate(), page.auth_getSinglePage.bind(page))
|
|
||||||
flaska.put('/api/auth/pages/:id', [
|
|
||||||
authenticate(),
|
|
||||||
FormidableHandler(formidable, { maxFileSize: 20 * 1024 * 1024, }),
|
|
||||||
], page.auth_updateCreateSinglePage.bind(page))
|
|
||||||
flaska.delete('/api/auth/pages/:id', authenticate(), page.auth_removeSinglePage.bind(page))
|
|
||||||
|
|
||||||
|
runCreateDatabase() {
|
||||||
|
return initPool(this.core, config.get('mssql'))
|
||||||
|
}
|
||||||
|
|
||||||
const article = new ArticleRoutes()
|
runStartListen() {
|
||||||
flaska.get('/api/articles/:path', article.getArticle.bind(article))
|
return this.flaska.listenAsync(this.port).then(() => {
|
||||||
flaska.get('/api/auth/articles', authenticate(), article.auth_getAllArticles.bind(article))
|
this.core.log.info('Server is listening on port ' + this.port)
|
||||||
flaska.get('/api/auth/articles/:id', authenticate(), article.auth_getSingleArticle.bind(article))
|
|
||||||
flaska.put('/api/auth/articles/:id', [
|
|
||||||
authenticate(),
|
|
||||||
FormidableHandler(formidable, { maxFileSize: 20 * 1024 * 1024, }),
|
|
||||||
], article.auth_updateCreateSingleArticle.bind(article))
|
|
||||||
flaska.delete('/api/auth/articles/:id', authenticate(), article.auth_removeSingleArticle.bind(article))
|
|
||||||
|
|
||||||
const authentication = new AuthenticationRoutes()
|
|
||||||
flaska.post('/api/authentication/login', JsonHandler(), authentication.login.bind(authentication))
|
|
||||||
|
|
||||||
const serve = new ServeHandler({
|
|
||||||
pageRoutes: page,
|
|
||||||
root: localUtil.getPathFromRoot('../public'),
|
|
||||||
version: core.app.running,
|
|
||||||
frontend: config.get('frontend:url'),
|
|
||||||
})
|
})
|
||||||
flaska.get('/::file', serve.serve.bind(serve))
|
}
|
||||||
|
|
||||||
return flaska.listenAsync(port).then(function() {
|
run() {
|
||||||
core.log.info('Server is listening on port ' + port)
|
this.addCustomRoutes()
|
||||||
})
|
this.runCreateServer()
|
||||||
|
this.runRegisterRoutes()
|
||||||
|
|
||||||
|
return this.runStartListen()
|
||||||
|
}
|
||||||
}
|
}
|
1
nfp_moe/.npmrc
Normal file
1
nfp_moe/.npmrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package-lock=false
|
18
nfp_moe/api/server.mjs
Normal file
18
nfp_moe/api/server.mjs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import config from '../base/config.mjs'
|
||||||
|
import Parent from '../base/server.mjs'
|
||||||
|
import ServeHandler from '../base/serve.mjs'
|
||||||
|
import PageRoutes from '../base/page/routes.mjs'
|
||||||
|
|
||||||
|
export default class Server extends Parent {
|
||||||
|
addCustomRoutes() {
|
||||||
|
let page = this.getRouteInstance(PageRoutes)
|
||||||
|
|
||||||
|
let localUtil = new this.core.sc.Util(import.meta.url)
|
||||||
|
this.routes.push(new ServeHandler({
|
||||||
|
pageRoutes: page,
|
||||||
|
root: localUtil.getPathFromRoot('../public'),
|
||||||
|
version: this.core.app.running,
|
||||||
|
frontend: config.get('frontend:url'),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
1
nfp_moe/base
Symbolic link
1
nfp_moe/base
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../base
|
|
@ -1,32 +1,11 @@
|
||||||
import config from './api/config.mjs'
|
import config from '../base/config.mjs'
|
||||||
|
|
||||||
export function start(http, port, ctx) {
|
export function start(http, port, ctx) {
|
||||||
config.stores.overrides.store = ctx.config
|
config.stores.overrides.store = ctx.config
|
||||||
|
|
||||||
return import('./api/server.mjs')
|
return import('./api/server.mjs')
|
||||||
.then(function(server) {
|
.then(function(module) {
|
||||||
return server.run(http, port, ctx)
|
let server = new module.default(http, port, ctx)
|
||||||
|
return server.run()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
import log from './api/log.mjs'
|
|
||||||
|
|
||||||
// Run the database script automatically.
|
|
||||||
import setup from './api/setup.mjs'
|
|
||||||
|
|
||||||
setup().catch(async (error) => {
|
|
||||||
log.error({ code: error.code, message: error.message }, 'Error while preparing database')
|
|
||||||
log.error('Unable to verify database integrity.')
|
|
||||||
log.warn('Continuing anyways')
|
|
||||||
// import('./api/config').then(module => {
|
|
||||||
// log.error(error, 'Error while preparing database')
|
|
||||||
// log.error({ config: module.default.get() }, 'config used')
|
|
||||||
// process.exit(1)
|
|
||||||
// })
|
|
||||||
}).then(() =>
|
|
||||||
import('./api/server.mjs')
|
|
||||||
).catch(error => {
|
|
||||||
log.error(error, 'Unknown error starting server')
|
|
||||||
})
|
|
||||||
*/
|
|
|
@ -22,7 +22,8 @@
|
||||||
"watch": {
|
"watch": {
|
||||||
"dev:server": {
|
"dev:server": {
|
||||||
"patterns": [
|
"patterns": [
|
||||||
"api/*"
|
"api/*",
|
||||||
|
"base/*"
|
||||||
],
|
],
|
||||||
"extensions": "js,mjs",
|
"extensions": "js,mjs",
|
||||||
"quiet": true,
|
"quiet": true,
|
||||||
|
|
Loading…
Reference in a new issue