Merge pull request #9 from tmshv/feature/next-static-export

Next static export
This commit is contained in:
tmshv authored and GitHub committed 2020-02-15 07:26:10 +03:00
commit a91daa07d1
97 files changed
+3441 -12463

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
{
"presets": [
"next/babel"
]
}
+2 -1
View File
@@ -18,4 +18,5 @@ dist
**/*.pyc
/coverage
**/.vscode
*.log
*.log
.now
+5 -4
View File
@@ -1,4 +1,5 @@
utils
scripts
test
src
.forestry
./utils
modules
data
uploads
+1 -1
View File
@@ -4,7 +4,7 @@ import { getPathWithNoTrailingSlash } from '../lib/url'
import { encodePage } from '../factory/page'
export async function getItem(req: Request, res: Response) {
const id = req.params.slug
const id = req.query.page
const page = getPathWithNoTrailingSlash(id)
const resource = await Page.findByUrl(page)
+1 -1
View File
@@ -42,7 +42,7 @@ async function main() {
app.get('/pages', page.getAll)
app.get('/pages/urls', page.getUrls)
app.get('/pages/:slug', page.getItem)
app.get('/page', page.getItem)
app.get('/images/:slug', image.getItem)
+6 -628
View File
@@ -1,635 +1,13 @@
const withCss = require('@zeit/next-css')
const withSass = require('@zeit/next-sass')
const { range } = require('lodash')
function createPath(page, map, query, items) {
return items.reduce((acc, path) => {
return {
...acc,
[path]: {
page,
query: query(map(path)),
}
}
}, {})
}
module.exports = withCss(withSass({
exportTrailingSlash: true,
serverRuntimeConfig: {
dbUri: '',
// Will only be available on the server side
// mySecret: 'secret',
// secondSecret: process.env.SECOND_SECRET // Pass through env variables
env: {
APP_ARTICLES_PAGE_SIZE: 15,
},
webpack(config) {
config.resolve.modules.push(__dirname)
return config;
},
exportPathMap: async (defaultPathMap, { dev }) => {
if (dev) {
return null
}
const articlesPageSize = 15
const articlesLastPage = 8
return {
// ...defaultPathMap,
'/': { page: '/', query: { pageSize: articlesPageSize } },
'/documents': { page: '/documents' },
'/gallery': { page: '/gallery' },
'/collective': { page: '/collective' },
'/schedule': { page: '/schedule' },
...createPath(
'/articles/[page]',
x => x.replace('/articles/', ''),
page => ({ page, pageSize: articlesPageSize }),
range(1, articlesLastPage + 1).map(x => `/articles/${x}`),
),
...createPath(
'/next',
x => x,
() => ({}),
[
'/info/vacancy',
'/info/new',
'/info/support',
'/info/education',
'/info/standards',
'/info/main',
'/info/paid-services',
'/info',
'/info/tools',
'/info/grants',
'/info/structure',
'/info/finances',
'/courses',
'/courses/batik',
'/courses/ceramics',
'/courses/composition',
'/courses/costume-composition',
'/courses/cg',
'/courses/scale-modeling',
'/courses/drawing',
'/courses/easel-composition',
'/courses/felting',
'/info/history',
'/courses/art-history',
'/info/documents',
'/info/online-education',
'/info/education/listochki',
'/info/education/matreshki',
'/info/education/perishki',
'/info/support/objects',
'/info/support/tools',
'/info/support/sport',
'/info/support/network',
'/info/education/painting',
'/info/education/art-for-beginners',
'/join',
'/info/audit',
'/summer-school/2017',
'/info/health-saving',
'/summer-school/2017',
'/info/education/painting',
'/info/education/art-for-beginners',
'/info/education/listochki',
'/info/education/matreshki',
'/info/education/perishki',
'/info/vacancy',
'/info/online-education',
'/info/documents',
'/info/new',
'/info/support',
'/info/support/objects',
'/info/support/tools',
'/info/education',
'/info/standards',
'/info/main',
'/info/paid-services',
'/join',
'/courses',
'/info/support/network',
'/info',
'/info/tools',
'/info/support/sport',
'/info/grants',
'/info/structure',
'/info/audit',
'/info/finances',
'/info/health-saving',
'/info/history',
'/courses/batik',
'/courses/felting',
'/courses/art-history',
'/courses/ceramics',
'/courses/composition',
'/courses/costume-composition',
'/courses/cg',
'/courses/scale-modeling',
'/courses/drawing',
'/courses/easel-composition',
'/courses/listochki',
'/courses/matreshki',
'/courses/perishki',
'/neighbors',
'/neighbors/2017',
'/neighbors/2017/continuing-the-chronicle',
'/neighbors/2017/favorite-view-of-native-place',
'/neighbors/2016',
'/neighbors/2016/continuing-the-chronicle',
'/neighbors/2016/quite-provincial-life',
'/neighbors/2016/illustrations-rimsky-korsakov-works',
'/neighbors/2017/quite-provincial-life',
'/neighbors/2016/favorite-view-of-native-place',
'/neighbors/2015',
'/neighbors/2015/industrial-landscape',
'/neighbors/2015/favorite-view-of-native-place',
'/neighbors/2015/continuing-the-chronicle',
'/neighbors/2015/these-places-remember-the-war',
'/info/corruption',
'/domik',
'/courses/ladushki',
'/info/security',
'/neighbors/2018/continuing-the-chronicle',
'/neighbors/2018',
'/neighbors/2018/favorite-view-of-native-place',
'/neighbors/2018/industrial-landscape',
'/info/navigator',
]
),
...createPath(
'/teacher/[person]',
x => x.replace('/teacher/', ''),
person => ({ person }),
[
'/teacher/as-timasheva',
'/teacher/va-sarzhin',
'/teacher/vv-voronova',
'/teacher/ea-burovtseva',
'/teacher/ey-tarasova',
'/teacher/iv-khramov',
'/teacher/in-vturina',
'/teacher/ma-khramova',
'/teacher/mg-timasheva',
'/teacher/my-valkova',
'/teacher/nv-andreeva',
'/teacher/od-gogoleva',
'/teacher/rk-timashev',
'/teacher/sa-latipova',
],
),
...createPath(
'/article/[article]',
x => x.replace('/article/', ''),
person => ({ person }),
[
'/article/2009-ntv',
'/article/2010-tv100',
'/article/oblastnoi-plener-udalsia-na-slavu',
'/article/pervyi-kinopokaz',
'/article/30',
'/article/kinopokaz',
'/article/elka-dlia-malyshei-2015',
'/article/vystavka-kukly-korennykh-narodov-leningradskoi-oblasti',
'/article/vystavka-kukly-srednei-azii-i-kavkaza',
'/article/detskii-sadik',
'/article/think-try-create',
'/article/konkurs-volshebnaia-kist-na-nekrasova',
'/article/master-klass-po-izgotovleniiu-vatnykh-kukol-na-nekrasova',
'/article/2015-pridumyvai-probui-tvori',
'/article/vystavka-vesna-2015',
'/article/exhibition-70',
'/article/vesna-2015',
'/article/vystavka-letnikh-rabot',
'/article/kinopokaz-chukovsky',
'/article/kino-domik',
'/article/vystavka-v-biblioteke-nekrasova',
'/article/konkurs-volshebnaia-kist',
'/article/russian-history-competition',
'/article/master-klass-pogoleshechnaia-kukla',
'/article/2016-pridumyvai-probui-tvori',
'/article/prazdnik-v-muzykalnoi-shkole-shlisselburga',
'/article/nabor-detei-s-7-mi-do-9-ti-let',
'/article/serebro-za-vizitku',
'/article/talant-vospityvaetsia-v-trude',
'/article/2016-10-kaniculi',
'/article/2016-tikhvin',
'/article/2016-mi-sosedi',
'/article/master-klass-po-kalligrafii',
'/article/2016-master-class-calligraphy',
'/article/2016-11-kaniculi',
'/article/vtoroi-master-klass-po-kalligrafii',
'/article/2016-master-class-vatnoe-papie-mashe',
'/article/2016-ny-master-class',
'/article/2016-master-class-calligraphy-2',
'/article/v-nominatsii-sovremennaia-kukla-pobedili-iolochnye-ukrasheniia-shlisselburgskoi-masteritsy',
'/article/anons-master-klassa-kukly-voennykh-let',
'/article/master-klass-kukly-voennykh-let',
'/article/deiateli-kultury-gordost-leningradskoi-oblasti',
'/article/gostevoi-dom-shlisselburg',
'/article/uchastie-v-konkurse-zvuk-i-forma',
'/article/vizit-gubernatora-leningradskoi-oblasti',
'/article/anons-konkursa-smotrimvidim',
'/article/anons-letnei-shkoly',
'/article/master-class-papier-mache-2017',
'/article/cats-master-class-2017',
'/article/vyborg-announcement-2017',
'/article/vyborg-info-2017',
'/article/smotrim-vidim-final-announcement-2017',
'/article/summer-school-info-2017',
'/article/smotrim-vidim-final-2017',
'/article/vyborg-2017',
'/article/2017-info-new',
'/article/preparing-2017',
'/article/sarzhin-star',
'/article/toropets',
'/article/sarzhin-october',
'/article/2017-10-kaniculi',
'/article/2017-10-workshop',
'/article/shlisselshved',
'/article/shlisselburg-315',
'/article/scholarship-2017',
'/article/sarzhin-70',
'/article/2017-10-workshop-ceramics',
'/article/2017-katavasia',
'/article/2017-golden-hands',
'/article/2016-liditse',
'/article/2017-the-authors-style',
'/article/2017-dpi-methods',
'/article/2017-oreshek-workshop',
'/article/2017-neighbors-vybord-rewarding',
'/article/2017-stars',
'/article/2017-petrovskaya-aquatoria-trip',
'/article/2017-dnt',
'/article/silver-pencil-2018',
'/article/magic-brush-2018',
'/article/blokada-workshop-2018',
'/article/best-teacher-2018',
'/article/save-nature-competition-2018',
'/article/win-day-2018',
'/article/site-iii-2018',
'/article/israel-2018',
'/article/blokada-workshop-2-2018',
'/article/plain-air-2018',
'/article/graduation-2018',
'/article/blokada-workshop-3-2018',
'/article/2012-timestamp',
'/article/plain-air-june-2018',
'/article/berezka-2018',
'/article/plain-air-pro-2018',
'/article/2018-neighbors-kronshtadt',
'/article/vyborg-competition-2018',
'/article/norway-2018',
'/article/family-album-2018',
'/article/2018-neighbors-ceremony',
'/article/2018-100',
'/article/2018-buduschee-moey-strany',
'/article/2019-nv-andreeva',
'/article/2019-blokada-workshop',
'/article/2018-culture-star',
'/article/2018-guests-from-germany',
'/article/2018-grants',
'/article/2018-inclusive-education',
'/article/2018-neighbors-announcement',
'/article/2019-fortuna',
'/article/2019-orchestra',
'/article/2019-magic-brush',
'/article/2019-silver-pencil',
'/article/2019-spring-subbotnik',
'/article/2019-win500',
'/article/2019-korushka',
'/article/2019-pleinair',
'/article/2019-kotiki',
'/article/2019-serafima',
'/article/2019-competition-authors-style',
'/article/2019-survey',
'/article/2019-navigator',
'/article/2019-unikum',
],
),
...createPath(
'/album/[album]',
x => x.replace('/album/', ''),
album => ({ album }),
[
'/album/2012-tekhnika-mokrogo-valianiia',
'/album/2013-valianie-zverushki',
'/album/2013-shlisselburg',
'/album/2013-natiurmort',
'/album/2015-kukly-valianie',
'/album/2014-kollazh',
'/album/2014-shlisselburg',
'/album/2014-elochnye-domiki',
'/album/2014-elochnye-igrushki',
'/album/2014-zhivopis-narody-severa',
'/album/2015-maketirovanie-doma-peterburga',
'/album/2015-zhivopis',
'/album/2015-maketirovanie',
'/album/2015-podvodnyi-mir-stankovaia-kompozitsiia',
'/album/2015-risunok',
'/album/2015-skazki',
'/album/2015-stankovaia-kompozitsiia',
'/album/2015-tsvety-stankovaia-kompozitsiia',
'/album/2015-graviura',
'/album/2015-guash',
'/album/2015-shlisselburg',
'/album/2015-dpk-portret-v-tekhnike-applikatsiia',
'/album/2015-nominatsiia-navstrechu-pobede',
'/album/2015-domiki',
'/album/2015-skulptury-na-temu-70-let-pobedy',
'/album/2015-history-competition',
'/album/2015-rozhdestvenskaia-skazka',
'/album/2016-konkurs-zimnie-zabavy',
'/album/2016-konkurs-na-kryliakh-babochek',
'/album/2016-konkurs-narodnye-motivy',
'/album/around-the-world',
'/album/comod',
'/album/leto-domovenok',
'/album/2016-tikhvin-mi-sosedi',
'/album/2017-konkurs-budushchee-moei-strany',
'/album/2017-konkurs-puteshestvie-k-dinozavram',
'/album/2017-konkurs-eto-mamin-den',
'/album/2017-konkurs-forma-i-zvuk',
'/album/pleinair-competition-2017',
'/album/ceramics-2017',
'/album/vyborg-makets-2017',
'/album/ceramics-march-2018',
'/album/ceramics-june-2018',
'/album/batik-june-2018',
'/album/village-pictures-june-2018',
'/album/fortress-june-2018',
'/album/baloons-june-2018',
'/album/drawing-jule-2018',
'/album/drawing-june-2018',
'/album/painting-june-2018',
],
),
...createPath(
'/document/[document]',
x => x.replace('/document/', ''),
document => ({ document }),
[
'/document/documents-2017-2018-kalendarnyi-grafik-2017-2018-pdf',
'/document/documents-arkhiv-obrazovatelnaia-litsenziia-2013-pdf',
'/document/documents-arkhiv-plan-vnutrishkolnogo-kontrolia-2012-pdf',
'/document/documents-arkhiv-polozhenie-o-vnutrishkolnom-kontrole-2012-pdf',
'/document/documents-arkhiv-polozhenie-o-pedagogicheskom-sovete-pdf',
'/document/documents-arkhiv-polozhenie-o-prieme-perevode-otchislenii-pdf',
'/document/documents-arkhiv-polozhenie-o-propusknom-rezhime-2015-pdf',
'/document/documents-arkhiv-pravila-i-polozhenie-o-prieme-v-shkolu-pdf',
'/document/documents-arkhiv-ustav-2013-pdf',
'/document/documents-dokumenty-dlia-postupleniia-dogovor-o-pozhertvovanii-pdf',
'/document/documents-dokumenty-dlia-postupleniia-dogovor-o-sotrudnichestve-pdf',
'/document/documents-dokumenty-dlia-postupleniia-zaiavlenie-roditelei-pdf',
'/document/documents-instruktsii-instruktsiia-po-organizatsii-okhrany-zhizni-i-zdorovia-detei-pdf',
'/document/documents-instruktsii-instruktsiia-po-okhrane-zhizni-i-zdorovia-detei-pdf',
'/document/documents-instruktsii-plan-vnutrishkolnogo-kontrolia-2016-pdf',
'/document/documents-instruktsii-pravila-vnutrennego-trudovogo-rasporiadka-pdf',
'/document/documents-materialno-tekhnicheskoe-obespechenie-i-osnashchennost-obrazovatelnogo-protsessa-vnutrishkolnyi-metodicheskii-bibliotechnyi-fond-pdf',
'/document/documents-materialno-tekhnicheskoe-obespechenie-i-osnashchennost-obrazovatelnogo-protsessa-dogovor-na-meditsinskoe-obsluzhivanie-pdf',
'/document/documents-materialno-tekhnicheskoe-obespechenie-i-osnashchennost-obrazovatelnogo-protsessa-dogovor-na-okazanie-uslug-po-organizatsii-pitaniia-pdf',
'/document/documents-materialno-tekhnicheskoe-obespechenie-i-osnashchennost-obrazovatelnogo-protsessa-dogovor-na-okazanie-uslug-sviazi-pdf',
'/document/documents-materialno-tekhnicheskoe-obespechenie-i-osnashchennost-obrazovatelnogo-protsessa-materialno-tekhnicheskoe-obespecheninie-obrazovatelnogo-protsessa-pdf',
'/document/documents-obrazovanie-dopolnitelnaia-obshcherazvivaiushchaia-programma-khudozhestvennoi-napravlennosti-izobrazitelnoe-iskusstvo-dlia-nachinaiushchikh-pdf',
'/document/documents-obrazovanie-dopolnitelnaia-obshcherazvivaiushchaia-programma-khudozhestvennoi-napravlennosti-dlia-obuchaiushchikhsia-podgotovitelnykh-klassov-v-dkhsh-listochki-pdf',
'/document/documents-obrazovanie-dopolnitelnaia-obshcherazvivaiushchaia-programma-khudozhestvennoi-napravlennosti-dlia-obuchaiushchikhsia-podgotovitelnykh-klassov-v-dkhsh-matreshki-pdf',
'/document/documents-obrazovanie-dopolnitelnaia-obshcherazvivaiushchaia-programma-khudozhestvennoi-napravlennosti-dlia-obuchaiushchikhsia-podgotovitelnykh-klassov-v-dkhsh-peryshki-pdf',
'/document/documents-obrazovanie-dopolnitelnaia-predprofessionalnaia-obshcheobrazovatelnaia-programma-v-oblasti-izobrazitelnogo-iskusstva-zhivopis-pdf',
'/document/documents-obrazovanie-dpop-zhivopis-pdf',
'/document/documents-obrazovanie-kalendarnyi-uchebnyi-grafik-pdf',
'/document/documents-obrazovanie-opisanie-obrazovatelnoi-programmy-izobrazitelnoe-iskusstvo-dlia-nachinaiushchikh-pdf',
'/document/documents-obrazovanie-opisanie-obshcheobrazovatelnoi-programmy-v-oblasti-izobrazitelnogo-iskusstva-zhivopis-pdf',
'/document/documents-obrazovanie-polozhenie-o-komplektovanii-i-napolniaemosti-grupp-pdf',
'/document/documents-obrazovanie-polozhenie-o-poriadke-obucheniia-po-individualnomu-uchebnomu-planu-pdf',
'/document/documents-obrazovanie-polozhenie-o-iazyke-obrazovaniia-v-mbudo-shlisselburgskaia-dkhsh-pdf',
'/document/documents-obrazovanie-programma-obucheniia-2017-pdf',
'/document/documents-obrazovanie-uchebnyi-plan-po-izobrazitelnomu-iskusstvu-pdf',
'/document/documents-obrazovanie-uchebnyi-plan-po-dopolnitelnoi-obshcherazvivaiushchei-podgotovitelnoi-programme-po-izobrazitelnomu-iskusstvu-pdf',
'/document/documents-obrazovanie-uchebnyi-plan-po-dopolnitelnoi-predprofessionalnoi-obshcheobrazovatelnoi-programme-v-oblasti-izobrazitelnogo-iskusstva-zhivopis-pdf',
'/document/documents-osnovnye-dokumenty-egriul-pdf',
'/document/documents-osnovnye-dokumenty-inn-pdf',
'/document/documents-osnovnye-dokumenty-kopiia-resheniia-o-naznachenii-rukovoditelia-pdf',
'/document/documents-osnovnye-dokumenty-obrazovatelnaia-litsenziia-2015-pdf',
'/document/documents-osnovnye-dokumenty-polozhenie-o-vnutrishkolnom-kontrole-2017-pdf',
'/document/documents-osnovnye-dokumenty-polozhenie-o-dobrovolnom-pozhertvovanii-pdf',
'/document/documents-osnovnye-dokumenty-polozhenie-o-kodekse-professionalnoi-etiki-pedagogicheskikh-rabotnikov-pdf',
'/document/documents-osnovnye-dokumenty-polozhenie-o-pedagogicheskom-sovete-pdf',
'/document/documents-osnovnye-dokumenty-polozhenie-o-poriadke-vydachi-dokumenta-ob-obuchenii-pdf',
'/document/documents-osnovnye-dokumenty-polozhenie-o-pravilakh-vnutrennego-rasporiadka-obuchaiushchikhsia-pdf',
'/document/documents-osnovnye-dokumenty-polozhenie-o-prieme-v-shkolu-pdf',
'/document/documents-osnovnye-dokumenty-polozhenie-o-propusknom-rezhime-pdf',
'/document/documents-osnovnye-dokumenty-polozhenie-o-sovete-shkoly-2017-pdf',
'/document/documents-osnovnye-dokumenty-polozhenie-o-formakh-periodichnosti-i-poriadke-tekushchego-kontrolia-uspevaemosti-promezhutochnoi-attestatsii-obuchaiaiushchikhsia-pdf',
'/document/documents-osnovnye-dokumenty-polozhenie-ob-appeliatsionnoi-komissii-pdf',
'/document/documents-osnovnye-dokumenty-polozhenie-ob-itogovoi-attestatsii-pdf',
'/document/documents-osnovnye-dokumenty-polozhenie-ob-obshchem-sobranii-rabotnikov-pdf',
'/document/documents-osnovnye-dokumenty-polozhenie-ob-usloviiakh-provedeniia-individualnogo-otbora-dlia-postupaiushchikh-s-ogranichennymi-vozmozhnostiami-zdorovia-pdf',
'/document/documents-osnovnye-dokumenty-polozhenie-po-promezhutochnoi-attestatsii-pdf',
'/document/documents-osnovnye-dokumenty-postanovlenie-o-sozdanii-munitsipalnogo-obrazovatelnogo-uchrezhdeniia-kultury-detskaia-khudozhestvennaia-shkola-pdf',
'/document/documents-osnovnye-dokumenty-programma-razvitiia-na-2017-2020-gg-pdf',
'/document/documents-osnovnye-dokumenty-rezhim-zaniatii-obuchaiushchikhsia-pdf',
'/document/documents-osnovnye-dokumenty-reshenie-o-sozdanii-shkoly-pdf',
'/document/documents-osnovnye-dokumenty-svidetelstvo-na-pomeshchenie-pdf',
'/document/documents-osnovnye-dokumenty-ustav-2015-pdf',
'/document/documents-plany-fkhd-munitsipalnoe-zadanie-na-2017-god-i-na-planovyi-period-2018-i-2019-godov-pdf',
'/document/documents-plany-fkhd-otchet-o-vypolnenii-munitsipalnogo-zadaniia-2016-god-pdf',
'/document/documents-plany-fkhd-plan-finansovoi-khoziaistvennoi-deiatelnosti-na-2016-god-pdf',
'/document/documents-plany-fkhd-plan-finansovoi-khoziaistvennoi-deiatelnosti-na-2017-god-pdf',
'/document/documents-platnye-obrazovatelnye-uslugi-dogovor-ob-okazanii-platnykh-obrazovatelnykh-uslug-pdf',
'/document/documents-platnye-obrazovatelnye-uslugi-zaiavlenie-na-platnye-obrazovatelnye-uslugi-pdf',
'/document/documents-platnye-obrazovatelnye-uslugi-polozhenie-o-poriadke-predostavleniia-platnykh-obrazovatelnykh-uslug-pdf',
'/document/documents-platnye-obrazovatelnye-uslugi-prikaz-ob-ustanovlenii-razmera-platnykh-obrazovatelnykh-uslug-pdf',
'/document/documents-prochee-2016-zaiavka-na-uchastie-v-44-mvkhpd-v-liditse-pdf',
'/document/documents-prochee-2016-rezultat-samoobsledovaniia-pdf',
'/document/documents-prochee-dostizheniia-obuchaiushchikhsia-v-2015-2016-godu-pdf',
'/document/documents-prochee-litsenziia-spbimi-pdf',
'/document/documents-prochee-otsenka-kachestva-uslug-uchrezhdeniia-kultury-pdf',
'/document/documents-prochee-polozhenie-smotrim-vidim-pdf',
'/document/documents-prochee-rasporiazhenie-o-prisvoenii-stipendii-2017-2018-pdf',
'/document/documents-prochee-rasporiazhenie-ot-30-05-2017-pdf',
'/document/documents-prochee-spisok-materialov-dlia-zaniatiia-v-khudozhestvennoi-shkole-pdf',
'/document/documents-samoobsledovanie-deiatelnosti-otchet-o-rezultatakh-samoobsledovaniia-01-04-2016-pdf',
'/document/documents-samoobsledovanie-deiatelnosti-otchet-o-rezultatakh-samoobsledovaniia-01-04-2017-pdf',
'/document/documents-tekushchii-kontrol-akt-proverki-komiteta-obshchego-i-professionalnogo-obrazovaniia-leningradskoi-oblasti-2017-pdf',
'/document/documents-uchebnye-programmy-izobrazitelnoe-iskusstvo-dlia-nachinaiushchikh-pdf',
'/document/documents-uchebnye-programmy-programma-po-predmetu-skulptura-pdf',
'/document/documents-uchebnye-programmy-progrmma-po-predmetu-plener-pdf',
'/document/documents-uchebnye-programmy-uchebnaia-programma-zhivopis-pdf',
'/document/documents-uchebnye-programmy-uchebnaia-programma-istoriia-iskusstva-pdf',
'/document/documents-uchebnye-programmy-uchebnaia-programma-prikladnaia-kompozitsiia-pdf',
'/document/documents-uchebnye-programmy-uchebnaia-programma-risunok-pdf',
'/document/documents-uchebnye-programmy-uchebnaia-programma-stankovaia-kompozitsiia-pdf',
'/document/documents-finansovo-khoziaistvennaia-deiatelnost-balans-gosudarstvennogo-(munitsipalnogo)-uchrezhdeniia-pdf',
'/document/documents-finansovo-khoziaistvennaia-deiatelnost-otchet-o-finansovykh-rezultatakh-deiatelnosti-uchrezhdeniia-pdf',
'/document/documents-finansovo-khoziaistvennaia-deiatelnost-otchet-ob-ispolnenii-uchrezhdeniem-plana-ego-finansovo-khoziaistvennoi-deiatelnosti-forma-737-kfo-4-pdf',
'/document/documents-finansovo-khoziaistvennaia-deiatelnost-otchet-ob-ispolnenii-uchrezhdeniem-plana-ego-finansovo-khoziaistvennoi-deiatelnosti-forma-737-kfo-5-pdf',
'/document/documents-finansovo-khoziaistvennaia-deiatelnost-otchet-ob-ispolnenii-uchrezhdeniem-plana-ego-finansovo-khoziaistvennoi-deiatelnosti-pdf',
'/document/pages-finansovo-khoziaistvennaia-deiatelnost-balans-pdf',
'/document/370f8cc297f40d170470e8768ddd227c',
'/document/792bf2823b7840318b02f7d2758af788',
'/document/f574413240ff8ff35091f023976688ee',
'/document/758260df3b7fa615e2fc8a3d2ed9e34c',
'/document/00c90a531da459ab9f10a3d52bac233f',
'/document/2eae32c837bbbd84f403dd7628354b1d',
'/document/fa42795ab1879d009961b5bf69e8c597',
'/document/8f74abfd61354daf8d73e4000b525fdd',
'/document/fe14659e16c98005f4c5168ee3cccc9a',
'/document/98564c3f7c9ce1e5c5f1aa1fe302eaf6',
'/document/6d7359437624b3dd1484d82be66f1ea0',
'/document/4e4ed5431035f97da26eb6b17d88658e',
'/document/dfef1cc07b4317d2d380efc4259dc363',
'/document/8c19548aa39c40a8a2abc4bca20714ca',
'/document/caef7508b14605982d483a5f05756714',
'/document/9f6683f63172a794fd3c6031f781d348',
'/document/acaa47304de042433e26e5eabbbfdeec',
'/document/e094b1f74e8d381b77b510d9f040a616',
'/document/b19092ace94edcf2e7ee0274207b8aa9',
'/document/47883f1e226c48c42e1cb7a9e811bec8',
'/document/ee54f828803e91c7f385b621143f0daf',
'/document/9149c0d182401643c6307ac9ffb1dc65',
'/document/7f229ba5f7882a8a2684289204a8308e',
'/document/37277ec7ddc90a316effef4a3a6ec1c3',
'/document/d50a7953d5a65f4155f7035740594a47',
'/document/ae3d2bc577bfd45f1cbc420f8691de2e',
'/document/b54ad2943689658cccd20c215724bc7d',
'/document/918b27593c43576dfd85b817005f9925',
'/document/77144e7b8eb675d0189c63be1818d964',
'/document/20f2fafe77196edb54857fe0b98438a6',
'/document/e0cdee9ba8a56919f92af7f0363e713f',
'/document/e3e2da7bdd4cdb4fb05a7d72a1c5aa24',
'/document/5883367e713100cfdfa074fbab675907',
'/document/0415c9a50eb0b56651bbcf3d7388a373',
'/document/aa69b76d675e1e30661365deb2e5deef',
'/document/4e35368e42c1840e89c3a3714d3f323b',
'/document/5bd563e2b1b8e0cb20ee2438a5d4888a',
'/document/b19069cc9b3c5be5a92487f9d3385064',
'/document/53f8111c3882521453d889aa84dedea3',
'/document/58e4a7445b19ae0dbe1ee6313cbcee50',
'/document/76a8fc2870f1254a52e80712115ea448',
'/document/dab46182c1c119210b3a8ba4a3079d15',
'/document/52101c7e4bb3bbdc315f93dff8b826ca',
'/document/5a677e08537853a1f4d11a7ae492f8b6',
'/document/2dc1c2e7cfa996c13fb90045c5e4b5fb',
'/document/952e1ed8ec2879eb4d2e300c1b50d890',
'/document/0d20b54c2023563ec0cdff648ed8070d',
'/document/82e9e339678a9385ab44897837a0e7f6',
'/document/ae64123551407d5c325f75d83f58a338',
'/document/bd1ace5ed47ee0958254f6a9732482cc',
'/document/534566e01344b0d1dfec8e3cb0dfc8a9',
'/document/739bd6b500c6bebe742df3d70a0c1fdc',
'/document/00a6c04a383bea80a619d4a86bb1b46f',
'/document/5c9708d3d3c9d6141ed7865a8a3d9566',
'/document/6fec7aa79b5edef99dc5498d8ddc9b28',
'/document/2f4ab4732107b2ce84e2dd9327d2788f',
'/document/d91de578e0a3b5d0878857bebf859124',
'/document/a056376a142ac924485ff6f222ab116d',
'/document/ded3933486acbc63ee0897d768a0af5a',
'/document/f3a21a6b0dc669034542c575dc4a9cb0',
'/document/f08cd96300031e83a55699f257046eb3',
'/document/77b23264aa63ebc2f60811fe46595140',
'/document/58269cb439dd9860b277a772a5651597',
'/document/e87b1ce25b43a4f4613bf35aa1ce17ef',
'/document/2a17275330cfc5933447fa5631a1a137',
'/document/e0726255b3b1275c468432e6825acc8c',
'/document/0d8ba20dc95b0b8cecde26f83821ac60',
'/document/cc02b4688caa24f55da4a36d54d89712',
'/document/3df73e48c1bc1acf4725e158a7abbea2',
'/document/c27d39f66acd8c1e50c66ddc5d0a2d39',
'/document/efd17fc5298d12a4b4a73238e01bc4c1',
'/document/19e094faea42621de8ea362493c6149a',
'/document/869d3c46dc48c31870ff169e1d53ba0f',
'/document/be8201b8f5b0d995faa851c8dee9bb30',
'/document/9e79ef49c9aa132f1b6be66ade0e55ff',
'/document/198f53b9de3c8123b0977d9350c26ace',
'/document/f16c8bc8830d6d5c939fcb3dc3e9dfc8',
'/document/60933dd039b87c2ce5b3f637ec791f71',
'/document/1c05245b4e5889bc0f3cc6e7ee3a022b',
'/document/fb589fa77a5e78a11e5aa90f27fdb10a',
'/document/631962a3257d1196f281f568d780d98f',
'/document/e8877efd3f9234997677b0595b0aaa22',
'/document/4fe9c744e0b1b9de8dec11db5d6ab29f',
'/document/85280b9f3f951c06284ab7fe77a595b6',
'/document/76607ea30bd2a7d86710781b6b9fddd4',
'/document/ec40bc1ed9ce38d563c2bece509b5021',
'/document/074b2b248efc55ae7fe9d157891c59e9',
'/document/1d038d40939be8999fe585f71579f09d',
'/document/8f1764396b0a6c1a0a32235ad4cd69eb',
'/document/4d6524f6c56d7cbeaee699c8b5e37c1b',
'/document/c2ea674b1a3069369b1829e568891b05',
'/document/6c1fb8a2428b7b95ca103363f1273c57',
'/document/e7dae8347ac64981377932e4a93f1237',
'/document/10098b1edb8a242253cdc0636106b880',
'/document/deba55994dd9dc1bd304c360656e3843',
'/document/976dec5025c210b879192929a40b6b27',
'/document/1d48ee849ddea9406a423e4b1e1a7405',
'/document/cc845874fd3b183b0cd17e7138075028',
'/document/81c9a9d965a12ceaf3564128bffd37ad',
'/document/e1720a6b5792a81788f10865ef6ecc79',
'/document/0f0b7ef81ba0d0a2af96a67322110751',
'/document/ba581ef075ea68be80f750ac467e5baf',
'/document/3c9062a9dd236b9d533160c0058c8648',
'/document/27958a0d3dd1e22e7b2f7c68ca753fb3',
'/document/3663f97b6151720d4749574dc9d1e8a4',
'/document/2d21b714a1ba64c24289e2e22d080202',
'/document/7ebc6f2cae58d6505143a20da047dd80',
'/document/1d67ff95477334666d8ae6451f1a32e2',
'/document/c2524a36ba4f9885dcd2abcb1fc00afd',
'/document/c49ef7b1944a96a8b2552aa7fdb60d1b',
'/document/d2b40e5804363a0f326f4d14ad9e0b73',
'/document/3149e573fa3a5b680662a4db84078044',
'/document/c7ccacb1e86467f1250084280e16a58f',
'/document/9b996ba28a688d2b2834b202e14ff317',
'/document/8ddbabaf875dfc1652ed327d9760b6cf',
'/document/4da000c1bbca5231c9768aaeccdffe1a',
'/document/c44153837b872ba80f5d94e7d3641114',
'/document/d99da3c6e7962195f6ae5ce31759d076',
'/document/6025a889bd12cec858d218f68686b916',
'/document/2b2672d859944f1c1b3a63b26ad693ab',
'/document/1a3234642a9bf0493c75511eb3bf98c5',
'/document/554c91d0c84c693f00b8666534f198d1',
'/document/a9bf213af314b8258d70e50ab0a5a3f9',
'/document/7bc0d8238189fd0497d6faf51c1ec6ce',
'/document/27b858d66f1dd403504139389f1c985f',
'/document/2f9873564859bf38c24c2735d3dfc537',
'/document/40256172e2be27799361b3980c39737b',
'/document/c08980a17eaffa33816aa0b9f52d35da',
'/document/26f7783a944186eed230ac234b999da6',
'/document/8b1abc982beae70ac2885856aeaef116',
'/document/d063d09eb6f2d1d765ab904cb211ff61',
'/document/36988b164efadc1348c81916c30076b2',
'/document/765798adfe9b6c75f72c20c716705b82',
'/document/9a0d71c2d8aa33273b9c631af2b47eb0',
'/document/1b8dc4f624b2db9c37097df6011948ca',
'/document/8d4ccb007257e11d7ca7c957d2fa715c',
'/document/7c1fcbed5fa818d1256a33c628eb6695',
'/document/94d1cb5860ec81e00e9fdf24af847a6b',
'/document/808a5a90dda72cc3411ca9c1942f0889',
'/document/b5ed5defd7154073dd44ed6f8cc4c51d',
'/document/65e8b74faa3d2839f08255f8e1835d8e',
'/document/f3af7921743fc47718ca39716fdc32d5',
'/document/3311e9b7d4e57893b94807c3acbde279',
'/document/4ec0139e3466527f71a102e26697b344',
'/document/491c8b0ea8032e4fe40e735711258277',
'/document/50d171c70356995b921c52568f1cf64e',
'/document/033e121e5bc2b487353f512c0258ed1f',
'/document/18831d6d910b64c1a7aaa409d694607c',
'/document/bee4ca5c3a191bd7c43bb7098259bfcb',
'/document/67e0ecd5b41e3b8b18b6d8e809b34f2d',
'/document/ffdd72718d2f9d6f824e0304fb4ed0e8',
'/document/49a03e53293d4e5be6e16b20fe78f107',
'/document/5282789e6ee9ec37b2322abc4011aa80',
'/document/e07f6b95ece80ed82977b096156f4ba8',
'/document/7fdf9aa60d503f2f01f89a8b19b0c770',
'/document/0053224c128c26593816041cfd923fcf',
'/document/c150b448136a61c9f30bbc5372a246d1',
'/document/40f2efd755b816586ea6fc4459159edf',
'/document/d8afbafcf061f1f28cea58f2c2271c68',
'/document/966fda6686c99a28bb1792730ea2057f',
'/document/7c4f6647a6e5e4b5175eb5ecae971320',
'/document/38b8b5be9a2ed9def3a4c80f98a80482',
'/document/e33c22584756e7bb9ee0cd84f7899f4b',
'/document/176559736c92b222ae27d0f54229dbcc',
]
),
}
}
}))
-16
View File
@@ -1,16 +0,0 @@
{
"version": 2,
"name": "hudozka",
"builds": [
{
"src": "dist/**",
"use": "@now/static"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "dist/$1"
}
]
}
+2546 -9508
View File
File diff suppressed because it is too large. Load diff
+7 -45
View File
@@ -3,12 +3,11 @@
"private": true,
"version": "6.0.0",
"description": "",
"main": "src/bin/www.js",
"scripts": {
"start": "NODE_ENV=production node src/server.js",
"dev": "ts-node --project server.tsconfig.json src/server.ts",
"next-dev": "next dev",
"build": "next build && next export -o dist && npm run sitemap && npm run feed",
"start": "next dev",
"build": "npm run next-build && npm run export && npm run sitemap && npm run feed",
"next-build": "next build",
"export": "next export -o dist",
"sitemap": "node scripts/sitemap.js",
"feed": "node scripts/feed.js",
"test": "mocha --compilers js:babel-register"
@@ -22,56 +21,21 @@
"dependencies": {
"axios": "^0.19.0",
"classnames": "^2.2.6",
"co": "^4.4.0",
"ejson": "^2.2.0",
"handlebars": "^4.5.3",
"immutable": "^3.8.2",
"instagram-node": "^0.5.1",
"koa": "^2.11.0",
"koa-route": "^3.1.0",
"lodash": "^4.17.15",
"markdown": "^0.5.0",
"moment": "^2.24.0",
"mongodb": "^2.1.14",
"mz": "^2.7.0",
"next": "^9.1.4",
"next": "^9.2.2-canary.20",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-redux": "^4.4.10",
"react-router": "^4.3.1",
"react-router-redux": "^4.0.8",
"redux": "^3.7.2",
"redux-thunk": "^2.3.0",
"request-promise": "^2.0.1",
"sitemap": "^1.5.0",
"thunkify": "^2.1.2",
"use-media": "^1.4.0"
},
"devDependencies": {
"@babel/cli": "^7.7.4",
"@babel/core": "^7.7.4",
"@babel/node": "^7.7.4",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-proposal-object-rest-spread": "^7.7.4",
"@babel/polyfill": "^7.7.0",
"@babel/preset-env": "^7.7.4",
"@babel/preset-typescript": "^7.7.4",
"@babel/register": "^7.7.4",
"@types/classnames": "^2.2.9",
"@types/koa": "^2.0.52",
"@types/koa-route": "^3.2.4",
"@types/mongodb": "^3.3.11",
"@types/lodash": "^4.14.149",
"@types/node": "^12.12.12",
"@types/react": "^16.9.12",
"@zeit/next-css": "^1.0.1",
"@zeit/next-sass": "^1.0.1",
"autoprefixer": "^6.3.6",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-react-jsx": "^6.23.0",
"babel-plugin-transform-regenerator": "^6.26.0",
"babel-preset-env": "^1.7.0",
"babel-register": "^6.26.0",
"cssnano": "^4.1.10",
"eslint": "^4.18.2",
"eslint-config-standard": "^6.2.1",
"eslint-plugin-promise": "^3.8.0",
@@ -81,8 +45,6 @@
"mocha": "^6.2.2",
"node-sass": "^4.13.0",
"should": "^8.2.2",
"ts-node": "^8.5.2",
"typescript": "^3.7.2",
"webpack": "^4.41.2"
"typescript": "^3.7.5"
}
}
+87
View File
@@ -0,0 +1,87 @@
import Head from 'next/head'
import { App } from 'src/components/App'
import { tail } from 'lodash'
import { Page } from 'src/components/Page'
import menuModel from 'src/models/menu'
import { buildMenu } from 'src/lib/menu'
import { Meta } from 'src/components/Meta'
import { meta } from 'src/lib/meta'
import { createApiUrl, requestGet, wrapInitialProps, IResponseItems } from 'src/next-lib'
function array<T>(value: T | T[]) {
return Array.isArray(value)
? value
: [value]
}
const Index = props => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
layout={'thin'}
>
<Head>
<title>{props.title}</title>
{/* <Meta meta={props.meta} /> */}
</Head>
<Page
showSocialShare={true}
>
{props.content}
</Page>
</App>
)
export const unstable_getStaticProps = async (ctx: any) => {
let slug = null
if (ctx.query) {
slug = '/' + array(ctx.query.slug).join('/')
} else {
slug = '/' + array(ctx.params.slug).join('/')
}
const page = await requestGet(createApiUrl(`/api/page?page=${slug}`), null)
if (!page) {
throw new Error(`Not found: ${slug}`)
}
const image = page.preview ? page.preview.artifacts.fb : {}
return {
props: {
content: page.data,
pageUrl: slug,
title: page.title,
meta: meta({
title: page.title,
image: image.src,
imageWidth: image.width,
imageHeight: image.height,
}),
}
}
}
export const unstable_getStaticPaths = async () => {
console.log('call all unstable_getStaticPaths')
const urls = await requestGet<IResponseItems<string>>(createApiUrl(`/api/pages/urls`), null)
if (!urls) {
return null
}
return {
paths: urls.items
.map(path => {
const slug = tail(path.split('/'))
return {
params: {
slug,
},
}
}),
}
}
export default Index
+4 -35
View File
@@ -1,43 +1,12 @@
import React from 'react'
import App, { Container } from 'next/app'
import 'src/style.css'
import App from 'next/app'
class MyApp extends App {
export default class MyApp extends App {
render() {
const { Component, pageProps } = this.props
return (
<Container>
<style global jsx>{`
@media (max-width: 31.25em) {
:root {
--smaller-font-size: 0.9rem;
--normal-font-size: 1rem;
--bigger-font-size: 1.1rem;
--big-font-size: 1.3rem;
--biggest-font-size: 2rem;
--font-size-h1: 1.5rem;
--font-size-h2: 1.5rem;
--font-size-h3: 1.25rem;
--content-thin: 90%;
--content-wide: 90%;
}
}
.noscroll {
overflow: hidden;
}
#__next {
height: 100%;
}
`}</style>
<Component {...pageProps} />
</Container>
<Component {...pageProps} />
)
}
}
export default MyApp
+4 -12
View File
@@ -1,20 +1,12 @@
import React from 'react'
import Document, { Html, Head, Main, NextScript } from 'next/document'
import { YMetrika } from '../src/components/YMetrika'
import { GAnalytics } from '../src/components/GAnalytics'
import { YMetrika } from 'src/components/YMetrika'
import { GAnalytics } from 'src/components/GAnalytics'
export default class MyDocument extends Document {
// static async getInitialProps(ctx) {
// const initialProps = await Document.getInitialProps(ctx);
// return { ...initialProps };
// }
render() {
return (
<Html>
<Head>
<style>{`body { margin: 0 } /* custom! */`}</style>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, maximum-scale=1.0" />
<meta name="referrer" content="unsafe-url" />
@@ -62,11 +54,11 @@ export default class MyDocument extends Document {
<GAnalytics account={'UA-60209057-1'} />
</Head>
<body className="custom_class">
<body>
<Main />
<NextScript />
</body>
</Html>
);
)
}
}
+4 -4
View File
@@ -1,7 +1,7 @@
import React from 'react'
import '../src/style/style.scss'
import 'src/style/style.scss'
import { NextPage } from 'next'
const Page = props => (
const Page: NextPage = () => (
<div className="body-wrapper">
<main className="body-wrapper__content">
<section className="error-404">
@@ -22,4 +22,4 @@ const Page = props => (
</div>
)
export default Page
export default Page
-43
View File
@@ -1,43 +0,0 @@
import React from 'react'
import Head from 'next/head'
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'
import { createApiUrl, requestGet, wrapInitialProps } from '../../src/next-lib'
const Page = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
layout={'thin'}
>
<Head>
<title>{props.album.title}</title>
</Head>
<Article
title={props.album.title}
date={props.album.date}
shareable={true}
>
<Html
html={props.album.post}
/>
</Article>
</App>
)
Page.getInitialProps = wrapInitialProps(async (ctx) => {
const pageUrl = '/gallery'
const id = ctx.query.album
const album = await requestGet(createApiUrl(ctx.req, `/api/albums/${id}`), {})
return {
album,
pageUrl,
}
})
export default Page
+86
View File
@@ -0,0 +1,86 @@
import Head from 'next/head'
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'
import { createApiUrl, requestGet, IResponseItems } from 'src/next-lib'
import { NextPage } from 'next'
type Props = {
pageUrl: string
data: any //(album)
}
const Page: NextPage<Props> = props => {
if (!props?.data) {
console.log('kek error', props)
return null
}
return (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
layout={'thin'}
>
<Head>
<title>{props.data.title}</title>
</Head>
<Article
title={props.data.title}
date={props.data.date}
shareable={true}
tags={[]}
>
<Html
html={props.data.post}
/>
</Article>
</App>
)
}
export const unstable_getStaticProps = async (ctx: any) => {
const pageUrl = '/gallery'
// const id = ctx.query.slug
const id = ctx.params.slug
const data = await requestGet(createApiUrl(`/api/albums/${id}`), null)
if (!data) {
throw new Error(`kek ${id}`)
}
return {
props: {
data,
pageUrl,
}
}
}
export const unstable_getStaticPaths = async () => {
console.log('call album unstable_getStaticPaths')
const urls = await requestGet<IResponseItems<string>>(createApiUrl(`/api/albums/urls`), null)
if (!urls) {
return null
}
return {
paths: urls.items
.map(x => x.replace('/album/', ''))
.map(slug => {
return {
params: {
slug,
},
}
}),
}
}
export default Page
-19
View File
@@ -1,19 +0,0 @@
import { connect } from '../../../src/core/db'
import Album from '../../../src/core/Album'
import { withMiddleware } from '../../../src/middlewares/withMiddleware'
export default withMiddleware(async (req, res) => {
await connect()
const id = req.query.album
const album = await Album.findById(id)
if (album) {
res.json(album)
} else {
res.status(404)
res.json({
error: `Album ${id} not found`
})
}
})
-19
View File
@@ -1,19 +0,0 @@
import { connect } from '../../../src/core/db'
import Album from '../../../src/core/Album'
import { withMiddleware } from '../../../src/middlewares/withMiddleware'
export default withMiddleware(async (req, res) => {
await connect()
const data = await Album.find({}, { sort: { date: -1 } })
if (data) {
res.json({
items: data,
})
} else {
res.status(404)
res.json({
error: `Article not found`
})
}
})
-20
View File
@@ -1,20 +0,0 @@
import { connect } from '../../../src/core/db'
import Album from '../../../src/core/Album'
export default async (req, res) => {
await connect()
const data = await Album.find({})
if (data) {
const items = data.map(x => `/album/${x.id}`)
res.json({
items
})
} else {
res.status(404)
res.json({
error: `Not found`
})
}
}
-21
View File
@@ -1,21 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import Article from '../../../src/core/Article'
import { encodeArticle } from '../../../src/server/articles'
import { withMiddleware } from '../../../src/middlewares/withMiddleware'
export default withMiddleware(async (req: NextApiRequest, res: NextApiResponse) => {
const {
query: { article: id }
} = req;
const article = await Article.findById(id)
if (article) {
res.json(encodeArticle(article))
} else {
res.status(404)
res.json({
error: `Article ${id} not found`
})
}
})
-24
View File
@@ -1,24 +0,0 @@
import { findArticles } from '../../../src/server/articles'
import { withMiddleware } from '../../../src/middlewares/withMiddleware'
const int = (value: string | string[]) => parseInt(Array.isArray(value)
? value[0]
: value
)
export default withMiddleware(async (req, res) => {
const { query } = req
const page = int(query.page)
const pageSize = int(query.pageSize)
const data = await findArticles(page, pageSize)
if (data) {
res.json(data)
} else {
res.status(404)
res.json({
error: `Articles not found`
})
}
})
-21
View File
@@ -1,21 +0,0 @@
import { connect } from '../../../src/core/db'
import Article from '../../../src/core/Article'
import { withMiddleware } from '../../../src/middlewares/withMiddleware'
export default withMiddleware(async (req, res) => {
await connect()
const data = await Article.find({})
if (data) {
const items = data.map(x => `/article/${x.id}`)
res.json({
items
})
} else {
res.status(404)
res.json({
error: `Not found`
})
}
})
-20
View File
@@ -1,20 +0,0 @@
import { connect } from '../../../src/core/db'
import Document from '../../../src/core/Document'
import { encodeFile } from '../../../src/api/files'
import { withMiddleware } from '../../../src/middlewares/withMiddleware'
export default withMiddleware(async (req, res) => {
await connect()
const id = req.query.document
const document = await Document.findById(id)
if (document) {
res.json(encodeFile(document))
} else {
res.status(404)
res.json({
error: `Document ${id} not found`
})
}
})
-24
View File
@@ -1,24 +0,0 @@
import { connect } from '../../../src/core/db'
import Document from '../../../src/core/Document'
import { encodeFile } from '../../../src/api/files'
import { withMiddleware } from '../../../src/middlewares/withMiddleware'
export default withMiddleware(async (req, res) => {
await connect()
const documents = await Document.find({hidden: false})
if (documents) {
const items = documents
.map(encodeFile)
.filter(x => x.imageUrl) // skip documents without preview image; needs to be refactored
res.json({
items,
})
} else {
res.status(404)
res.json({
error: `Documents not found`
})
}
})
-21
View File
@@ -1,21 +0,0 @@
import { connect } from '../../../src/core/db'
import Document from '../../../src/core/Document'
import { withMiddleware } from '../../../src/middlewares/withMiddleware'
export default withMiddleware(async (req, res) => {
await connect()
const data = await Document.find({})
if (data) {
const items = data.map(x => `/document/${x.id}`)
res.json({
items
})
} else {
res.status(404)
res.json({
error: `Not found`
})
}
})
-17
View File
@@ -1,17 +0,0 @@
import Image from '../../src/core/Image'
import { encodeImage } from '../../src/api/image'
import { withMiddleware } from '../../src/middlewares/withMiddleware'
export default withMiddleware(async (req, res) => {
const file = req.query.file
const image = await Image.findByFile(file)
if (image) {
res.json(encodeImage(image))
} else {
res.status(404)
res.json({
error: `Image ${file} not found`
})
}
})
-21
View File
@@ -1,21 +0,0 @@
import { connect } from '../../src/core/db'
import Page from '../../src/core/Page'
import { getPathWithNoTrailingSlash } from '../../src/lib/url'
import { encodePage } from '../../src/api/page'
import { withMiddleware } from '../../src/middlewares/withMiddleware'
export default withMiddleware(async (req, res) => {
await connect()
const page = getPathWithNoTrailingSlash(req.query.page)
const resource = await Page.findByUrl(page)
if (resource) {
res.json(encodePage(resource))
} else {
res.status(404)
res.json({
error: `Image ${page} not found`
})
}
})
-17
View File
@@ -1,17 +0,0 @@
import Page from '../../../src/core/Page'
import { withMiddleware } from '../../../src/middlewares/withMiddleware'
export default withMiddleware(async (req, res) => {
const items = await Page.find({})
if (items) {
res.json({
items,
})
} else {
res.status(404)
res.json({
error: `Article ${id} not found`
})
}
})
-21
View File
@@ -1,21 +0,0 @@
import { connect } from '../../../src/core/db'
import Page from '../../../src/core/Page'
import { withMiddleware } from '../../../src/middlewares/withMiddleware'
export default withMiddleware(async (req, res) => {
await connect()
const data = await Page.find({})
if (data) {
const items = data.map(x => x.url)
res.json({
items,
})
} else {
res.status(404)
res.json({
error: `Not found`
})
}
})
-20
View File
@@ -1,20 +0,0 @@
import { connect } from '../../../src/core/db'
import Teacher from '../../../src/core/Teacher'
import { encodePerson } from '../../../src/api/persons'
import { withMiddleware } from '../../../src/middlewares/withMiddleware'
export default withMiddleware(async (req, res) => {
await connect()
const id = req.query.person
const data = await Teacher.findById(id)
if (data) {
res.json(encodePerson(data))
} else {
res.status(404)
res.json({
error: `Article ${id} not found`
})
}
})
-33
View File
@@ -1,33 +0,0 @@
import { connect } from '../../../src/core/db'
import Teacher from '../../../src/core/Teacher'
import { prioritySort } from '../../../src/lib/sort'
import { encodePerson } from '../../../src/api/persons'
import { withMiddleware } from '../../../src/middlewares/withMiddleware'
export default withMiddleware(async (req, res) => {
await connect()
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`
})
}
})
-21
View File
@@ -1,21 +0,0 @@
import { connect } from '../../../src/core/db'
import Teacher from '../../../src/core/Teacher'
import { withMiddleware } from '../../../src/middlewares/withMiddleware'
export default withMiddleware(async (req, res) => {
await connect()
const data = await Teacher.find({})
if (data) {
const items = data.map(x => `/teacher/${x.id}`)
res.json({
items
})
} else {
res.status(404)
res.json({
error: `Not found`
})
}
})
-18
View File
@@ -1,18 +0,0 @@
import { findSchedule } from '../../src/server/schedule'
import { withMiddleware } from '../../src/middlewares/withMiddleware'
export default withMiddleware(async (req, res) => {
const period = req.query.period
const semester = req.query.semester
const data = await findSchedule(req.collection, period, semester)
if (data) {
res.json(data)
} else {
res.status(404)
res.json({
error: `Schedule ${period}:${semester} not found`
})
}
})
-55
View File
@@ -1,55 +0,0 @@
import React from 'react'
import Head from 'next/head'
import { get } from 'lodash'
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'
import { Meta } from '../../src/components/Meta'
import { meta } from '../../src/lib/meta'
import { createApiUrl, requestGet, wrapInitialProps } from '../../src/next-lib'
const Page = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
layout={'thin'}
>
<Head>
<title>{props.article.title}</title>
<Meta meta={props.meta} />
</Head>
<Article
title={props.article.title}
date={props.article.date}
tags={props.article.tags}
shareable={true}
>
<Html
html={props.article.post}
/>
</Article>
</App>
)
Page.getInitialProps = wrapInitialProps(async (ctx) => {
const pageUrl = '/'
const id = ctx.query.article
const article = await requestGet(createApiUrl(ctx.req, `/api/articles/${id}`), {})
const image = get(article, 'preview.artifacts.fb', {})
return {
article,
pageUrl,
meta: meta({
title: article.title,
image: image.src,
imageWidth: image.width,
imageHeight: image.height,
}),
}
})
export default Page
+95
View File
@@ -0,0 +1,95 @@
import Head from 'next/head'
import { get } from 'lodash'
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'
import { Meta } from 'src/components/Meta'
import { meta } from 'src/lib/meta'
import { createApiUrl, requestGet, IResponseItems } from 'src/next-lib'
import { NextPage } from 'next'
type Props = {
pageUrl: string
meta: any
data: any //(article)
}
const Page: NextPage<Props> = props => {
if (!props.data) {
console.log('kek error', props)
return null
}
return (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
layout={'thin'}
>
<Head>
<title>{props.data.title}</title>
<Meta meta={props.meta} />
</Head>
<Article
title={props.data.title}
date={props.data.date}
tags={props.data.tags}
shareable={true}
>
<Html
html={props.data.post}
/>
</Article>
</App>
)
}
export const unstable_getStaticProps = async (ctx: any) => {
const pageUrl = '/'
const id = ctx.params.slug
const data = await requestGet(createApiUrl(`/api/articles/${id}`), null)
if (!data) {
return null
}
const image = get(data, 'preview.artifacts.fb', {})
return {
props: {
data,
pageUrl,
meta: meta({
title: data.title,
image: image.src,
imageWidth: image.width,
imageHeight: image.height,
}),
}
}
}
export const unstable_getStaticPaths = async () => {
console.log('call article unstable_getStaticPaths')
const urls = await requestGet<IResponseItems<string>>(createApiUrl(`/api/articles/urls`), null)
if (!urls) {
return null
}
return {
paths: urls.items
.map(x => x.replace('/article/', ''))
.map(slug => {
return {
params: {
slug,
},
}
}),
}
}
export default Page
-52
View File
@@ -1,52 +0,0 @@
import React from 'react'
import Head from 'next/head'
import { App } from '../../src/components/App'
import { ArticleCardList } from '../../src/components/ArticleCardList'
import menuModel from '../../src/models/menu'
import { buildMenu } from '../../src/lib/menu'
import { meta } from '../../src/lib/meta'
import { Meta } from '../../src/components/Meta'
import { createApiUrl, requestGet, wrapInitialProps } from '../../src/next-lib'
const Page = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
layout={'wide'}
>
<Head>
<title>{props.title}</title>
<Meta meta={props.meta} />
</Head>
<ArticleCardList
articles={props.articles}
prevPage={props.prevPage}
nextPage={props.nextPage}
/>
</App>
)
Page.getInitialProps = wrapInitialProps(async (ctx) => {
const pageUrl = '/'
const page = parseInt(ctx.query.page)
const pageSize = parseInt(ctx.query.pageSize) || 15
const res = await requestGet(createApiUrl(ctx.req, `/api/articles?page=${page}&pageSize=${pageSize}`), {})
const articles = res.items || []
const nextPage = res.nextPage
const prevPage = res.prevPage
const title = 'События'
return {
articles,
nextPage,
prevPage,
pageUrl,
title,
meta: meta({
title,
})
}
})
export default Page
+104
View File
@@ -0,0 +1,104 @@
import Head from 'next/head'
import { range } from 'lodash'
import { App } from 'src/components/App'
import { ArticleCardList } from 'src/components/ArticleCardList'
import menuModel from 'src/models/menu'
import { buildMenu } from 'src/lib/menu'
import { meta } from 'src/lib/meta'
import { Meta } from 'src/components/Meta'
import { createApiUrl, requestGet, IResponseItems } from 'src/next-lib'
import { NextPage } from 'next'
const pageSize = Number(process.env.APP_ARTICLES_PAGE_SIZE)
type Props = {
pageUrl: string
meta: any
title: string
data: any //(list of article)
nextPage: number
prevPage: number
}
const Page: NextPage<Props> = props => {
if (!props.data) {
console.log('kek error', props)
return null
}
return (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
layout={'wide'}
>
<Head>
<title>{props.title}</title>
<Meta meta={props.meta} />
</Head>
<ArticleCardList
articles={props.data}
prevPage={props.prevPage}
nextPage={props.nextPage}
/>
</App>
)
}
export const unstable_getStaticProps = async (ctx: any) => {
const pageUrl = '/'
// const page = parseInt(ctx.query.page)
const page = parseInt(ctx.params.slug)
const res = await requestGet<IResponseItems<any[]>>(
createApiUrl(`/api/articles?page=${page}&pageSize=${pageSize}`),
null
)
if (!res) {
return null
}
const data = res.items || []
const nextPage = (res as any).nextPage
const prevPage = (res as any).prevPage
const title = 'События'
return {
props: {
data,
nextPage,
prevPage,
pageUrl,
title,
meta: meta({
title,
})
}
}
}
export const unstable_getStaticPaths = async () => {
console.log('call articles unstable_getStaticPaths')
const urls = await requestGet<IResponseItems<string>>(createApiUrl(`/api/articles/urls`), null)
if (!urls) {
return null
}
const count = urls.items.length
const pages = Math.ceil(count / pageSize)
return {
paths: range(1, pages + 1)
.map(i => {
return {
params: {
slug: `${i}`,
},
}
}),
}
}
export default Page
-73
View File
@@ -1,73 +0,0 @@
import React from 'react'
import Head from 'next/head'
import { get } from 'lodash'
import { App } from '../src/components/App'
import { Meta } from '../src/components/Meta'
import { CollectiveImage } from '../src/components/CollectiveImage'
import { PersonCard } from '../src/components/PersonCard'
import { CardList } from '../src/components/CardList'
import menuModel from '../src/models/menu'
import { buildMenu } from '../src/lib/menu'
import { meta } from '../src/lib/meta'
import { createApiUrl, requestGet, wrapInitialProps } from '../src/next-lib'
const Page = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
layout={'wide'}
>
<Head>
<title>{props.title}</title>
<Meta meta={props.meta} />
</Head>
{!props.image ? null : (
<CollectiveImage
data={props.image}
style={{
marginBottom: 'var(--double-margin)',
}}
/>
)}
<CardList
items={props.persons}
renderItem={(person, index) => (
<PersonCard
key={index}
profile={person}
picture={person.picture}
url={person.url}
name={person.name}
/>
)}
/>
</App>
)
Page.getInitialProps = wrapInitialProps(async (ctx) => {
const pageUrl = ctx.req.url
const res = await requestGet(createApiUrl(ctx.req, '/api/persons'), {})
const persons = res.items || []
const title = 'Преподаватели Шлиссельбургской ДХШ'
const imageFile = 'Images/HudozkaCollective2017.jpg'
const resImage = await requestGet(createApiUrl(ctx.req, `/api/image?file=${imageFile}`), null)
const image = get(resImage, 'artifacts.large', null)
return {
persons,
image,
pageUrl,
title,
meta: meta({
title,
url: pageUrl,
description: 'Преподаватели Шлиссельбургской ДХШ',
})
}
})
export default Page
+96
View File
@@ -0,0 +1,96 @@
import Head from 'next/head'
import { get } from 'lodash'
import { App } from 'src/components/App'
import { Meta } from 'src/components/Meta'
import { CollectiveImage } from 'src/components/CollectiveImage'
import { PersonCard } from 'src/components/PersonCard'
import { CardList } from 'src/components/CardList'
import menuModel from 'src/models/menu'
import { buildMenu } from 'src/lib/menu'
import { meta } from 'src/lib/meta'
import { IMeta } from 'src/types'
import { createApiUrl, requestGet, IResponseItems } from 'src/next-lib'
import { NextPage, NextPageContext } from 'next'
interface IPerson {
}
type Props = {
pageUrl: string
title: string
image: string
meta: IMeta
data: IPerson[]
}
const Page: NextPage<Props> = props => {
if (!props.data) {
console.log('kek error')
return
}
return (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
layout={'wide'}
>
<Head>
<title>{props.title}</title>
<Meta meta={props.meta} />
</Head>
{!props.image ? null : (
<CollectiveImage
data={props.image}
style={{
marginBottom: 'var(--double-margin)',
}}
/>
)}
<CardList
items={props.data}
renderItem={(person, index) => (
<PersonCard
key={index}
profile={person}
picture={person.picture}
url={person.url}
name={person.name}
/>
)}
/>
</App>
)
}
export const unstable_getStaticProps = async (ctx: NextPageContext) => {
// const pageUrl = ctx.req.url
const pageUrl = '/collective'
const res = await requestGet<IResponseItems<IPerson>>(createApiUrl('/api/persons'), { items: [] })
const data = res.items || []
const title = 'Преподаватели Шлиссельбургской ДХШ'
const imageFile = 'Images/HudozkaCollective2017.jpg'
const resImage = await requestGet(createApiUrl(`/api/image?file=${imageFile}`), null)
const image = get(resImage, 'artifacts.large', null)
return {
props: {
data,
image,
pageUrl,
title,
meta: meta({
title,
url: pageUrl,
description: 'Преподаватели Шлиссельбургской ДХШ',
})
}
}
}
export default Page
-42
View File
@@ -1,42 +0,0 @@
import React from 'react'
import Head from 'next/head'
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'
import { meta } from '../../src/lib/meta'
import { Meta } from '../../src/components/Meta'
import { createApiUrl, requestGet, wrapInitialProps } from '../../src/next-lib'
const Page = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
layout={'wide'}
>
<Head>
<title>{props.file.title}</title>
<Meta meta={props.meta} />
</Head>
<Document {...props.file} />
<Share />
</App>
)
Page.getInitialProps = wrapInitialProps(async (ctx) => {
const pageUrl = '/documents'
const id = ctx.query.document
const file = await requestGet(createApiUrl(ctx.req, `/api/files/${id}`), {})
return {
file,
pageUrl,
meta: meta({
title: file.meta,
})
}
})
export default Page
+81
View File
@@ -0,0 +1,81 @@
import React from 'react'
import Head from 'next/head'
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'
import { meta } from 'src/lib/meta'
import { Meta } from 'src/components/Meta'
import { createApiUrl, requestGet, IResponseItems } from 'src/next-lib'
import { NextPage } from 'next'
import { IMeta } from 'src/types'
type Props = {
pageUrl: string
data: any //(file)
meta: IMeta
}
const Page: NextPage<Props> = props => {
if (!props.data) {
console.log('kek error', props)
return null
}
return (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
layout={'wide'}
>
<Head>
<title>{props.data.title}</title>
<Meta meta={props.meta} />
</Head>
<Document {...props.data} />
<Share />
</App>
)
}
export const unstable_getStaticProps = async (ctx: any) => {
const pageUrl = '/documents'
const id = ctx.params.slug
const data = await requestGet(createApiUrl(`/api/files/${id}`), {})
return {
props: {
data,
pageUrl,
meta: meta({
// title: file.meta,
})
}
}
}
export const unstable_getStaticPaths = async () => {
console.log('call file unstable_getStaticPaths')
const urls = await requestGet<IResponseItems<string>>(createApiUrl(`/api/files/urls`), null)
if (!urls) {
return null
}
return {
paths: urls.items
.map(x => x.replace('/document/', ''))
.map(slug => {
return {
params: {
slug,
},
}
}),
}
}
export default Page
+21 -21
View File
@@ -1,15 +1,13 @@
import React from 'react'
import Head from 'next/head'
import { App } from '../src/components/App'
import { Meta } from '../src/components/Meta'
import menuModel from '../src/models/menu'
import { buildMenu } from '../src/lib/menu'
import { meta } from '../src/lib/meta'
import { IDocument } from '../src/types'
import { DocumentList } from '../src/components/DocumentList'
import { unique } from '../src/utils/common'
import { createApiUrl, requestGet, wrapInitialProps, IResponseItems } from '../src/next-lib'
import { App } from 'src/components/App'
import { Meta } from 'src/components/Meta'
import menuModel from 'src/models/menu'
import { buildMenu } from 'src/lib/menu'
import { meta } from 'src/lib/meta'
import { IDocument } from 'src/types'
import { DocumentList } from 'src/components/DocumentList'
import { unique } from 'src/utils/common'
import { createApiUrl, requestGet, IResponseItems } from 'src/next-lib'
import { NextPage } from 'next'
interface IDocumentCategory {
@@ -125,22 +123,24 @@ const Page: NextPage<IProps> = props => (
</App>
)
Page.getInitialProps = wrapInitialProps(async (ctx) => {
export const unstable_getStaticProps = async (ctx) => {
const pageUrl = '/documents'
const res = await requestGet<IResponseItems<IDocument>>(createApiUrl(ctx.req, '/api/files'), { items: [] })
const res = await requestGet<IResponseItems<IDocument>>(createApiUrl('/api/files'), { items: [] })
const files = res.items
const title = 'Документы'
const collections = getSorted(files)
return {
collections,
pageUrl,
title,
meta: meta({
props: {
collections,
pageUrl,
title,
description: 'Документы Шлиссельбургской ДХШ',
})
meta: meta({
title,
description: 'Документы Шлиссельбургской ДХШ',
})
}
}
})
}
export default Page
export default Page
+24 -19
View File
@@ -1,14 +1,13 @@
import React from 'react'
import Head from 'next/head'
import { App } from '../src/components/App'
import { Meta } from '../src/components/Meta'
import menuModel from '../src/models/menu'
import { buildMenu } from '../src/lib/menu'
import { splitBy } from '../src/lib/array'
import { meta } from '../src/lib/meta'
import { createApiUrl, requestGet, wrapInitialProps } from '../src/next-lib'
import { App } from 'src/components/App'
import { Meta } from 'src/components/Meta'
import menuModel from 'src/models/menu'
import { buildMenu } from 'src/lib/menu'
import { splitBy } from 'src/lib/array'
import { meta } from 'src/lib/meta'
import { createApiUrl, requestGet } from 'src/next-lib'
import { NextPage } from 'next'
import { PageHeader } from '../src/components/PageHeader'
import { PageHeader } from 'src/components/PageHeader'
const albumsByYear = splitBy(album => new Date(album.date).getFullYear())
@@ -96,22 +95,28 @@ const Page: NextPage<IProps> = props => (
</App>
)
Page.getInitialProps = wrapInitialProps(async (ctx) => {
const pageUrl = ctx.req.url
const res = await requestGet<any>(createApiUrl(ctx.req, `/api/albums`), {})
export const unstable_getStaticProps = async (ctx: any) => {
// const pageUrl = ctx.req.url
const pageUrl = '/gallery'
const res = await requestGet<any>(createApiUrl(`/api/albums`), null)
if (!res) {
return null
}
const items = res.items || []
const albumCollections = albumsByYear(items)
const title = 'Галерея'
return {
collections: [...albumCollections.entries()],
pageUrl,
title,
meta: meta({
props: {
collections: [...albumCollections.entries()],
pageUrl,
title,
description: 'Галерея работ учащихся Шлиссельбургской Детской Художественной Школы'
})
meta: meta({
title,
description: 'Галерея работ учащихся Шлиссельбургской Детской Художественной Школы'
})
}
}
})
}
export default Page
+23 -22
View File
@@ -1,13 +1,12 @@
import React from 'react'
import Head from 'next/head'
import { App } from '../src/components/App'
import { Meta } from '../src/components/Meta'
import { ArticleCardList } from '../src/components/ArticleCardList'
import { HudozkaTitle } from '../src/components/HudozkaTitle'
import menuModel from '../src/models/menu'
import { meta } from '../src/lib/meta'
import { buildMenu } from '../src/lib/menu'
import { createApiUrl, requestGet, wrapInitialProps } from '../src/next-lib'
import { App } from 'src/components/App'
import { Meta } from 'src/components/Meta'
import { ArticleCardList } from 'src/components/ArticleCardList'
import { HudozkaTitle } from 'src/components/HudozkaTitle'
import menuModel from 'src/models/menu'
import { meta } from 'src/lib/meta'
import { buildMenu } from 'src/lib/menu'
import { createApiUrl, requestGet } from 'src/next-lib'
import { NextPage } from 'next'
interface IProps {
@@ -41,27 +40,29 @@ const Page: NextPage<IProps> = props => (
</App>
)
Page.getInitialProps = wrapInitialProps(async (ctx) => {
export const unstable_getStaticProps = async () => {
const pageUrl = '/'
const page = 1
const pageSize = ctx.query.pageSize || 15
const res = await requestGet(createApiUrl(ctx.req, `/api/articles?page=${page}&pageSize=${pageSize}`), {}) as any
const pageSize = process.env.APP_ARTICLES_PAGE_SIZE
const res = await requestGet(createApiUrl(`/api/articles?page=${page}&pageSize=${pageSize}`), {}) as any
const articles = res.items || []
const nextPage = res.nextPage
const prevPage = res.prevPage
const title = 'Шлиссельбургская ДХШ'
return {
articles,
nextPage,
prevPage,
pageUrl,
title,
meta: meta({
props: {
articles,
nextPage,
prevPage,
pageUrl,
title,
url: pageUrl,
})
meta: meta({
title,
url: pageUrl,
})
}
}
})
}
export default Page
export default Page
-50
View File
@@ -1,50 +0,0 @@
import React from 'react'
import Head from 'next/head'
import { App } from '../src/components/App'
import { Page } from '../src/components/Page'
import menuModel from '../src/models/menu'
import { buildMenu } from '../src/lib/menu'
import { Meta } from '../src/components/Meta'
import { meta } from '../src/lib/meta'
import { createApiUrl, requestGet, wrapInitialProps } from '../src/next-lib'
const Index = props => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
layout={'thin'}
>
<Head>
<title>{props.title}</title>
<Meta meta={props.meta} />
</Head>
<Page
showSocialShare={true}
>
{props.content}
</Page>
</App>
)
Index.getInitialProps = wrapInitialProps(async (ctx) => {
const pageUrl = ctx.req.url
const page = await requestGet(createApiUrl(ctx.req, `/api/page?page=${pageUrl}`), {})
const image = page.preview ? page.preview.artifacts.fb : {}
return {
content: page.data,
pageUrl,
title: page.title,
meta: meta({
title: page.title,
image: image.src,
imageWidth: image.width,
imageHeight: image.height,
}),
}
})
export default Index
-42
View File
@@ -1,42 +0,0 @@
import React from 'react'
import Head from 'next/head'
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'
import { createApiUrl, requestGet, wrapInitialProps } from '../src/next-lib'
const Page = (props) => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
layout={'wide'}
>
<Head>
<title>{props.title}</title>
</Head>
{/*<ScheduleList schedules={schedules}/>*/}
<Schedule {...props.schedule} />
<Share />
</App>
)
Page.getInitialProps = wrapInitialProps(async (ctx) => {
const pageUrl = ctx.req.url
const period = '2016-2017'
const semester = 'spring'
const schedule = await requestGet(createApiUrl(ctx.req, `/api/schedule?period=${period}&semester=${semester}`), {})
return {
schedule,
pageUrl,
title: 'Расписание',
}
})
export default Page
+54
View File
@@ -0,0 +1,54 @@
import Head from 'next/head'
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'
import { createApiUrl, requestGet } from 'src/next-lib'
import { NextPage } from 'next'
type Props = {
pageUrl: string
title: string
data: any //(schedule)
}
const Page: NextPage<Props> = props => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
layout={'wide'}
>
<Head>
<title>{props.title}</title>
</Head>
{/*<ScheduleList schedules={schedules}/>*/}
<Schedule {...props.data} />
<Share />
</App>
)
export const unstable_getStaticProps = async (ctx: any) => {
// const pageUrl = ctx.req.url
const pageUrl = '/schedule'
const period = '2016-2017'
const semester = 'spring'
const data = await requestGet(createApiUrl(`/api/schedule?period=${period}&semester=${semester}`), null)
if (!data) {
return null
}
return {
props: {
data,
pageUrl,
title: 'Расписание',
}
}
}
export default Page
+88 -47
View File
@@ -1,15 +1,14 @@
import React from 'react'
import Head from 'next/head'
import { App } from '../../src/components/App'
import Article from '../../src/components/Article'
import { Meta } from '../../src/components/Meta'
import menuModel from '../../src/models/menu'
import { buildMenu } from '../../src/lib/menu'
import { meta } from '../../src/lib/meta'
import { createApiUrl, requestGet, wrapInitialProps } from '../../src/next-lib'
import { NextPage } from 'next'
import { IMeta } from '../../src/types'
import Html from '../../src/components/Html'
import { App } from 'src/components/App'
import Article from 'src/components/Article'
import { Meta } from 'src/components/Meta'
import menuModel from 'src/models/menu'
import { buildMenu } from 'src/lib/menu'
import { meta } from 'src/lib/meta'
import { createApiUrl, requestGet, IResponseItems } from 'src/next-lib'
import { NextPage, NextPageContext } from 'next'
import { IMeta } from 'src/types'
import { Html } from 'src/components/Html'
interface IProps {
pageUrl: string
@@ -18,50 +17,92 @@ interface IProps {
person: any
}
const Index: NextPage<IProps> = props => (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
layout={'thin'}
>
<Head>
<title>{props.title}</title>
<Meta meta={props.meta} />
</Head>
const Index: NextPage<IProps> = props => {
if (!props?.person?.post) {
console.log('kek error', props)
<Article
title={''}
tags={[]}
date={null}
shareable={true}
return null
}
return (
<App
menu={buildMenu(props.pageUrl, menuModel)}
showAuthor={true}
menuPadding={true}
layout={'thin'}
>
<Html
html={props.person.post}
/>
</Article>
</App>
)
<Head>
<title>{props.title}</title>
<Meta meta={props.meta} />
</Head>
{!props.person.post ? null : (
<Article
title={''}
tags={[]}
date={null}
shareable={true}
>
<Html
html={props.person.post}
/>
</Article>
)}
</App>
)
}
export const unstable_getStaticProps = async (ctx: any) => {
console.log('call unstable_getStaticProps', ctx)
Index.getInitialProps = wrapInitialProps(async (ctx) => {
const pageUrl = '/collective'
const id = ctx.query.person
const person: any = await requestGet(createApiUrl(ctx.req, `/api/persons/${id}`), {})
// const id = ctx.query.person
// const id = ctx.params.person ?? 'va-sarzhin'
const id = ctx.params.person //?? 'mg-timasheva'
if (!id) {
throw new Error('sry')
}
const person = await requestGet<any>(createApiUrl(`/api/persons/${id}`), null)
const name = person.name || []
const title = name.join(' ')
const image = person.preview ? person.preview.artifacts.fb : {}
return {
pageUrl,
person,
title,
meta: meta({
title,
image: image.src,
imageWidth: image.width,
imageHeight: image.height,
})
if (!person?.post) {
throw new Error(`post kek ${id}`)
}
})
return {
props: {
pageUrl,
person,
title,
meta: meta({
title,
image: image.src,
imageWidth: image.width,
imageHeight: image.height,
})
}
}
}
export const unstable_getStaticPaths = async () => {
console.log('call unstable_getStaticPaths')
const urls = await requestGet<IResponseItems<string>>(createApiUrl(`/api/persons/urls`), null)
// const urls = {items: ['/teacher/va-sarzhin']}
return {
paths: urls.items
.map(x => x.replace('/teacher/', ''))
.map(person => ({
params: {
person,
},
})),
}
}
export default Index
+1 -1
View File
@@ -3,7 +3,7 @@ const axios = require('axios')
const fs = require('fs').promises
const api = axios.create({
baseURL: 'http://localhost:3000',
baseURL: 'https://api.tmshv.com/hudozka',
})
async function getArticles(pageSize) {
+1 -1
View File
@@ -5,7 +5,7 @@ const homeUrl = 'https://art.shlisselburg.org'
const sitemapCacheTime = 600000
const api = axios.create({
baseURL: 'http://localhost:3000',
baseURL: 'https://api.tmshv.com/hudozka',
})
async function getMenuUrls(frequency = 'daily') {
-14
View File
@@ -1,14 +0,0 @@
{
"compilerOptions": {
"noEmitOnError": false,
"target": "es5",
"module": "commonjs",
"jsx": "preserve",
"outDir": "./build",
"strict": false,
"esModuleInterop": true
},
"include": [
"src/**/*.ts"
]
}
-36
View File
@@ -1,36 +0,0 @@
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,
}
}
-24
View File
@@ -1,24 +0,0 @@
import ImageArtifactType from '../core/ImageArtifactType'
export function encodeImage(image) {
const artifacts = [
ImageArtifactType.SMALL,
ImageArtifactType.MEDIUM,
ImageArtifactType.BIG,
ImageArtifactType.LARGE,
ImageArtifactType.ORIGIN,
ImageArtifactType.ORIGINAL,
ImageArtifactType.FACEBOOK = 'fb'
].reduce((acc, size) => {
return {
...acc,
[size]: image.getPicture(size),
}
}, {})
return {
artifacts,
hash: image.hash,
file: image.file,
}
}
-12
View File
@@ -1,12 +0,0 @@
import { encodeImage } from './image'
export function encodePage(page) {
return {
id: page.id,
title: page.title,
url: page.url,
data: page.data,
preview: encodeImage(page.preview),
tokens: page.tokens,
}
}
-13
View File
@@ -1,13 +0,0 @@
import ImageArtifactType from '../core/ImageArtifactType'
import { encodeImage } from './image'
const profilePicture = profile => profile.picture.getPicture(ImageArtifactType.BIG)
export function encodePerson(person) {
return {
...person,
shortName: person.shortName(),
name: person.splitName(),
picture: profilePicture(person),
preview: encodeImage(person.picture),
}
}
@@ -1,8 +1,13 @@
import React from 'react'
import Paginator from './Paginator'
import { ArticleCard } from './ArticleCard'
export const ArticleCardList = ({ articles, nextPage, prevPage }) => (
export interface IArticleCardListProps {
articles: any[]
nextPage: number | null
prevPage: number | null
}
export const ArticleCardList:React.FC<IArticleCardListProps> = ({ articles, nextPage, prevPage }) => (
<div>
<style jsx>{`
.body {
-7
View File
@@ -1,7 +0,0 @@
import React from 'react'
export default function Html(props) {
return (
<div dangerouslySetInnerHTML={{ __html: props.html }} />
)
}
+7
View File
@@ -0,0 +1,7 @@
export type HtmlProps = {
html: string
}
export const Html: React.FC<HtmlProps> = props => (
<div dangerouslySetInnerHTML={{ __html: props.html }} />
)
-4
View File
@@ -17,10 +17,6 @@ export const Overlay: React.FC<IOverlayProps> = props => {
}
}, [props.onClickOverlay])
React.useEffect(() => {
document.body.classList.toggle('noscroll', props.show)
}, [props.show])
return (
<Transition
duration={duration}
+2 -1
View File
@@ -1,9 +1,10 @@
import * as React from 'react'
import { Share } from '../Share'
import Html from '../Html'
import { Html } from '../Html'
export interface IPageProps {
showSocialShare: boolean
children: string
}
export const Page: React.FC<IPageProps> = props => (
@@ -1,7 +1,5 @@
import React from 'react'
const { getDates } = require('../utils/date')
const { shortName } = require('../models/collective')
import { getDates } from 'src/utils/date'
import { shortName } from 'src/models/collective'
const now = new Date()
const dates = getDates(now)
-22
View File
@@ -1,22 +0,0 @@
import path from 'path'
export const port = process.env['PORT'] || 3000
export const name = 'Hudozka'
export const host = 'art.shlisselburg.org'
export const protocol = 'https://'
export const homeUrl = protocol + host
export const index = path.join(__dirname, 'templates/main.html')
export const sitemapCacheTime = 600000// 600 sec - cache purge period
export const viewMain = path.join(__dirname, 'views/main.hbs')
export const view404 = path.join(__dirname, 'views/404.hbs')
export const sessionConfig = {
key: 'sid', /** (string) cookie key */
maxAge: 86400000, /** (number) maxAge in ms */
overwrite: false, /** (boolean) can overwrite or not */
httpOnly: false, /** (boolean) httpOnly or not */
signed: true, /** (boolean) signed or not */
}
-63
View File
@@ -1,63 +0,0 @@
import Data from './Data'
import Image from './Image'
import ImageArtifactType from './ImageArtifactType'
import { find, findOne } from '../lib/store'
const previewTypes = [
ImageArtifactType.SMALL,
ImageArtifactType.MEDIUM,
ImageArtifactType.BIG,
ImageArtifactType.ORIGIN,
]
const store = () => Data.getStore('Album')
export default class Album {
static async find(query, options = {}) {
const items = await find(store(), query, options as any)
return Promise.all(items.map(processAlbum))
}
static async findById(id) {
const item = await findOne(store(), { id })
return !item
? null
: processAlbum(item)
}
}
const previewFromImage = (imgs = []) => imgs.length
? imgs[0]
: null
const processAlbum = async album => {
let preview = {}
// {
// data: [
// {
// url: null,
// width: 0,
// height: 0,
// }
// ]
// }
const previewImageId = album.preview ? album.preview : previewFromImage(album.images)
if (previewImageId) {
const image = await Image.findById(previewImageId)
if (image) {
// const imageArtifacts = image.findArtifact(previewTypes)
// if (imageArtifacts) {
// preview = imageArtifacts
// }
preview = image.getPicture(ImageArtifactType.SMALL)
}
}
return {
...album,
preview,
url: `/album/${album.id}`,
}
}
-134
View File
@@ -1,134 +0,0 @@
import Data from './Data'
import Image from './Image'
import Config from './Config'
import { find, findOne, total } from '../lib/store'
const store = () => Data.getStore<Article>('Article')
function make(C) {
return x => new C(x)
}
export default class Article {
static async find(query, options = {}): Promise<Article[]> {
const items = await find(store(), query, options as any)
return Promise.all(items.map(processArticle))
}
static async findById(id) {
const item = await findOne(store(), { id })
return !item
? null
: processArticle(item)
}
static async total(query = {}) {
return total(store(), query)
}
static async findPinned() {
const now = new Date()
const query = { until: { $gte: now } }
return Article.find(query, {
sort: { date: -1 },
})
}
public id: any
public images: any
public post: any
public folder: any
public date: any
public title: any
public until: any
public tags: any
public content: any
public hash: any
public type: any
public origin: any
public file: any
public version: any
public preview: any
public url: any
constructor(data) {
this.id = data.id
this.images = data.images
this.post = data.post
this.folder = data.folder
this.date = data.date
this.title = data.title
this.until = data.until
this.tags = (data.tags || []).map(make(Tag))
this.content = data.content
this.hash = data.hash
this.type = data.type
this.origin = data.origin
this.file = data.file
this.version = data.version
this.preview = data.preview
this.url = `/article/${this.id}`
}
plain() {
return {
id: this.id,
images: this.images,
post: this.post,
folder: this.folder,
date: this.date,
title: this.title,
until: this.until,
tags: this.tags,
content: this.content,
hash: this.hash,
type: this.type,
origin: this.origin,
file: this.file,
version: this.version,
preview: this.preview,
url: this.url,
}
}
}
class Tag {
public name: string
constructor(tag) {
this.name = tag;
}
}
const previewFromImage = (imgs = []) => imgs.length
? imgs[0]
: null
async function resolveDefaultPreview() {
const config = await Config.findConfig()
if (!config) return null
return Image.findByFile(config.articleCardDefaultPreview)
}
async function processArticle(article) {
let preview = null
const previewId = article.preview
? article.preview
: previewFromImage(article.images)
if (previewId) {
preview = await Image.findById(previewId)
} else {
preview = await resolveDefaultPreview()
}
return new Article({
...article,
preview,
})
}
-42
View File
@@ -1,42 +0,0 @@
import Data from './Data'
import { findOne } from '../lib/store'
const store = () => Data.getStore('Settings')
export default class Config {
static async findConfig() {
const config = await Config.findById('settings')
return config || new Config({} as any)
}
static async findById(id: string) {
const data = await findOne(store(), { id })
if (!data) return null
return new Config(data)
}
public hash: any
public file: any
public server: any
public articlesPerPage: any
public collectiveOrder: any
public articleCardDefaultPreview: any
public collectiveImage: any
public redirect: any
constructor({ hash, file, ...data }) {
this.hash = hash
this.file = file
this.server = data.server
this.articlesPerPage = data.articlesPerPage || 5
this.collectiveOrder = data.collectiveOrder || []
this.articleCardDefaultPreview = data.articleCardDefaultPreview
this.collectiveImage = data.collectiveImage
const redirect = data.redirect || {}
this.redirect = new Map(Object
.entries(redirect)
)
}
}
-18
View File
@@ -1,18 +0,0 @@
import { Collection } from 'mongodb'
import * as db from './db'
const store = new Map<string, Collection>()
export default class Data {
static setStore(key: string, collection: Collection) {
store.set(key, collection)
}
static getStore<T>(key: string): Collection<T> {
return store.get(key)
}
static getCollection(name: string): Collection {
return db.collection(name)
}
}
-68
View File
@@ -1,68 +0,0 @@
import Data from './Data'
import Image from './Image'
import { find, findOne } from '../lib/store'
const store = () => Data.getStore('Document')
export default class Document {
static async find(query, options = {}) {
const items = await find(store(), query, options as any)
return Promise.all(items.map(processDocument))
}
static async findById(id) {
const data = await findOne(store(), { id })
if (!data) return null
return processDocument(data)
}
public id: any
public hash: any
public file: any
public fileInfo: any
public type: any
public title: any
public category: any
public url: any
public preview: any
public viewUrl: any
constructor(data) {
this.id = data.id
this.hash = data.hash
this.file = data.file
// In the future: use fileInfo only
this.fileInfo = data.fileInfo
? new DocumentFile(data.fileInfo)
: new DocumentFile(data.file)
this.type = data.type
this.title = data.title
this.category = data.category
this.url = data.url
this.preview = data.preview
this.viewUrl = `/document/${this.id}`
}
}
class DocumentFile {
public name: any
public size: any
constructor(data) {
this.name = data.name
this.size = data.size
}
}
const processDocument = async data => {
const imageId = data.preview
const preview = await Image.findById(imageId)
return new Document({
...data,
preview,
})
}
-81
View File
@@ -1,81 +0,0 @@
import Data from './Data'
import ImageArtifactType from './ImageArtifactType'
import { findOne } from '../lib/store'
import { mapOf } from '../server/lib'
const store = () => Data.getStore('Image')
export default class Image {
static async findById(id) {
const data = await findOne(store(), { _id: id })
if (!data) return null
const artifacts = data.data
return new Image({ ...data, artifacts })
}
static async findByFile(file: string) {
const data = await findOne(store(), { file })
if (!data) return null
const artifacts = data.data
return new Image({ ...data, artifacts })
}
public hash: any
public file: any
public artifacts: any
constructor({ hash, file, artifacts }) {
this.hash = hash
this.file = file
this.artifacts = mapOf(ImageArtifact, artifacts)
}
getArtifact(size) {
return this.artifacts.get(size)
}
findArtifact(sizes) {
for (let size of sizes) {
const a = this.getArtifact(size)
if (a) return a
}
return null
}
getPicture(size) {
const retinaSize = ImageArtifactType.retina(size)
const a = this.getArtifact(size)
const a2 = this.getArtifact(retinaSize)
const srcset = !a2
? []
: [{
url: a2.url,
density: ImageArtifactType.RETINA_DENSITY,
}]
return {
src: a.url,
width: a.width,
height: a.height,
set: srcset,
}
}
}
class ImageArtifact {
public url: any
public size: any
public width: any
public height: any
constructor({ url, size, width, height }) {
this.url = url
this.size = size
this.width = width
this.height = height
}
}
-104
View File
@@ -1,104 +0,0 @@
import Data from './Data'
import { find as storeFind, findOne, total } from '../lib/store'
import Image from './Image'
import Config from './Config'
const store = () => Data.getStore('Page')
export default class Page {
static async find(query, options = {}): Promise<Page[]> {
const items = await storeFind(store(), query, options as any)
return Promise.all(items.map(processPage))
}
static async findById(id) {
const item = await findOne(store(), { id })
return !item
? null
: processPage(item)
}
static async findByUrl(url) {
const item = await findOne(store(), { url })
return !item
? null
: processPage(item)
}
static async total(query = {}) {
return total(store(), query)
}
public id: any
public hash: any
public file: any
public url: string
public content: any
public title: any
public data: any
public images: any
public documents: any
public preview: any
public tokens: any[]
constructor(data) {
this.id = data.id
this.hash = data.hash
this.file = data.file
this.url = data.url
this.content = data.content
this.title = data.title
this.data = data.data
this.images = data.images
this.documents = data.documents
this.preview = data.preview
this.tokens = data.tokens
}
plain() {
return {
id: this.id,
images: this.images,
documents: this.documents,
data: this.data,
title: this.title,
content: this.content,
hash: this.hash,
file: this.file,
preview: this.preview,
url: this.url,
}
}
}
const previewFromImage = (imgs = []) => imgs.length
? imgs[0]
: null
async function resolveDefaultPreview() {
const config = await Config.findConfig()
if (!config) return null
return Image.findByFile(config.articleCardDefaultPreview)
}
async function processPage(article) {
let preview = null
const previewId = article.preview
? article.preview
: previewFromImage(article.images)
if (previewId) {
preview = await Image.findById(previewId)
} else {
preview = await resolveDefaultPreview()
}
return new Page({
...article,
preview,
})
}
-87
View File
@@ -1,87 +0,0 @@
import Data from './Data'
import Image from './Image'
import { find, findOne } from '../lib/store'
const store = () => Data.getStore('Teacher')
export default class Teacher {
static async find(query, options = {}) {
const items = await find(store(), query, options as any)
return Promise.all(items.map(processProfile))
}
static async findById(id) {
const data = await findOne(store(), { id })
if (!data) return null
return processProfile(data)
}
public id: any
public post: any
public file: any
public edu: any
public picture: any
public preview: any
public diploma: any
public position: any
public name: any
public status: any
public hash: any
public url: any
constructor(data) {
this.id = data.id
this.post = data.post
this.file = data.file
this.edu = data.edu
this.picture = data.picture
this.preview = data.preview
this.diploma = data.diploma
this.position = data.position
this.name = data.name
this.status = data.status
this.hash = data.hash
this.url = `/teacher/${this.id}`
}
/**
*
* "First Middle Last" -> "F. M. Last"
*
* @returns {string}
*/
shortName() {
const f = i => i.replace(/^(.).+/, '$1')
const [first, middle, last] = this.splitName()
return `${f(first)}. ${f(middle)}. ${last}`
}
/**
*
* "First Middle Last" -> ["First", "Middle", "Last"]
*
* @returns {Array|{index: number, input: string}}
*/
splitName() {
const name = this.name
const m = /(.+)\s(.+)\s(.+)/.exec(name)
return m.slice(1)
}
}
const processProfile = async profile => {
const imageId = profile.picture
const previewId = profile.preview
const picture = await Image.findById(imageId)
const preview = await Image.findById(previewId)
return new Teacher({
...profile,
picture,
preview,
})
}
-48
View File
@@ -1,48 +0,0 @@
import Data from './Data'
import Page from './Page'
import Article from './Article'
const Album = require('./Album')
const Teacher = require('./Teacher')
const Document = require('./Document')
const Image = require('./Image')
import Settings from './Config'
const getConfig = require('next/config').default
const { MongoClient, ObjectId } = require('mongodb')
var db
export async function connect() {
if (db) {
return db
}
const mongoUri = getConfig().serverRuntimeConfig.dbUri
const connection = await MongoClient.connect(mongoUri)
db = init(connection)
return db
}
export function collection(name) {
return db.collection(name)
}
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 init(db) {
console.log('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'))
return db
}
-19
View File
@@ -1,19 +0,0 @@
import {c} from '../core/db';
const PAGES = 'pages';
export async function getPageUrls() {
return await c(PAGES)
.find({}, {url: 1})
.toArray();
}
/**
* Find a person by it's id
*
* @param {string} url
* @returns {Promise|*}
*/
export async function getPage(url) {
return await c(PAGES).findOne({url: url});
}
-20
View File
@@ -1,20 +0,0 @@
/**
*
* @param redirectMap
* [{String url, String redirect}, ...]
* @returns {*}
*/
function redirect(redirectMap) {
return async (ctx, next) => {
const path = ctx.request.path
if (redirectMap.has(path)) {
const url = redirectMap.get(path)
ctx.redirect(url)
} else {
await next()
}
}
}
module.exports = redirect
-20
View File
@@ -1,20 +0,0 @@
// import { MongoClient } from 'mongodb';
// const { connect, collection } = require('../../src/core/db')
import {collection, connect} from '../core/db'
// const client = new MongoClient(process.env.MONGODB_URI, { useNewUrlParser: true });
export const withCollection = handler => async (req, res) => {
await connect()
req.collection = collection
// if (!client.isConnected()) {
// return client.connect().then(() => {
// req.db = client.db('nextjsmongodbapp');
// return handler(req, res);
// });
// }
// req.db = client.db('nextjsmongodbapp');
return handler(req, res)
}
-6
View File
@@ -1,6 +0,0 @@
import { withCollection } from './withCollection'
import { NextApiRequest, NextApiResponse } from 'next'
export type Handler = (req: NextApiRequest, res: NextApiResponse) => void
export const withMiddleware = (handler: Handler) => withCollection(handler)
-22
View File
@@ -1,22 +0,0 @@
const courses = {
painting: ['Живопись', ''],
drawing: ['Рисунок', '/courses/drawing'],
sculpting: ['Скульптура', ''],
sculpture: ['Скульптура', ''],
ceramic: ['Керамика', '/courses/ceramics'],
ceramics: ['Керамика', '/courses/ceramics'],
composition: ['Композиция', ''],
'easel-composition': ['Станковая композиция', ''],
dpi: ['ДПИ', ''],
'art-talks': ['Беседа по искусству', ''],
history: ['История искусства', '/courses/art-history'],
cg: ['Комп. графика', '/courses/cg'],
}
export function getCourse(id) {
const course = id in courses ? courses[id] : null
if (!course) return null
const [title, url] = course
return {id, title, url}
}
-44
View File
@@ -1,44 +0,0 @@
export const autumnMonth = 8;
export const springMonth = 0;
export function scheduleDate(schedule){
let {period, semester} = schedule;
let i = semester === 'spring' ? 1 : 0;
let year = getPeriod(period)[i];
return new Date(
year,
i ? springMonth : autumnMonth,
1
);
}
/**
*
* f("2012-2015") -> [2012, 2015]
* f(["2012", "2015"]) -> [2012, 2015]
* f([2012, 2015]) -> [2012, 2015]
*
* @param i
* @returns {*}
*/
export function getPeriod(i){
if (typeof i === 'string') return getPeriod(i.split('-'));
return i.map(Number);
}
/**
*
* f("2012-2015") -> "2012-2015"
* f(["2012", "2015"]) -> "2012-2015"
* f([2012, 2015]) -> "2012-2015"
*
* @param i
* @returns {*}
*/
export function getPeriodString(i) {
return getPeriod(i)
.join('-')
}
+2 -23
View File
@@ -31,27 +31,6 @@ export function wrapInitialProps(fn: (ctx: NextPageContext) => void) {
}
}
export function createApiUrl(req, path) {
const { origin } = absoluteUrl(req, 'localhost:3000')
return `${origin}${path}`
}
function absoluteUrl(req, defaultLocalhost) {
let protocol = 'https:'
let host = req
? (req.headers['x-forwarded-host'] || req.headers['host'])
: window.location.host
host = host || defaultLocalhost
if (host.indexOf('localhost') > -1) {
if (defaultLocalhost) host = defaultLocalhost
protocol = 'http:'
}
const origin = protocol + '//' + host
return {
protocol,
host,
origin,
}
export function createApiUrl(path: string) {
return `https://api.tmshv.com/hudozka${path}`
}
-45
View File
@@ -1,45 +0,0 @@
import Koa from 'koa'
import next from 'next'
import { get } from 'koa-route'
import { connect } from './core/db'
import Page from './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
app.prepare().then(async () => {
try {
await connect()
} catch (e) {
console.error(e.message)
process.exit(1)
}
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()
})
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}`)
})
})
-71
View File
@@ -1,71 +0,0 @@
import Article from '../core/Article'
import ImageArtifactType from '../core/ImageArtifactType'
import { sortBy } from '../utils/sort'
import { timestamp } from '../lib/date'
import { encodeImage } from '../api/image'
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: number, pageSize: number) {
const limit = pageSize
const skip = (page - 1) * pageSize
const pinnedArticles = page === 1
? await Article.findPinned()
: []
const total = await Article.total()
const totalPages = Math.ceil(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,
totalPages,
}
}
export function encodeArticle(article) {
return {
...article,
preview: encodeImage(article.preview),
}
}
-18
View File
@@ -1,18 +0,0 @@
const { collection } = require('../core/db')
// import { collection } from '../core/db'
export async function getCollective(query = {}) {
return await collection('collective')
.find(query)
.toArray();
}
/**
* Find a person by it's id
*
* @param id
* @returns {Promise|*}
*/
export async function getPerson(id) {
return await collection('collective').findOne({ id: id });
}
-14
View File
@@ -1,14 +0,0 @@
/**
*
* {a: {}, b: {}, ...} -> Map (a: Class, b: Class)
*
* @param Class
* @param object
*/
export function mapOf(Class, object) {
return Object
.entries(object)
.reduce((map, i) => (
map.set(i[0], new Class(i[1]))
), new Map())
}
-149
View File
@@ -1,149 +0,0 @@
// import {collection} from '../core/db'
// const {collection} = require('../core/db')
import {createMap} from '../utils/common'
import {getCollective} from './collective'
import {scheduleDate, getPeriod as schedulePeriod, getPeriodString} from '../models/schedule'
import {getCourse} from '../models/course'
const last = require('../lib/array').last
const sortBy = require('../lib/sort').sortBy
const SCHEDULES = 'schedules'
export async function findSchedule(collection, period, semester) {
const schedule = await collection(SCHEDULES).findOne({
$or: [
{period: period},
{period: schedulePeriod(period)}
],
semester: semester
})
return schedule ? migrate(schedule) : null
}
async function findAllScheduleParams() {
const schedules = await collection(SCHEDULES)
.find({}, {
period: 1,
semester: 1,
_id: 0,
})
.toArray()
return schedules
.map(i => ({
...i,
period: schedulePeriod(i.period),
}))
.sort(sortBy(
i => scheduleDate(i).getTime(),
true,
))
}
async function findDefaultScheduleParams() {
const schedules = await findAllScheduleParams()
return last(schedules
.map(i => ({
...i,
period: getPeriodString(i.period),
}))
)
}
function text({semester, period}) {
const word = semester => [['spring', 'Весна'], ['autumn', 'Осень']]
.find(i => i[0] === semester)
[1]
const periodIndex = semester === 'spring' ? 1 : 0
const year = period[periodIndex]
return `${word(semester)} ${year} года`
}
async function migrate(schedule) {
const versions = {
'1.0': migrate10to20,
'2.0': migrate20to30,
}
const version = schedule.version
const update = versions[version]
return version in versions ? migrate(update(schedule)) : schedule
}
function migrate10to20(schedule) {
const dayNames = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
const time = time => time
.map(i => i.split(' '))
const week = days => days.reduce((week, lesson, index) => {
if (lesson) {
const day = dayNames[index]
week[day] = [Object.assign(lesson, {
time: time(lesson.time)
})]
}
return week
}, {})
return Object.assign(schedule, {
version: '2.0',
period: schedulePeriod(schedule.period),
groups: schedule.schedule
.map(group => ({
name: group.group,
time: group.time,
week: week(group.week)
}))
})
}
async function migrate20to30(schedule) {
const days = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat']//, 'sun']
const persons = await getCollective()
const person = createMap(i => i.id, persons)
const week = week => days
.map(day => week[day] || [])
.map(lessons => lessons.map(lesson))
const lesson = i => ({
time: i.time,
course: getCourse(i.lesson),
teacher: person.has(i.teacher) ? teacher(i.teacher) : teacherDummy(i.teacher)
})
const teacher = id => ({
id: id,
url: `/teacher/${id}`,
name: personName(id)
})
const teacherDummy = name => ({
id: name,
url: null,
name: name
})
const personName = id => person.has(id) ? person.get(id).name : null
return Object.assign(schedule, {
version: '3.0',
groups: schedule.groups
.map(group => ({
name: group.name,
time: group.time,
week: week(group.week)
}))
})
}
// exports.findSchedule = findSchedule
// exports.findAllScheduleParams = findAllScheduleParams
// exports.findDefaultScheduleParams = findDefaultScheduleParams
// exports.text = text
+70 -11
View File
@@ -1,8 +1,7 @@
:root {
//SHADOW
--white-shadow: 0 0.1em 0.3em rgba(0, 0, 0, 0.2);
//FONTS
/* FONTS */
--title-font: Shadow, 'PT Serif', Georgia, serif;
--text-font: 'PT Sans', Arial, sans-serif;
@@ -12,15 +11,12 @@
--big-font-size: 1.5rem;
--biggest-font-size: 2.25rem;
//MENU
/* MENU */
--menu-thickness: 2.5em;
--menu-colored-mark-thickness: 4px;
--menu-inactive-item-color: hsl(0, 0%, 50%);
//ANIMATION
$short-duration: 0.3s;
//SIZE
/* SIZE */
--mobile-width: 31.25em;
--desktop-width: 59.99999em;
@@ -37,7 +33,7 @@
--text-color: #303030;
--text-color-second: #ccc;
//FONTS
/* FONTS */
--font-size-default: 14pt;
--font-size-second: 0.9rem;
@@ -45,7 +41,7 @@
--font-size-h2: 2.5rem;
--font-size-h3: 1.5rem;
//COLOR
/* COLOR */
--dark-color: hsl(0, 0%, 20%);
--darker-color: hsl(0, 0%, 10%);
--light-color: hsl(0, 0%, 80%);
@@ -65,13 +61,76 @@
--schedule-row-color: hsl(210, 55%, 90%);
//MARGINS
/* MARGINS */
--small-margin: 0.2em;
--half-margin: 0.5em;
--single-margin: 1em;
--double-margin: 2em;
--post-margin: 5em;
//OTHER
/* OTHER */
--block-border-radius: 3px;
}
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
}
body {
min-width: 320px;
color: var(--text-color);
}
h1 {
text-align: center;
font-size: var(--font-size-h1);
hyphens: none;
}
h2 {
margin: var(--single-margin) 0;
font-size: var(--font-size-h2);
hyphens: none;
}
h3 {
margin: var(--single-margin) 0;
font-size: var(--font-size-h3);
hyphens: none;
}
.actor {
border-bottom: 1px dashed black;
}
.actor:hover {
border-bottom: 2px solid black;
}
@media (max-width: 31.25em) {
:root {
--smaller-font-size: 0.9rem;
--normal-font-size: 1rem;
--bigger-font-size: 1.1rem;
--big-font-size: 1.3rem;
--biggest-font-size: 2rem;
--font-size-h1: 1.5rem;
--font-size-h2: 1.5rem;
--font-size-h3: 1.25rem;
--content-thin: 90%;
--content-wide: 90%;
}
}
-2
View File
@@ -1,5 +1,3 @@
@import "variables";
.album-block {
//margin: 1em auto 2em;
-2
View File
@@ -1,5 +1,3 @@
@import "../variables";
.ArticleTags {
--article-card-width: 30%;
--article-card-margin: var(--single-margin) var(--half-margin);
-23
View File
@@ -1,23 +0,0 @@
.page-collective {
&.ng-leave {
animation: slideOutLeft var(--short-duration) both ease-in;
}
&.ng-enter {
animation: slideInRight var(--short-duration) both ease-in;
}
}
.page-collective {
&__collective-image {
//min-height: 40em;
}
}
@media (max-width: var(--mobile-width)) {
.page-collective {
&__collective-image {
display: none;
}
}
}
-10
View File
@@ -1,10 +0,0 @@
.page-schedule {
&.ng-leave {
transform-origin: 0% 0%;
animation: rotateFall var(--short-duration) both ease-in;
}
&.ng-enter {
animation: scaleUp var(--short-duration) both ease-in;
}
}
-2
View File
@@ -1,5 +1,3 @@
@import "../variables";
.parallax {
min-width: 1280px;
width: 1280px;
@@ -1,5 +1,3 @@
@import "../variables";
$user-size: 40px;
$half-margin: $single-margin/2;
$image-border-radius: .35em;
-2
View File
@@ -1,5 +1,3 @@
@import "variables";
footer {
padding: 0 var(--half-margin) var(--half-margin);
position: relative;
-45
View File
@@ -1,45 +0,0 @@
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
}
body {
min-width: 320px;
color: var(--text-color);
}
h1 {
text-align: center;
font-size: var(--font-size-h1);
hyphens: none;
}
h2 {
margin: var(--single-margin) 0;
font-size: var(--font-size-h2);
hyphens: none;
}
h3 {
margin: var(--single-margin) 0;
font-size: var(--font-size-h3);
hyphens: none;
}
.actor {
border-bottom: 1px dashed black;
&:hover {
border-bottom: 2px solid black;
}
}
-4
View File
@@ -15,12 +15,8 @@
@import "components/album";
@import "components/parallax";
@import "components/page-schedule";
@import "components/page-collective";
@import "components/ArticleTags";
@import "main";
@import "body-wrapper";
@import "content";
@import "page-animation";
-2
View File
@@ -1,5 +1,3 @@
@import "variables";
.vertical-image {
position: relative;
width: 100%;
-17
View File
@@ -1,20 +1,3 @@
/**
* Generates list of numbers
* @param {Number} start starting value
* @param {Number} num amount of numbers in list
* @param {Number} step difference value
* @return {Array} list of numbers
*/
export function range(start, num, step) {
var list = [];
var val = start;
for (var i = 0; i < num; i++) {
list.push(val);
val += step;
}
return list;
}
/**
* Return a closure that compares a Number argument with specified Number
* @param withNumber Number to compare with
+4 -9
View File
@@ -1,16 +1,11 @@
import {range} from './common';
import { range } from 'lodash'
/**
*
* @param start
* @returns {*}
*/
export function getDates(start) {
export function getDates(start: Date) {
const oneDay = (1000 * 60 * 60 * 24);
const startTime = start.getTime();
const startDay = start.getDay();
return range(0, 6, 1)
return range(0, 6)
.reduce((dates, weekDay) => {
let delta = 1 + weekDay - startDay;
let time = startTime + (oneDay * delta);
@@ -18,4 +13,4 @@ export function getDates(start) {
return dates.concat([new Date(time)]);
}, [])
.map(i => i.getDate());
}
}
-17
View File
@@ -1,17 +0,0 @@
import {getPeriod} from '../../src/models/schedule';
describe('models', () => {
describe('schedule.js', () => {
describe('getPeriod', () => {
it('should accept a string and return an array', () => {
getPeriod('2016-2017').should.be.Array;
});
it('should accept a string "XXXX-YYYY" and return an array [XXXX, YYYY]', () => {
const sample = '2016-2017';
const result = getPeriod(sample);
result.should.be.eql([2016, 2017]);
});
});
});
});
+1
View File
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"baseUrl": ".",
"target": "es5",
"lib": [
"dom",