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 }) => ( + + {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)}
*/} +
+ + {props.content} + +
+
+) + +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/components/Page.js b/src/components/Page.js index f3a33210..35a7c13f 100644 --- a/src/components/Page.js +++ b/src/components/Page.js @@ -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}) => ( -
-
- {getHtml(children)} -
+export default function Page({ children, shareable }) { + return ( +
+
+ +
- {!shareable ? null : ( - - )} -
-) - -module.exports = Page + {!shareable ? null : ( + + )} +
+ ) +} 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 = ( -
- {teacher.post} -
- ) - - 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}) => ( - - {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}`) + }) +}) +