15 files changed
-586
No files matched your search
@@ -1,47 +0,0 @@
|
||||
import { Response, Request } from 'express'
|
||||
import Album from '../core/Album'
|
||||
|
||||
export async function getItem(req: Request, res: Response) {
|
||||
const id = req.params.slug
|
||||
const album = await Album.findById(id)
|
||||
|
||||
if (album) {
|
||||
res.json(album)
|
||||
} else {
|
||||
res.status(404)
|
||||
res.json({
|
||||
error: `Resource ${id} not found`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAll(req: Request, res: Response) {
|
||||
const data = await Album.find({}, { sort: { date: -1 } })
|
||||
|
||||
if (data) {
|
||||
res.json({
|
||||
items: data,
|
||||
})
|
||||
} else {
|
||||
res.status(404)
|
||||
res.json({
|
||||
error: `Not found`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUrls (req: Request, res: Response) {
|
||||
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`
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,65 +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 {
|
||||
public id: string
|
||||
|
||||
static async find(query, options = {}): Promise<Album[]> {
|
||||
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}`,
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,6 @@ function init(db: 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('Document', db.collection('documents'))
|
||||
Data.setStore('Settings', db.collection('settings'))
|
||||
|
||||
@@ -21,7 +21,6 @@ export function encodeFile(file) {
|
||||
const category = (file.category || '')
|
||||
.replace('Documents/', '')
|
||||
.replace('Pages/', '')
|
||||
.replace('Albums/', '')
|
||||
.replace('Articles /', '')
|
||||
|
||||
return {
|
||||
|
||||
@@ -2,7 +2,6 @@ import { config } from 'dotenv'
|
||||
import express from 'express'
|
||||
import { connect } from './db'
|
||||
import * as article from './controllers/article'
|
||||
import * as album from './controllers/album'
|
||||
import * as schedule from './controllers/schedule'
|
||||
import * as file from './controllers/file'
|
||||
import * as page from './controllers/page'
|
||||
@@ -28,10 +27,6 @@ async function main() {
|
||||
app.get('/articles/urls', article.getUrls)
|
||||
app.get('/articles/:slug', article.getItem)
|
||||
|
||||
app.get('/albums', album.getAll)
|
||||
app.get('/albums/urls', album.getUrls)
|
||||
app.get('/albums/:slug', album.getItem)
|
||||
|
||||
app.get('/files', file.getAll)
|
||||
app.get('/files/urls', file.getUrls)
|
||||
app.get('/files/:slug', file.getItem)
|
||||
|
||||
@@ -43,8 +43,6 @@ skip_unchanged = value('skip_unchanged')
|
||||
|
||||
image_processing_enabled = value('image_processing')
|
||||
|
||||
album_html_img_class = 'hudozka-product'
|
||||
|
||||
image_ext = '.jpg'
|
||||
image_sizes = [
|
||||
('original', None, None),
|
||||
@@ -76,18 +74,6 @@ date_formats = [
|
||||
'%d.%m.%Y %H:%M:%S'
|
||||
]
|
||||
|
||||
date_formats_direct = [
|
||||
'%Y',
|
||||
'%m.%Y',
|
||||
'%d.%m.%Y',
|
||||
]
|
||||
|
||||
date_formats_reverse = [
|
||||
'%Y',
|
||||
'%Y.%m',
|
||||
'%Y.%m.%d'
|
||||
]
|
||||
|
||||
origin = value('origin')
|
||||
|
||||
collection_settings = 'settings'
|
||||
@@ -95,12 +81,10 @@ collection_images = 'images'
|
||||
collection_documents = 'documents'
|
||||
collection_schedules = 'schedules'
|
||||
collection_articles = 'articles'
|
||||
collection_albums = 'albums'
|
||||
collection_pages = 'pages'
|
||||
|
||||
hash_salt_articles = '6'
|
||||
hash_salt_pages = '4'
|
||||
hash_salt_albums = '1'
|
||||
hash_salt_documents = '4'
|
||||
hash_salt_images = '5'
|
||||
|
||||
@@ -113,7 +97,6 @@ dir_settings = './Settings'
|
||||
dir_documents = './Documents'
|
||||
dir_schedules = './Schedules'
|
||||
dir_articles = './Articles'
|
||||
dir_albums = './Gallery'
|
||||
dir_pages = './Pages'
|
||||
dir_images = './Images'
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import asyncio
|
||||
import logging
|
||||
|
||||
from sync.models.Image import Image
|
||||
from sync.models.Album import Album
|
||||
from sync.models.Article import Article
|
||||
from sync.models.Page import Page
|
||||
from sync.models.Schedule import Schedule
|
||||
@@ -71,13 +70,6 @@ async def run(run_interval=0):
|
||||
sync_schedules.set_options(True, True, True)
|
||||
sync_schedules.validate_urls = False
|
||||
|
||||
sync_albums = Sync(
|
||||
provider=io(settings.provider_root),
|
||||
model=Album,
|
||||
)
|
||||
sync_albums.set_options(True, True, True)
|
||||
sync_albums.validate_urls = False
|
||||
|
||||
sync_settings = Sync(
|
||||
provider=io(settings.provider_root),
|
||||
model=Settings,
|
||||
@@ -99,7 +91,6 @@ async def run(run_interval=0):
|
||||
run_sync(sync_pages),
|
||||
run_sync(sync_articles),
|
||||
run_sync(sync_schedules),
|
||||
run_sync(sync_albums),
|
||||
run_sync(sync_settings),
|
||||
run_sync(sync_images),
|
||||
])
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
import logging
|
||||
|
||||
import os
|
||||
from lxml import etree
|
||||
|
||||
import lxml.html
|
||||
|
||||
import kazimir
|
||||
import settings
|
||||
from db import collection
|
||||
from sync import create_date_and_title_from_folder_name, images_from_html, create_post_from_image_list, create_date, \
|
||||
create_post
|
||||
from sync.data import Provider, list_images
|
||||
from sync.models import Model
|
||||
from sync.models.Image import Image
|
||||
|
||||
from utils.fn import swap_ext, first, map_cases, constant, combine
|
||||
from utils.hash import hash_str
|
||||
from utils.io import read_yaml_md, parse_yaml_front_matter
|
||||
from utils.text.transform import url_encode_text
|
||||
|
||||
logger = logging.getLogger(settings.name + '.Album')
|
||||
store = collection(settings.collection_albums)
|
||||
|
||||
|
||||
class Album(Model):
|
||||
@staticmethod
|
||||
async def find(query):
|
||||
return store.find(query)
|
||||
|
||||
@staticmethod
|
||||
async def delete(query):
|
||||
return store.find_one_and_delete(query)
|
||||
|
||||
@staticmethod
|
||||
async def scan(provider):
|
||||
scan_dir = settings.dir_albums
|
||||
documents = [i for i in provider.scan(scan_dir) if provider.is_dir(i)]
|
||||
documents = [Album.read(provider, i) for i in documents]
|
||||
return documents
|
||||
|
||||
@staticmethod
|
||||
def read(provider, path):
|
||||
# PARSE FOLDER NAME -> GET OPTIONAL DATE/TITLE
|
||||
date, title = create_date_and_title_from_folder_name(
|
||||
os.path.basename(path)
|
||||
)
|
||||
|
||||
# CREATE BASIC_MANIFEST BASED ON FOLDER NAME AND IMAGE CONTENT
|
||||
document = {
|
||||
'folder': path,
|
||||
'date': date,
|
||||
'title': title,
|
||||
}
|
||||
|
||||
# TRY TO FILL UP BASIC_MANIFEST WITH MD_MANIFEST
|
||||
manifest_file = provider.type_filter(path, '.md')
|
||||
manifest_file = manifest_file[0] if len(manifest_file) else None
|
||||
manifest = get_manifest(provider, manifest_file) if manifest_file else {}
|
||||
manifest = manifest if manifest else {}
|
||||
|
||||
file = manifest_file if manifest_file else path
|
||||
return Album(
|
||||
provider,
|
||||
file,
|
||||
params={
|
||||
**document,
|
||||
**manifest,
|
||||
},
|
||||
)
|
||||
|
||||
def __init__(self, provider, file, params=None):
|
||||
self.__hash_salt = settings.hash_salt_albums
|
||||
|
||||
self.version = '1'
|
||||
|
||||
self.origin = settings.origin
|
||||
self.post = None
|
||||
self.images = []
|
||||
self.documents = []
|
||||
self.preview = None
|
||||
super().__init__(provider, store, file, params=params)
|
||||
|
||||
def init(self):
|
||||
if self.has_param('content'):
|
||||
images, documents = kazimir.extract_files(self.get_param('content'))
|
||||
else:
|
||||
images = list_images(self.provider, self.file)
|
||||
images = [os.path.relpath(i, self.file) for i in images]
|
||||
documents = []
|
||||
self.params['images'] = images
|
||||
self.params['documents'] = documents
|
||||
|
||||
self.__set_id()
|
||||
self.__set_hash()
|
||||
|
||||
def validate(self):
|
||||
document = self.params
|
||||
if 'date' not in document or not document['date']:
|
||||
return None
|
||||
|
||||
if 'title' not in document or not document['title']:
|
||||
return None
|
||||
|
||||
async def build(self, **kwargs):
|
||||
sizes = kwargs['sizes']
|
||||
folder = self.get_param('folder')
|
||||
|
||||
if self.has_param('content'):
|
||||
markdown_post = self.get_param('content')
|
||||
else:
|
||||
markdown_post = create_post_from_image_list(self.get_param('images'))
|
||||
|
||||
post, images, documents = await create_post(self.provider, folder, markdown_post, sizes)
|
||||
self.post = post
|
||||
self.images = images
|
||||
self.documents = documents
|
||||
|
||||
if self.has_param('preview'):
|
||||
preview = self.get_param('preview')
|
||||
preview = self._get_relpath(preview)
|
||||
preview = await Image.new(self.provider, preview, sizes=sizes)
|
||||
self.preview = preview
|
||||
|
||||
def create_products(self, doc, image_file_fn):
|
||||
pass
|
||||
# image_id = id_from_file
|
||||
# iter_album = iterate_iter_over_fns([
|
||||
# lambda product: {
|
||||
# **product,
|
||||
# 'image': self.create_image(
|
||||
# image_file_fn(product['file']),
|
||||
# url_creator(
|
||||
# doc['id'],
|
||||
# image_id(product['file'])
|
||||
# )
|
||||
# )
|
||||
# }
|
||||
# ])
|
||||
#
|
||||
# return lmap(
|
||||
# create_product,
|
||||
# iter_album(doc['album'])
|
||||
# )
|
||||
|
||||
def bake(self):
|
||||
preview = None if not self.preview else self.preview.ref
|
||||
|
||||
return {
|
||||
**self.params,
|
||||
'images': [x.ref for x in self.images],
|
||||
'documents': [x.ref for x in self.documents],
|
||||
'preview': preview,
|
||||
'post': self.post,
|
||||
'id': self.id,
|
||||
'hash': self.hash,
|
||||
'file': self.file,
|
||||
'origin': self.origin,
|
||||
'version': self.version,
|
||||
}
|
||||
|
||||
def __set_id(self):
|
||||
if 'id' in self.params:
|
||||
self.id = self.params['id']
|
||||
else:
|
||||
year = str(self.params['date'].year)
|
||||
title = self.params['title']
|
||||
self.id = url_encode_text('%s-%s' % (year, title))
|
||||
|
||||
def __set_hash(self):
|
||||
files = self.get_param('images')
|
||||
files = [self._get_relpath(i) for i in files]
|
||||
|
||||
if not self.provider.is_dir(self.file):
|
||||
files.append(self.file)
|
||||
|
||||
files = sorted(files)
|
||||
files = [self.provider.hash(i) for i in files]
|
||||
|
||||
self.hash = hash_str(files)
|
||||
self.hash = hash_str(
|
||||
combine([self.__hash_salt] + [hash_str(self.params)] + files)
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return '<Album id={} file={}>'.format(self.id, self.file)
|
||||
|
||||
|
||||
def get_manifest(provider, filepath):
|
||||
data = provider.read(filepath).read().decode('utf-8')
|
||||
manifest = parse_yaml_front_matter(data)
|
||||
|
||||
if 'date' in manifest:
|
||||
manifest['date'] = create_date(str(manifest['date']), settings.date_formats_direct)
|
||||
|
||||
if 'id' in manifest:
|
||||
manifest['id'] = str(manifest['id'])
|
||||
|
||||
return manifest
|
||||
@@ -1,78 +0,0 @@
|
||||
import Head from 'next/head'
|
||||
import { App } from 'src/components/App'
|
||||
import { Article } from 'src/components/Article'
|
||||
import { Html } from 'src/components/Html'
|
||||
import { createApiUrl, requestGet, IResponseItems } from 'src/next-lib'
|
||||
import { NextPage } from 'next'
|
||||
|
||||
type Props = {
|
||||
data: any //(album)
|
||||
}
|
||||
|
||||
const Page: NextPage<Props> = props => {
|
||||
if (!props?.data) {
|
||||
console.log('kek error', props)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<App
|
||||
showAuthor={true}
|
||||
>
|
||||
<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 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1,115 +0,0 @@
|
||||
import Head from 'next/head'
|
||||
import { App } from 'src/components/App'
|
||||
import { Meta } from 'src/components/Meta'
|
||||
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'
|
||||
|
||||
const albumsByYear = splitBy(album => new Date(album.date).getFullYear())
|
||||
|
||||
function getMeta() {
|
||||
return {
|
||||
description: 'Галерея работ учащихся Шлиссельбургской Детской Художественной Школы'
|
||||
}
|
||||
}
|
||||
|
||||
function resize(maxWidth, width, height) {
|
||||
const ratio = maxWidth / width
|
||||
|
||||
return [
|
||||
width * ratio,
|
||||
height * ratio,
|
||||
]
|
||||
}
|
||||
|
||||
const m = 1
|
||||
const AlbumImage = ({ data, alt }) => {
|
||||
const [width, height] = resize(200, data.width, data.height)
|
||||
return (
|
||||
<picture>
|
||||
<img
|
||||
className="opa"
|
||||
alt={alt}
|
||||
src={data.src}
|
||||
width={width * m}
|
||||
height={height * m}
|
||||
srcSet={(data.set || []).map(({ url, density }) => `${url} ${density}x`)}
|
||||
/>
|
||||
</picture>
|
||||
)
|
||||
}
|
||||
|
||||
const GItem = ({ album }) => (
|
||||
<div className="gallery-item">
|
||||
<a className="invisible" href={album.url}>
|
||||
<AlbumImage data={album.preview} alt={album.title} />
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
|
||||
const ACollection = ({ title, albums }) => (
|
||||
<div className="album-collection">
|
||||
<div className="album-collection__title">{title}</div>
|
||||
<div className="album-collection__body">{
|
||||
albums.map((album, index) => (
|
||||
<GItem key={index} album={album} />
|
||||
))
|
||||
}</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
interface IProps {
|
||||
meta: any
|
||||
title: string
|
||||
collections: any[]
|
||||
}
|
||||
|
||||
const Page: NextPage<IProps> = props => (
|
||||
<App
|
||||
showAuthor={true}
|
||||
wide={true}
|
||||
>
|
||||
<Head>
|
||||
<title>{props.title}</title>
|
||||
<Meta meta={props.meta} />
|
||||
</Head>
|
||||
|
||||
<PageHeader
|
||||
title={props.title}
|
||||
/>
|
||||
|
||||
{props.collections.map(([year, albums], index) => (
|
||||
<ACollection
|
||||
key={index}
|
||||
title={year}
|
||||
albums={albums}
|
||||
/>
|
||||
))}
|
||||
</App>
|
||||
)
|
||||
|
||||
export const unstable_getStaticProps = async (ctx: any) => {
|
||||
// const pageUrl = ctx.req.url
|
||||
const res = await requestGet<any>(createApiUrl(`/api/albums`), null)
|
||||
if (!res) {
|
||||
return null
|
||||
}
|
||||
const items = res.items || []
|
||||
const albumCollections = albumsByYear(items)
|
||||
const title = 'Галерея'
|
||||
|
||||
return {
|
||||
props: {
|
||||
collections: [...albumCollections.entries()],
|
||||
title,
|
||||
meta: meta({
|
||||
title,
|
||||
description: 'Галерея работ учащихся Шлиссельбургской Детской Художественной Школы'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Page
|
||||
@@ -14,7 +14,6 @@ async function getMenuUrls(frequency = 'daily') {
|
||||
'/schedule',
|
||||
'/documents',
|
||||
'/collective',
|
||||
'/gallery',
|
||||
]
|
||||
return menu
|
||||
// .filter(i => 'url' in i)
|
||||
@@ -33,14 +32,6 @@ async function getPageUrls(frequency = 'daily') {
|
||||
}))
|
||||
}
|
||||
|
||||
async function getAlbumsUrls() {
|
||||
const res = await api.get('/api/albums/urls')
|
||||
return res.data.items.map(url => ({
|
||||
changefreq: 'monthly',
|
||||
url,
|
||||
}))
|
||||
}
|
||||
|
||||
async function getArticleUrls() {
|
||||
const res = await api.get('/api/articles/urls')
|
||||
|
||||
@@ -54,7 +45,6 @@ async function getArticleUrls() {
|
||||
let urls = await Promise.all([
|
||||
getMenuUrls(),
|
||||
getPageUrls(),
|
||||
getAlbumsUrls(),
|
||||
getArticleUrls(),
|
||||
])
|
||||
urls = urls.reduce((urls, i) => urls.concat(i))
|
||||
|
||||
@@ -47,10 +47,6 @@ export default {
|
||||
name: 'Коллектив',
|
||||
href: '/collective',
|
||||
},
|
||||
{
|
||||
name: 'Галерея',
|
||||
href: '/gallery',
|
||||
},
|
||||
{
|
||||
name: '«Мы соседи»',
|
||||
href: '/neighbors',
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
.album-collection{
|
||||
&__body{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
position: relative;
|
||||
justify-content: flex-start;
|
||||
align-items:center;
|
||||
|
||||
margin: 0 0 var(--double-margin);
|
||||
}
|
||||
|
||||
&__title{
|
||||
text-align: center;
|
||||
margin: var(--single-margin )0;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
.gallery-item {
|
||||
box-sizing: padding-box;
|
||||
padding: 0;
|
||||
|
||||
margin: 0 var(--single-margin) var(--double-margin);
|
||||
|
||||
a.invisible {
|
||||
border-bottom: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: var(--mobile-width)) {
|
||||
.gallery-item {
|
||||
margin: 0 0 var(--double-margin);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
@import "components/album-collection";
|
||||
@import "components/gallery-item";
|
||||
@import "components/document";
|
||||
@import "components/document-row";
|
||||
@import "components/documents-container";
|
||||
|
||||
Reference in new issue
Block a user