app moved to next.js

This commit is contained in:
tmshv committed 2019-08-01 14:44:25 +03:00
1 parent dcca513aea
commit 43af2996bf
48 files changed
+1147 -1027

No files matched your search

+5 -5
View File
@@ -5,11 +5,11 @@
"description": "",
"main": "src/bin/www.js",
"scripts": {
"start": "node src/index.js",
"start-next": "NODE_ENV=production node src/index.js",
"dev": "babel-node src/index.js",
"build-next": "next build",
"out": "babel src/ -d out/",
"start": "NODE_ENV=production node src/index.js",
"dev": "node src/server.js",
"next": "next",
"build": "next build",
"export": "npm run build && next export -o dist",
"gulp": "gulp",
"test": "mocha --compilers js:babel-register",
"coverage": "istanbul cover mocha -- --compilers js:babel-register"
+27
View File
@@ -0,0 +1,27 @@
import React from 'react'
import Document, { Html, Head, Main, NextScript } from 'next/document'
export default class MyDocument extends Document {
// static async getInitialProps(ctx) {
// const initialProps = await Document.getInitialProps(ctx);
// return { ...initialProps };
// }
render() {
return (
<Html>
<Head>
<style>{`body { margin: 0 } /* custom! */`}</style>
<script src='/static/likely.js'></script>
<link href='/static/likely.css' rel='stylesheet' />
</Head>
<body className="custom_class">
<Main />
<NextScript />
</body>
</Html>
);
}
}
+42
View File
@@ -0,0 +1,42 @@
import React from 'react'
import axios from 'axios'
import App from '../../src/components/App'
import Article from '../../src/components/Article'
import Html from '../../src/components/Html'
import menuModel from '../../src/models/menu'
import { buildMenu } from '../../src/lib/menu'
const Page = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
>
<div className="content content_thin">
<Article
title={props.album.title}
date={props.album.date}
shareable={true}
>
<Html
html={props.album.post}
/>
</Article>
</div>
</App>
)
Page.getInitialProps = async (ctx) => {
const pageUrl = ctx.req.url
const id = ctx.query.album
const apiUrl = `http://localhost:3000/api/albums/${id}`
const res = await axios.get(apiUrl)
const album = res.data
return {
album,
pageUrl,
}
}
export default Page
+19
View File
@@ -0,0 +1,19 @@
import { dbUri } from '../../../src/config'
import { connect } from '../../../src/core/db'
import Album from '../../../src/core/Album'
export default async (req, res) => {
await connect(dbUri)
const id = req.query.album
const album = await Album.findById(id)
if (album) {
res.json(album)
} else {
res.status(404)
res.json({
error: `Album ${id} not found`
})
}
}
+19
View File
@@ -0,0 +1,19 @@
import { dbUri } from '../../../src/config'
import { connect } from '../../../src/core/db'
import Album from '../../../src/core/Album'
export default async (req, res) => {
await connect(dbUri)
const data = await Album.find({}, { sort: { date: -1 } })
if (data) {
res.json({
items: data,
})
} else {
res.status(404)
res.json({
error: `Article not found`
})
}
}
+22
View File
@@ -0,0 +1,22 @@
import { dbUri } from '../../../src/config'
import { connect } from '../../../src/core/db'
import Article from '../../../src/core/Article'
export default async (req, res) => {
await connect(dbUri)
const {
query: { article: id }
} = req;
const article = await Article.findById(id)
if (article) {
res.json(article)
} else {
res.status(404)
res.json({
error: `Article ${id} not found`
})
}
}
+22
View File
@@ -0,0 +1,22 @@
import { dbUri } from '../../../src/config'
import { connect } from '../../../src/core/db'
import { findArticles } from '../../../src/api/articles'
export default async (req, res) => {
await connect(dbUri)
const { query } = req
const page = parseInt(query.page)
const pageSize = parseInt(query.pageSize)
const data = await findArticles(page, pageSize)
if (data) {
res.json(data)
} else {
res.status(404)
res.json({
error: `Article ${id} not found`
})
}
}
+20
View File
@@ -0,0 +1,20 @@
import { dbUri } from '../../../src/config'
import { connect } from '../../../src/core/db'
import Document from '../../../src/core/Document'
import { encodeFile } from '../../../src/api/files'
export default async (req, res) => {
await connect(dbUri)
const id = req.query.document
const document = await Document.findById(id)
if (document) {
res.json(encodeFile(document))
} else {
res.status(404)
res.json({
error: `Document ${id} not found`
})
}
}
+24
View File
@@ -0,0 +1,24 @@
import { dbUri } from '../../../src/config'
import { connect } from '../../../src/core/db'
import Document from '../../../src/core/Document'
import { encodeFile } from '../../../src/api/files'
export default async (req, res) => {
await connect(dbUri)
const documents = await Document.find({hidden: false})
if (documents) {
const items = documents
.map(encodeFile)
.filter(x => x.imageUrl) // skip documents without preview image; needs to be refactored
res.json({
items,
})
} else {
res.status(404)
res.json({
error: `Documents not found`
})
}
}
+21
View File
@@ -0,0 +1,21 @@
import { dbUri } from '../../src/config'
import { connect } from '../../src/core/db'
import Image from '../../src/core/Image'
import ImageArtifactType from '../../src/core/ImageArtifactType'
export default async (req, res) => {
await connect(dbUri)
const file = req.query.file
const image = await Image.findByFile(file)
if (image) {
const data = image.getPicture(ImageArtifactType.LARGE)
res.json(data)
} else {
res.status(404)
res.json({
error: `Image ${file} not found`
})
}
}
+26
View File
@@ -0,0 +1,26 @@
import { dbUri } from '../../src/config'
import { connect } from '../../src/core/db'
import Page from '../../src/core/Page'
import { getPathWithNoTrailingSlash } from '../../src/lib/url'
export default async (req, res) => {
await connect(dbUri)
const page = getPathWithNoTrailingSlash(req.query.page)
const resource = await Page.findByUrl(page)
if (resource) {
res.json({
id: resource.id,
title: resource.title,
url: resource.url,
data: resource.data,
preview: resource.preview,
})
} else {
res.status(404)
res.json({
error: `Image ${page} not found`
})
}
}
+21
View File
@@ -0,0 +1,21 @@
import { dbUri } from '../../../src/config'
import { connect } from '../../../src/core/db'
import Teacher from '../../../src/core/Teacher'
import { encodePerson } from '../../../src/api/persons'
export default async (req, res) => {
await connect(dbUri)
const id = req.query.person
const person = await Teacher.findById(id)
const data = encodePerson(person)
if (data) {
res.json(data)
} else {
res.status(404)
res.json({
error: `Article ${id} not found`
})
}
}
+33
View File
@@ -0,0 +1,33 @@
import { dbUri } from '../../../src/config'
import { connect } from '../../../src/core/db'
import Teacher from '../../../src/core/Teacher'
import { prioritySort } from '../../../src/lib/sort'
import { encodePerson } from '../../../src/api/persons'
export default async (req, res) => {
await connect(dbUri)
const order = [
'mg-timasheva',
'va-sarzhin',
'od-gogoleva',
'my-valkova',
'nv-andreeva',
'vv-voronova',
]
const teachersSorted = prioritySort.bind(null, [...order], t => t.id)
const teachers = await Teacher.find({ hidden: false })
const data = teachersSorted(teachers).map(encodePerson)
if (data) {
res.json({
items: data,
})
} else {
res.status(404)
res.json({
error: `Article ${id} not found`
})
}
}
+20
View File
@@ -0,0 +1,20 @@
import { dbUri } from '../../src/config'
import { connect } from '../../src/core/db'
import { findSchedule } from '../../src/core/schedule'
export default async (req, res) => {
await connect(dbUri)
const period = req.query.period
const semester = req.query.semester
const data = await findSchedule(period, semester)
if (data) {
res.json(data)
} else {
res.status(404)
res.json({
error: `Schedule ${period}:${semester} not found`
})
}
}
+43
View File
@@ -0,0 +1,43 @@
import React from 'react'
import axios from 'axios'
import App from '../../src/components/App'
import Article from '../../src/components/Article'
import Html from '../../src/components/Html'
import menuModel from '../../src/models/menu'
import { buildMenu } from '../../src/lib/menu'
const Page = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
>
<div className={'content content_thin'}>
<Article
title={props.article.title}
date={props.article.date}
tags={props.article.tags}
shareable={true}
>
<Html
html={props.article.post}
/>
</Article>
</div>
</App>
)
Page.getInitialProps = async (ctx) => {
const pageUrl = ctx.req.url
const id = ctx.query.article
const apiUrl = `http://localhost:3000/api/articles/${id}`
const res = await axios.get(apiUrl)
const article = res.data
return {
article,
pageUrl,
}
}
export default Page
+52
View File
@@ -0,0 +1,52 @@
import React from 'react'
import axios from 'axios'
import App from '../../src/components/App'
import ArticleCardList from '../../src/components/ArticleCardList'
import menuModel from '../../src/models/menu'
import { buildMenu } from '../../src/lib/menu'
function getMeta(article) {
return {
title: article.title,
}
}
// async function renderHome(path, pageSize) {
// return render(path, Component, getMeta({ title: 'Шлиссельбургская ДХШ' }), { showAuthor: true })
// }
const Page = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
>
<div className="content content_wide">
<ArticleCardList
articles={props.articles}
prevPage={props.prevPage}
nextPage={props.nextPage}
/>
</div>
</App>
)
Page.getInitialProps = async (ctx) => {
const pageUrl = ctx.req.url
const page = parseInt(ctx.query.page)
const pageSize = 10
const apiUrl = `http://localhost:3000/api/articles?page=${page}&pageSize=${pageSize}`
const res = await axios.get(apiUrl)
const articles = res.data.items
const nextPage = res.data.nextPage
const prevPage = res.data.prevPage
return {
articles,
nextPage,
prevPage,
pageUrl,
}
}
export default Page
+64
View File
@@ -0,0 +1,64 @@
import React from 'react'
import axios from 'axios'
import App from '../src/components/App'
import CollectiveImage from '../src/components/CollectiveImage'
import PersonCard from '../src/components/PersonCard'
import menuModel from '../src/models/menu'
import { buildMenu } from '../src/lib/menu'
function getMeta(article) {
return {
title: article.title,
}
}
const PersonCardList = ({ children }) => (
<div className="PersonCardList">
{children}
</div>
)
const Page = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
>
<div className="content content_semi-wide">
{!props.image ? null : (
<CollectiveImage data={props.image} />
)}
<PersonCardList>
{props.persons.map((person, index) => (
<PersonCard
key={index}
profile={person}
picture={person.picture}
url={person.url}
name={person.name}
/>
))}
</PersonCardList>
</div>
</App>
)
Page.getInitialProps = async (ctx) => {
const pageUrl = ctx.req.url
const apiUrl = `http://localhost:3000/api/persons`
const res = await axios.get(apiUrl)
const persons = res.data.items
const imageFile = 'Images/HudozkaCollective2017.jpg'
const imageUrl = `http://localhost:3000/api/image?file=${imageFile}`
const resImage = await axios.get(imageUrl)
return {
persons,
image: resImage.data,
pageUrl,
}
}
export default Page
+35
View File
@@ -0,0 +1,35 @@
import React from 'react'
import axios from 'axios'
import App from '../../src/components/App'
import Document from '../../src/components/Document'
import Share from '../../src/components/Share'
import menuModel from '../../src/models/menu'
import { buildMenu } from '../../src/lib/menu'
const Page = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
>
<div className="content content_wide">
<Document {...props.file} />
<Share />
</div>
</App>
)
Page.getInitialProps = async (ctx) => {
const pageUrl = ctx.req.url
const id = ctx.query.document
const apiUrl = `http://localhost:3000/api/files/${id}`
const res = await axios.get(apiUrl)
const file = res.data
return {
file,
pageUrl,
}
}
export default Page
+135
View File
@@ -0,0 +1,135 @@
import React from 'react'
import axios from 'axios'
import App from '../src/components/App'
import menuModel from '../src/models/menu'
import { buildMenu } from '../src/lib/menu'
import DocumentList from '../src/components/DocumentList'
import ImageArtifactType from '../src/core/ImageArtifactType'
import { prioritySort } from '../src/lib/sort'
import { splitBy } from '../src/lib/array'
import { unique } from '../src/utils/common'
function getDocumentsMeta() {
return {
title: 'Документы',
description: 'Документы Шлиссельбургской ДХШ',
}
}
function getMeta(teacher) {
return {
title: teacher.name,
description: teacher.name,
}
}
function getSorted(documents) {
const compose = (...fns) => value => fns
.map(fn => fn)
.reverse()
.reduce((acc, fn) => fn(acc), value)
const pass = fn => value => {
fn(value)
return value
}
const subtract = (a, b) => a - b
const uniqueCategories = unique(i => i.category)
const priority = {
'Основные документы': [
'https://static.shlisselburg.org/art/uploads/ustav-2015.pdf',
'https://static.shlisselburg.org/art/uploads/obrazovatelnaia-litsenziia-2015.pdf',
'https://static.shlisselburg.org/art/uploads/svidetelstvo-na-pomeshchenie.pdf',
'https://static.shlisselburg.org/art/uploads/egriul.pdf',
'https://static.shlisselburg.org/art/uploads/inn.pdf',
'https://static.shlisselburg.org/art/uploads/reshenie-o-sozdanii-shkoly.pdf',
'https://static.shlisselburg.org/art/uploads/kopiia-resheniia-o-naznachenii-rukovoditelia.pdf',
'https://static.shlisselburg.org/art/uploads/postanovlenie-o-sozdanii-munitsipalnogo-obrazovatelnogo-uchrezhdeniia-kultury-detskaia-khudozhestvennaia-shkola.pdf',
],
'Учебные программы': [
'https://static.shlisselburg.org/art/uploads/uchebnaia-programma-zhivopis.pdf',
]
}
const CATEGORY_PRIORITY = [
'Основные документы',
'Планы ФХД',
'Самообследование деятельности',
'Инструкции',
'Образование',
'Учебные программы',
'Платные образовательные услуги',
'Дополнительные общеразвивающие программы',
'Документы для поступления',
]
const sortBy = fn => (a, b) => [a, b]
.map(fn)
.reduce(subtract)
const getPriorityFn = (m, list) => value => list.includes(value)
? list.indexOf(value)
: m
const safeSelect = (d, store, key) => key in store
? store[key]
: d
const categoryPriority = getPriorityFn(10000, CATEGORY_PRIORITY)
const documentPriority = ({ category, fileUrl }) => compose(
getPriorityFn.bind(null, 10000),
safeSelect.bind(null, [], priority)
)(category)(fileUrl)
const documentsOf = category => documents.filter(i => i.category === category)
const sorted = documents => documents.sort(sortBy(documentPriority))
const collectDocumentsByCategory = compose(sorted, documentsOf)
return uniqueCategories(documents)
.sort(sortBy(categoryPriority))
.reduce((acc, name) => [...acc, {
name,
items: collectDocumentsByCategory(name)
}], [])
}
const Page = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
>
<div className="content content_thin">
{/* <pre>{JSON.stringify(props, null, 4)}</pre> */}
{props.collections.map(({ name, items }, index) => (
<DocumentList
key={index}
name={name}
documents={items}
/>
))}
</div>
</App>
)
Page.getInitialProps = async (ctx) => {
const pageUrl = ctx.req.url
const apiUrl = `http://localhost:3000/api/files`
const res = await axios.get(apiUrl)
const files = res.data.items
const collections = getSorted(files)
return {
collections,
pageUrl,
}
}
export default Page
+90
View File
@@ -0,0 +1,90 @@
import React from 'react'
import axios from 'axios'
import App from '../src/components/App'
// import Album from '../src/components/Album'
import Article from '../src/components/Article'
import menuModel from '../src/models/menu'
import { buildMenu } from '../src/lib/menu'
import { splitBy } from '../src/lib/array'
const albumsByYear = splitBy(album => new Date(album.date).getFullYear())
function getMeta() {
return {
title: 'Галерея',
description: 'Галерея работ учащихся Шлиссельбургской Детской Художественной Школы'
}
}
const m = 1
const AlbumImage = ({ data, alt }) => (
<picture>
<img
className="opa"
alt={alt}
src={data.src}
width={data.width * m}
height={data.height * m}
srcSet={(data.set || []).map(({ url, density }) => `${url} ${density}x`)}
/>
</picture>
)
const GItem = ({ album }) => (
<div className="gallery-item">
<a className="invisible" href={album.url}>
<AlbumImage data={album.preview} alt={album.title} />
</a>
</div>
)
const ACollection = ({ title, albums }) => (
<div className="album-collection">
<div className="album-collection__title">{title}</div>
<div className="album-collection__body">{
albums.map((album, index) => (
<GItem key={index} album={album} />
))
}</div>
</div>
)
const Page = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
>
<div className="content content_semi-wide">
<Article
// title={meta.title}
title={'Галерея'}
>
{props.collections.map(([year, albums], index) => (
<ACollection
key={index}
title={year}
albums={albums}
/>
))
}
</Article>
</div>
</App>
)
Page.getInitialProps = async (ctx) => {
const pageUrl = ctx.req.url
const apiUrl = `http://localhost:3000/api/albums`
const res = await axios.get(apiUrl)
const items = res.data.items
const albumCollections = albumsByYear(items)
return {
collections: [...albumCollections.entries()],
pageUrl,
}
}
export default Page
+55
View File
@@ -0,0 +1,55 @@
import React from 'react'
import axios from 'axios'
import App from '../src/components/App'
import ArticleCardList from '../src/components/ArticleCardList'
import HudozkaTitle from '../src/components/HudozkaTitle'
import menuModel from '../src/models/menu'
import { buildMenu } from '../src/lib/menu'
function getMeta(article) {
return {
title: article.title,
}
}
// async function renderHome(path, pageSize) {
// return render(path, Component, getMeta({ title: 'Шлиссельбургская ДХШ' }), { showAuthor: true })
// }
const Page = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
>
<div className="content content_wide">
<HudozkaTitle />
<ArticleCardList
articles={props.articles}
prevPage={props.prevPage}
nextPage={props.nextPage}
/>
</div>
</App>
)
Page.getInitialProps = async (ctx) => {
const pageUrl = ctx.req.url
const page = 1
const pageSize = 10
const apiUrl = `http://localhost:3000/api/articles?page=${page}&pageSize=${pageSize}`
const res = await axios.get(apiUrl)
const articles = res.data.items
const nextPage = res.data.nextPage
const prevPage = res.data.prevPage
return {
articles,
nextPage,
prevPage,
pageUrl,
}
}
export default Page
+38
View File
@@ -0,0 +1,38 @@
import React from 'react'
import axios from 'axios'
import App from '../src/components/App'
import Page from '../src/components/Page'
import menuModel from '../src/models/menu'
import { buildMenu } from '../src/lib/menu'
const Index = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
>
{/* <pre>{JSON.stringify(props, null, 4)}</pre> */}
<div className={'content content_thin'}>
<Page
shareable={true}
>
{props.content}
</Page>
</div>
</App>
)
Index.getInitialProps = async (ctx) => {
const pageUrl = ctx.req.url
// // const pageUrl = '/neighbors/2018'
const apiUrl = `http://localhost:3000/api/page?page=${pageUrl}`
const res = await axios.get(apiUrl)
const page = res.data
return {
content: page.data,
pageUrl,
}
}
export default Index
+39
View File
@@ -0,0 +1,39 @@
import React from 'react'
import axios from 'axios'
import App from '../src/components/App'
import Schedule from '../src/components/Schedule'
import Share from '../src/components/Share'
import menuModel from '../src/models/menu'
import { buildMenu } from '../src/lib/menu'
const Page = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
>
<div className="content content_wide">
{/*<ScheduleList schedules={schedules}/>*/}
<Schedule {...props.schedule} />
<Share />
</div>
</App>
)
Page.getInitialProps = async (ctx) => {
const pageUrl = ctx.req.url
const period = '2016-2017'
const semester = 'spring'
const apiUrl = `http://localhost:3000/api/schedule?period=${period}&semester=${semester}`
const res = await axios.get(apiUrl)
const schedule = res.data
return {
schedule,
pageUrl,
}
}
export default Page
+35
View File
@@ -0,0 +1,35 @@
import React from 'react'
import axios from 'axios'
import App from '../../src/components/App'
import Page from '../../src/components/Page'
import menuModel from '../../src/models/menu'
import { buildMenu } from '../../src/lib/menu'
const Index = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
>
<div className="content content_thin">
<Page shareable={true}>
{props.person.post}
</Page>
</div>
</App>
)
Index.getInitialProps = async (ctx) => {
const pageUrl = ctx.req.url
const id = ctx.query.person
const apiUrl = `http://localhost:3000/api/persons/${id}`
const res = await axios.get(apiUrl)
const person = res.data
return {
pageUrl,
person,
}
}
export default Index
+62
View File
@@ -0,0 +1,62 @@
import Article from '../core/Article'
import ImageArtifactType from '../core/ImageArtifactType'
import { sortBy } from '../utils/sort'
import { timestamp } from '../lib/date'
export const sortArticleByDate = sortBy(
x => timestamp(new Date(x.date))
)
export async function findArticlesNin(nin, skip, limit, sort) {
const query = {
_id: { $nin: nin }
}
return Article.find(query, {
sort,
skip,
limit,
})
}
export async function findArticles(page, pageSize) {
const limit = pageSize
const skip = (page - 1) * pageSize
const pinnedArticles = page === 1
? await Article.findPinned()
: []
const total = await Article.total()
const totalPages = total / pageSize
const id = i => i._id
const pinnedIds = pinnedArticles.map(id)
const articles = await findArticlesNin(pinnedIds, skip, limit, { date: -1 })
const prevPage = page > 1
? page - 1
: null
const nextPage = page < totalPages
? page + 1
: null
const items = [
...pinnedArticles.sort(sortArticleByDate),
...articles
]
.map(x => x.plain())
.map(x => ({
...x,
preview: !x.preview ? null : (
x.preview.getPicture(ImageArtifactType.MEDIUM)
)
}))
return {
items,
prevPage,
nextPage,
}
}
+36
View File
@@ -0,0 +1,36 @@
import ImageArtifactType from '../core/ImageArtifactType'
const sizes = [
ImageArtifactType.MEDIUM,
ImageArtifactType.BIG,
ImageArtifactType.ORIGIN,
]
export function encodeFilePreview(file, sizes) {
const preview = file.preview
if (!preview) return null
const image = preview.findArtifact(sizes)
if (!image) return null
return image.url
}
export function encodeFile(file) {
const imageUrl = encodeFilePreview(file, sizes)
const category = (file.category || '')
.replace('Documents/', '')
.replace('Pages/', '')
.replace('Albums/', '')
.replace('Articles /', '')
return {
imageUrl,
category,
title: file.title,
fileName: file.fileInfo.name,
fileSize: file.fileInfo.size,
fileUrl: file.url,
url: file.viewUrl,
}
}
+29
View File
@@ -0,0 +1,29 @@
const { get } = require('koa-route')
const getPathWithNoTrailingSlash = require('../lib/url').getPathWithNoTrailingSlash
const Page = require('../core/Page')
function getPage() {
return get('/api/pages/*', async ctx => {
const path = getPathWithNoTrailingSlash(ctx.path)
const pagePath = path.replace('/api/pages', '')
const resource = await Page.findByUrl(pagePath)
if (!resource) {
ctx.status = 404
ctx.body = {
error: 'not found'
}
} else {
ctx.body = {
id: resource.id,
title: resource.title,
url: resource.url,
data: resource.data,
preview: resource.preview,
}
}
})
}
exports.getPage = getPage
+11
View File
@@ -0,0 +1,11 @@
import ImageArtifactType from '../core/ImageArtifactType'
const profilePicture = profile => profile.picture.getPicture(ImageArtifactType.BIG)
export function encodePerson(person) {
return {
...person,
shortName: person.shortName(),
name: person.splitName(),
picture: profilePicture(person),
}
}
+2
View File
@@ -3,6 +3,8 @@ const Menu = require('./Menu')
const Footer = require('./Footer')
const Comments = require('./Comments')
import '../style/style.scss'
const App = ({children, menu, showAuthor, menuPadding}) => (
<div className="body-wrapper theme-default">
<header>
+17 -15
View File
@@ -1,17 +1,19 @@
const React = require('react')
const Share = require('./Share')
const getHtml = require('../lib/component').getHtml
import React from 'react'
import Share from './Share'
import Html from './Html'
const Page = ({children, shareable}) => (
<div className="Article Article--cloud">
<div className="Article-Body">
{getHtml(children)}
</div>
export default function Page({ children, shareable }) {
return (
<div className={'Article Article--cloud'}>
<div className={'Article-Body'}>
<Html
html={children}
/>
</div>
{!shareable ? null : (
<Share/>
)}
</div>
)
module.exports = Page
{!shareable ? null : (
<Share />
)}
</div>
)
}
+12 -6
View File
@@ -1,13 +1,19 @@
const db = require('./db')
const store = new Map()
class Data {
static setStore(Class, collection) {
store.set(Class, collection)
}
static setStore(Class, collection) {
store.set(Class, collection)
}
static getStore(Class) {
return store.get(Class)
}
static getStore(Class) {
return store.get(Class)
}
static getCollection(name) {
return db.collection(name)
}
}
module.exports = Data
+28 -23
View File
@@ -7,37 +7,42 @@ const Document = require('./Document')
const Image = require('./Image')
const Settings = require('./Config')
const {MongoClient, ObjectId} = require('mongodb')
const { MongoClient, ObjectId } = require('mongodb')
export var db
var db
export async function connect(uri) {
const c = await MongoClient.connect(uri)
db = init(c)
return db
async function connect(uri) {
const connection = await MongoClient.connect(uri)
db = init(connection)
return db
}
export function collection(name) {
return db.collection(name)
function collection(name) {
return db.collection(name)
}
export const c = collection
export function id(i) {
if (typeof i === 'string') return ObjectId(i)
else if (i instanceof ObjectId) return i
else if (i instanceof Object) return '_id' in i ? ObjectId(i._id) : null
return null
function id(i) {
if (typeof i === 'string') return ObjectId(i)
else if (i instanceof ObjectId) return i
else if (i instanceof Object) return '_id' in i ? ObjectId(i._id) : null
return null
}
function init(db) {
Data.setStore(Page, db.collection('pages'))
Data.setStore(Article, db.collection('articles'))
Data.setStore(Album, db.collection('albums'))
Data.setStore(Image, db.collection('images'))
Data.setStore(Teacher, db.collection('collective'))
Data.setStore(Document, db.collection('documents'))
Data.setStore(Settings, db.collection('settings'))
// Data.setStore('Page', db.collection('pages'))
Data.setStore(Page, db.collection('pages'))
Data.setStore(Article, db.collection('articles'))
Data.setStore(Album, db.collection('albums'))
Data.setStore(Image, db.collection('images'))
Data.setStore(Teacher, db.collection('collective'))
Data.setStore(Document, db.collection('documents'))
Data.setStore(Settings, db.collection('settings'))
return db
return db
}
exports.getDb = () => db
exports.connect = connect
exports.collection = collection
exports.c = collection
exports.id = id
+1 -11
View File
@@ -1,8 +1,7 @@
import React from 'react'
import {renderToStaticMarkup} from 'react-dom/server'
const App = require('../components/App')
const Html = require('../components/Html')
import App from '../components/App'
function renderApp({component, ...options}) {
const app = (
@@ -13,13 +12,4 @@ function renderApp({component, ...options}) {
return renderToStaticMarkup(app)
}
function getHtml(html) {
return (
<Html
html={html}
/>
)
}
exports.renderApp = renderApp
exports.getHtml = getHtml
+9 -5
View File
@@ -1,8 +1,10 @@
const {readFile} = require('async-file')
import React from 'react'
import Html from '../components/Html'
const { readFile } = require('async-file')
const handlebars = require('handlebars')
const renderApp = require('../lib/component').renderApp
const getHtml = require('../lib/component').getHtml
const menuModel = require('../models/menu').default
const buildMenu = require('./menu').buildMenu
@@ -32,9 +34,11 @@ const isHtml = data => typeof data === 'string'
async function render(path, data, meta, options = {}) {
const renderOptions = {...defaultOptions, ...options}
const component = isHtml(data)
? getHtml(data)
: data
const component = !isHtml(data) ? data : (
<Html
html={data}
/>
)
const menu = buildMenu(path, menuModel)
const content = renderApp({...renderOptions, menu, component})
-34
View File
@@ -1,34 +0,0 @@
const React = require('react')
const Article = require('../components/Article')
const Album = require('../core/Album')
const {render} = require('../lib/render')
const getHtml = require('../lib/component').getHtml
function getMeta(album) {
return {
title: album.title,
description: album.title,
}
}
async function renderAlbum(id) {
const album = await Album.findById(id)
if (!album) return null
const Component = (
<div className="content content_thin">
<Article
title={album.title}
date={album.date}
shareable={true}
>
{getHtml(album.post)}
</Article>
</div>
)
return render(album.url, Component, getMeta(album), {commentsEnabled: true, menuPadding: true})
}
exports.renderAlbum = renderAlbum
-131
View File
@@ -1,131 +0,0 @@
const React = require('react')
const Article = require('../core/Article')
const ArticleComponent = require('../components/Article')
const ArticleCardList = require('../components/ArticleCardList')
const {render} = require('../lib/render')
const timestamp = require('../lib/date').timestamp
const ImageArtifactType = require('../core/ImageArtifactType')
const {sortBy} = require('../utils/sort')
const getHtml = require('../lib/component').getHtml
const sortArticleByDate = sortBy(
x => timestamp(new Date(x.date))
)
function getMeta(article) {
const types = [
ImageArtifactType.FACEBOOK,
ImageArtifactType.MEDIUM,
ImageArtifactType.BIG,
ImageArtifactType.ORIGIN,
]
const meta = {
title: article.title,
}
if (article.preview) {
try {
const artifact = article.preview.findArtifact(types)
meta.image = artifact.url
meta.imageWidth = artifact.width
meta.imageHeight = artifact.height
} catch (error) {
meta.image = 'https://art.shlisselburg.org/entrance.jpg'
meta.imageWidth = 1200
meta.imageHeight = 630
}
}
return meta
}
async function findArticlesNin(nin, skip, limit, sort) {
const query = {
_id: {$nin: nin}
}
return Article.find(query, {
sort,
skip,
limit,
})
}
async function createArticleCardList(page, pageSize) {
const limit = pageSize
const skip = (page - 1) * pageSize
const pinnedArticles = page === 1
? await Article.findPinned()
: []
const total = await Article.total()
const totalPages = total / pageSize
const id = i => i._id
const pinnedIds = pinnedArticles.map(id)
const articles = await findArticlesNin(pinnedIds, skip, limit, {date: -1})
const prevPage = page > 1
? page - 1
: null
const nextPage = page < totalPages
? page + 1
: null
const content = [
...pinnedArticles.sort(sortArticleByDate),
...articles
]
.map(x => x.plain())
.map(x => ({
...x,
preview: !x.preview ? null : (
x.preview.getPicture(ImageArtifactType.MEDIUM)
)
}))
return (
<ArticleCardList
articles={content}
prevPage={prevPage}
nextPage={nextPage}
/>
)
}
async function renderArticles(path, page, pageSize) {
const articles = await createArticleCardList(page, pageSize)
const Component = (
<div className="content content_wide">
{articles}
</div>
)
return render(path, Component, getMeta({title: `Статьи`}))
}
async function renderArticle(id) {
const article = await Article.findById(id)
if (!article) return null
const Component = (
<div className="content content_thin">
<ArticleComponent
title={article.title}
date={article.date}
tags={article.tags}
shareable={true}
>
{getHtml(article.post)}
</ArticleComponent>
</div>
)
return render(article.url, Component, getMeta(article), {commentsEnabled: true, menuPadding: true})
}
exports.createArticleCardList = createArticleCardList
exports.renderArticles = renderArticles
exports.renderArticle = renderArticle
-26
View File
@@ -1,26 +0,0 @@
const React = require('react')
const HudozkaTitle = require('../components/HudozkaTitle')
const {createArticleCardList} = require('../render/articles')
const {render} = require('../lib/render')
function getMeta(article) {
return {
title: article.title,
}
}
async function renderHome(path, pageSize) {
const articles = await createArticleCardList(1, pageSize)
const Component = (
<div className="content content_wide">
<HudozkaTitle/>
{articles}
</div>
)
return render(path, Component, getMeta({title: 'Шлиссельбургская ДХШ'}), {showAuthor: true})
}
exports.renderHome = renderHome
-116
View File
@@ -1,116 +0,0 @@
const React = require('react')
const Picture = require('../components/Image')
const CollectiveImage = require('../components/CollectiveImage')
const Page = require('../components/Page')
const PersonCard = require('../components/PersonCard')
const {render} = require('../lib/render')
const timestamp = require('../lib/date').timestamp
const ImageArtifactType = require('../core/ImageArtifactType')
const Teacher = require('../core/Teacher')
const Image = require('../core/Image')
const Config = require('../core/Config')
const {sortBy} = require('../utils/sort')
const {prioritySort} = require('../lib/sort')
const metaImageTypesPriority = [
ImageArtifactType.FACEBOOK,
ImageArtifactType.MEDIUM,
ImageArtifactType.BIG,
ImageArtifactType.ORIGIN,
]
function getMeta(teacher) {
const meta = {
title: teacher.name,
}
if (teacher.preview) {
try {
const artifact = teacher.preview.findArtifact(metaImageTypesPriority)
meta.image = artifact.url
meta.imageWidth = artifact.width
meta.imageHeight = artifact.height
} catch (error) {
}
}
return meta
}
function getCollectiveMeta(picture) {
const meta = {
title: 'Преподаватели Шлиссельбургской ДХШ',
description: 'Преподаватели Шлиссельбургской ДХШ',
}
if (picture) {
const artifact = picture.findArtifact(metaImageTypesPriority)
meta.image = artifact.url
meta.imageWidth = artifact.width
meta.imageHeight = artifact.height
}
return meta
}
async function renderPerson(id) {
const teacher = await Teacher.findById(id)
if (!teacher) return null
const Component = (
<div className="content content_thin">
<Page shareable={true}>{teacher.post}</Page>
</div>
)
return render(teacher.url, Component, getMeta(teacher), {commentsEnabled: true, menuPadding: true})
}
const profilePicture = profile => profile.picture.getPicture(ImageArtifactType.BIG)
async function renderCollective(path, order) {
const config = await Config.findConfig()
if (!config) return null
const collectiveImage = await Image.findByFile(config.collectiveImage)
let teachers = await Teacher.find({hidden: false})
if (!teachers) return null
const teachersSorted = prioritySort.bind(null, [...order], t => t.id)
teachers = teachersSorted(teachers)
const image = collectiveImage
? collectiveImage.getPicture(ImageArtifactType.LARGE)
: null
const PersonCardList = ({children}) => (
<div className="PersonCardList">
{children}
</div>
)
const Component = (
<div className="content content_semi-wide">
{!image ? null : (
<CollectiveImage data={image}/>
)}
<PersonCardList>
{teachers.map((teacher, index) => (
<PersonCard
key={index}
profile={teacher}
picture={profilePicture(teacher)}
url={teacher.url}
name={teacher.splitName()}
/>
))}
</PersonCardList>
</div>
)
return render(path, Component, getCollectiveMeta(collectiveImage), {menuPadding: true})
}
exports.renderPerson = renderPerson
exports.renderCollective = renderCollective
-17
View File
@@ -1,17 +0,0 @@
const {renderAlbum} = require('../render/albums')
const {get} = require('koa-route')
function getAlbum() {
return get('/album/:id', async (ctx, id) => {
const body = await renderAlbum(id)
if (body) {
ctx.type = 'text/html'
ctx.body = body
} else {
ctx.status = 404
}
})
}
exports.getAlbum = getAlbum
-46
View File
@@ -1,46 +0,0 @@
const {get} = require('koa-route')
const Article = require('../components/Article')
const {renderArticles, renderArticle} = require('../render/articles')
const getPathWithNoTrailingSlash = require('../lib/url').getPathWithNoTrailingSlash
const accepts = require('./index').accepts
function getArticles(pageSize) {
return get('/articles/:page?', async (ctx, page) => {
page = parseInt(page) || 1
if (page === 1) {
ctx.redirect('/')
} else {
const path = getPathWithNoTrailingSlash(ctx.path)
ctx.body = await renderArticles(path, page, pageSize)
}
})
}
function getArticle() {
return get('/article/:id', accepts({
'text/html': async (ctx, id) => {
const body = await renderArticle(id)
if (body) {
ctx.type = 'text/html'
ctx.body = body
} else {
ctx.status = 404
}
},
'application/json': async (ctx, id) => {
const article = await Article.findById(id)
if (article) {
ctx.body = article
} else {
ctx.status = 404
}
}
}))
}
exports.getArticles = getArticles
exports.getArticle = getArticle
-126
View File
@@ -1,126 +0,0 @@
const React = require('react')
const Share = require('../components/Share')
const DocumentComponent = require('../components/Document')
const Document = require('../core/Document')
const ImageArtifactType = require('../core/ImageArtifactType')
const {render} = require('../lib/render')
const {prioritySort} = require('../lib/sort')
const getPathWithNoTrailingSlash = require('../lib/url').getPathWithNoTrailingSlash
const {get} = require('koa-route')
const {splitBy} = require('../lib/array')
const {unique} = require('../utils/common')
const {bakeDocument} = require('./documents')
const sizes = [
ImageArtifactType.MEDIUM,
ImageArtifactType.BIG,
ImageArtifactType.ORIGIN,
]
function getMeta(document) {
return {
title: document.title,
description: document.title,
}
}
function getDocument() {
return get('/document/:id', async (ctx, id) => {
const path = getPathWithNoTrailingSlash(ctx.path)
const document = await Document.findById(id)
const data = document
? await bakeDocument(document)
: null
if (data) {
const Component = (
<div className="content content_wide">
<DocumentComponent {...data}/>
<Share/>
</div>
)
ctx.type = 'text/html'
ctx.body = await render(path, Component, getMeta(document), {menuPadding: true})
} else {
ctx.status = 404
}
})
}
function getSorted(documents) {
const compose = (...fns) => value => fns
.map(fn => fn)
.reverse()
.reduce((acc, fn) => fn(acc), value)
const pass = fn => value => {
fn(value)
return value
}
const subtract = (a, b) => a - b
const uniqueCategories = unique(i => i.category)
const priority = {
'Основные документы': [
'https://static.shlisselburg.org/art/uploads/ustav-2015.pdf',
'https://static.shlisselburg.org/art/uploads/obrazovatelnaia-litsenziia-2015.pdf',
'https://static.shlisselburg.org/art/uploads/svidetelstvo-na-pomeshchenie.pdf',
'https://static.shlisselburg.org/art/uploads/egriul.pdf',
'https://static.shlisselburg.org/art/uploads/inn.pdf',
'https://static.shlisselburg.org/art/uploads/reshenie-o-sozdanii-shkoly.pdf',
'https://static.shlisselburg.org/art/uploads/kopiia-resheniia-o-naznachenii-rukovoditelia.pdf',
'https://static.shlisselburg.org/art/uploads/postanovlenie-o-sozdanii-munitsipalnogo-obrazovatelnogo-uchrezhdeniia-kultury-detskaia-khudozhestvennaia-shkola.pdf',
],
'Учебные программы': [
'https://static.shlisselburg.org/art/uploads/uchebnaia-programma-zhivopis.pdf',
]
}
const CATEGORY_PRIORITY = [
'Основные документы',
'Планы ФХД',
'Самообследование деятельности',
'Инструкции',
'Образование',
'Учебные программы',
'Платные образовательные услуги',
'Дополнительные общеразвивающие программы',
'Документы для поступления',
]
const sortBy = fn => (a, b) => [a, b]
.map(fn)
.reduce(subtract)
const getPriorityFn = (m, list) => value => list.includes(value)
? list.indexOf(value)
: m
const safeSelect = (d, store, key) => key in store
? store[key]
: d
const categoryPriority = getPriorityFn(10000, CATEGORY_PRIORITY)
const documentPriority = ({category, fileUrl}) => compose(
getPriorityFn.bind(null, 10000),
safeSelect.bind(null, [], priority)
)(category)(fileUrl)
const documentsOf = category => documents.filter(i => i.category === category)
const sorted = documents => documents.sort(sortBy(documentPriority))
const collectDocumentsByCategory = compose(sorted, documentsOf)
return uniqueCategories(documents)
.sort(sortBy(categoryPriority))
.reduce((acc, name) => [...acc, {
name,
items: collectDocumentsByCategory(name)
}], [])
}
exports.getDocument = getDocument
-159
View File
@@ -1,159 +0,0 @@
const React = require('react')
const Article = require('../components/Article')
const DocumentList = require('../components/DocumentList')
const Document = require('../core/Document')
const ImageArtifactType = require('../core/ImageArtifactType')
const {render} = require('../lib/render')
const {prioritySort} = require('../lib/sort')
const getPathWithNoTrailingSlash = require('../lib/url').getPathWithNoTrailingSlash
const {get} = require('koa-route')
const {splitBy} = require('../lib/array')
const {unique} = require('../utils/common')
const sizes = [
ImageArtifactType.MEDIUM,
ImageArtifactType.BIG,
ImageArtifactType.ORIGIN,
]
function getDocumentsMeta() {
return {
title: 'Документы',
description: 'Документы Шлиссельбургской ДХШ',
}
}
function getMeta(teacher) {
return {
title: teacher.name,
description: teacher.name,
}
}
async function bakeDocument(document) {
const preview = document.preview
if (!preview) return null
const image = preview.findArtifact(sizes)
if (!image) return null
const category = (document.category || '')
.replace('Documents/', '')
.replace('Pages/', '')
.replace('Albums/', '')
.replace('Articles /', '')
return {
category,
title: document.title,
fileName: document.fileInfo.name,
fileSize: document.fileInfo.size,
fileUrl: document.url,
imageUrl: image.url,
url: document.viewUrl,
}
}
function getDocuments() {
return get('/documents', async ctx => {
const path = getPathWithNoTrailingSlash(ctx.path)
let documents = await Document.find({hidden: false})
if (documents) {
documents = await Promise.all(documents.map(bakeDocument))
documents = documents.filter(Boolean)
const collections = getSorted(documents)
const Component = (
<div className="content content_thin">
{collections.map(({name, items}, index) => (
<DocumentList key={index} name={name} documents={items}/>
))}
</div>
)
ctx.type = 'text/html'
ctx.body = await render(path, Component, getDocumentsMeta(), {menuPadding: true})
} else {
ctx.status = 404
}
})
}
function getSorted(documents) {
const compose = (...fns) => value => fns
.map(fn => fn)
.reverse()
.reduce((acc, fn) => fn(acc), value)
const pass = fn => value => {
fn(value)
return value
}
const subtract = (a, b) => a - b
const uniqueCategories = unique(i => i.category)
const priority = {
'Основные документы': [
'https://static.shlisselburg.org/art/uploads/ustav-2015.pdf',
'https://static.shlisselburg.org/art/uploads/obrazovatelnaia-litsenziia-2015.pdf',
'https://static.shlisselburg.org/art/uploads/svidetelstvo-na-pomeshchenie.pdf',
'https://static.shlisselburg.org/art/uploads/egriul.pdf',
'https://static.shlisselburg.org/art/uploads/inn.pdf',
'https://static.shlisselburg.org/art/uploads/reshenie-o-sozdanii-shkoly.pdf',
'https://static.shlisselburg.org/art/uploads/kopiia-resheniia-o-naznachenii-rukovoditelia.pdf',
'https://static.shlisselburg.org/art/uploads/postanovlenie-o-sozdanii-munitsipalnogo-obrazovatelnogo-uchrezhdeniia-kultury-detskaia-khudozhestvennaia-shkola.pdf',
],
'Учебные программы': [
'https://static.shlisselburg.org/art/uploads/uchebnaia-programma-zhivopis.pdf',
]
}
const CATEGORY_PRIORITY = [
'Основные документы',
'Планы ФХД',
'Самообследование деятельности',
'Инструкции',
'Образование',
'Учебные программы',
'Платные образовательные услуги',
'Дополнительные общеразвивающие программы',
'Документы для поступления',
]
const sortBy = fn => (a, b) => [a, b]
.map(fn)
.reduce(subtract)
const getPriorityFn = (m, list) => value => list.includes(value)
? list.indexOf(value)
: m
const safeSelect = (d, store, key) => key in store
? store[key]
: d
const categoryPriority = getPriorityFn(10000, CATEGORY_PRIORITY)
const documentPriority = ({category, fileUrl}) => compose(
getPriorityFn.bind(null, 10000),
safeSelect.bind(null, [], priority)
)(category)(fileUrl)
const documentsOf = category => documents.filter(i => i.category === category)
const sorted = documents => documents.sort(sortBy(documentPriority))
const collectDocumentsByCategory = compose(sorted, documentsOf)
return uniqueCategories(documents)
.sort(sortBy(categoryPriority))
.reduce((acc, name) => [...acc, {
name,
items: collectDocumentsByCategory(name)
}], [])
}
exports.bakeDocument = bakeDocument
exports.getDocuments = getDocuments
-90
View File
@@ -1,90 +0,0 @@
const React = require('react')
const Article = require('../components/Article')
const Album = require('../core/Album')
const {render} = require('../lib/render')
const {splitBy} = require('../lib/array')
const getPathWithNoTrailingSlash = require('../lib/url').getPathWithNoTrailingSlash
const {get} = require('koa-route')
function getMeta() {
return {
title: 'Галерея',
description: 'Галерея работ учащихся Шлиссельбургской Детской Художественной Школы'
}
}
// {data.set.map((s, i) => (
// <source
// key={i}
// src={i}
// />
// ))}
// const m = 0.5
const m = 1
const AlbumImage = ({data, alt}) => (
<picture>
<img
className="opa"
alt={alt}
src={data.src}
width={data.width * m}
height={data.height * m}
srcSet={(data.set || []).map(({url, density}) => `${url} ${density}x`)}
/>
</picture>
)
const GItem = ({album}) => (
<div className="gallery-item">
<a className="invisible" href={album.url}>
<AlbumImage data={album.preview} alt={album.title}/>
</a>
</div>
)
const ACollection = ({title, albums}) => (
<div className="album-collection">
<div className="album-collection__title">{title}</div>
<div className="album-collection__body">{
albums.map((album, index) => (
<GItem key={index} album={album}/>
))
}</div>
</div>
)
function getGallery() {
return get('/gallery', async ctx => {
const path = getPathWithNoTrailingSlash(ctx.path)
const query = {}
const albums = await Album.find(query, {sort: {date: -1}})
if (albums.length) {
const meta = getMeta()
const albumsByYear = splitBy(a => a.date.getFullYear())(albums)
const Component = (
<div className="content content_wide">
<Article title={meta.title}>
{
[...albumsByYear.entries()].map(([year, albums], index) => (
<ACollection
key={index}
title={year}
albums={albums}
/>
))
}
</Article>
</div>
)
ctx.type = 'text/html'
ctx.body = await render(path, Component, getMeta(), {menuPadding: true})
} else {
ctx.status = 404
}
})
}
exports.getGallery = getGallery
-12
View File
@@ -1,12 +0,0 @@
const {get} = require('koa-route')
const {renderHome} = require('../render/home')
const getPathWithNoTrailingSlash = require('../lib/url').getPathWithNoTrailingSlash
function getHome(pageSize) {
return get('/', async ctx => {
const path = getPathWithNoTrailingSlash(ctx.path)
ctx.body = await renderHome(path, pageSize)
})
}
exports.getHome = getHome
-67
View File
@@ -1,67 +0,0 @@
import {readFile} from 'async-file'
import handlebars from 'handlebars'
import {map, assign} from '../utils/common'
import {index as indexFile} from '../config'
export function queryObject() {
let toTrue = map(true, i => i === 'true')
let toFalse = map(false, i => i === 'false')
const processors = [toTrue, toFalse]
return async(ctx, next) => {
ctx.query = Object
.keys(ctx.query)
.reduce((query, key) => assign(
query,
key,
processors
.reduce((value, fn) => fn(value), query[key])
), ctx.query)
await next()
}
}
export function accepts(routes) {
return async function (ctx) {
const types = Object
.keys(routes)
.filter(type => ctx.request.accepts(type))
if (types.length) {
let fn = routes[types[0]]
await fn.apply(ctx, arguments)
} else {
ctx.status = 406
}
}
}
export function index(fn) {
return async function (ctx) {
let test = true
if (fn) test = await fn.apply(ctx, arguments)
if (!test) ctx.status = 404
const data = await getInitialData()
const source = await readFile(indexFile, 'utf-8')
const template = handlebars.compile(source)
ctx.type = 'text/html'
ctx.body = template({data: JSON.stringify(data)})
}
}
export function json(fn) {
return accepts({
'text/html': index(),
'text/plain': index(),
'application/json': fn
})
}
async function getInitialData() {
return {pages: []}
}
-16
View File
@@ -1,16 +0,0 @@
const {renderPage} = require('../render/page')
const {get} = require('koa-route')
const getPathWithNoTrailingSlash = require('../lib/url').getPathWithNoTrailingSlash
module.exports = function (handleNotFound) {
return get('*', async ctx => {
const path = getPathWithNoTrailingSlash(ctx.path)
const body = await renderPage(path)
if (body) {
ctx.type = 'text/html'
ctx.body = body
} else {
await handleNotFound(ctx)
}
})
}
-34
View File
@@ -1,34 +0,0 @@
const getPathWithNoTrailingSlash = require('../lib/url').getPathWithNoTrailingSlash
const {renderPerson, renderCollective} = require('../render/persons')
const {get} = require('koa-route')
function getCollective(order) {
return get('/collective', async ctx => {
const path = getPathWithNoTrailingSlash(ctx.path)
const body = await renderCollective(path, order)
if (body) {
ctx.type = 'text/html'
ctx.body = body
} else {
ctx.status = 404
}
})
}
function getTeacher() {
return get('/teacher/:id', async (ctx, id) => {
const body = await renderPerson(id)
if (body) {
ctx.type = 'text/html'
ctx.body = body
} else {
ctx.status = 404
}
})
}
exports.getCollective = getCollective
exports.getTeacher = getTeacher
+33 -88
View File
@@ -1,101 +1,46 @@
import path from 'path'
import { createServer } from 'http'
import Koa from 'koa'
import serve from 'koa-static'
import logger from 'koa-logger'
import convert from 'koa-convert'
import conditional from 'koa-conditional-get'
import etag from 'koa-etag'
import helmet from 'koa-helmet'
import cookie from 'koa-cookie'
import session from 'koa-session'
import bodyParser from 'koa-bodyparser'
import next from 'next'
import {queryObject} from './routes'
import handlebars from 'handlebars'
const {sessionConfig, view404} = require('./config')
const error = require('./middlewares/error')
const redirect = require('./middlewares/redirect')
const home = require('./routes/home')
const article = require('./routes/articles')
const albums = require('./routes/albums')
const gallery = require('./routes/gallery')
const teachers = require('./routes/teachers')
const documents = require('./routes/documents')
const document = require('./routes/document')
const schedule = require('./routes/schedule')
const pages = require('./routes/pages')
const sitemap = require('./routes/sitemap')
const dirPublic = path.join(__dirname, '../public')
handlebars.registerHelper('raw-helper', options => options.fn())
const Koa = require('koa')
const next = require('next')
const { get } = require('koa-route')
const { connect } = require('./core/db')
const Page = require('./core/Page')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
const port = parseInt(process.env.PORT, 10) || 3000
function nextHandler() {
return async ctx => {
await handle(ctx.req, ctx.res)
ctx.respond = false
app.prepare().then(async () => {
try {
await connect(process.env.DB_URI)
} catch (e) {
console.error(e.message)
process.exit(1)
}
}
export default async function (config) {
const $ = convert
await app.prepare()
const server = new Koa()
server.keys = ['1234']
server.proxy = true
server.use(bodyParser({
extendTypes: {
json: 'application/ejson'
}
}))
server.use(logger())
server.use(convert(session(sessionConfig, server)))
server.use(cookie())
server.use($(conditional()))
server.use($(etag()))
server.use(serve(dirPublic))
server.use(redirect(config.redirect))
server.use($(helmet()))
server.use(queryObject())
server.use(async (ctx, next) => {
if (ctx.session.isNew) {
ctx.session.startDate = new Date()
}
await next()
})
server.use(error.notFound(view404))
server.use(sitemap())
// server.use(edit.getEdit())
server.use(home.getHome(config.articlesPerPage))
server.use(gallery.getGallery())
server.use(article.getArticles(config.articlesPerPage))
server.use(article.getArticle())
server.use(albums.getAlbum())
server.use(teachers.getCollective(config.collectiveOrder))
server.use(teachers.getTeacher())
server.use(documents.getDocuments())
server.use(document.getDocument())
server.use(schedule.getSchedule())
server.use(pages(nextHandler()))
const pages = await Page.find({})
const pagesUrls = pages.map(x => x.url)
const server = new Koa()
server.use(async (ctx, next) => {
ctx.res.statusCode = 200
await next()
})
return createServer(server.callback())
}
server.use(get('*', async ctx => {
const url = ctx.req.url
if (pagesUrls.includes(url)) {
await app.render(ctx.req, ctx.res, '/next', ctx.query)
} else {
await handle(ctx.req, ctx.res)
}
ctx.respond = false
}))
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`)
})
})