Some development, first databse connection established

This commit is contained in:
Jonatan Nilsson 2022-04-19 17:42:07 +00:00
parent 21545dcd6f
commit 0ab9521e7a
10 changed files with 272 additions and 29 deletions

View file

@ -54,6 +54,13 @@ nconf.defaults({
"frontend": { "frontend": {
"url": "http://beta01.nfp.moe" "url": "http://beta01.nfp.moe"
}, },
"mssql": {
"floor": 1,
"ceiling": 2,
"heartbeatSecs": 20,
"inactivityTimeoutSecs": 60,
"connectionString": "Driver={ODBC Driver 17 for SQL Server}; Server=localhost;UID=dev; PWD=dev; Database=nfp_moe",
},
"fileSize": 524288000, "fileSize": 524288000,
"upload": { "upload": {
"baseurl": "https://cdn.nfp.is", "baseurl": "https://cdn.nfp.is",

26
api/page/model.mjs Normal file
View file

@ -0,0 +1,26 @@
/*
Page model:
{
filename,
filetype,
small_image,
medium_image,
large_image,
*small_url,
*medium_url,
*large_url,
size,
staff_id,
is_deleted,
created_at,
updated_at,
}
*/
export async function getTree(ctx) {
let res = await ctx.db.promises.callProc('pages_gettree', [])
console.log(res)
return {}
}

16
api/page/routes.mjs Normal file
View file

@ -0,0 +1,16 @@
import * as Page from './model.mjs'
import * as security from './security.mjs'
export default class PageRoutes {
constructor(opts = {}) {
Object.assign(this, {
Page: opts.Page || Page,
security: opts.security || security,
})
}
/** GET: /api/pagetree */
async getPageTree(ctx) {
ctx.body = await this.Page.getTree(ctx)
}
}

0
api/page/security.mjs Normal file
View file

124
api/pagination/helpers.mjs Normal file
View file

@ -0,0 +1,124 @@
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
}

33
api/pagination/parser.mjs Normal file
View file

@ -0,0 +1,33 @@
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)
}
}
}
}

38
api/serve.mjs Normal file
View file

@ -0,0 +1,38 @@
import path from 'path'
import { FileResponse, HttpError } from 'flaska'
import fs from 'fs/promises'
export default class ServeHandler {
constructor(opts = {}) {
Object.assign(this, {
fs: opts.fs || fs,
root: opts.root,
})
}
/** GET: /::file */
serve(ctx) {
if (ctx.params.file.startsWith('api/')) {
throw new HttpError(404, 'Not Found: ' + ctx.params.file, { status: 404, message: 'Not Found: ' + ctx.params.file })
}
let file = path.resolve(path.join(this.root, ctx.params.file ? ctx.params.file : 'index.html'))
if (!file.startsWith(this.root)) {
ctx.status = 404
ctx.body = 'HTTP 404 Error'
return
}
return this.fs.stat(file).catch((err) => {
if (err.code === 'ENOENT') {
file = path.resolve(path.join(this.root, 'index.html'))
return this.fs.stat(file)
}
return Promise.reject(err)
})
.then(function(stat) {
ctx.body = new FileResponse(file, stat)
})
}
}

View file

@ -1,16 +1,16 @@
import path from 'path' import { Flaska, QueryHandler } from 'flaska'
import fs from 'fs/promises' import MSSQL from 'msnodesqlv8'
import { Flaska, FileResponse, HttpError, QueryHandler } from 'flaska'
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 ParserMiddleware from './parser/middleware.mjs' import ParserMiddleware from './pagination/parser.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)
const staticRoot = localUtil.getPathFromRoot('../public')
// Create our server
const flaska = new Flaska({ const flaska = new Flaska({
log: core.log, log: core.log,
nonce: ['script-src'], nonce: ['script-src'],
@ -25,6 +25,22 @@ export function run(http, port, core) {
}, },
}, http) }, http)
// Create our database pool
const pool = new MSSQL.Pool(config.get('mssql'))
core.log.info(config.get('mssql'), 'MSSQL database setttings')
pool.on('open', function() {
core.log.info('MSSQL connection open')
})
pool.on('error', function(error) {
core.log.error(error, 'Error in MSSQL pool')
})
pool.open()
// configure our server
if (config.get('NODE_ENV') === 'development') { if (config.get('NODE_ENV') === 'development') {
flaska.devMode() flaska.devMode()
} }
@ -33,10 +49,12 @@ export function run(http, port, core) {
flaska.before(function(ctx) { flaska.before(function(ctx) {
ctx.state.started = new Date().getTime() ctx.state.started = new Date().getTime()
ctx.db = pool
}) })
flaska.before(QueryHandler()) flaska.before(QueryHandler())
flaska.before(parser.contextParser()) flaska.before(parser.contextParser())
//
flaska.after(function(ctx) { 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
@ -67,30 +85,10 @@ export function run(http, port, core) {
// flaska.get('/api/articles/public/:id', article.getPublicSingleArticle.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('/api/pages/:pageId/articles/public', article.getPublicAllPageArticles.bind(article))
flaska.get('/::file', function(ctx) { const serve = new ServeHandler({
if (ctx.params.file.startsWith('api/')) { root: localUtil.getPathFromRoot('../public'),
throw new HttpError(404, 'Not Found: ' + ctx.params.file, { status: 404, message: 'Not Found: ' + ctx.params.file })
}
let file = path.resolve(path.join(staticRoot, ctx.params.file ? ctx.params.file : 'index.html'))
if (!file.startsWith(staticRoot)) {
ctx.status = 404
ctx.body = 'HTTP 404 Error'
return
}
return fs.stat(file).catch(function(err) {
if (err.code === 'ENOENT') {
file = path.resolve(path.join(staticRoot, 'index.html'))
return fs.stat(file)
}
return Promise.reject(err)
})
.then(function(stat) {
ctx.body = new FileResponse(file, stat)
})
}) })
flaska.get('/::file', serve.serve.bind(serve))
return flaska.listenAsync(port).then(function() { return flaska.listenAsync(port).then(function() {
core.log.info('Server is listening on port ' + port) core.log.info('Server is listening on port ' + port)

View file

@ -3,7 +3,7 @@ import config from './api/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_flaska.mjs') return import('./api/server.mjs')
.then(function(server) { .then(function(server) {
return server.run(http, port, ctx) return server.run(http, port, ctx)
}) })

View file

@ -55,6 +55,7 @@
"http-errors": "^1.7.2", "http-errors": "^1.7.2",
"json-mask": "^0.3.8", "json-mask": "^0.3.8",
"knex-core": "^0.19.5", "knex-core": "^0.19.5",
"msnodesqlv8": "^2.4.7",
"nconf-lite": "^1.0.1", "nconf-lite": "^1.0.1",
"parse-torrent": "^7.0.1", "parse-torrent": "^7.0.1",
"pg": "^8.7.3", "pg": "^8.7.3",