Merge branch 'master' into feature/next-static-export
This commit is contained in:
commit
744f127585
48 files changed
+2572
No files matched your search
@@ -0,0 +1,2 @@
|
|||||||
|
.env
|
||||||
|
*.tar
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
.env
|
||||||
|
*.tar
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# BUILD
|
||||||
|
FROM node:12-alpine as build
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Building app
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# RUNTIME
|
||||||
|
FROM node:12-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm install --production
|
||||||
|
|
||||||
|
COPY --from=build /app/build ./build
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
ENV PORT 80
|
||||||
|
ENV NODE_ENV production
|
||||||
|
|
||||||
|
# Running the app
|
||||||
|
CMD [ "npm", "start" ]
|
||||||
Generated
+693
@@ -0,0 +1,693 @@
|
|||||||
|
{
|
||||||
|
"name": "hudozka-api",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/bson": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-K6VAEdLVJFBxKp8m5cRTbUfeZpuSvOuLKJLrgw9ANIXo00RiyGzgH4BKWWR4F520gV4tWmxG7q9sKQRVDuzrBw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/classnames": {
|
||||||
|
"version": "2.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.9.tgz",
|
||||||
|
"integrity": "sha512-MNl+rT5UmZeilaPxAVs6YaPC2m6aA8rofviZbhbxpPpl61uKodfdQVsBtgJGTqGizEf02oW3tsVe7FYB8kK14A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/mongodb": {
|
||||||
|
"version": "3.3.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.3.16.tgz",
|
||||||
|
"integrity": "sha512-i1Ov36BXdp+urtPsaSvrNfCdsaVn4Ukq1m1kGyzdWB1+eg3gJ68unXU5LNmnF4EAMRFY6FXZzA7/W3NST40b8g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/bson": "*",
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/node": {
|
||||||
|
"version": "13.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.1.tgz",
|
||||||
|
"integrity": "sha512-Zq8gcQGmn4txQEJeiXo/KiLpon8TzAl0kmKH4zdWctPj05nWwp1ClMdAVEloqrQKfaC48PNLdgN/aVaLqUrluA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"accepts": {
|
||||||
|
"version": "1.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||||
|
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
|
||||||
|
"requires": {
|
||||||
|
"mime-types": "~2.1.24",
|
||||||
|
"negotiator": "0.6.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"arg": {
|
||||||
|
"version": "4.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||||
|
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"array-flatten": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||||
|
},
|
||||||
|
"bl": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==",
|
||||||
|
"requires": {
|
||||||
|
"readable-stream": "^2.3.5",
|
||||||
|
"safe-buffer": "^5.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"body-parser": {
|
||||||
|
"version": "1.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
||||||
|
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
|
||||||
|
"requires": {
|
||||||
|
"bytes": "3.1.0",
|
||||||
|
"content-type": "~1.0.4",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"http-errors": "1.7.2",
|
||||||
|
"iconv-lite": "0.4.24",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"qs": "6.7.0",
|
||||||
|
"raw-body": "2.4.0",
|
||||||
|
"type-is": "~1.6.17"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bytes": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"http-errors": {
|
||||||
|
"version": "1.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
|
||||||
|
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
|
||||||
|
"requires": {
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"setprototypeof": "1.1.1",
|
||||||
|
"statuses": ">= 1.5.0 < 2",
|
||||||
|
"toidentifier": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iconv-lite": {
|
||||||
|
"version": "0.4.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
|
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||||
|
"requires": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||||
|
},
|
||||||
|
"raw-body": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
|
||||||
|
"requires": {
|
||||||
|
"bytes": "3.1.0",
|
||||||
|
"http-errors": "1.7.2",
|
||||||
|
"iconv-lite": "0.4.24",
|
||||||
|
"unpipe": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bson": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/bson/-/bson-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-TdiJxMVnodVS7r0BdL42y/pqC9cL2iKynVwA0Ho3qbsQYr428veL3l7BQyuqiw+Q5SqqoT0m4srSY/BlZ9AxXg=="
|
||||||
|
},
|
||||||
|
"buffer-from": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"content-disposition": {
|
||||||
|
"version": "0.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
|
||||||
|
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "5.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"content-type": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
|
||||||
|
},
|
||||||
|
"cookie": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
|
||||||
|
},
|
||||||
|
"cookie-signature": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
|
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
||||||
|
},
|
||||||
|
"core-util-is": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||||
|
},
|
||||||
|
"denque": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ=="
|
||||||
|
},
|
||||||
|
"depd": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
|
||||||
|
},
|
||||||
|
"destroy": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
|
||||||
|
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
|
||||||
|
},
|
||||||
|
"diff": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"dotenv": {
|
||||||
|
"version": "8.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
|
||||||
|
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
|
||||||
|
},
|
||||||
|
"ee-first": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||||
|
},
|
||||||
|
"encodeurl": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
||||||
|
},
|
||||||
|
"escape-html": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
|
||||||
|
},
|
||||||
|
"etag": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||||
|
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
|
||||||
|
},
|
||||||
|
"express": {
|
||||||
|
"version": "4.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
|
||||||
|
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
|
||||||
|
"requires": {
|
||||||
|
"accepts": "~1.3.7",
|
||||||
|
"array-flatten": "1.1.1",
|
||||||
|
"body-parser": "1.19.0",
|
||||||
|
"content-disposition": "0.5.3",
|
||||||
|
"content-type": "~1.0.4",
|
||||||
|
"cookie": "0.4.0",
|
||||||
|
"cookie-signature": "1.0.6",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"etag": "~1.8.1",
|
||||||
|
"finalhandler": "~1.1.2",
|
||||||
|
"fresh": "0.5.2",
|
||||||
|
"merge-descriptors": "1.0.1",
|
||||||
|
"methods": "~1.1.2",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"parseurl": "~1.3.3",
|
||||||
|
"path-to-regexp": "0.1.7",
|
||||||
|
"proxy-addr": "~2.0.5",
|
||||||
|
"qs": "6.7.0",
|
||||||
|
"range-parser": "~1.2.1",
|
||||||
|
"safe-buffer": "5.1.2",
|
||||||
|
"send": "0.17.1",
|
||||||
|
"serve-static": "1.14.1",
|
||||||
|
"setprototypeof": "1.1.1",
|
||||||
|
"statuses": "~1.5.0",
|
||||||
|
"type-is": "~1.6.18",
|
||||||
|
"utils-merge": "1.0.1",
|
||||||
|
"vary": "~1.1.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path-to-regexp": {
|
||||||
|
"version": "0.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||||
|
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"feed": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/feed/-/feed-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-dAXWXM8QMxZ1DRnAxDmy1MaWZFlh1Ku7TU3onbXgHrVJynsxkNGPUed1AxszVW8AXo43xExronVkIqK+ACsoBA==",
|
||||||
|
"requires": {
|
||||||
|
"xml-js": "^1.6.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"finalhandler": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"parseurl": "~1.3.3",
|
||||||
|
"statuses": "~1.5.0",
|
||||||
|
"unpipe": "~1.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"forwarded": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
|
||||||
|
},
|
||||||
|
"fresh": {
|
||||||
|
"version": "0.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||||
|
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
|
||||||
|
},
|
||||||
|
"http-errors": {
|
||||||
|
"version": "1.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
|
||||||
|
"integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
|
||||||
|
"requires": {
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"inherits": "2.0.4",
|
||||||
|
"setprototypeof": "1.1.1",
|
||||||
|
"statuses": ">= 1.5.0 < 2",
|
||||||
|
"toidentifier": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
|
},
|
||||||
|
"ipaddr.js": {
|
||||||
|
"version": "1.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
|
||||||
|
"integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
|
||||||
|
},
|
||||||
|
"lodash": {
|
||||||
|
"version": "4.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||||
|
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
||||||
|
},
|
||||||
|
"make-error": {
|
||||||
|
"version": "1.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
|
||||||
|
"integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"media-typer": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
|
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
||||||
|
},
|
||||||
|
"memory-pager": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"merge-descriptors": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
|
||||||
|
},
|
||||||
|
"mime": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
|
||||||
|
},
|
||||||
|
"mime-db": {
|
||||||
|
"version": "1.43.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
|
||||||
|
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
|
||||||
|
},
|
||||||
|
"mime-types": {
|
||||||
|
"version": "2.1.26",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
|
||||||
|
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
|
||||||
|
"requires": {
|
||||||
|
"mime-db": "1.43.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mongodb": {
|
||||||
|
"version": "3.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.3.tgz",
|
||||||
|
"integrity": "sha512-II7P7A3XUdPiXRgcN96qIoRa1oesM6qLNZkzfPluNZjVkgQk3jnQwOT6/uDk4USRDTTLjNFw2vwfmbRGTA7msg==",
|
||||||
|
"requires": {
|
||||||
|
"bl": "^2.2.0",
|
||||||
|
"bson": "^1.1.1",
|
||||||
|
"denque": "^1.4.1",
|
||||||
|
"require_optional": "^1.0.1",
|
||||||
|
"safe-buffer": "^5.1.2",
|
||||||
|
"saslprep": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
},
|
||||||
|
"negotiator": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||||
|
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
|
||||||
|
},
|
||||||
|
"on-finished": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||||
|
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
|
||||||
|
"requires": {
|
||||||
|
"ee-first": "1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parseurl": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
|
||||||
|
},
|
||||||
|
"process-nextick-args": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||||
|
},
|
||||||
|
"proxy-addr": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
|
||||||
|
"requires": {
|
||||||
|
"forwarded": "~0.1.2",
|
||||||
|
"ipaddr.js": "1.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"qs": {
|
||||||
|
"version": "6.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||||
|
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
|
||||||
|
},
|
||||||
|
"range-parser": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
|
||||||
|
},
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "2.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||||
|
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||||
|
"requires": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.3",
|
||||||
|
"isarray": "~1.0.0",
|
||||||
|
"process-nextick-args": "~2.0.0",
|
||||||
|
"safe-buffer": "~5.1.1",
|
||||||
|
"string_decoder": "~1.1.1",
|
||||||
|
"util-deprecate": "~1.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require_optional": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
|
||||||
|
"requires": {
|
||||||
|
"resolve-from": "^2.0.0",
|
||||||
|
"semver": "^5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resolve-from": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
|
},
|
||||||
|
"safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
|
},
|
||||||
|
"saslprep": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"sparse-bitfield": "^3.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sax": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||||
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
|
||||||
|
},
|
||||||
|
"send": {
|
||||||
|
"version": "0.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
|
||||||
|
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"destroy": "~1.0.4",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"etag": "~1.8.1",
|
||||||
|
"fresh": "0.5.2",
|
||||||
|
"http-errors": "~1.7.2",
|
||||||
|
"mime": "1.6.0",
|
||||||
|
"ms": "2.1.1",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"range-parser": "~1.2.1",
|
||||||
|
"statuses": "~1.5.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serve-static": {
|
||||||
|
"version": "1.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
|
||||||
|
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
|
||||||
|
"requires": {
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"parseurl": "~1.3.3",
|
||||||
|
"send": "0.17.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"setprototypeof": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
|
||||||
|
},
|
||||||
|
"sitemap": {
|
||||||
|
"version": "1.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sitemap/-/sitemap-1.13.0.tgz",
|
||||||
|
"integrity": "sha1-Vpy+IYAgKSamKiZs094Jyc60P4M=",
|
||||||
|
"requires": {
|
||||||
|
"underscore": "^1.7.0",
|
||||||
|
"url-join": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"source-map-support": {
|
||||||
|
"version": "0.5.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz",
|
||||||
|
"integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"source-map": "^0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sparse-bitfield": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
|
||||||
|
"integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"memory-pager": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"statuses": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||||
|
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
|
||||||
|
},
|
||||||
|
"string_decoder": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "~5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"toidentifier": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
||||||
|
},
|
||||||
|
"ts-node": {
|
||||||
|
"version": "8.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.6.2.tgz",
|
||||||
|
"integrity": "sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"arg": "^4.1.0",
|
||||||
|
"diff": "^4.0.1",
|
||||||
|
"make-error": "^1.1.1",
|
||||||
|
"source-map-support": "^0.5.6",
|
||||||
|
"yn": "3.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type-is": {
|
||||||
|
"version": "1.6.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||||
|
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||||
|
"requires": {
|
||||||
|
"media-typer": "0.3.0",
|
||||||
|
"mime-types": "~2.1.24"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"version": "3.7.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz",
|
||||||
|
"integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"underscore": {
|
||||||
|
"version": "1.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.2.tgz",
|
||||||
|
"integrity": "sha512-D39qtimx0c1fI3ya1Lnhk3E9nONswSKhnffBI0gME9C99fYOkNi04xs8K6pePLhvl1frbDemkaBQ5ikWllR2HQ=="
|
||||||
|
},
|
||||||
|
"unpipe": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
|
||||||
|
},
|
||||||
|
"url-join": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/url-join/-/url-join-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-dBxsL0WWxIMNZxhGCSDQySIC3Hg="
|
||||||
|
},
|
||||||
|
"util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||||
|
},
|
||||||
|
"utils-merge": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||||
|
},
|
||||||
|
"vary": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
||||||
|
},
|
||||||
|
"xml-js": {
|
||||||
|
"version": "1.6.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
|
||||||
|
"integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
|
||||||
|
"requires": {
|
||||||
|
"sax": "^1.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"yn": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "hudozka-api",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "src/server.ts",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node build/server.js",
|
||||||
|
"dev": "ts-node src/server.ts",
|
||||||
|
"build": "tsc",
|
||||||
|
"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": {
|
||||||
|
"dotenv": "^8.2.0",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"feed": "^4.0.0",
|
||||||
|
"lodash": "^4.17.15",
|
||||||
|
"mongodb": "^3.5.3",
|
||||||
|
"sitemap": "^1.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/classnames": "^2.2.9",
|
||||||
|
"@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 '../factory/file'
|
||||||
|
|
||||||
|
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 '../factory/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 '../factory/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 '../factory/person'
|
||||||
|
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,36 @@
|
|||||||
|
import ImageArtifactType from '../core/ImageArtifactType'
|
||||||
|
|
||||||
|
const sizes = [
|
||||||
|
ImageArtifactType.MEDIUM,
|
||||||
|
ImageArtifactType.BIG,
|
||||||
|
ImageArtifactType.ORIGIN,
|
||||||
|
]
|
||||||
|
|
||||||
|
export function encodeFilePreview(file, sizes) {
|
||||||
|
const preview = file.preview
|
||||||
|
if (!preview) return null
|
||||||
|
|
||||||
|
const image = preview.findArtifact(sizes)
|
||||||
|
if (!image) return null
|
||||||
|
|
||||||
|
return image.url
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encodeFile(file) {
|
||||||
|
const imageUrl = encodeFilePreview(file, sizes)
|
||||||
|
const category = (file.category || '')
|
||||||
|
.replace('Documents/', '')
|
||||||
|
.replace('Pages/', '')
|
||||||
|
.replace('Albums/', '')
|
||||||
|
.replace('Articles /', '')
|
||||||
|
|
||||||
|
return {
|
||||||
|
imageUrl,
|
||||||
|
category,
|
||||||
|
title: file.title,
|
||||||
|
fileName: file.fileInfo.name,
|
||||||
|
fileSize: file.fileInfo.size,
|
||||||
|
fileUrl: file.url,
|
||||||
|
url: file.viewUrl,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* Date -> Unix time
|
||||||
|
*
|
||||||
|
* @param date
|
||||||
|
*/
|
||||||
|
export const timestamp = (date: Date) => date.getTime()
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
const getExt = (name: string) => /\.(\w+)$/.exec(name)[1]
|
||||||
|
const defaultExt = (ext: string) => ext.toUpperCase()
|
||||||
|
const exts = ['png', 'pdf', 'jpg', 'jpeg', 'gif', 'doc']
|
||||||
|
const extd = ['ПНГ', 'ПДФ', 'ЖПГ', 'ЖПГ', 'ГИФ', 'ДОК']
|
||||||
|
const units = ['байт', 'КБ', 'МБ', 'ГБ', 'ТБ', 'ПБ']
|
||||||
|
|
||||||
|
export function ext(filname: string): string {
|
||||||
|
const ext = getExt(filname)
|
||||||
|
const i = exts.indexOf(ext)
|
||||||
|
|
||||||
|
return i > -1
|
||||||
|
? extd[i]
|
||||||
|
: defaultExt(ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function size(bytes: any, precision = 1): string {
|
||||||
|
if (isNaN(parseFloat(bytes)) || !isFinite(bytes)) return ''
|
||||||
|
|
||||||
|
const number = Math.floor(Math.log(bytes) / Math.log(1024))
|
||||||
|
const size = (bytes / Math.pow(1024, Math.floor(number))).toFixed(precision)
|
||||||
|
|
||||||
|
return `${size} ${units[number]}`
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
function prioritySort(priority, key, list) {
|
||||||
|
priority = priority.reverse()
|
||||||
|
|
||||||
|
let weight = i => {
|
||||||
|
const p = key(i)
|
||||||
|
const index = priority.indexOf(p)
|
||||||
|
return index > -1
|
||||||
|
? index + 1
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return sort(list, weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
function sort(list, fn, reverse = false) {
|
||||||
|
return list
|
||||||
|
.map(i => [fn(i), i])
|
||||||
|
.sort((a, b) => reverse
|
||||||
|
? a[0] - b[0]
|
||||||
|
: b[0] - a[0])
|
||||||
|
.map(i => i[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortBy(fn, reverse = false) {
|
||||||
|
return (i1, i2) => {
|
||||||
|
const t1 = fn(i1)
|
||||||
|
const t2 = fn(i2)
|
||||||
|
|
||||||
|
return reverse
|
||||||
|
? t1 - t2
|
||||||
|
: t2 - t1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.prioritySort = prioritySort
|
||||||
|
exports.sortBy = sortBy
|
||||||
|
exports.sort = sort
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
export async function find(collection, query, {sort, skip, limit}) {
|
||||||
|
const cursor = collection.find(query)
|
||||||
|
|
||||||
|
if (sort) cursor.sort(sort)
|
||||||
|
if (skip) cursor.skip(skip)
|
||||||
|
if (limit) cursor.limit(limit)
|
||||||
|
|
||||||
|
return cursor.toArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function findOne(collection, query) {
|
||||||
|
return collection.findOne(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function total(collection, query) {
|
||||||
|
return collection
|
||||||
|
.find(query)
|
||||||
|
.count()
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
function titleCase(str) {
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function trim(string) {
|
||||||
|
return string
|
||||||
|
.replace(/^\s+/, '')
|
||||||
|
.replace(/\s+$/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.titleCase = titleCase
|
||||||
|
exports.trim = trim
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
function getPathWithNoTrailingSlash(path='') {
|
||||||
|
path = path.charAt(path.length - 1) === '/'
|
||||||
|
? path.substr(0, path.length - 1)
|
||||||
|
: path
|
||||||
|
return path.length === 0
|
||||||
|
? '/'
|
||||||
|
: path
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEqualPath(a, b, ignoreTrailingSlash = false) {
|
||||||
|
return ignoreTrailingSlash
|
||||||
|
? getPathWithNoTrailingSlash(a) === getPathWithNoTrailingSlash(b)
|
||||||
|
: a === b
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMatchPathPattern(pattern, path, ignoreTrailingSlash = false) {
|
||||||
|
const test = ignoreTrailingSlash
|
||||||
|
? getPathWithNoTrailingSlash(path)
|
||||||
|
: path
|
||||||
|
return pattern.test(test)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* /article/* -> ^\/article\/[/\w\d-_]+\/?$
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @return {RegExp}
|
||||||
|
*/
|
||||||
|
function urlToPattern(url) {
|
||||||
|
const u = url.replace('/*', '[/\\w\\d-_]+')
|
||||||
|
const pattern = `^${u}/?$`
|
||||||
|
|
||||||
|
return new RegExp(pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getPathWithNoTrailingSlash = getPathWithNoTrailingSlash
|
||||||
|
exports.isMatchPathPattern = isMatchPathPattern
|
||||||
|
exports.urlToPattern = urlToPattern
|
||||||
|
exports.isEqualPath = isEqualPath
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
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}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
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('-')
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { config } 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'
|
||||||
|
|
||||||
|
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}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
config()
|
||||||
|
main()
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import Article from '../core/Article'
|
||||||
|
import ImageArtifactType from '../core/ImageArtifactType'
|
||||||
|
import { sortBy } from '../utils/sort'
|
||||||
|
import { timestamp } from '../lib/date'
|
||||||
|
import { encodeImage } from '../factory/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),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const { collection } = require('../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 });
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* {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())
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
import { createMap } from '../utils/common'
|
||||||
|
import { getCollective } from './collective'
|
||||||
|
import {
|
||||||
|
scheduleDate,
|
||||||
|
getPeriod as schedulePeriod,
|
||||||
|
getPeriodString
|
||||||
|
} from '../models/schedule'
|
||||||
|
import { getCourse } from '../models/course'
|
||||||
|
|
||||||
|
// import { last } from 'lodash'
|
||||||
|
// 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,
|
||||||
|
// ))
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* @returns {Function} (i) => {}
|
||||||
|
*/
|
||||||
|
export function numberEquals(withNumber) {
|
||||||
|
return i => i === withNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a closure that compares a second Number argument with specified Number
|
||||||
|
* @param withNumber Number to compare with
|
||||||
|
* @returns {Function} (i) => {}
|
||||||
|
*/
|
||||||
|
export function indexEquals(withNumber) {
|
||||||
|
return (_, i) => i === withNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a reduce function
|
||||||
|
* [['spring', 'Весена'], ['autumn', 'Осень']] -> 'Весна' or 'Осень' depend on byCondition result
|
||||||
|
* @param byCondition
|
||||||
|
* @param map
|
||||||
|
*/
|
||||||
|
export function select(byCondition, map = i => i) {
|
||||||
|
return (result, item) => byCondition(item) ? map(item) : map(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select an item in Array by specified selector function
|
||||||
|
* @param scope
|
||||||
|
* @param selector
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function choise(scope, selector) {
|
||||||
|
return scope.reduce(selector, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unique<T, U>(fn: (value: T) => U): (list: T[]) => U[] {
|
||||||
|
return list => Array.from(
|
||||||
|
new Set(list.map(fn))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function branch(fn) {
|
||||||
|
let keys = unique(fn);
|
||||||
|
|
||||||
|
return list => keys(list)
|
||||||
|
.reduce((dict: any, key: any) => {
|
||||||
|
dict[key] = list.filter(
|
||||||
|
i => fn(i) === key
|
||||||
|
);
|
||||||
|
|
||||||
|
return dict;
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assign(dict, key, value) {
|
||||||
|
dict[key] = value;
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function map(v, fn) {
|
||||||
|
return value => fn(value) ? v : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* f(g, [x, y, z, ...]) => map of {g(x): x, g(y): y, g(z): z}
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
* @param selector
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function createMap(selector, list) {
|
||||||
|
return list.reduce((map, i) => map.set(selector(i), i), new Map());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* f(g) => x ? g(x) : null
|
||||||
|
*
|
||||||
|
* @param {function} fn
|
||||||
|
* @returns {function(*=): null}
|
||||||
|
*/
|
||||||
|
export function nullSafe(fn) {
|
||||||
|
return i => i ? fn(i) : null;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import {range} from './common';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param start
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function getDates(start) {
|
||||||
|
const oneDay = (1000 * 60 * 60 * 24);
|
||||||
|
const startTime = start.getTime();
|
||||||
|
const startDay = start.getDay();
|
||||||
|
|
||||||
|
return range(0, 6, 1)
|
||||||
|
.reduce((dates, weekDay) => {
|
||||||
|
let delta = 1 + weekDay - startDay;
|
||||||
|
let time = startTime + (oneDay * delta);
|
||||||
|
|
||||||
|
return dates.concat([new Date(time)]);
|
||||||
|
}, [])
|
||||||
|
.map(i => i.getDate());
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* f([x, y, z, ...]) -> f(v) -> {v.x, v.y, v.z}
|
||||||
|
*
|
||||||
|
* @param fields
|
||||||
|
* @returns {function(*): *}
|
||||||
|
*/
|
||||||
|
export function select(fields=[]){
|
||||||
|
return i => fields.reduce((out, key) => {
|
||||||
|
out[key] = i[key];
|
||||||
|
return out;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export function query(list) {
|
||||||
|
return list
|
||||||
|
.reduce(
|
||||||
|
(query, param) => query.concat([`${param[0]}=${param[1]}`]),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
.join('&');
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
export function pop(data){
|
||||||
|
var Populator = function(data){
|
||||||
|
this.target = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
Populator.prototype = {
|
||||||
|
populate: function(p){
|
||||||
|
var self = this;
|
||||||
|
if(typeof p == "function"){
|
||||||
|
self.target = p(self.target);
|
||||||
|
}else if(p instanceof Array){
|
||||||
|
p.forEach(function(i){
|
||||||
|
self.target = self.populate(i).result();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
},
|
||||||
|
result: function(){
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var fun = new Populator(data);
|
||||||
|
return fun;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function populate(fn, key){
|
||||||
|
return (dict) => {
|
||||||
|
let id = dict[key];
|
||||||
|
let value = fn(id);
|
||||||
|
dict[key] = value ? value : id;
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
export function sortByPattern(list, pattern, map) {
|
||||||
|
let weight = i => {
|
||||||
|
const index = pattern.indexOf(map(i))
|
||||||
|
return index > -1 ? pattern.length - index : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return sortByWeight(list, weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sortByWeight(list, fn) {
|
||||||
|
return list
|
||||||
|
.map(i => [fn(i), i])
|
||||||
|
.sort((a, b) => b[0] - a[0])
|
||||||
|
.map(i => i[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sortBy(fn) {
|
||||||
|
return (i1, i2) => {
|
||||||
|
let t1 = fn(i1)
|
||||||
|
let t2 = fn(i2)
|
||||||
|
|
||||||
|
if (t1 < t2) return 1
|
||||||
|
if (t1 > t2) return -1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import {map} from './common';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a function that accept one String parameter and returns result of replacing.
|
||||||
|
*
|
||||||
|
* @param searchValue
|
||||||
|
* @param replaceValue
|
||||||
|
*/
|
||||||
|
export const stringReplace = (searchValue, replaceValue) =>
|
||||||
|
str => str.replace(searchValue, replaceValue);
|
||||||
|
|
||||||
|
let toTrue = map(true, i => i === 'true');
|
||||||
|
let toFalse = map(false, i => i === 'false');
|
||||||
|
const boolProcessors = [toTrue, toFalse];
|
||||||
|
|
||||||
|
export function stringToBoolean(string){
|
||||||
|
return boolProcessors
|
||||||
|
.reduce(
|
||||||
|
(value, fn) => fn(value),
|
||||||
|
string
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./build",
|
||||||
|
"target": "esnext",
|
||||||
|
"lib": [
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": false,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"**/*.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in new issue
Block a user