api copied to hudozka-api module based on express
This commit is contained in:
1 parent
70007d3199
commit
fc0724a900
21 files changed
+1093
No files matched your search
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "hudozka-api",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "src/server.ts",
|
||||
"scripts": {
|
||||
"start": "ts-node src/server.ts",
|
||||
"sitemap": "node scripts/sitemap.js",
|
||||
"feed": "node scripts/feed.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tmshv/hudozka"
|
||||
},
|
||||
"author": "Roman Timashev",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^0.19.0",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"feed": "^4.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
"moment": "^2.24.0",
|
||||
"mongodb": "^3.5.3",
|
||||
"sitemap": "^1.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/classnames": "^2.2.9",
|
||||
"@types/koa": "^2.11.1",
|
||||
"@types/koa-route": "^3.2.4",
|
||||
"@types/mongodb": "^3.3.16",
|
||||
"@types/node": "^13.7.1",
|
||||
"ts-node": "^8.6.2",
|
||||
"typescript": "^3.7.5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
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`
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import Article from '../core/Article'
|
||||
import { encodeArticle, findArticles } from '../server/articles'
|
||||
import { Request, Response } from 'express'
|
||||
|
||||
const int = (value: string | string[]) => parseInt(Array.isArray(value)
|
||||
? value[0]
|
||||
: value
|
||||
)
|
||||
|
||||
export async function getItem(req: Request, res: Response) {
|
||||
const id = req.params.slug
|
||||
const article = await Article.findById(id)
|
||||
|
||||
if (article) {
|
||||
res.json(encodeArticle(article))
|
||||
} else {
|
||||
res.status(404)
|
||||
res.json({
|
||||
error: `Resource ${id} not found`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAll(req: Request, res: Response) {
|
||||
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`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUrls(req: Request, res: Response) {
|
||||
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`
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Request, Response } from 'express'
|
||||
import Document from '../core/Document'
|
||||
import { encodeFile } from '../api/files'
|
||||
|
||||
export async function getItem(req: Request, res: Response) {
|
||||
const id = req.params.slug
|
||||
const document = await Document.findById(id)
|
||||
|
||||
if (document) {
|
||||
res.json(encodeFile(document))
|
||||
} else {
|
||||
res.status(404)
|
||||
res.json({
|
||||
error: `Resource ${id} not found`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAll(req: Request, res: Response) {
|
||||
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: `Not found`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUrls(req: Request, res: Response) {
|
||||
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`
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import Image from '../core/Image'
|
||||
import { encodeImage } from '../api/image'
|
||||
import { Request, Response } from 'express'
|
||||
|
||||
export async function getItem(req: Request, res: Response) {
|
||||
const file = req.params.slug
|
||||
const image = await Image.findByFile(file)
|
||||
|
||||
if (image) {
|
||||
res.json(encodeImage(image))
|
||||
} else {
|
||||
res.status(404)
|
||||
res.json({
|
||||
error: `Resource ${file} not found`
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Request, Response } from 'express'
|
||||
import Page from '../core/Page'
|
||||
import { getPathWithNoTrailingSlash } from '../lib/url'
|
||||
import { encodePage } from '../api/page'
|
||||
|
||||
export async function getItem(req: Request, res: Response) {
|
||||
const id = req.params.slug
|
||||
const page = getPathWithNoTrailingSlash(id)
|
||||
const resource = await Page.findByUrl(page)
|
||||
|
||||
if (resource) {
|
||||
res.json(encodePage(resource))
|
||||
} else {
|
||||
res.status(404)
|
||||
res.json({
|
||||
error: `Resource ${page} not found`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAll(req: Request, res: Response) {
|
||||
const items = await Page.find({})
|
||||
|
||||
if (items) {
|
||||
res.json({
|
||||
items,
|
||||
})
|
||||
} else {
|
||||
res.status(404)
|
||||
res.json({
|
||||
error: `Not found`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUrls(req: Request, res: Response) {
|
||||
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`
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import Teacher from '../core/Teacher'
|
||||
import { encodePerson } from '../api/persons'
|
||||
import { Request, Response } from 'express'
|
||||
import { prioritySort } from '../lib/sort'
|
||||
|
||||
export async function getItem(req: Request, res: Response) {
|
||||
const id = req.params.slug
|
||||
const data = await Teacher.findById(id)
|
||||
|
||||
if (data) {
|
||||
res.json(encodePerson(data))
|
||||
} else {
|
||||
res.status(404)
|
||||
res.json({
|
||||
error: `Resource ${id} not found`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAll(req: Request, res: Response) {
|
||||
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: `Not found`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUrls(req: Request, res: Response) {
|
||||
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`
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { findSchedule } from '../server/schedule'
|
||||
import { Request, Response } from 'express'
|
||||
import { collection } from '../db'
|
||||
|
||||
export async function getItem(req: Request, res: Response) {
|
||||
const period = req.query.period
|
||||
const semester = req.query.semester
|
||||
const data = await findSchedule(collection, period, semester)
|
||||
|
||||
if (data) {
|
||||
res.json(data)
|
||||
} else {
|
||||
res.status(404)
|
||||
res.json({
|
||||
error: `Schedule ${period}:${semester} not found`
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
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}`,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
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,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
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)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
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 = {}): Promise<Document[]> {
|
||||
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,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
class ImageArtifactType {
|
||||
constructor() {
|
||||
throw new Error('Cannot create instance of ImageArtifactType')
|
||||
}
|
||||
}
|
||||
|
||||
ImageArtifactType.SMALL = 'small'
|
||||
ImageArtifactType.MEDIUM = 'medium'
|
||||
ImageArtifactType.BIG = 'big'
|
||||
ImageArtifactType.LARGE = 'large'
|
||||
ImageArtifactType.ORIGIN = 'original'
|
||||
ImageArtifactType.ORIGINAL = 'original'
|
||||
|
||||
ImageArtifactType.FACEBOOK = 'fb'
|
||||
|
||||
ImageArtifactType.RETINA_DENSITY = 2
|
||||
ImageArtifactType.retina = type => `${type}@${ImageArtifactType.RETINA_DENSITY}`
|
||||
|
||||
module.exports = ImageArtifactType
|
||||
@@ -0,0 +1,104 @@
|
||||
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,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
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 = {}): Promise<Teacher[]> {
|
||||
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,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
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});
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import Data from './core/Data'
|
||||
import { MongoClient, ObjectId, Db } from 'mongodb'
|
||||
|
||||
var db: Db
|
||||
|
||||
export async function connect(dbName: string, mongoUri: string) {
|
||||
if (db) {
|
||||
return db
|
||||
}
|
||||
const connection = await MongoClient.connect(mongoUri, { useUnifiedTopology: true })
|
||||
db = init(connection.db(dbName))
|
||||
return db
|
||||
}
|
||||
|
||||
export function collection(name) {
|
||||
return db.collection(name)
|
||||
}
|
||||
|
||||
export function id(i) {
|
||||
if (typeof i === 'string') return new ObjectId(i)
|
||||
else if (i instanceof ObjectId) return i
|
||||
else if (i instanceof Object) return '_id' in i ? new ObjectId(i._id) : null
|
||||
return null
|
||||
}
|
||||
|
||||
function init(db: 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
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import dotenv from 'dotenv'
|
||||
import express from 'express'
|
||||
import { connect } from './db'
|
||||
import * as person from './controllers/person'
|
||||
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'
|
||||
import * as image from './controllers/image'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
async function main() {
|
||||
const port = parseInt(process.env.PORT, 10) || 3000
|
||||
const mongoUri = process.env.MONGO_URI
|
||||
|
||||
try {
|
||||
await connect('hudozka', mongoUri)
|
||||
|
||||
console.log('connected')
|
||||
} catch (e) {
|
||||
console.error(e.message)
|
||||
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const app = express()
|
||||
app.get('/persons', person.getAll)
|
||||
app.get('/persons/urls', person.getUrls)
|
||||
app.get('/persons/:slug', person.getItem)
|
||||
|
||||
app.get('/articles', article.getAll)
|
||||
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)
|
||||
|
||||
app.get('/pages', page.getAll)
|
||||
app.get('/pages/urls', page.getUrls)
|
||||
app.get('/pages/:slug', page.getItem)
|
||||
|
||||
app.get('/images/:slug', image.getItem)
|
||||
|
||||
app.get('/schedule', schedule.getItem)
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`> Ready on http://localhost:${port}`)
|
||||
})
|
||||
}
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"esModuleInterop": true,
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts"
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user