From 43af2996bf749e687c95a5006c5a4fb4843cb591 Mon Sep 17 00:00:00 2001
From: Roman Timashev
Date: Thu, 1 Aug 2019 14:44:25 +0300
Subject: [PATCH] app moved to next.js
---
package.json | 10 +-
pages/_document.jsx | 27 ++++++
pages/album/[album].js | 42 +++++++++
pages/api/albums/[album].js | 19 ++++
pages/api/albums/index.js | 19 ++++
pages/api/articles/[article].js | 22 +++++
pages/api/articles/index.js | 22 +++++
pages/api/files/[document].js | 20 ++++
pages/api/files/index.js | 24 +++++
pages/api/image.js | 21 +++++
pages/api/page.js | 26 ++++++
pages/api/persons/[person].js | 21 +++++
pages/api/persons/index.js | 33 +++++++
pages/api/schedule.js | 20 ++++
pages/article/[article].jsx | 43 +++++++++
pages/articles/[page].jsx | 52 +++++++++++
pages/collective.jsx | 64 +++++++++++++
pages/document/[document].jsx | 35 +++++++
pages/documents.jsx | 135 +++++++++++++++++++++++++++
pages/gallery.jsx | 90 ++++++++++++++++++
pages/index.jsx | 55 +++++++++++
pages/next.jsx | 38 ++++++++
pages/schedule.jsx | 39 ++++++++
pages/teacher/[person].jsx | 35 +++++++
src/api/articles.js | 62 +++++++++++++
src/api/files.js | 36 ++++++++
src/api/pages.js | 29 ++++++
src/api/persons.js | 11 +++
src/components/App.js | 2 +
src/components/Page.js | 32 ++++---
src/core/Data.js | 18 ++--
src/core/db.js | 51 +++++-----
src/lib/component.js | 12 +--
src/lib/render.js | 14 ++-
src/render/albums.js | 34 -------
src/render/articles.js | 131 --------------------------
src/render/home.js | 26 ------
src/render/persons.js | 116 -----------------------
src/routes/albums.js | 17 ----
src/routes/articles.js | 46 ---------
src/routes/document.js | 126 -------------------------
src/routes/documents.js | 159 --------------------------------
src/routes/gallery.js | 90 ------------------
src/routes/home.js | 12 ---
src/routes/index.js | 67 --------------
src/routes/pages.js | 16 ----
src/routes/teachers.js | 34 -------
src/server.js | 121 +++++++-----------------
48 files changed, 1147 insertions(+), 1027 deletions(-)
create mode 100644 pages/_document.jsx
create mode 100644 pages/album/[album].js
create mode 100644 pages/api/albums/[album].js
create mode 100644 pages/api/albums/index.js
create mode 100644 pages/api/articles/[article].js
create mode 100644 pages/api/articles/index.js
create mode 100644 pages/api/files/[document].js
create mode 100644 pages/api/files/index.js
create mode 100644 pages/api/image.js
create mode 100644 pages/api/page.js
create mode 100644 pages/api/persons/[person].js
create mode 100644 pages/api/persons/index.js
create mode 100644 pages/api/schedule.js
create mode 100644 pages/article/[article].jsx
create mode 100644 pages/articles/[page].jsx
create mode 100644 pages/collective.jsx
create mode 100644 pages/document/[document].jsx
create mode 100644 pages/documents.jsx
create mode 100644 pages/gallery.jsx
create mode 100644 pages/index.jsx
create mode 100644 pages/next.jsx
create mode 100644 pages/schedule.jsx
create mode 100644 pages/teacher/[person].jsx
create mode 100644 src/api/articles.js
create mode 100644 src/api/files.js
create mode 100644 src/api/pages.js
create mode 100644 src/api/persons.js
delete mode 100644 src/render/albums.js
delete mode 100644 src/render/articles.js
delete mode 100644 src/render/home.js
delete mode 100644 src/render/persons.js
delete mode 100644 src/routes/albums.js
delete mode 100644 src/routes/articles.js
delete mode 100644 src/routes/document.js
delete mode 100644 src/routes/documents.js
delete mode 100644 src/routes/gallery.js
delete mode 100644 src/routes/home.js
delete mode 100644 src/routes/index.js
delete mode 100644 src/routes/pages.js
delete mode 100644 src/routes/teachers.js
diff --git a/package.json b/package.json
index 0722d249..d75ce02c 100644
--- a/package.json
+++ b/package.json
@@ -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"
diff --git a/pages/_document.jsx b/pages/_document.jsx
new file mode 100644
index 00000000..69d7a40f
--- /dev/null
+++ b/pages/_document.jsx
@@ -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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/pages/album/[album].js b/pages/album/[album].js
new file mode 100644
index 00000000..a9a18ebe
--- /dev/null
+++ b/pages/album/[album].js
@@ -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) => (
+
+
+
+)
+
+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
\ No newline at end of file
diff --git a/pages/api/albums/[album].js b/pages/api/albums/[album].js
new file mode 100644
index 00000000..a88f69e9
--- /dev/null
+++ b/pages/api/albums/[album].js
@@ -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`
+ })
+ }
+}
diff --git a/pages/api/albums/index.js b/pages/api/albums/index.js
new file mode 100644
index 00000000..de1cf601
--- /dev/null
+++ b/pages/api/albums/index.js
@@ -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`
+ })
+ }
+}
diff --git a/pages/api/articles/[article].js b/pages/api/articles/[article].js
new file mode 100644
index 00000000..3a35e4af
--- /dev/null
+++ b/pages/api/articles/[article].js
@@ -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`
+ })
+ }
+}
diff --git a/pages/api/articles/index.js b/pages/api/articles/index.js
new file mode 100644
index 00000000..fd977d01
--- /dev/null
+++ b/pages/api/articles/index.js
@@ -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`
+ })
+ }
+}
diff --git a/pages/api/files/[document].js b/pages/api/files/[document].js
new file mode 100644
index 00000000..60758a54
--- /dev/null
+++ b/pages/api/files/[document].js
@@ -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`
+ })
+ }
+}
diff --git a/pages/api/files/index.js b/pages/api/files/index.js
new file mode 100644
index 00000000..44728a8a
--- /dev/null
+++ b/pages/api/files/index.js
@@ -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`
+ })
+ }
+}
diff --git a/pages/api/image.js b/pages/api/image.js
new file mode 100644
index 00000000..a4465dbd
--- /dev/null
+++ b/pages/api/image.js
@@ -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`
+ })
+ }
+}
diff --git a/pages/api/page.js b/pages/api/page.js
new file mode 100644
index 00000000..7012f719
--- /dev/null
+++ b/pages/api/page.js
@@ -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`
+ })
+ }
+}
diff --git a/pages/api/persons/[person].js b/pages/api/persons/[person].js
new file mode 100644
index 00000000..08ba331c
--- /dev/null
+++ b/pages/api/persons/[person].js
@@ -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`
+ })
+ }
+}
diff --git a/pages/api/persons/index.js b/pages/api/persons/index.js
new file mode 100644
index 00000000..aa4b8355
--- /dev/null
+++ b/pages/api/persons/index.js
@@ -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`
+ })
+ }
+}
diff --git a/pages/api/schedule.js b/pages/api/schedule.js
new file mode 100644
index 00000000..ef596840
--- /dev/null
+++ b/pages/api/schedule.js
@@ -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`
+ })
+ }
+}
diff --git a/pages/article/[article].jsx b/pages/article/[article].jsx
new file mode 100644
index 00000000..897f80f7
--- /dev/null
+++ b/pages/article/[article].jsx
@@ -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) => (
+
+
+
+)
+
+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
\ No newline at end of file
diff --git a/pages/articles/[page].jsx b/pages/articles/[page].jsx
new file mode 100644
index 00000000..7cb685be
--- /dev/null
+++ b/pages/articles/[page].jsx
@@ -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) => (
+
+
+
+)
+
+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
\ No newline at end of file
diff --git a/pages/collective.jsx b/pages/collective.jsx
new file mode 100644
index 00000000..2d4f75e4
--- /dev/null
+++ b/pages/collective.jsx
@@ -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 }) => (
+
+ {children}
+
+)
+
+const Page = (props) => (
+
+
+ {!props.image ? null : (
+
+ )}
+
+
+ {props.persons.map((person, index) => (
+
+ ))}
+
+
+
+)
+
+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
\ No newline at end of file
diff --git a/pages/document/[document].jsx b/pages/document/[document].jsx
new file mode 100644
index 00000000..bc53f65d
--- /dev/null
+++ b/pages/document/[document].jsx
@@ -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) => (
+
+
+
+
+
+
+)
+
+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
\ No newline at end of file
diff --git a/pages/documents.jsx b/pages/documents.jsx
new file mode 100644
index 00000000..80a79b0b
--- /dev/null
+++ b/pages/documents.jsx
@@ -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) => (
+
+
+ {/*
{JSON.stringify(props, null, 4)} */}
+
+ {props.collections.map(({ name, items }, index) => (
+
+ ))}
+
+
+)
+
+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
\ No newline at end of file
diff --git a/pages/gallery.jsx b/pages/gallery.jsx
new file mode 100644
index 00000000..3dd84b73
--- /dev/null
+++ b/pages/gallery.jsx
@@ -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 }) => (
+
+
`${url} ${density}x`)}
+ />
+
+)
+
+const GItem = ({ album }) => (
+
+)
+
+const ACollection = ({ title, albums }) => (
+
+
{title}
+
{
+ albums.map((album, index) => (
+
+ ))
+ }
+
+)
+
+
+const Page = (props) => (
+
+
+
+ {props.collections.map(([year, albums], index) => (
+
+ ))
+ }
+
+
+
+)
+
+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
\ No newline at end of file
diff --git a/pages/index.jsx b/pages/index.jsx
new file mode 100644
index 00000000..bdaf7802
--- /dev/null
+++ b/pages/index.jsx
@@ -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) => (
+
+
+
+)
+
+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
\ No newline at end of file
diff --git a/pages/next.jsx b/pages/next.jsx
new file mode 100644
index 00000000..9e320461
--- /dev/null
+++ b/pages/next.jsx
@@ -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) => (
+
+ {/* {JSON.stringify(props, null, 4)} */}
+
+
+)
+
+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
\ No newline at end of file
diff --git a/pages/schedule.jsx b/pages/schedule.jsx
new file mode 100644
index 00000000..df94c351
--- /dev/null
+++ b/pages/schedule.jsx
@@ -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) => (
+
+
+ {/**/}
+
+
+
+
+
+
+)
+
+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
\ No newline at end of file
diff --git a/pages/teacher/[person].jsx b/pages/teacher/[person].jsx
new file mode 100644
index 00000000..3bf95cc5
--- /dev/null
+++ b/pages/teacher/[person].jsx
@@ -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) => (
+
+
+
+ {props.person.post}
+
+
+
+)
+
+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
\ No newline at end of file
diff --git a/src/api/articles.js b/src/api/articles.js
new file mode 100644
index 00000000..88ebf709
--- /dev/null
+++ b/src/api/articles.js
@@ -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,
+ }
+}
\ No newline at end of file
diff --git a/src/api/files.js b/src/api/files.js
new file mode 100644
index 00000000..1866de43
--- /dev/null
+++ b/src/api/files.js
@@ -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,
+ }
+}
diff --git a/src/api/pages.js b/src/api/pages.js
new file mode 100644
index 00000000..58fc21d9
--- /dev/null
+++ b/src/api/pages.js
@@ -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
diff --git a/src/api/persons.js b/src/api/persons.js
new file mode 100644
index 00000000..e04b1155
--- /dev/null
+++ b/src/api/persons.js
@@ -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),
+ }
+}
\ No newline at end of file
diff --git a/src/components/App.js b/src/components/App.js
index b990e38e..2d669ae8 100644
--- a/src/components/App.js
+++ b/src/components/App.js
@@ -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}) => (
+ )
+}
diff --git a/src/core/Data.js b/src/core/Data.js
index 5f84f721..70388deb 100644
--- a/src/core/Data.js
+++ b/src/core/Data.js
@@ -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
diff --git a/src/core/db.js b/src/core/db.js
index b3a6e123..e0022efb 100644
--- a/src/core/db.js
+++ b/src/core/db.js
@@ -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
\ No newline at end of file
diff --git a/src/lib/component.js b/src/lib/component.js
index 3be95f64..6da5df89 100644
--- a/src/lib/component.js
+++ b/src/lib/component.js
@@ -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 (
-
- )
-}
-
exports.renderApp = renderApp
-exports.getHtml = getHtml
diff --git a/src/lib/render.js b/src/lib/render.js
index 1ce10f4c..d793e9be 100644
--- a/src/lib/render.js
+++ b/src/lib/render.js
@@ -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 : (
+
+ )
const menu = buildMenu(path, menuModel)
const content = renderApp({...renderOptions, menu, component})
diff --git a/src/render/albums.js b/src/render/albums.js
deleted file mode 100644
index 4f48cd04..00000000
--- a/src/render/albums.js
+++ /dev/null
@@ -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 = (
-
-
- {getHtml(album.post)}
-
-
- )
-
- return render(album.url, Component, getMeta(album), {commentsEnabled: true, menuPadding: true})
-}
-
-exports.renderAlbum = renderAlbum
diff --git a/src/render/articles.js b/src/render/articles.js
deleted file mode 100644
index db70403c..00000000
--- a/src/render/articles.js
+++ /dev/null
@@ -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 (
-
- )
-}
-
-async function renderArticles(path, page, pageSize) {
- const articles = await createArticleCardList(page, pageSize)
-
- const Component = (
-
- {articles}
-
- )
-
- return render(path, Component, getMeta({title: `Статьи`}))
-}
-
-async function renderArticle(id) {
- const article = await Article.findById(id)
- if (!article) return null
-
- const Component = (
-
-
- {getHtml(article.post)}
-
-
- )
-
- return render(article.url, Component, getMeta(article), {commentsEnabled: true, menuPadding: true})
-}
-
-exports.createArticleCardList = createArticleCardList
-exports.renderArticles = renderArticles
-exports.renderArticle = renderArticle
diff --git a/src/render/home.js b/src/render/home.js
deleted file mode 100644
index 5707506d..00000000
--- a/src/render/home.js
+++ /dev/null
@@ -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 = (
-
-
-
- {articles}
-
- )
-
- return render(path, Component, getMeta({title: 'Шлиссельбургская ДХШ'}), {showAuthor: true})
-}
-
-exports.renderHome = renderHome
diff --git a/src/render/persons.js b/src/render/persons.js
deleted file mode 100644
index db65fbd5..00000000
--- a/src/render/persons.js
+++ /dev/null
@@ -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 = (
-
- )
-
- 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}) => (
-
- {children}
-
- )
-
- const Component = (
-
- {!image ? null : (
-
- )}
-
-
- {teachers.map((teacher, index) => (
-
- ))}
-
-
- )
-
- return render(path, Component, getCollectiveMeta(collectiveImage), {menuPadding: true})
-}
-
-exports.renderPerson = renderPerson
-exports.renderCollective = renderCollective
diff --git a/src/routes/albums.js b/src/routes/albums.js
deleted file mode 100644
index 5f343912..00000000
--- a/src/routes/albums.js
+++ /dev/null
@@ -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
diff --git a/src/routes/articles.js b/src/routes/articles.js
deleted file mode 100644
index 081608cf..00000000
--- a/src/routes/articles.js
+++ /dev/null
@@ -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
diff --git a/src/routes/document.js b/src/routes/document.js
deleted file mode 100644
index b86d327e..00000000
--- a/src/routes/document.js
+++ /dev/null
@@ -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 = (
-
-
-
-
-
- )
-
- 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
diff --git a/src/routes/documents.js b/src/routes/documents.js
deleted file mode 100644
index ee4b527a..00000000
--- a/src/routes/documents.js
+++ /dev/null
@@ -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 = (
-
- {collections.map(({name, items}, index) => (
-
- ))}
-
- )
-
- 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
diff --git a/src/routes/gallery.js b/src/routes/gallery.js
deleted file mode 100644
index fed8aa6c..00000000
--- a/src/routes/gallery.js
+++ /dev/null
@@ -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) => (
-//
-// ))}
-// const m = 0.5
-const m = 1
-const AlbumImage = ({data, alt}) => (
-
-
`${url} ${density}x`)}
- />
-
-)
-
-const GItem = ({album}) => (
-
-)
-
-const ACollection = ({title, albums}) => (
-
-
{title}
-
{
- albums.map((album, index) => (
-
- ))
- }
-
-)
-
-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 = (
-
-
- {
- [...albumsByYear.entries()].map(([year, albums], index) => (
-
- ))
- }
-
-
- )
-
- ctx.type = 'text/html'
- ctx.body = await render(path, Component, getMeta(), {menuPadding: true})
- } else {
- ctx.status = 404
- }
- })
-}
-
-exports.getGallery = getGallery
diff --git a/src/routes/home.js b/src/routes/home.js
deleted file mode 100644
index 63c395bb..00000000
--- a/src/routes/home.js
+++ /dev/null
@@ -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
diff --git a/src/routes/index.js b/src/routes/index.js
deleted file mode 100644
index 131a5420..00000000
--- a/src/routes/index.js
+++ /dev/null
@@ -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: []}
-}
diff --git a/src/routes/pages.js b/src/routes/pages.js
deleted file mode 100644
index 3724823c..00000000
--- a/src/routes/pages.js
+++ /dev/null
@@ -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)
- }
- })
-}
diff --git a/src/routes/teachers.js b/src/routes/teachers.js
deleted file mode 100644
index c25b81fd..00000000
--- a/src/routes/teachers.js
+++ /dev/null
@@ -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
diff --git a/src/server.js b/src/server.js
index 082b5b1e..52aae47c 100644
--- a/src/server.js
+++ b/src/server.js
@@ -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}`)
+ })
+})
+