Big Optimisations and such
This commit is contained in:
parent
4f529e22dd
commit
eaf2edd6c3
46 changed files with 842 additions and 395 deletions
|
@ -16,6 +16,9 @@ jobs:
|
|||
command: apk update && apk upgrade && apk add --no-cache bash git openssh
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- run:
|
||||
name: Replace version in config
|
||||
command: sed -i .org "s/circleci_version_number/${CIRCLE_BUILD_NUM}/g" config/config.default.json
|
||||
- run:
|
||||
name: Build docker image
|
||||
command: docker build -t ${di}:build_${CIRCLE_BUILD_NUM} -t ${di}:${CIRCLE_SHA1} -t ${di}:${dtag} .
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
},
|
||||
"globals": {
|
||||
"FroalaEditor": "readonly",
|
||||
"gapi": "readonly"
|
||||
"gapi": "readonly",
|
||||
"m": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"env": {
|
||||
|
|
|
@ -76,6 +76,17 @@ const Article = bookshelf.createModel({
|
|||
return result
|
||||
})
|
||||
},
|
||||
|
||||
getFrontpageArticles() {
|
||||
return this.query(qb => {
|
||||
qb.orderBy('updated_at', 'DESC')
|
||||
})
|
||||
.fetchPage({
|
||||
pageSize: 10,
|
||||
page: 1,
|
||||
withRelated: ['files', 'media', 'banner'],
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
export default Article
|
||||
|
|
|
@ -44,7 +44,7 @@ const Media = bookshelf.createModel({
|
|||
},
|
||||
|
||||
url() {
|
||||
return `${Media.baseUrl}${this.get('large_image')}`
|
||||
return `${Media.baseUrl}${this.get('medium_image')}`
|
||||
},
|
||||
|
||||
thumb() {
|
||||
|
|
|
@ -61,6 +61,12 @@ const Page = bookshelf.createModel({
|
|||
})
|
||||
.fetch({ require, withRelated, ctx })
|
||||
},
|
||||
getTree() {
|
||||
return this.query(qb => {
|
||||
qb.where({ parent_id: null })
|
||||
qb.select(['id', 'name', 'path'])
|
||||
}).fetchAll({ withRelated: ['children'] })
|
||||
},
|
||||
})
|
||||
|
||||
export default Page
|
||||
|
|
|
@ -1,5 +1,82 @@
|
|||
import { readFileSync } from 'fs'
|
||||
import send from 'koa-send'
|
||||
import dot from 'dot'
|
||||
import defaults from './defaults.mjs'
|
||||
import config from './config.mjs'
|
||||
import Page from './page/model.mjs'
|
||||
import Article from './article/model.mjs'
|
||||
|
||||
const body = readFileSync('./public/index.html').toString()
|
||||
const bodyTemplate = dot.template(body)
|
||||
|
||||
async function sendIndex(ctx, path) {
|
||||
let tree = null
|
||||
let data = null
|
||||
let links = null
|
||||
try {
|
||||
tree = (await Page.getTree()).toJSON()
|
||||
tree.forEach(item => (
|
||||
item.children = item.children.map(x => (
|
||||
{ id: x.id, name: x.name, path: x.path }
|
||||
))
|
||||
))
|
||||
if (path === '/') {
|
||||
data = await Article.getFrontpageArticles()
|
||||
|
||||
if (data.pagination.rowCount > 10) {
|
||||
links = {
|
||||
current: { title: 'Page 1' },
|
||||
next: { page: 2, title: 'Next' },
|
||||
last: { page: Math.ceil(data.pagination.rowCount / 10), title: 'Last' },
|
||||
}
|
||||
} else {
|
||||
links = {
|
||||
current: { title: 'Page 1' },
|
||||
}
|
||||
}
|
||||
data = data.toJSON().map(x => ({
|
||||
id: x.id,
|
||||
created_at: x.created_at,
|
||||
path: x.path,
|
||||
description: x.description,
|
||||
name: x.name,
|
||||
media: x.media && ({
|
||||
medium_url: x.media.medium_url,
|
||||
small_url: x.media.small_url,
|
||||
}) || null,
|
||||
banner: x.banner && ({
|
||||
large_url: x.banner.large_url,
|
||||
medium_url: x.banner.medium_url,
|
||||
small_url: x.banner.small_url,
|
||||
}) || null,
|
||||
files: x.files && x.files.map(f => ({
|
||||
filename: f.filename,
|
||||
url: f.url,
|
||||
magnet: f.magnet,
|
||||
meta: f.meta.torrent && ({
|
||||
torrent: {
|
||||
files: f.meta.torrent.files.map(tf => ({
|
||||
name: tf.name,
|
||||
size: tf.size,
|
||||
})),
|
||||
},
|
||||
}) || {},
|
||||
})) || [],
|
||||
}))
|
||||
}
|
||||
} catch (e) {
|
||||
ctx.log.error(e)
|
||||
}
|
||||
ctx.body = bodyTemplate({
|
||||
v: config.get('CIRCLECI_VERSION'),
|
||||
tree: JSON.stringify(tree),
|
||||
data: JSON.stringify(data),
|
||||
links: JSON.stringify(links),
|
||||
})
|
||||
ctx.set('Content-Length', Buffer.byteLength(ctx.body))
|
||||
ctx.set('Cache-Control', 'max-age=0')
|
||||
ctx.set('Content-Type', 'text/html; charset=utf-8')
|
||||
}
|
||||
|
||||
export function serve(docRoot, pathname, options = {}) {
|
||||
options.root = docRoot
|
||||
|
@ -22,9 +99,14 @@ export function serve(docRoot, pathname, options = {}) {
|
|||
opts = defaults({ maxage: 2592000 * 1000 }, opts)
|
||||
}
|
||||
|
||||
if (filepath === '/index.html') {
|
||||
return sendIndex(ctx, '/')
|
||||
}
|
||||
|
||||
return send(ctx, filepath, opts).catch((er) => {
|
||||
if (er.code === 'ENOENT' && er.status === 404) {
|
||||
return send(ctx, '/index.html', options)
|
||||
return sendIndex(ctx)
|
||||
// return send(ctx, '/index.html', options)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
15
app/admin.js
Normal file
15
app/admin.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
const EditPage = require('./admin/editpage')
|
||||
const AdminPages = require('./admin/pages')
|
||||
const AdminArticles = require('./admin/articles')
|
||||
const EditArticle = require('./admin/editarticle')
|
||||
const AdminStaffList = require('./admin/stafflist')
|
||||
const EditStaff = require('./admin/editstaff')
|
||||
|
||||
window.addAdminRoutes = [
|
||||
['/admin/pages', AdminPages],
|
||||
['/admin/pages/:key', EditPage],
|
||||
['/admin/articles', AdminArticles],
|
||||
['/admin/articles/:id', EditArticle],
|
||||
['/admin/staff', AdminStaffList],
|
||||
['/admin/staff/:id', EditStaff],
|
||||
]
|
79
app/admin.scss
Normal file
79
app/admin.scss
Normal file
|
@ -0,0 +1,79 @@
|
|||
@import './_common';
|
||||
|
||||
.error {
|
||||
font-size: 0.8em;
|
||||
color: $secondary-dark-bg;
|
||||
font-weight: bold;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
$bordercolor: $primary-bg;
|
||||
$headcolor: $primary-light-bg;
|
||||
$headtext: $primary-light-fg;
|
||||
|
||||
.admin-wrapper table {
|
||||
width: calc(100% - 20px);
|
||||
margin: 10px;
|
||||
border: solid 1px $bordercolor;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
font-size: 0.8em;
|
||||
|
||||
thead th {
|
||||
background-color: $headcolor;
|
||||
border: solid 1px $bordercolor;
|
||||
color: $headtext;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
tbody td {
|
||||
text-align: left;
|
||||
border: solid 1px $bordercolor;
|
||||
color: $table-fg;
|
||||
padding: 10px;
|
||||
}
|
||||
a,
|
||||
a:visited,
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
color: $secondary-dark-bg;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
button {
|
||||
color: $secondary-dark-bg;
|
||||
background: transparent;
|
||||
border: 1px solid $secondary-dark-bg;
|
||||
}
|
||||
|
||||
td.right,
|
||||
th.right {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.floating-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #00000099;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@import 'admin/admin';
|
||||
@import 'widgets/admin';
|
||||
|
||||
.darkmodeon {
|
||||
.maincontainer .admin-wrapper {
|
||||
color: $main-fg;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: $dark_secondary-dark-bg;
|
||||
}
|
||||
}
|
|
@ -38,21 +38,3 @@
|
|||
@import 'pages';
|
||||
@import 'articles';
|
||||
@import 'staff';
|
||||
|
||||
.darkmodeon {
|
||||
.admin-wrapper {
|
||||
background: $dark_primary-bg;
|
||||
}
|
||||
|
||||
.admin-actions {
|
||||
background: $dark_primary-bg;
|
||||
|
||||
span {
|
||||
color: $dark_primary-fg;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $dark_secondary-light-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const m = require('mithril')
|
||||
|
||||
const { getAllArticlesPagination, removeArticle } = require('../api/article')
|
||||
const { fetchPage } = require('../api/pagination')
|
||||
const Dialogue = require('../widgets/dialogue')
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const m = require('mithril')
|
||||
|
||||
const Authentication = require('../authentication')
|
||||
const FileUpload = require('../widgets/fileupload')
|
||||
const Froala = require('./froala')
|
||||
|
@ -120,6 +118,9 @@ const EditArticle = {
|
|||
if (this.error) return
|
||||
|
||||
this.article.description = vnode.state.froala && vnode.state.froala.html.get() || this.article.description
|
||||
if (this.article.description) {
|
||||
this.article.description = this.article.description.replace(/<p[^>]+data-f-id="pbf"[^>]+>[^>]+>[^>]+>[^>]+>/, '')
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const m = require('mithril')
|
||||
|
||||
const Authentication = require('../authentication')
|
||||
const FileUpload = require('../widgets/fileupload')
|
||||
const Froala = require('./froala')
|
||||
|
@ -98,6 +96,9 @@ const EditPage = {
|
|||
if (this.error) return
|
||||
|
||||
this.page.description = vnode.state.froala ? vnode.state.froala.html.get() : this.page.description
|
||||
if (this.page.description) {
|
||||
this.page.description = this.page.description.replace(/<p[^>]+data-f-id="pbf"[^>]+>[^>]+>[^>]+>[^>]+>/, '')
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const m = require('mithril')
|
||||
|
||||
const { createStaff, updateStaff, getStaff } = require('../api/staff')
|
||||
|
||||
const EditStaff = {
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
const m = require('mithril')
|
||||
|
||||
const Authentication = require('../authentication')
|
||||
const { getAllPages, removePage } = require('../api/page')
|
||||
const Dialogue = require('../widgets/dialogue')
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const m = require('mithril')
|
||||
|
||||
const { getAllStaff, removeStaff } = require('../api/staff')
|
||||
const Dialogue = require('../widgets/dialogue')
|
||||
const Pages = require('../widgets/pages')
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
const m = require('mithril')
|
||||
const Authentication = require('../authentication')
|
||||
|
||||
exports.sendRequest = function(options, isPagination) {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
const m = require('mithril')
|
||||
const { sendRequest } = require('./common')
|
||||
|
||||
exports.uploadMedia = function(file) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { sendRequest } = require('./common')
|
||||
|
||||
const Tree = []
|
||||
const Tree = window.__nfptree || []
|
||||
|
||||
exports.Tree = Tree
|
||||
|
||||
|
@ -25,7 +25,7 @@ exports.createPage = function(body) {
|
|||
})
|
||||
}
|
||||
|
||||
exports.getTree = function(body) {
|
||||
exports.getTree = function() {
|
||||
return sendRequest({
|
||||
method: 'GET',
|
||||
url: '/api/pages?tree=true&includes=children&fields=id,name,path,children(id,name,path)',
|
||||
|
|
120
app/app.scss
120
app/app.scss
File diff suppressed because one or more lines are too long
|
@ -54,6 +54,7 @@ const Article = {
|
|||
m('.fr-view', [
|
||||
this.article.media
|
||||
? m('a.cover', {
|
||||
rel: 'noopener',
|
||||
href: this.article.media.url,
|
||||
}, m('img', { src: this.article.media.medium_url, alt: 'Cover image for ' + this.article.name }))
|
||||
: null,
|
||||
|
|
|
@ -1,30 +1,35 @@
|
|||
const m = require('mithril')
|
||||
|
||||
const storageName = 'logintoken'
|
||||
const loadingListeners = []
|
||||
|
||||
window.googleLoaded = function() {
|
||||
Authentication.loadedGoogle = true
|
||||
while (Authentication.loadingListeners.length) {
|
||||
Authentication.loadingListeners.pop()()
|
||||
}
|
||||
}
|
||||
|
||||
const Authentication = {
|
||||
currentUser: null,
|
||||
isAdmin: false,
|
||||
loadedGoogle: false,
|
||||
loadingGoogle: false,
|
||||
loadingListeners: [],
|
||||
authListeners: [],
|
||||
|
||||
updateToken: function(token) {
|
||||
if (!token) return Authentication.clearToken()
|
||||
localStorage.setItem(storageName, token)
|
||||
Authentication.currentUser = JSON.parse(atob(token.split('.')[1]))
|
||||
|
||||
if (Authentication.authListeners.length) {
|
||||
Authentication.authListeners.forEach(function(x) { x(Authentication.currentUser) })
|
||||
}
|
||||
},
|
||||
|
||||
clearToken: function() {
|
||||
Authentication.currentUser = null
|
||||
localStorage.removeItem(storageName)
|
||||
Authentication.isAdmin = false
|
||||
},
|
||||
|
||||
addEvent: function(event) {
|
||||
Authentication.authListeners.push(event)
|
||||
},
|
||||
|
||||
setAdmin: function(item) {
|
||||
Authentication.isAdmin = item
|
||||
},
|
||||
|
||||
createGoogleScript: function() {
|
||||
|
@ -50,6 +55,15 @@ const Authentication = {
|
|||
},
|
||||
}
|
||||
|
||||
if (!window.googleLoaded) {
|
||||
window.googleLoaded = function() {
|
||||
Authentication.loadedGoogle = true
|
||||
while (Authentication.loadingListeners.length) {
|
||||
Authentication.loadingListeners.pop()()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Authentication.updateToken(localStorage.getItem(storageName))
|
||||
|
||||
module.exports = Authentication
|
||||
|
|
|
@ -10,7 +10,7 @@ const Darkmode = {
|
|||
Darkmode.darkIsOn = true
|
||||
} else {
|
||||
localStorage.removeItem(storageName)
|
||||
document.body.className = ''
|
||||
document.body.className = 'daymode'
|
||||
Darkmode.darkIsOn = false
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
const m = require('mithril')
|
||||
const { Tree } = require('../api/page')
|
||||
const Authentication = require('../authentication')
|
||||
const Darkmode = require('../darkmode')
|
||||
|
||||
const Footer = {
|
||||
oninit: function(vnode) {
|
||||
|
@ -9,11 +8,6 @@ const Footer = {
|
|||
},
|
||||
|
||||
view: function() {
|
||||
var pixelRatio = window.devicePixelRatio || 1
|
||||
var darkPrefix = ''
|
||||
if (Darkmode.darkIsOn) {
|
||||
darkPrefix = 'dark_'
|
||||
}
|
||||
return [
|
||||
m('div.sitemap', [
|
||||
m('div', 'Sitemap'),
|
||||
|
@ -31,16 +25,15 @@ const Footer = {
|
|||
!Authentication.currentUser
|
||||
? m(m.route.Link, { class: 'root', href: '/login' }, 'Login')
|
||||
: null,
|
||||
m('div.meta', ['©'
|
||||
m('div.meta', [
|
||||
'©'
|
||||
+ this.year
|
||||
+ ' NFP Encodes - nfp@nfp.moe - ',
|
||||
m('a', { href: 'https://www.iubenda.com/privacy-policy/31076050', target: '_blank' }, 'Privacy Policy'),
|
||||
m('a', { rel: 'noopener', href: 'https://www.iubenda.com/privacy-policy/31076050', target: '_blank' }, 'Privacy Policy'),
|
||||
' (Fuck EU)',
|
||||
])
|
||||
]),
|
||||
m('div.footer-logo', { style: {
|
||||
'background-image': pixelRatio > 1 ? 'url("/assets/img/' + darkPrefix + 'tsun.jpg")' : 'url("/assets/img/' + darkPrefix + 'tsun_small.jpg")'
|
||||
} }),
|
||||
]),
|
||||
m('div.footer-logo'),
|
||||
]
|
||||
},
|
||||
}
|
||||
|
|
|
@ -73,3 +73,35 @@ footer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.daymode footer .footer-logo {
|
||||
background-image: url("/assets/img/tsun_small.jpg");
|
||||
}
|
||||
|
||||
.darkmodeon footer .footer-logo {
|
||||
background-image: url("/assets/img/dark_tsun_small.jpg");
|
||||
}
|
||||
|
||||
@media
|
||||
only screen and (-webkit-min-device-pixel-ratio: 2),
|
||||
only screen and ( min--moz-device-pixel-ratio: 2),
|
||||
only screen and ( -o-min-device-pixel-ratio: 2/1),
|
||||
only screen and ( min-device-pixel-ratio: 2),
|
||||
only screen and ( min-resolution: 192dpi),
|
||||
only screen and ( min-resolution: 2dppx) {
|
||||
.daymode .footer-logo {
|
||||
background-image: url("/assets/img/tsun.jpg");
|
||||
}
|
||||
|
||||
.darkmodeon .footer-logo {
|
||||
background-image: url("/assets/img/dark_tsun.jpg");
|
||||
}
|
||||
}
|
||||
|
||||
@media (pointer:coarse) {
|
||||
footer .sitemap a.root,
|
||||
footer .sitemap a.child {
|
||||
// padding: 10px 10px;
|
||||
// display: inline-block;
|
||||
}
|
||||
}
|
||||
|
|
208
app/froala.scss
Normal file
208
app/froala.scss
Normal file
|
@ -0,0 +1,208 @@
|
|||
.fr-view {
|
||||
word-wrap: break-word;
|
||||
|
||||
.clearfix::after {
|
||||
clear: both;
|
||||
display: block;
|
||||
content: "";
|
||||
height: 0
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6, p, dl, ol, ul {
|
||||
margin: 0 0 1em !important;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: solid 2px $main-fg;
|
||||
margin-left: 0;
|
||||
padding-left: 5px;
|
||||
color: $main-fg;
|
||||
}
|
||||
|
||||
.hide-by-clipping {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
border: 0
|
||||
}
|
||||
|
||||
img.fr-rounded,
|
||||
.fr-img-caption.fr-rounded img {
|
||||
border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-background-clip: padding;
|
||||
-webkit-background-clip: padding-box;
|
||||
background-clip: padding-box
|
||||
}
|
||||
|
||||
img.fr-bordered,
|
||||
.fr-img-caption.fr-bordered img {
|
||||
border: solid 5px #CCC
|
||||
}
|
||||
|
||||
img.fr-bordered {
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box
|
||||
}
|
||||
|
||||
.fr-img-caption.fr-bordered img {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box
|
||||
}
|
||||
|
||||
span.fr-emoticon {
|
||||
font-weight: normal;
|
||||
font-family: "Apple Color Emoji", "Segoe UI Emoji", "NotoColorEmoji", "Segoe UI Symbol", "Android Emoji", "EmojiSymbols";
|
||||
display: inline;
|
||||
line-height: 0
|
||||
}
|
||||
|
||||
img {
|
||||
position: relative;
|
||||
max-width: 100%
|
||||
}
|
||||
|
||||
img.fr-dib {
|
||||
margin: 5px auto;
|
||||
display: block;
|
||||
float: none;
|
||||
vertical-align: top
|
||||
}
|
||||
|
||||
img.fr-dib.fr-fil {
|
||||
margin-left: 0;
|
||||
text-align: left
|
||||
}
|
||||
|
||||
img.fr-dib.fr-fir {
|
||||
margin-right: 0;
|
||||
text-align: right
|
||||
}
|
||||
|
||||
img.fr-dii {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
vertical-align: bottom;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
max-width: calc(100% - (2 * 5px))
|
||||
}
|
||||
|
||||
img.fr-dii.fr-fil {
|
||||
float: left;
|
||||
margin: 5px 5px 5px 0;
|
||||
max-width: calc(100% - 5px)
|
||||
}
|
||||
|
||||
img.fr-dii.fr-fir {
|
||||
float: right;
|
||||
margin: 5px 0 5px 5px;
|
||||
max-width: calc(100% - 5px)
|
||||
}
|
||||
|
||||
span.fr-img-caption {
|
||||
position: relative;
|
||||
max-width: 100%
|
||||
}
|
||||
|
||||
span.fr-img-caption.fr-dib {
|
||||
margin: 5px auto;
|
||||
display: block;
|
||||
float: none;
|
||||
vertical-align: top
|
||||
}
|
||||
|
||||
span.fr-img-caption.fr-dib.fr-fil {
|
||||
margin-left: 0;
|
||||
text-align: left
|
||||
}
|
||||
|
||||
span.fr-img-caption.fr-dib.fr-fir {
|
||||
margin-right: 0;
|
||||
text-align: right
|
||||
}
|
||||
|
||||
span.fr-img-caption.fr-dii {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
vertical-align: bottom;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
max-width: calc(100% - (2 * 5px))
|
||||
}
|
||||
|
||||
span.fr-img-caption.fr-dii.fr-fil {
|
||||
float: left;
|
||||
margin: 5px 5px 5px 0;
|
||||
max-width: calc(100% - 5px)
|
||||
}
|
||||
|
||||
span.fr-img-caption.fr-dii.fr-fir {
|
||||
float: right;
|
||||
margin: 5px 0 5px 5px;
|
||||
max-width: calc(100% - 5px)
|
||||
}
|
||||
|
||||
a.fr-strong {
|
||||
font-weight: 700
|
||||
}
|
||||
|
||||
a.fr-green {
|
||||
color: green
|
||||
}
|
||||
|
||||
.fr-img-caption {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.fr-img-caption .fr-img-wrap {
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.fr-img-caption .fr-img-wrap img {
|
||||
display: block;
|
||||
margin: auto;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.fr-img-caption .fr-img-wrap>span {
|
||||
margin: auto;
|
||||
display: block;
|
||||
padding: 5px 5px 10px;
|
||||
font-size: 14px;
|
||||
font-weight: initial;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-opacity: 0.9;
|
||||
-moz-opacity: 0.9;
|
||||
opacity: 0.9;
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
|
||||
width: 100%;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
a { color: $secondary-dark-bg; }
|
||||
dt { font-weight: bold; }
|
||||
ol { list-style-type: decimal; padding-left: 40px; }
|
||||
ul { list-style-type: disc; padding-left: 40px; }
|
||||
h1 { font-size: 1.8em; font-weight: bold; }
|
||||
h2 { font-size: 1.6em; font-weight: bold; }
|
||||
h3 { font-size: 1.4em; font-weight: bold; }
|
||||
h4 { font-size: 1.2em; font-weight: bold; }
|
||||
h5 { font-size: 1.0em; font-weight: bold; }
|
||||
h6 { font-size: 0.8em; font-weight: bold; }
|
||||
hr { width: 100%; }
|
||||
strong { font-weight: 700 }
|
||||
|
||||
}
|
|
@ -5,15 +5,25 @@ const { getAllArticlesPagination } = require('../api/article')
|
|||
const { fetchPage } = require('../api/pagination')
|
||||
const Pages = require('../widgets/pages')
|
||||
const Newsitem = require('../widgets/newsitem')
|
||||
const Darkmode = require('../darkmode')
|
||||
|
||||
module.exports = {
|
||||
const Frontpage = {
|
||||
oninit: function(vnode) {
|
||||
this.error = ''
|
||||
this.loading = false
|
||||
this.featured = null
|
||||
this.links = null
|
||||
|
||||
if (window.__nfpdata
|
||||
&& window.__nfplinks) {
|
||||
this.links = window.__nfplinks
|
||||
this.articles = window.__nfpdata
|
||||
this.lastpage = '1'
|
||||
window.__nfpdata = null
|
||||
window.__nfplinks = null
|
||||
Frontpage.processFeatured(vnode, this.articles)
|
||||
} else {
|
||||
this.fetchArticles(vnode)
|
||||
}
|
||||
},
|
||||
|
||||
onupdate: function(vnode) {
|
||||
|
@ -30,19 +40,14 @@ module.exports = {
|
|||
this.lastpage = m.route.param('page') || '1'
|
||||
|
||||
return fetchPage(getAllArticlesPagination({
|
||||
per_page: 10,
|
||||
per_page: 2,
|
||||
page: this.lastpage,
|
||||
includes: ['parent', 'files', 'media', 'banner'],
|
||||
}))
|
||||
.then(function(result) {
|
||||
vnode.state.articles = result.data
|
||||
vnode.state.links = result.links
|
||||
|
||||
for (var i = result.data.length - 1; i >= 0; i--) {
|
||||
if (result.data[i].banner) {
|
||||
vnode.state.featured = result.data[i]
|
||||
}
|
||||
}
|
||||
Frontpage.processFeatured(vnode, result.data)
|
||||
})
|
||||
.catch(function(err) {
|
||||
vnode.state.error = err.message
|
||||
|
@ -53,19 +58,18 @@ module.exports = {
|
|||
})
|
||||
},
|
||||
|
||||
processFeatured: function(vnode, data) {
|
||||
for (var i = data.length - 1; i >= 0; i--) {
|
||||
if (data[i].banner) {
|
||||
vnode.state.featured = data[i]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
var deviceWidth = window.innerWidth
|
||||
|
||||
var bannerPath = ''
|
||||
var asuna_side = ''
|
||||
|
||||
if (deviceWidth > 800) {
|
||||
if (Darkmode.darkIsOn) {
|
||||
asuna_side = '/assets/img/dark_asuna_frontpage.jpg'
|
||||
} else {
|
||||
asuna_side = '/assets/img/asuna_frontpage.jpg'
|
||||
}
|
||||
}
|
||||
|
||||
if (this.featured && this.featured.banner) {
|
||||
var pixelRatio = window.devicePixelRatio || 1
|
||||
|
@ -75,12 +79,12 @@ module.exports = {
|
|||
|| (deviceWidth < 600 && pixelRatio > 1)) {
|
||||
bannerPath = this.featured.banner.medium_url
|
||||
} else {
|
||||
bannerPath = this.featured.banner.url
|
||||
bannerPath = this.featured.banner.large_url
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
(this.featured && this.featured.banner
|
||||
(bannerPath
|
||||
? m('a.frontpage-banner', {
|
||||
href: '/article/' + this.featured.path,
|
||||
style: { 'background-image': 'url("' + bannerPath + '")' },
|
||||
|
@ -103,7 +107,7 @@ module.exports = {
|
|||
]
|
||||
}),
|
||||
]),
|
||||
asuna_side ? m('img', { src: asuna_side, alt: 'Asuna standing tall welcomes you' }) : null,
|
||||
m('div.asunaside'),
|
||||
]),
|
||||
m('.frontpage-news', [
|
||||
(this.loading
|
||||
|
@ -121,3 +125,5 @@ module.exports = {
|
|||
]
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = Frontpage
|
||||
|
|
|
@ -38,10 +38,6 @@ frontpage {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
img {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.categories {
|
||||
padding: 10px 10px 20px;
|
||||
margin-bottom: 20px;
|
||||
|
@ -89,6 +85,10 @@ frontpage {
|
|||
newsitem {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.asunaside {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1000px){
|
||||
|
@ -113,6 +113,26 @@ frontpage {
|
|||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px){
|
||||
frontpage .asunaside {
|
||||
display: block;
|
||||
width: 200px;
|
||||
height: 480px;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: top left;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.daymode frontpage .asunaside {
|
||||
background-image: url("/assets/img/asuna_frontpage.jpg");
|
||||
}
|
||||
|
||||
.darkmodeon frontpage .asunaside {
|
||||
background-image: url("/assets/img/dark_asuna_frontpage.jpg");
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px){
|
||||
.frontpage-banner {
|
||||
width: 100%;
|
||||
|
|
70
app/index.js
70
app/index.js
|
@ -1,4 +1,5 @@
|
|||
const m = require('mithril')
|
||||
window.m = m
|
||||
|
||||
m.route.prefix = ''
|
||||
|
||||
|
@ -7,31 +8,72 @@ const Footer = require('./footer/footer')
|
|||
const Frontpage = require('./frontpage/frontpage')
|
||||
const Login = require('./login/login')
|
||||
const Logout = require('./login/logout')
|
||||
const EditPage = require('./admin/editpage')
|
||||
const Page = require('./pages/page')
|
||||
const Article = require('./article/article')
|
||||
const AdminPages = require('./admin/pages')
|
||||
const AdminArticles = require('./admin/articles')
|
||||
const EditArticle = require('./admin/editarticle')
|
||||
const AdminStaffList = require('./admin/stafflist')
|
||||
const EditStaff = require('./admin/editstaff')
|
||||
const Authentication = require('./authentication')
|
||||
|
||||
const menuRoot = document.getElementById('nav')
|
||||
const mainRoot = document.getElementById('main')
|
||||
const footerRoot = document.getElementById('footer')
|
||||
|
||||
m.route(mainRoot, '/', {
|
||||
const allRoutes = {
|
||||
'/': Frontpage,
|
||||
'/login': Login,
|
||||
'/logout': Logout,
|
||||
'/page/:id': Page,
|
||||
'/article/:id': Article,
|
||||
'/admin/pages': AdminPages,
|
||||
'/admin/pages/:key': EditPage,
|
||||
'/admin/articles': AdminArticles,
|
||||
'/admin/articles/:id': EditArticle,
|
||||
'/admin/staff': AdminStaffList,
|
||||
'/admin/staff/:id': EditStaff,
|
||||
})
|
||||
}
|
||||
|
||||
m.route(mainRoot, '/', allRoutes)
|
||||
m.mount(menuRoot, Menu)
|
||||
m.mount(footerRoot, Footer)
|
||||
|
||||
let loadingAdmin = false
|
||||
let loadedAdmin = false
|
||||
let loaded = 0
|
||||
|
||||
const onLoaded = function() {
|
||||
loaded++
|
||||
if (loaded < 2) return
|
||||
|
||||
if (window.addAdminRoutes) {
|
||||
window.addAdminRoutes.forEach(function (item) {
|
||||
allRoutes[item[0]] = item[1]
|
||||
})
|
||||
m.route(mainRoot, '/', allRoutes)
|
||||
}
|
||||
|
||||
Authentication.setAdmin(Authentication.currentUser && Authentication.currentUser.level >= 10)
|
||||
loadedAdmin = true
|
||||
m.redraw()
|
||||
}
|
||||
|
||||
const loadAdmin = function(user) {
|
||||
if (loadingAdmin) {
|
||||
if (loadedAdmin) {
|
||||
Authentication.setAdmin(user && user.level >= 10)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (!user || user.level < 10) return
|
||||
|
||||
loadingAdmin = true
|
||||
|
||||
let element = document.createElement('link')
|
||||
element.setAttribute('rel', 'stylesheet')
|
||||
element.setAttribute('type', 'text/css')
|
||||
element.setAttribute('href', '/assets/admin.css')
|
||||
element.onload = onLoaded
|
||||
document.getElementsByTagName('head')[0].appendChild(element)
|
||||
|
||||
element = document.createElement('script')
|
||||
element.setAttribute('type', 'text/javascript')
|
||||
element.setAttribute('src', '/assets/admin.js')
|
||||
element.onload = onLoaded
|
||||
document.body.appendChild(element)
|
||||
}
|
||||
|
||||
Authentication.addEvent(loadAdmin)
|
||||
if (Authentication.currentUser) {
|
||||
loadAdmin(Authentication.currentUser)
|
||||
}
|
||||
|
|
|
@ -18,11 +18,13 @@ const Menu = {
|
|||
oninit: function(vnode) {
|
||||
Menu.onbeforeupdate()
|
||||
|
||||
if (Tree.length) return
|
||||
|
||||
Menu.loading = true
|
||||
|
||||
getTree()
|
||||
.then(function(results) {
|
||||
Tree.splice(0, Tree.Length)
|
||||
Tree.splice(0, Tree.length)
|
||||
Tree.push.apply(Tree, results)
|
||||
})
|
||||
.catch(function(err) {
|
||||
|
@ -35,13 +37,10 @@ const Menu = {
|
|||
},
|
||||
|
||||
view: function() {
|
||||
var pixelRatio = window.devicePixelRatio || 1
|
||||
return [
|
||||
m('div.top', [
|
||||
m(m.route.Link,
|
||||
{ href: '/', class: 'logo', style: {
|
||||
'background-image': pixelRatio > 1 ? 'url("/assets/img/logo.jpg")' : 'url("/assets/img/logo_small.jpg")'
|
||||
} },
|
||||
{ href: '/', class: 'logo' },
|
||||
m('h2', 'NFP Moe')
|
||||
),
|
||||
m('aside', Authentication.currentUser ? [
|
||||
|
@ -51,16 +50,16 @@ const Menu = {
|
|||
(Darkmode.darkIsOn
|
||||
? m('button.dark', { onclick: Darkmode.setDarkMode.bind(Darkmode, false) }, 'Day mode')
|
||||
: m('button.dark', { onclick: Darkmode.setDarkMode.bind(Darkmode, true) }, 'Night mode')
|
||||
)
|
||||
),
|
||||
]),
|
||||
(Authentication.currentUser.level >= 10
|
||||
(Authentication.isAdmin
|
||||
? m('div.adminlinks', [
|
||||
m(m.route.Link, { href: '/admin/articles/add' }, 'Create article'),
|
||||
m(m.route.Link, { href: '/admin/articles' }, 'Articles'),
|
||||
m(m.route.Link, { href: '/admin/pages' }, 'Pages'),
|
||||
m(m.route.Link, { hidden: Authentication.currentUser.level < 100, href: '/admin/staff' }, 'Staff'),
|
||||
])
|
||||
: null
|
||||
: (Authentication.currentUser.level > 10 ? m('div.loading-spinner') : null)
|
||||
),
|
||||
] : (Darkmode.darkIsOn
|
||||
? m('button.dark', { onclick: Darkmode.setDarkMode.bind(Darkmode, false) }, 'Day mode')
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
display: flex;
|
||||
color: $primary-dark-fg;
|
||||
text-decoration: none;
|
||||
background-image: url("/assets/img/logo_small.jpg");
|
||||
}
|
||||
|
||||
h2 {
|
||||
|
@ -74,6 +75,11 @@
|
|||
min-width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
position: relative;
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,6 +127,18 @@
|
|||
}
|
||||
}
|
||||
|
||||
@media
|
||||
only screen and (-webkit-min-device-pixel-ratio: 2),
|
||||
only screen and ( min--moz-device-pixel-ratio: 2),
|
||||
only screen and ( -o-min-device-pixel-ratio: 2/1),
|
||||
only screen and ( min-device-pixel-ratio: 2),
|
||||
only screen and ( min-resolution: 192dpi),
|
||||
only screen and ( min-resolution: 2dppx) {
|
||||
#nav .top a.logo {
|
||||
background-image: url("/assets/img/logo.jpg");
|
||||
}
|
||||
}
|
||||
|
||||
.darkmodeon {
|
||||
#nav {
|
||||
.top {
|
||||
|
|
133
app/widgets/admin.scss
Normal file
133
app/widgets/admin.scss
Normal file
|
@ -0,0 +1,133 @@
|
|||
|
||||
fileupload {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
.showicon,
|
||||
.showbordericon,
|
||||
.display {
|
||||
flex-grow: 2;
|
||||
}
|
||||
|
||||
.showbordericon {
|
||||
border: 3px solid $title-fg;
|
||||
border-style: dashed;
|
||||
background-image: url('./img/upload.svg');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 50px;
|
||||
}
|
||||
|
||||
.showicon {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background-image: url('./img/upload.svg');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
align-self: center;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.display {
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #33333388;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
opacity: 0.01;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
text-indent: -9999px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.remove {
|
||||
border: none;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 60px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background-image: url('./img/delete.svg');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-color: transparent;
|
||||
background-size: contain;
|
||||
z-index: 3;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
dialogue {
|
||||
background: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
width: calc(100% - 40px);
|
||||
max-width: 500px;
|
||||
color: $main-fg;
|
||||
|
||||
h2 {
|
||||
background: $secondary-dark-bg;
|
||||
color: $secondary-dark-fg;
|
||||
font-size: 1.5em;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
border: 1px solid $secondary-dark-bg;
|
||||
background: transparent;
|
||||
color: $secondary-dark-bg;
|
||||
padding: 5px 15px;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
button.alert {
|
||||
border-color: red;
|
||||
color: red;
|
||||
}
|
||||
|
||||
button.cancel {
|
||||
border-color: #999;
|
||||
color: #999;
|
||||
}
|
||||
}
|
|
@ -1,137 +1,4 @@
|
|||
|
||||
|
||||
fileupload {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
|
||||
.showicon,
|
||||
.showbordericon,
|
||||
.display {
|
||||
flex-grow: 2;
|
||||
}
|
||||
|
||||
.showbordericon {
|
||||
border: 3px solid $title-fg;
|
||||
border-style: dashed;
|
||||
background-image: url('./img/upload.svg');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 50px;
|
||||
}
|
||||
|
||||
.showicon {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background-image: url('./img/upload.svg');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
align-self: center;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.display {
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #33333388;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
opacity: 0.01;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
text-indent: -9999px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.remove {
|
||||
border: none;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 60px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background-image: url('./img/delete.svg');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-color: transparent;
|
||||
background-size: contain;
|
||||
z-index: 3;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
dialogue {
|
||||
background: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
width: calc(100% - 40px);
|
||||
max-width: 500px;
|
||||
|
||||
h2 {
|
||||
background: $secondary-dark-bg;
|
||||
color: $secondary-dark-fg;
|
||||
font-size: 1.5em;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
border: 1px solid $secondary-dark-bg;
|
||||
background: transparent;
|
||||
color: $secondary-dark-bg;
|
||||
padding: 5px 15px;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
button.alert {
|
||||
border-color: red;
|
||||
color: red;
|
||||
}
|
||||
|
||||
button.cancel {
|
||||
border-color: #999;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
newsentry {
|
||||
display: flex;
|
||||
color: $meta-fg;
|
||||
|
@ -275,6 +142,10 @@ newsitem {
|
|||
flex: 2 1 auto;
|
||||
padding: 0 5px 5px;
|
||||
|
||||
&.extrapadding {
|
||||
padding: 0 15px 5px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: 0 !important;
|
||||
font-size: 1.3em;
|
||||
|
@ -342,28 +213,14 @@ pages {
|
|||
.newsitemcontent {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.entrycontent.extrapadding {
|
||||
padding: 0 5px 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.darkmodeon {
|
||||
fileupload {
|
||||
.showbordericon {
|
||||
border: 3px solid $dark_title-fg;
|
||||
}
|
||||
}
|
||||
|
||||
dialogue {
|
||||
h2 {
|
||||
background: $dark_secondary-dark-bg;
|
||||
color: $dark_secondary-dark-fg;
|
||||
}
|
||||
|
||||
button {
|
||||
border: 1px solid $dark_secondary-dark-bg;
|
||||
color: $dark_secondary-dark-bg;
|
||||
}
|
||||
}
|
||||
|
||||
newsentry {
|
||||
color: $dark_meta-fg;
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const m = require('mithril')
|
||||
|
||||
const Dialogue = {
|
||||
view: function(vnode) {
|
||||
return m('div.floating-container', {
|
||||
|
@ -10,7 +8,7 @@ const Dialogue = {
|
|||
m('div.buttons', [
|
||||
m('button', { class: vnode.attrs.yesclass || '', onclick: vnode.attrs.onyes }, vnode.attrs.yes),
|
||||
m('button', { class: vnode.attrs.noclass || '', onclick: vnode.attrs.onno }, vnode.attrs.no),
|
||||
])
|
||||
]),
|
||||
])
|
||||
)
|
||||
},
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const m = require('mithril')
|
||||
|
||||
const Fileinfo = {
|
||||
getPrefix(vnode) {
|
||||
if (!vnode.attrs.file.filename.endsWith('.torrent')) {
|
||||
|
@ -49,6 +47,7 @@ const Fileinfo = {
|
|||
m('span.prefix', this.getPrefix(vnode) + ':'),
|
||||
m('a', {
|
||||
target: '_blank',
|
||||
rel: 'noopener',
|
||||
href: vnode.attrs.file.url,
|
||||
}, this.getDownloadName(vnode)),
|
||||
vnode.attrs.file.magnet
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
const m = require('mithril')
|
||||
const { uploadMedia } = require('../api/media')
|
||||
|
||||
const FileUpload = {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
const m = require('mithril')
|
||||
const Fileinfo = require('./fileinfo')
|
||||
|
||||
const Newsentry = {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
const m = require('mithril')
|
||||
const Fileinfo = require('./fileinfo')
|
||||
|
||||
const Newsitem = {
|
||||
|
@ -14,8 +13,10 @@ const Newsitem = {
|
|||
? m('a.cover', {
|
||||
href: '/article/' + vnode.attrs.path,
|
||||
}, m('img', { alt: 'Image for news item ' + vnode.attrs.name, src: pixelRatio > 1 ? vnode.attrs.media.medium_url : vnode.attrs.media.small_url }))
|
||||
: m('a.cover.nobg'),
|
||||
m('div.entrycontent', [
|
||||
: null,
|
||||
m('div.entrycontent', {
|
||||
class: vnode.attrs.media ? '' : 'extrapadding',
|
||||
}, [
|
||||
(vnode.attrs.description
|
||||
? m('.fr-view', m.trust(vnode.attrs.description))
|
||||
: null),
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const m = require('mithril')
|
||||
|
||||
const Pages = {
|
||||
oninit: function(vnode) {
|
||||
this.onpage = vnode.attrs.onpage || function() {}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"port": 4030,
|
||||
"host": "0.0.0.0"
|
||||
},
|
||||
"CIRCLECI_VERSION": "circleci_version_number",
|
||||
"knex": {
|
||||
"client": "pg",
|
||||
"connection": {
|
||||
|
|
49
config/config.default.json.org
Normal file
49
config/config.default.json.org
Normal file
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"NODE_ENV": "development",
|
||||
"server": {
|
||||
"port": 4030,
|
||||
"host": "0.0.0.0"
|
||||
},
|
||||
"CIRCLECI_VERSION": "circleci_version_number",
|
||||
"knex": {
|
||||
"client": "pg",
|
||||
"connection": {
|
||||
"host" : "127.0.0.1",
|
||||
"user" : "postgres",
|
||||
"password" : "postgres",
|
||||
"database" : "nfpmoe"
|
||||
},
|
||||
"connectionslave": null,
|
||||
"migrations": {
|
||||
},
|
||||
"acquireConnectionTimeout": 10000
|
||||
},
|
||||
"bunyan": {
|
||||
"name": "nfpmoe",
|
||||
"streams": [{
|
||||
"stream": "process.stdout",
|
||||
"level": "debug"
|
||||
}
|
||||
]
|
||||
},
|
||||
"frontend": {
|
||||
"url": "http://localhost:8080"
|
||||
},
|
||||
"jwt": {
|
||||
"secret": "this-is-my-secret",
|
||||
"options": {
|
||||
"expiresIn": 604800
|
||||
}
|
||||
},
|
||||
"googleid": "1076074914074-3no1difo1jq3dfug3glfb25pn1t8idud.apps.googleusercontent.com",
|
||||
"sessionsecret": "this-is-session-secret-lol",
|
||||
"bcrypt": 5,
|
||||
"fileSize": 524288000,
|
||||
"upload": {
|
||||
"baseurl": "http://192.168.42.14",
|
||||
"port": "2111",
|
||||
"host": "storage01.nfp.is",
|
||||
"name": "nfpmoe-dev",
|
||||
"secret": "nfpmoe-dev"
|
||||
}
|
||||
}
|
11
package.json
11
package.json
|
@ -19,13 +19,15 @@
|
|||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"start": "node --experimental-modules index.mjs",
|
||||
"build": "sass -s compressed app/app.scss public/assets/app.css && browserify -p tinyify --no-commondir -o public/assets/app.js app/index.js",
|
||||
"build": "sass -s compressed app/app.scss public/assets/app.css && sass -s compressed app/admin.scss public/assets/admin.css && browserify -p tinyify --no-commondir -o public/assets/app.js app/index.js && browserify -p tinyify --no-commondir -o public/assets/admin.js app/admin.js",
|
||||
"build:check": "browserify -o public/assets/app.js app/index.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"watch:api": "nodemon --experimental-modules index.mjs | bunyan",
|
||||
"watch:app": "watchify -g envify -d app/index.js -o public/assets/app.js",
|
||||
"watch:sass": "sass --watch app/app.scss public/assets/app.css",
|
||||
"dev": "run-p watch:api watch:app watch:sass",
|
||||
"watch:app:admin": "watchify -g envify -d app/admin.js -o public/assets/admin.js",
|
||||
"watch:app:public": "watchify -g envify -d app/index.js -o public/assets/app.js",
|
||||
"watch:sass:public": "sass --watch app/app.scss public/assets/app.css",
|
||||
"watch:sass:admin": "sass --watch app/admin.scss public/assets/admin.css",
|
||||
"dev": "run-p watch:api watch:app:public watch:app:admin watch:sass:public watch:sass:admin",
|
||||
"prod": "npm run build && npm start",
|
||||
"docker": "docker run -it --rm --name nfp_moe -e knex__connection__host -e NODE_ENV -p 4030:4030 -v \"$PWD\":/usr/src/app -w /usr/src/app node",
|
||||
"docker:install": "npm run docker -- npm install",
|
||||
|
@ -47,6 +49,7 @@
|
|||
"bcrypt": "^3.0.0",
|
||||
"bookshelf": "^0.15.1",
|
||||
"bunyan-lite": "^1.0.1",
|
||||
"dot": "^1.1.2",
|
||||
"format-link-header": "^2.1.0",
|
||||
"googleapis": "^42.0.0",
|
||||
"http-errors": "^1.7.2",
|
||||
|
|
1
public/assets/admin.css
Normal file
1
public/assets/admin.css
Normal file
File diff suppressed because one or more lines are too long
1
public/assets/admin.css.map
Normal file
1
public/assets/admin.css.map
Normal file
|
@ -0,0 +1 @@
|
|||
{"version":3,"sourceRoot":"","sources":["../../app/admin.scss","../../app/_common.scss","../../app/admin/admin.scss","../../app/admin/pages.scss","../../app/admin/articles.scss","../../app/admin/staff.scss","../../app/widgets/admin.scss"],"names":[],"mappings":"AAEA,OACE,eACA,MCSkB,QDRlB,iBACA,oBAOF,qBACE,wBACA,YACA,yBACA,yBACA,iBACA,eAEA,8BACE,iBClBe,QDmBf,yBACA,MCnBe,KDoBf,aACA,gBAEF,8BACE,gBACA,yBACA,MCfO,KDgBP,aAEF,mFAGE,qBACA,MCzBgB,QD0BhB,iBAGF,4BACE,MC9BgB,QD+BhB,uBACA,yBAGF,4DAEE,iBAIJ,oBACE,eACA,MACA,SACA,OACA,QACA,qBACA,aACA,sBACA,uBACA,mBE/DF,eACE,YACA,aACA,sBACA,WDJW,QCKX,oBAGF,eACE,WDTW,QCUX,aACA,uBACA,gBAEA,oBACE,MDdS,KCeT,aACA,eACA,iBAGF,iBACE,aACA,qBACA,MDdiB,QCejB,eACA,iBAIJ,4CAGE,2BCjCF,iBACE,kBACA,gBACA,iBAEA,wBACE,aACA,WFCW,QECX,2BACE,MFDS,KEIX,0BACE,eACA,qBACA,iBACA,MFNe,KEUnB,4BACE,gBAEA,oCACE,aAIJ,kCACE,kBACA,gBAEA,2CACE,0BAIJ,sBACE,oBAEA,+BACE,aAGF,uCACE,aACA,kBAIJ,oBACE,mBAGF,kCACE,YACA,YACA,kBAIJ,mBACE,cC/DF,oBACE,kBACA,gBACA,iBAEA,2BACE,aACA,WHCW,QGCX,8BACE,MHDS,KGIX,6BACE,eACA,qBACA,iBACA,MHNe,KGUnB,+BACE,gBAEA,uCACE,aAIJ,qCACE,kBACA,gBAGF,yBACE,oBAEA,kCACE,aAGF,0CACE,aACA,kBAIJ,uBACE,mBAGF,qCACE,YACA,YACA,kBAEA,0CACE,WAIJ,gCACE,kBACA,aACA,cACA,gBACA,YACA,yBACA,WH1DiB,QG2DjB,MH1DiB,KG2DjB,kBAEA,sCACE,kBACA,MACA,OACA,QACA,SACA,YACA,WACA,eACA,oBACA,UAIJ,0BACE,oBACA,WACA,aACA,sBACA,oBACA,gBAEA,6BACE,gBACA,iBACA,kBACA,mBACA,6BAKN,sBACE,cCzGF,kBACE,kBACA,gBACA,iBAEA,yBACE,aACA,WJCW,QICX,4BACE,MJDS,KIIX,2BACE,eACA,qBACA,iBACA,MJNe,KIUnB,uBACE,oBAEA,gCACE,aAGF,wCACE,aACA,kBAIJ,qBACE,mBAGF,mCACE,YACA,YACA,kBAEA,wCACE,WC5CN,WACE,kBACA,aACA,oBACA,sBACA,wBAEA,oEAGE,YAGF,2BACE,sBACA,oBACA,yCACA,2BACA,4BACA,qBAGF,qBACE,kBACA,QACA,UACA,WACA,YACA,yCACA,2BACA,4BACA,wBAGF,eACE,gBACA,WACA,kBACA,iBAGF,oBACE,sBACA,4BACA,2BAGF,4BACE,kBACA,MACA,OACA,QACA,SACA,qBACA,WAGF,iBACE,kBACA,MACA,OACA,QACA,SACA,YACA,WACA,eACA,oBACA,UAGF,mBACE,YACA,kBACA,QACA,WACA,WACA,YACA,yCACA,2BACA,4BACA,6BACA,wBACA,UACA,aACA,eAIJ,SACE,gBACA,aACA,sBACA,kBACA,wBACA,gBACA,MLvEQ,KKyER,YACE,WLtFgB,QKuFhB,MLtFgB,KKuFhB,gBACA,aAGF,WACE,aAGF,kBACE,aACA,6BACA,aAGF,gBACE,yBACA,uBACA,MLzGgB,QK0GhB,iBACA,gBAGF,sBACE,iBACA,UAGF,uBACE,kBACA,WN3DF,0CACE,MC/CM,KDkDR,mBACE,MC7BqB","file":"admin.css"}
|
1
public/assets/admin.js
Normal file
1
public/assets/admin.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -6,20 +6,22 @@
|
|||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/png" href="/assets/img/favicon.png">
|
||||
<link rel="Stylesheet" href="/assets/app.css?v=4" type="text/css" />
|
||||
<link rel="Stylesheet" href="/assets/app.css?v={{=it.v}}" type="text/css" />
|
||||
<link rel="preconnect" href="https://cdn-nfp.global.ssl.fastly.net" />
|
||||
<meta name="google-signin-client_id" content="1076074914074-3no1difo1jq3dfug3glfb25pn1t8idud.apps.googleusercontent.com">
|
||||
</head>
|
||||
<body>
|
||||
<body class="daymode">
|
||||
<script type="text/javascript">
|
||||
if (localStorage.getItem('darkmode')) {
|
||||
document.body.className = 'darkmodeon'
|
||||
}
|
||||
if (localStorage.getItem('darkmode')) {document.body.className = 'darkmodeon';}
|
||||
window.__nfptree = {{=it.tree}};
|
||||
window.__nfpdata = {{=it.data}};
|
||||
window.__nfplinks = {{=it.links}};
|
||||
</script>
|
||||
<div class="maincontainer">
|
||||
<div id="nav"></div>
|
||||
<main id="main"></main>
|
||||
<footer id="footer"></footer>
|
||||
</div>
|
||||
<script type="text/javascript" src="/assets/app.js?v=4"></script>
|
||||
<script type="text/javascript" src="/assets/app.js?v={{=it.v}}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in a new issue