Füge Docker- und Babel-Konfigurationen hinzu, aktualisiere Svelte- und Esbuild-Setups, erweitere Typdefinitionen und aktualisiere die README-Datei

This commit is contained in:
Sebastian Frank 2025-03-27 13:52:13 +00:00
parent 77cb64b260
commit 7a6a2cbd22
Signed by: apairon
SSH Key Fingerprint: SHA256:lYVOnGlR42QHj7wuqfFgGw8cKbfyZUpzeRDGVBBAHQU
16 changed files with 447 additions and 84 deletions

6
.env

@ -11,6 +11,12 @@ SENTRY_PROJECT=
RSYNC_HOST=ftp1.webmakers.de
RSYNC_PORT=22223
PRODUCTION_SERVER=dock4.basehosts.de
PRODUCTION_TIBI_PREFIX=wmbasic
PRODUCTION_PATH=/webroots2/customers/_CUSTOMER_ID_/____
STAGING_PATH=/staging/__ORG__/__PROJECT__/dev
LIVE_URL=https://www
STAGING_URL=https://dev-__PROJECT_NAME__.staging.testversion.online

@ -10,7 +10,7 @@
"check-parameters"
],
"no-var-keyword": true,
"svelteSortOrder": "scripts-markup-styles",
"svelteSortOrder": "scripts-options-markup-styles",
"svelteStrictMode": true,
"svelteBracketNewLine": true,
"svelteAllowShorthand": true,

14
Dockerfile Normal file

@ -0,0 +1,14 @@
FROM node:latest
ADD webserver/ /webserver/
WORKDIR /webserver
RUN npm install
EXPOSE 80
ADD frontend/ /data/
ENV INDEX ""
ENV API ""
ENV WEBROOT ""
CMD [ "node", "webserver.js" ]

@ -1,13 +1,16 @@
DOCKER_COMPOSE := docker compose -f docker-compose-local.yml
.DEFAULT_GOAL := help
.PHONY: docker-up docker-start docker-down docker-logs
DOCKER_COMPOSE=docker compose -f docker-compose-local.yml
export UID := $(shell id -u)
export GID := $(shell id -g)
.DEFAULT_GOAL := help
.PHONY: docker-up docker-up-tibi-dev docker-start docker-start-tibi-dev docker-down docker-ps docker-logs yarn-upgrade fix-permissions
include ./.env
help: ## show this help
@echo MAKE TARGETS
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
docker-up: ## bring docker compose stack up in background
$(DOCKER_COMPOSE) --profile tibi up -d
@ -15,27 +18,58 @@ docker-up: ## bring docker compose stack up in background
docker-up-tibi-dev: ## bring docker compose stack up in background with tibi-dev
$(DOCKER_COMPOSE) --profile tibi-dev up -d
docker-up-chisel: ## bring up chisel tunnel
$(DOCKER_COMPOSE) --profile chisel up -d
docker-down: ## take docker compose stack down
$(DOCKER_COMPOSE) --profile tibi-dev --profile tibi down
$(DOCKER_COMPOSE) --profile tibi-dev --profile tibi --profile chisel down
docker-start: ## start docker compose stack in foreground and take it down after CTRL-C
$(DOCKER_COMPOSE) --profile tibi up; $(DOCKER_COMPOSE) --profile tibi-dev --profile tibi down
$(DOCKER_COMPOSE) --profile tibi up; $(DOCKER_COMPOSE) --profile tibi-dev --profile tibi --profile chisel down
docker-start-tibi-dev: ## start docker compose stack in foreground and take it down after CTRL-C (with tibi-dev)
$(DOCKER_COMPOSE) --profile tibi-dev up; $(DOCKER_COMPOSE) --profile tibi-dev --profile tibi down
$(DOCKER_COMPOSE) --profile tibi-dev up; $(DOCKER_COMPOSE) --profile tibi-dev --profile tibi --profile chisel down
docker-ps: ## show container state
$(DOCKER_COMPOSE) --profile tibi-dev --profile tibi ps
$(DOCKER_COMPOSE) --profile tibi-dev --profile tibi --profile chisel ps
docker-logs: ## show docker logs and follow
$(DOCKER_COMPOSE) --profile tibi-dev --profile tibi logs -f || true
$(DOCKER_COMPOSE) --profile tibi-dev --profile tibi --profile chisel logs -f --tail=100 || true
docker-pull: ## pull docker images
$(DOCKER_COMPOSE) --profile tibi-dev --profile tibi --profile chisel pull
docker-%:
$(DOCKER_COMPOSE) $*
yarn-upgrade: # interactive yarn upgrade
yarn-upgrade: ## interactive yarn upgrade
$(DOCKER_COMPOSE) run --rm yarnstart yarn upgrade-interactive
$(DOCKER_COMPOSE) restart yarnstart
fix-permissions: # set files/directories owner to UID:GID from .env
sudo chown -R $(UID):$(GID) ./
fix-permissions: ## set files/directories owner to UID:GID from .env
sudo chown -R $(CODER_UID):$(CODER_GID) ./
mongo-sync-master-to-local: ## sync mongo from master to local
$(DOCKER_COMPOSE) up mongo -d
read -s -p "Enter chisel password: " CHISEL_PASSWORD; \
chisel client --auth coder:$$CHISEL_PASSWORD http://$(PRODUCTION_SERVER):10987 27017:mongo:27017 &
sleep 3
mongodump --archive --gzip --db=$(PRODUCTION_TIBI_PREFIX)_$(TIBI_NAMESPACE) | $(DOCKER_COMPOSE) exec -T mongo mongorestore --archive --gzip --nsFrom='$(PRODUCTION_TIBI_PREFIX)_$(TIBI_NAMESPACE).*' --nsTo='tibi_$(TIBI_NAMESPACE).*' --nsInclude='$(PRODUCTION_TIBI_PREFIX)_$(TIBI_NAMESPACE).*' --drop
sleep 3
killall chisel
media-sync-master-to-local: ## sync attachments from master to local
rsync -v -e "ssh -i ~/.ssh/id_deploy -p 22223 -l 10052300,33,$(PRODUCTION_PATH)/media" -az --info=progress2 ftp1.webmakers.de:/ media/
mongo-sync-local-to-staging: ## sync mongo from local to staging area
$(DOCKER_COMPOSE) up mongo -d
read -s -p "Enter chisel password: " CHISEL_PASSWORD; \
chisel client --auth coder:$$CHISEL_PASSWORD https://chisel-dev-tibi.staging.testversion.online 27017:mongo:27017 &
sleep 3
$(DOCKER_COMPOSE) exec -T mongo mongodump --archive --gzip --db=$(TIBI_PREFIX)_$(TIBI_NAMESPACE) | mongorestore --archive --gzip --nsFrom='$(TIBI_PREFIX)_$(TIBI_NAMESPACE).*' --nsTo='$(TIBI_PREFIX)_$(TIBI_NAMESPACE).*' --nsInclude='$(TIBI_PREFIX)_$(TIBI_NAMESPACE).*' --drop
sleep 3
killall chisel
media-sync-local-to-staging: ## sync attachments from local to staging area
sudo rsync -v -az --info=progress2 media $(STAGING_PATH)/

@ -27,24 +27,6 @@ git lfs pull
yarn install
```
### Entwickeln mit dev-Webserver
```sh
yarn start
```
oder mit abweichender API für "/api"-Proxy
```sh
API_BASE=https://login.tibicms.de/api/v1_/__NAMESPACE__ yarn start
```
### Entwickeln mit externem Webserver (z.B. vscode live server)
```sh
yarn dev
```
### Entwickeln auf dem Code-Server mit Docker Compose Stack
```sh
@ -58,17 +40,9 @@ make docker-down
| UI | URL |
| --- | --- |
| Website | https://tibi-svelte-starter.code.testversion.online/ |
| Tibi Admin | https://tibi-svelte-starter-tibiadmin.code.testversion.online/ |
| Admin Mongo | https://tibi-svelte-starter-adminmongo.code.testversion.online/ |
| Maildev | https://tibi-svelte-starter-maildev.code.testversion.online/ |
### Testen
```sh
yarn build:istanbul # instrumentiert Code für coverage-Report
yarn cy:docker:run # oder mit Xserver und UI cy:docker:open
```
| Website | <https://tibi-svelte-starter.code.testversion.online/> |
| Tibi Admin | <https://tibi-svelte-starter-tibiadmin.code.testversion.online/> |
| Maildev | <https://tibi-svelte-starter-maildev.code.testversion.online/> |
### Bauen
@ -81,4 +55,7 @@ yarn build:legacy
# serverseitiges Rendering
yarn build:server
# Admin-Module
yarn build:admin
```

@ -10,16 +10,11 @@
"version": "3",
"proposals": true
},
"targets": ">0.5%, IE 11, not dead"
"targets": ">0.5%, IE 11, not dead",
"debug": true,
"forceAllTransforms": true
}
]
],
"plugins": [
[
"@babel/plugin-transform-spread",
{
"loose": true
}
]
]
"plugins": []
}

5
babel.config.server.json Normal file

@ -0,0 +1,5 @@
{
"sourceMaps": "inline",
"inputSourceMap": true,
"plugins": [["@babel/plugin-transform-async-to-generator"]]
}

@ -2,12 +2,18 @@ name: ${PROJECT_NAME}
services:
yarnstart:
image: node:18
user: ${UID}:${GID}
profiles:
- tibi
- tibi-dev
image: node:20
volumes:
- ./:/data
- ./tmp:/tmp
- ./tmp/nonexistent:/nonexistent
- ./tmp/.npm:/.npm
- ./tmp/.yarn:/.yarn
working_dir: /data
command: sh -c "yarn install && API_BASE=http://tibiserver:8080/api/v1/_/${TIBI_NAMESPACE} yarn start"
command: sh -c "yarn install && API_BASE=http://tibiserver:8080/api/v1/_/${TIBI_NAMESPACE} yarn start${START_SCRIPT}"
expose:
- 3000
labels:
@ -15,25 +21,36 @@ services:
- online.testversion.code.subdomain=${PROJECT_NAME}
- traefik.http.routers.${PROJECT_NAME}-yarnstart.middlewares=${PROJECT_NAME}-yarnstart
- traefik.http.middlewares.${PROJECT_NAME}-yarnstart.basicauth.usersfile=${PWD}/.basic-auth-web
user: ${CODER_UID}:${CODER_GID}
tibiserver:
image: gitbase.de/cms/tibi-server
profiles:
- tibi
image: gitbase.de/cms/tibi-server
volumes:
- ./:/data
environment:
DB_DIAL: mongodb://mongo
DB_PREFIX: ${TIBI_PREFIX}
MAIL_HOST: maildev:25
SECURITY_ALLOWABSOLUTEPATHS: "true"
SECURITY_ALLOWUPPERPATHS: "true"
depends_on:
- mongo
expose:
- 8080
labels:
- traefik.enable=true
- traefik.http.services.${PROJECT_NAME}-tibiserver.loadbalancer.server.port=8080
- online.testversion.code.subdomain=${PROJECT_NAME}-tibiserver
tibiserver-dev:
hostname: tibiserver
build:
context: ./
dockerfile: ./../../cms/tibi-server/Dockerfile.air
profiles:
- tibi-dev
image: cosmtrek/air
volumes:
- ./:/data
- ./../../cms/tibi-server:/tibi-server
@ -44,14 +61,22 @@ services:
DB_DIAL: mongodb://mongo
DB_PREFIX: ${TIBI_PREFIX}
MAIL_HOST: maildev:25
SECURITY_ALLOWABSOLUTEPATHS: "true"
SECURITY_ALLOWUPPERPATHS: "true"
depends_on:
- mongo
user: ${UID}:${GID}
user: ${CODER_UID}:${CODER_GID}
expose:
- 8080
labels:
- traefik.enable=true
- traefik.http.services.${PROJECT_NAME}-tibiserver.loadbalancer.server.port=8080
- online.testversion.code.subdomain=${PROJECT_NAME}-tibiserver
tibiadmin:
image: gitbase.de/cms/tibi-admin
profiles:
- tibi
image: gitbase.de/cms/tibi-admin
environment:
INDEX: spa.html
WEBROOT: /data
@ -70,7 +95,7 @@ services:
tibiadmin-dev:
profiles:
- tibi-dev
image: node:18
image: node:20
volumes:
- ./../../cms/tibi-admin:/data
working_dir: /data
@ -82,20 +107,24 @@ services:
- online.testversion.code.subdomain=${PROJECT_NAME}-tibiadmin-dev
- traefik.http.routers.${PROJECT_NAME}-tibiadmin-dev.middlewares=${PROJECT_NAME}-tibiadmin-dev
- traefik.http.middlewares.${PROJECT_NAME}-tibiadmin-dev.basicauth.usersfile=${PWD}/.basic-auth-code
user: ${UID}:${GID}
user: ${CODER_UID}:${CODER_GID}
mongo:
profiles:
- tibi
- tibi-dev
image: gitbase.de/server/mongo:4.2
user: ${UID}:${GID}
volumes:
- ./tmp/mongo-data:/data/db
user: ${CODER_UID}:${CODER_GID}
adminmongo:
profiles:
- tibi
- tibi-dev
image: gitbase.de/server/adminmongo
environment:
CONN_NAME: mongo
# DB_USERNAME: root
# DB_PASSWORD: root
DB_HOST: mongo
PORT: 1234
expose:
@ -107,6 +136,9 @@ services:
- traefik.http.middlewares.${PROJECT_NAME}-adminmongo.basicauth.usersfile=${PWD}/.basic-auth-code
maildev:
profiles:
- tibi
- tibi-dev
image: maildev/maildev
command: node bin/maildev --web 1080 --smtp 25 -v --hide-extensions=STARTTLS
expose:
@ -117,3 +149,14 @@ services:
- traefik.http.services.${PROJECT_NAME}-maildev.loadbalancer.server.port=1080
- traefik.http.routers.${PROJECT_NAME}-maildev.middlewares=${PROJECT_NAME}-maildev
- traefik.http.middlewares.${PROJECT_NAME}-maildev.basicauth.usersfile=${PWD}/.basic-auth-code
chisel:
profiles:
- chisel
image: jpillora/chisel
expose:
- 8080
labels:
- traefik.enable=true
- online.testversion.code.subdomain=${PROJECT_NAME}-chisel
command: server --port 8080 --auth coder:coder

@ -0,0 +1,14 @@
services:
www:
build: .
image: ${PROJECT_NAME}:dev
container_name: dev-${PROJECT_NAME}
environment:
#INDEX: spa.html
SSR: https://dev-tibi-server.staging.testversion.online/api/v1/_/${TIBI_NAMESPACE}/ssr?url=
WEBROOT: /data
API: /tibiapi:https://dev-tibi-server.staging.testversion.online/api/v1/_/${TIBI_NAMESPACE}
PORT: 80
labels:
traefik.enable: "true"
online.testversion.staging.subdomain: dev-${PROJECT_NAME}

22
esbuild.config.admin.js Normal file

@ -0,0 +1,22 @@
const config = require("./esbuild.config.js")
const svelteConfig = require("./svelte.config")
config.options.minify = false
config.options.entryPoints = ["./frontend/src/admin.ts"]
config.options.outfile = "./" + config.distDir + "/admin.mjs"
delete config.options.outdir
config.options.splitting = false
config.options.plugins = [
config.sveltePlugin({
compilerOptions: {
css: false,
hydratable: false,
dev: (process.argv?.length > 2 ? process.argv[2] : "build") !== "build",
},
preprocess: svelteConfig.preprocess,
cache: true,
}),
config.resolvePlugin,
]
module.exports = config

@ -1,3 +1,5 @@
const fs = require("fs")
const resolvePlugin = {
name: "resolvePlugin",
setup(build) {
@ -15,11 +17,12 @@ const resolvePlugin = {
const sveltePlugin = require("esbuild-svelte")
const distDir = "frontend"
const frontendDir = "./frontend"
const distDir = frontendDir + "/dist"
console.log("copy public dir...")
const copydir = require("copy-dir")
copydir.sync(__dirname + "/public", __dirname + "/" + distDir)
// console.log("copy public dir...")
// const copydir = require("copy-dir")
// copydir.sync(__dirname + "/public", __dirname + "/" + distDir)
/*copydir.sync(
__dirname + "/public/index.html",
__dirname + "/" + distDir + "/template.html"
@ -28,22 +31,27 @@ copydir.sync(__dirname + "/public", __dirname + "/" + distDir)
const svelteConfig = require("./svelte.config")
const esbuildSvelte = sveltePlugin({
compilerOptions: {
css: false,
css: "external",
hydratable: true,
dev: (process.argv?.length > 2 ? process.argv[2] : "build") !== "build",
},
preprocess: svelteConfig.preprocess,
cache: true,
filterWarnings: (warning) => {
// filter out a11y
if (warning.code.match(/^a11y/)) return false
return true
},
})
const options = {
logLevel: "info",
color: true,
entryPoints: ["./src/index.ts"],
outfile: "./" + distDir + "/_dist_/index.mjs",
metafile: true, //"./" + distDir + "/_dist_/meta.json",
entryPoints: ["./frontend/src/index.ts"],
outfile: distDir + "/index.mjs",
metafile: true,
format: "esm",
minify: true,
minify: process.argv[2] == "build",
bundle: true,
splitting: false,
plugins: [esbuildSvelte, resolvePlugin],
@ -53,8 +61,6 @@ const options = {
".eot": "file",
".svg": "file",
".ttf": "file",
".png": "file",
".jpg": "file",
},
sourcemap: true,
target: ["es2020", "chrome61", "firefox60", "safari11", "edge18"],
@ -64,22 +70,44 @@ const bsMiddleware = []
if (process.argv[2] == "start") {
const { createProxyMiddleware } = require("http-proxy-middleware")
const apiBase = process.env.API_BASE || "http://localhost:8080/api/v1/_/" + process.env.NAMESPACE
const dotEnv = fs.readFileSync(__dirname + "/.env", "utf8")
const TIBI_NAMESPACE = dotEnv.match(/TIBI_NAMESPACE=(.*)/)[1]
const apiBase = process.env.API_BASE || "http://localhost:8080/api/v1/_/" + TIBI_NAMESPACE
bsMiddleware.push(
createProxyMiddleware("/api", {
createProxyMiddleware({
pathFilter: "/api",
target: apiBase,
pathRewrite: { "^/api": "" },
changeOrigin: true,
logLevel: "debug",
})
)
if (process.env.SSR) {
bsMiddleware.push(
createProxyMiddleware({
pathFilter: function (path, req) {
return !path.match(/\./)
},
target: apiBase,
changeOrigin: true,
logLevel: "debug",
pathRewrite: function (path, req) {
console.log(path)
return "/ssr?url=" + encodeURIComponent(path)
},
})
)
}
}
module.exports = {
sveltePlugin: sveltePlugin,
resolvePlugin: resolvePlugin,
options: options,
distDir,
watch: {
path: [__dirname + "/src/**/*"],
path: [__dirname + "/" + frontendDir + "/src/**/*"],
},
serve: {
onRequest(args) {
@ -88,7 +116,7 @@ module.exports = {
},
browserSync: {
server: {
baseDir: distDir,
baseDir: frontendDir,
middleware: [
require("morgan")("dev"),
...bsMiddleware,

@ -5,7 +5,10 @@ config.options.sourcemap = "inline"
config.options.minify = false
config.options.platform = "node"
config.options.format = "cjs"
config.options.entryPoints = ["./src/ssr.ts"]
// es2015 will transform async/await to generators, but not working with svelte
// so we need babel to transform async/await to promises
// config.options.target = "es2015"
config.options.entryPoints = ["./frontend/src/ssr.ts"]
config.options.outfile = __dirname + "/_temp/app.server.js"
config.options.plugins = [
config.sveltePlugin({
@ -16,6 +19,11 @@ config.options.plugins = [
dev: (process.argv?.length > 2 ? process.argv[2] : "build") !== "build",
},
preprocess: svelteConfig.preprocess,
filterWarnings: (warning) => {
// filter out a11y
if (warning.code.match(/^a11y/)) return false
return true
},
}),
config.resolvePlugin,
]

45
frontend/src/admin.ts Normal file

@ -0,0 +1,45 @@
import type { SvelteComponent } from "svelte"
function getRenderedElement(
component: typeof SvelteComponent,
options?: { props: { [key: string]: any }; addCss?: string[] },
nestedElements?: { tagName: string; className?: string }[]
) {
const el = document.createElement("div")
el.attachShadow({ mode: "open" })
const body = document.createElement("body")
// build nested divs with css classes
let target: HTMLElement = body
nestedElements?.forEach((e) => {
const newElement = document.createElement(e.tagName)
if (e.className) {
newElement.className = e.className
}
target.appendChild(newElement)
target = newElement
})
el.shadowRoot.appendChild(body)
options?.addCss?.forEach((css) => {
const link = document.createElement("link")
link.rel = "stylesheet"
link.href = css
link.type = "text/css"
el.shadowRoot.appendChild(link)
})
new component({
target: target,
props: options?.props,
})
return el
}
export {
getRenderedElement,
// pass also required svelte components here
}

160
frontend/src/api.ts Normal file

@ -0,0 +1,160 @@
import { get } from "svelte/store"
import { apiRequest, obj2str } from "../../api/hooks/lib/ssr"
import * as sentry from "./sentry"
import { apiBaseOverride } from "./lib/store"
// fetch polyfill
// [MIT License](LICENSE.md) © [Jason Miller](https://jasonformat.com/)
const _f = function (url: string, options?: { [key: string]: any }) {
if (typeof XMLHttpRequest === "undefined") {
return Promise.resolve(null)
}
options = options || {}
return new Promise((resolve, reject) => {
const request = new XMLHttpRequest()
const keys: string[] = []
// @ts-ignore
const all = []
const headers = {}
const response = () => ({
ok: ((request.status / 100) | 0) == 2, // 200-299
statusText: request.statusText,
status: request.status,
url: request.responseURL,
text: () => Promise.resolve(request.responseText),
json: () => Promise.resolve(request.responseText).then(JSON.parse),
blob: () => Promise.resolve(new Blob([request.response])),
clone: response,
headers: {
// @ts-ignore
keys: () => keys,
// @ts-ignore
entries: () => all,
// @ts-ignore
get: (n) => headers[n.toLowerCase()],
// @ts-ignore
has: (n) => n.toLowerCase() in headers,
},
})
request.open(options.method || "get", url, true)
request.onload = () => {
request
.getAllResponseHeaders()
// @ts-ignore
.replace(/^(.*?):[^\S\n]*([\s\S]*?)$/gm, (m, key, value) => {
keys.push((key = key.toLowerCase()))
all.push([key, value])
// @ts-ignore
headers[key] = headers[key] ? `${headers[key]},${value}` : value
})
resolve(response())
}
request.onerror = reject
request.withCredentials = options.credentials == "include"
for (const i in options.headers) {
request.setRequestHeader(i, options.headers[i])
}
request.send(options.body || null)
})
}
// fetch must be declared after sentry import to get the hijacked fetch
// @ts-ignore
export const _fetch: typeof fetch =
typeof fetch === "undefined" ? (typeof window === "undefined" ? _f : window.fetch || _f) : fetch
export const api = async <T>(
endpoint: string,
options?: ApiOptions,
body?: any
): Promise<{ data: T; count: number } | any> => {
const _apiBaseOverride = get(apiBaseOverride) || ""
let data = await apiRequest(_apiBaseOverride + endpoint, options, body, sentry, _fetch)
// @ts-ignore
// console.log(data, "data")
return data
}
const cache: {
[key: string]: {
expire: number
data: any
}
} = {}
type CollectionNameT = "medialib" | "content" | "product"
type EntryTypeSwitch<T> = T extends "medialib"
? MedialibEntry
: T extends "content"
? ContentEntry
: T extends "product"
? ProductEntry
: never
export async function getDBEntries<T extends CollectionNameT>(
collectionName: T,
filter?: { [key: string]: any },
sort: string = "sort",
limit?: number,
offset?: number,
projection?: string,
params?: { [key: string]: string }
): Promise<EntryTypeSwitch<T>[]> {
const c = await api<EntryTypeSwitch<T>[]>(collectionName, {
filter,
sort,
limit,
offset,
projection,
params,
})
return c.data
}
export async function getCachedEntries<T extends CollectionNameT>(
collectionName: T,
filter?: { [key: string]: any },
sort: string = "sort",
limit?: number,
offset?: number,
projection?: string,
params?: { [key: string]: string }
): Promise<EntryTypeSwitch<T>[]> {
const filterStr = obj2str({ collectionName, filter, sort, limit, offset, projection, params })
if (cache[filterStr] && cache[filterStr].expire >= Date.now()) {
return cache[filterStr].data
}
const entries = await getDBEntries<T>(collectionName, filter, sort, limit, offset, projection, params)
// expire in 1h
cache[filterStr] = { expire: Date.now() + 1000 * 60 * 60, data: entries }
return entries
}
export async function getDBEntry<T extends CollectionNameT>(
collectionName: T,
filter: { [key: string]: any },
params?: { [key: string]: string }
) {
return (await getDBEntries<T>(collectionName, filter, "_id", null, null, null, params))?.[0]
}
export async function getCachedEntry<T extends CollectionNameT>(
collectionName: T,
filter: { [key: string]: any },
params?: { [key: string]: string }
) {
return (await getCachedEntries<T>(collectionName, filter, "_id", null, null, null, params))?.[0]
}
export async function postDBEntry<T extends CollectionNameT>(collectionName: T, entry: EntryTypeSwitch<T>) {
return api<EntryTypeSwitch<T>>(collectionName, { method: "POST" }, entry)
}

@ -9,9 +9,6 @@ module.exports = {
sveltePreprocess({
sourceMap: true,
typescript: false,
/* scss: {
includePaths: ["src/theme"],
}, */
}),
],
}

15
types/global.d.ts vendored

@ -41,3 +41,18 @@ interface FileField {
type: string
size: number
}
interface MedialibEntry {
id: string
// ...
}
interface ContentEntry {
id: string
// ...
}
interface ProductEntry {
id: string
// ...
}