More development
This commit is contained in:
parent
f35c195680
commit
a2b1fc9bc8
13 changed files with 114 additions and 208 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -59,6 +59,7 @@ typings/
|
||||||
|
|
||||||
# Local development config file
|
# Local development config file
|
||||||
config/config.json
|
config/config.json
|
||||||
|
config.json
|
||||||
package-lock.json
|
package-lock.json
|
||||||
public/assets/app.js
|
public/assets/app.js
|
||||||
public/assets/app.css
|
public/assets/app.css
|
||||||
|
|
|
@ -149,122 +149,6 @@ Article.prototype = createPrototype({
|
||||||
is_featured: false,
|
is_featured: false,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/*parent() {
|
|
||||||
return this.belongsTo(Page, 'parent_id')
|
|
||||||
},
|
|
||||||
|
|
||||||
banner() {
|
|
||||||
return this.belongsTo(Media, 'banner_id')
|
|
||||||
},
|
|
||||||
|
|
||||||
media() {
|
|
||||||
return this.belongsTo(Media, 'media_id')
|
|
||||||
},
|
|
||||||
|
|
||||||
staff() {
|
|
||||||
return this.belongsTo(Staff, 'staff_id')
|
|
||||||
},
|
|
||||||
|
|
||||||
files() {
|
|
||||||
return this.hasManyFiltered(File, 'file', 'article_id')
|
|
||||||
.query(qb => {
|
|
||||||
qb.orderBy('id', 'asc')
|
|
||||||
})
|
|
||||||
},*/
|
|
||||||
|
|
||||||
/*getAll(ctx, where = {}, withRelated = [], orderBy = 'id', limitToday = false) {
|
|
||||||
return this.query(qb => {
|
|
||||||
this.baseQueryAll(ctx, qb, where, orderBy)
|
|
||||||
if (limitToday) {
|
|
||||||
qb.where('published_at', '<=', (new Date()).toISOString())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.fetchPage({
|
|
||||||
pageSize: ctx.state.pagination.perPage,
|
|
||||||
page: ctx.state.pagination.page,
|
|
||||||
withRelated,
|
|
||||||
ctx: ctx,
|
|
||||||
})
|
|
||||||
.then(result => {
|
|
||||||
ctx.state.pagination.total = result.pagination.rowCount
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
getSingle(id, withRelated = [], require = true, ctx = null, limitToday = false) {
|
|
||||||
return this.query(qb => {
|
|
||||||
qb.where(subq => {
|
|
||||||
subq.where({ id: Number(id) || 0 })
|
|
||||||
.orWhere({ path: id })
|
|
||||||
})
|
|
||||||
if (limitToday && (!ctx || !ctx.state.user || ctx.state.user.level < 10)) {
|
|
||||||
qb.where('published_at', '<=', (new Date()).toISOString())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.fetch({ require, withRelated, ctx })
|
|
||||||
},
|
|
||||||
|
|
||||||
async getFeatured(withRelated = [], ctx = null) {
|
|
||||||
let data = await this.query(qb => {
|
|
||||||
qb.where({ is_featured: true })
|
|
||||||
.where('published_at', '<=', (new Date()).toISOString())
|
|
||||||
})
|
|
||||||
.fetch({ require: false, withRelated, ctx })
|
|
||||||
if (!data) {
|
|
||||||
data = await this.query(qb => {
|
|
||||||
qb.where('published_at', '<=', (new Date()).toISOString())
|
|
||||||
.whereNotNull('banner_id')
|
|
||||||
})
|
|
||||||
.fetch({ require: false, withRelated, ctx })
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
|
|
||||||
getAllFromPage(ctx, pageId, withRelated = [], orderBy = 'id', limitToday = false) {
|
|
||||||
return this.query(qb => {
|
|
||||||
this.baseQueryAll(ctx, qb, {}, orderBy)
|
|
||||||
qb.leftOuterJoin('pages', 'articles.parent_id', 'pages.id')
|
|
||||||
qb.where(subq => {
|
|
||||||
subq.where('pages.id', pageId)
|
|
||||||
.orWhere('pages.parent_id', pageId)
|
|
||||||
})
|
|
||||||
if (limitToday) {
|
|
||||||
qb.where('published_at', '<=', (new Date()).toISOString())
|
|
||||||
}
|
|
||||||
qb.select('articles.*')
|
|
||||||
})
|
|
||||||
.fetchPage({
|
|
||||||
pageSize: ctx.state.pagination.perPage,
|
|
||||||
page: ctx.state.pagination.page,
|
|
||||||
withRelated,
|
|
||||||
ctx: ctx,
|
|
||||||
})
|
|
||||||
.then(result => {
|
|
||||||
ctx.state.pagination.total = result.pagination.rowCount
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
setAllUnfeatured() {
|
|
||||||
return bookshelf.knex('articles')
|
|
||||||
.where({ is_featured: true })
|
|
||||||
.update({
|
|
||||||
is_featured: false,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
getFrontpageArticles(page = 1) {
|
|
||||||
return this.query(qb => {
|
|
||||||
qb.orderBy('published_at', 'DESC')
|
|
||||||
.where('published_at', '<=', (new Date()).toISOString())
|
|
||||||
})
|
|
||||||
.fetchPage({
|
|
||||||
pageSize: 10,
|
|
||||||
page: page,
|
|
||||||
withRelated: ['files', 'media', 'banner', 'parent', 'staff'],
|
|
||||||
})
|
|
||||||
},*/
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const articleInstance = new Article()
|
const articleInstance = new Article()
|
||||||
|
|
|
@ -46,45 +46,44 @@ nconf.env({
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// Load package.json for name and such
|
// Load empty overrides that can be overwritten later
|
||||||
let pckg = JSON.parse(readFileSync('./package.json'))
|
nconf.overrides({})
|
||||||
|
|
||||||
pckg = _.pick(pckg, ['name', 'version', 'description', 'author', 'license', 'homepage'])
|
nconf.defaults({
|
||||||
|
"NODE_ENV": "development",
|
||||||
if (nconf.get('DATABASE_URL')) {
|
"knex": {
|
||||||
pckg.knex = { connection: nconf.get('DATABASE_URL') }
|
"client": "pg",
|
||||||
}
|
"connection": {
|
||||||
|
"host" : "127.0.0.1",
|
||||||
// Load overrides as second priority
|
"user" : "postgres",
|
||||||
nconf.overrides(pckg)
|
"password" : "postgres",
|
||||||
|
"database" : "nfpmoe"
|
||||||
|
},
|
||||||
// Load any overrides from the appropriate config file
|
"connectionslave": null,
|
||||||
let configFile = 'config/config.json'
|
"migrations": {
|
||||||
|
},
|
||||||
/* istanbul ignore else */
|
"acquireConnectionTimeout": 10000
|
||||||
if (nconf.get('NODE_ENV') === 'test') {
|
},
|
||||||
configFile = 'config/config.test.json'
|
"frontend": {
|
||||||
}
|
"url": "http://beta01.nfp.moe"
|
||||||
|
},
|
||||||
/* istanbul ignore if */
|
"jwt": {
|
||||||
if (nconf.get('NODE_ENV') === 'production') {
|
"secret": "this-is-my-secret",
|
||||||
configFile = 'config/config.production.json'
|
"options": {
|
||||||
}
|
"expiresIn": 604800
|
||||||
|
}
|
||||||
nconf.file('main', configFile)
|
},
|
||||||
|
"sessionsecret": "this-is-session-secret-lol",
|
||||||
// Load defaults
|
"bcrypt": 5,
|
||||||
nconf.file('default', 'config/config.default.json')
|
"fileSize": 524288000,
|
||||||
|
"upload": {
|
||||||
|
"baseurl": "https://cdn.nfp.is",
|
||||||
// Final sanity checks
|
"port": "2111",
|
||||||
/* istanbul ignore if */
|
"host": "storage01.nfp.is",
|
||||||
if (typeof global.it === 'function' & !nconf.inTest()) {
|
"name": "nfpmoe-dev",
|
||||||
// eslint-disable-next-line no-console
|
"secret": "nfpmoe-dev"
|
||||||
console.log('Critical: potentially running test on production enviroment. Shutting down.')
|
}
|
||||||
process.exit(1)
|
})
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default nconf
|
export default nconf
|
||||||
|
|
15
api/knex.mjs
15
api/knex.mjs
|
@ -3,7 +3,6 @@ import knexCore from 'knex-core'
|
||||||
|
|
||||||
import config from './config.mjs'
|
import config from './config.mjs'
|
||||||
import defaults from './defaults.mjs'
|
import defaults from './defaults.mjs'
|
||||||
import log from './log.mjs'
|
|
||||||
|
|
||||||
const knex = knexCore(config.get('knex'))
|
const knex = knexCore(config.get('knex'))
|
||||||
|
|
||||||
|
@ -73,14 +72,24 @@ export function createPrototype(opts) {
|
||||||
},
|
},
|
||||||
|
|
||||||
async getAllQuery(query, queryContext = null) {
|
async getAllQuery(query, queryContext = null) {
|
||||||
|
console.log('1')
|
||||||
let context = (queryContext || query).queryContext()
|
let context = (queryContext || query).queryContext()
|
||||||
if (!context.tables) throw new Error('getAll was called before query')
|
if (!context.tables) throw new Error('getAll was called before query')
|
||||||
let tables = context.tables
|
let tables = context.tables
|
||||||
let tableMap = new Map(tables)
|
let tableMap = new Map(tables)
|
||||||
|
|
||||||
let data = await query
|
try {
|
||||||
|
console.log(query)
|
||||||
|
console.log(query.toString())
|
||||||
|
let data = await query
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
console.log('3')
|
||||||
|
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
|
console.log('e1')
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +185,8 @@ export function createPrototype(opts) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('2')
|
||||||
|
|
||||||
return out
|
return out
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
28
api/log.mjs
28
api/log.mjs
|
@ -1,28 +0,0 @@
|
||||||
import bunyan from 'bunyan-lite'
|
|
||||||
import config from './config.mjs'
|
|
||||||
import * as defaults from './defaults.mjs'
|
|
||||||
|
|
||||||
// Clone the settings as we will be touching
|
|
||||||
// on them slightly.
|
|
||||||
let settings = defaults.default(config.get('bunyan'))
|
|
||||||
|
|
||||||
// Replace any instance of 'process.stdout' with the
|
|
||||||
// actual reference to the process.stdout.
|
|
||||||
for (let i = 0; i < settings.streams.length; i++) {
|
|
||||||
/* istanbul ignore else */
|
|
||||||
if (settings.streams[i].stream === 'process.stdout') {
|
|
||||||
settings.streams[i].stream = process.stdout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create our logger.
|
|
||||||
const log = bunyan.createLogger(settings)
|
|
||||||
|
|
||||||
export default log
|
|
||||||
|
|
||||||
log.logMiddleware = () =>
|
|
||||||
(ctx, next) => {
|
|
||||||
ctx.log = log
|
|
||||||
|
|
||||||
return next()
|
|
||||||
}
|
|
|
@ -25,7 +25,7 @@ export default class PageRoutes {
|
||||||
async getSinglePage(ctx) {
|
async getSinglePage(ctx) {
|
||||||
await this.security.ensureIncludes(ctx)
|
await this.security.ensureIncludes(ctx)
|
||||||
|
|
||||||
ctx.body = await this.Page.getSingle(ctx.params.id, ctx.state.filter.includes, true, ctx)
|
ctx.body = await this.Page.getSingle(ctx.params.pageId, ctx.state.filter.includes, true, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** POST: /api/pages */
|
/** POST: /api/pages */
|
||||||
|
|
|
@ -11,11 +11,9 @@ export default class ParserMiddleware {
|
||||||
}
|
}
|
||||||
|
|
||||||
contextParser() {
|
contextParser() {
|
||||||
return (ctx, next) => {
|
return (ctx) => {
|
||||||
ctx.state.pagination = this.pagination.parsePagination(ctx)
|
ctx.state.pagination = this.pagination.parsePagination(ctx)
|
||||||
ctx.state.filter = this.pagination.parseFilter(ctx)
|
ctx.state.filter = this.pagination.parseFilter(ctx)
|
||||||
|
|
||||||
return next()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import { Flaska, FileResponse, HttpError } from 'flaska'
|
import { Flaska, FileResponse, HttpError, QueryHandler } from 'flaska'
|
||||||
|
|
||||||
|
import config from './config.mjs'
|
||||||
|
import PageRoutes from './page/routes.mjs'
|
||||||
|
import ArticleRoutes from './article/routes.mjs'
|
||||||
|
import ParserMiddleware from './parser/middleware.mjs'
|
||||||
|
|
||||||
export function run(http, port, core) {
|
export function run(http, port, core) {
|
||||||
let localUtil = new core.sc.Util(import.meta.url)
|
let localUtil = new core.sc.Util(import.meta.url)
|
||||||
|
@ -8,11 +13,29 @@ export function run(http, port, core) {
|
||||||
|
|
||||||
const flaska = new Flaska({
|
const flaska = new Flaska({
|
||||||
log: core.log,
|
log: core.log,
|
||||||
|
nonce: ['script-src'],
|
||||||
|
nonceCacheLength: 50,
|
||||||
|
defaultHeaders: {
|
||||||
|
'Server': 'Flaska',
|
||||||
|
'X-Content-Type-Options': 'nosniff',
|
||||||
|
'Content-Security-Policy': `default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:; object-src 'none'; frame-ancestors 'none'; script-src 'self'`,
|
||||||
|
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||||
|
'Cross-Origin-Resource-Policy': 'same-origin',
|
||||||
|
'Cross-Origin-Embedder-Policy': 'require-corp',
|
||||||
|
},
|
||||||
}, http)
|
}, http)
|
||||||
|
|
||||||
|
if (config.get('NODE_ENV') === 'development') {
|
||||||
|
flaska.devMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new ParserMiddleware()
|
||||||
|
|
||||||
flaska.before(function(ctx) {
|
flaska.before(function(ctx) {
|
||||||
ctx.state.started = new Date().getTime()
|
ctx.state.started = new Date().getTime()
|
||||||
})
|
})
|
||||||
|
flaska.before(QueryHandler())
|
||||||
|
flaska.before(parser.contextParser())
|
||||||
|
|
||||||
flaska.after(function(ctx) {
|
flaska.after(function(ctx) {
|
||||||
let ended = new Date().getTime()
|
let ended = new Date().getTime()
|
||||||
|
@ -33,6 +56,16 @@ 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()
|
||||||
|
flaska.get('/api/pagetree', page.getPageTree.bind(page))
|
||||||
|
flaska.get('/api/pages', page.getAllPages.bind(page))
|
||||||
|
flaska.get('/api/pages/:pageId', page.getSinglePage.bind(page))
|
||||||
|
|
||||||
|
const article = new ArticleRoutes()
|
||||||
|
flaska.get('/api/articles/public', article.getPublicAllArticles.bind(article))
|
||||||
|
flaska.get('/api/articles/public/:id', article.getPublicSingleArticle.bind(article))
|
||||||
|
flaska.get('/api/pages/:pageId/articles/public', article.getPublicAllPageArticles.bind(article))
|
||||||
|
|
||||||
flaska.get('/::file', function(ctx) {
|
flaska.get('/::file', function(ctx) {
|
||||||
if (ctx.params.file.startsWith('api/')) {
|
if (ctx.params.file.startsWith('api/')) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { createPrototype, safeColumns } from '../knex.mjs'
|
import { createPrototype, safeColumns } from '../knex.mjs'
|
||||||
import bcrypt from 'bcrypt'
|
// import bcrypt from 'bcrypt'
|
||||||
/*import config from '../config.mjs'*/
|
/*import config from '../config.mjs'*/
|
||||||
|
|
||||||
/* Staff model:
|
/* Staff model:
|
||||||
|
|
11
db.json
11
db.json
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"core": {
|
|
||||||
"version": 1,
|
|
||||||
"nfp_moe": {
|
|
||||||
"active": "static",
|
|
||||||
"latestInstalled": "",
|
|
||||||
"updater": "",
|
|
||||||
"versions": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
20
dev.mjs
20
dev.mjs
|
@ -1,10 +1,22 @@
|
||||||
|
import fs from 'fs'
|
||||||
import { ServiceCore } from 'service-core'
|
import { ServiceCore } from 'service-core'
|
||||||
import * as index from './index.mjs'
|
import * as index from './index.mjs'
|
||||||
|
|
||||||
var core = new ServiceCore('nfp_moe', import.meta.url)
|
var core = new ServiceCore('nfp_moe', import.meta.url, 4030, '')
|
||||||
core.setConfig({
|
|
||||||
port: 4030,
|
let config = {
|
||||||
})
|
frontend: {
|
||||||
|
url: 'http://localhost:4030'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
config = JSON.parse(fs.readFileSync('./config.json'))
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
config.port = 4030
|
||||||
|
|
||||||
|
core.setConfig(config)
|
||||||
core.init(index).then(function() {
|
core.init(index).then(function() {
|
||||||
return core.run()
|
return core.run()
|
||||||
})
|
})
|
|
@ -1,7 +1,12 @@
|
||||||
import { run } from "./api/server_flaska.mjs"
|
import config from './api/config.mjs'
|
||||||
|
|
||||||
export function start(http, port, ctx) {
|
export function start(http, port, ctx) {
|
||||||
return run(http, port, ctx)
|
config.stores.overrides.store = ctx.config
|
||||||
|
|
||||||
|
return import('./api/server_flaska.mjs')
|
||||||
|
.then(function(server) {
|
||||||
|
return server.run(http, port, ctx)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -9,7 +9,9 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node --experimental-modules index.mjs",
|
"start": "node --experimental-modules index.mjs",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"build": "asbundle app/index.js public/assets/app.js && asbundle app/admin.js public/assets/admin.js",
|
"bla": "sass --help",
|
||||||
|
"build:prod": "sass -s compressed app/app.scss public/assets/app.css && sass -s compressed app/admin.scss public/assets/admin.css && asbundle app/index.js public/assets/app.js && asbundle app/admin.js public/assets/admin.js",
|
||||||
|
"build": "sass app/app.scss public/assets/app.css && sass app/admin.scss public/assets/admin.css && asbundle app/index.js public/assets/app.js && asbundle app/admin.js public/assets/admin.js",
|
||||||
"build:watch": "npm-watch build",
|
"build:watch": "npm-watch build",
|
||||||
"dev:server": "node dev.mjs | bunyan",
|
"dev:server": "node dev.mjs | bunyan",
|
||||||
"dev:server:watch": "npm-watch dev:server",
|
"dev:server:watch": "npm-watch dev:server",
|
||||||
|
|
Loading…
Reference in a new issue