diff --git a/.env b/.env
index 54cc99f..511978f 100644
--- a/.env
+++ b/.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
diff --git a/.prettierrc b/.prettierrc
index 5aa9151..1923026 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -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,
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..6e3111e
--- /dev/null
+++ b/Dockerfile
@@ -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" ]
diff --git a/Makefile b/Makefile
index 864c7dc..f759581 100644
--- a/Makefile
+++ b/Makefile
@@ -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)/
diff --git a/README.md b/README.md
index 1c0f76f..f76ca27 100644
--- a/README.md
+++ b/README.md
@@ -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 | |
+| Tibi Admin | |
+| Maildev | |
### Bauen
@@ -81,4 +55,7 @@ yarn build:legacy
# serverseitiges Rendering
yarn build:server
+
+# Admin-Module
+yarn build:admin
```
diff --git a/babel.config.json b/babel.config.json
index 0e70282..bfc729c 100644
--- a/babel.config.json
+++ b/babel.config.json
@@ -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": []
}
diff --git a/babel.config.server.json b/babel.config.server.json
new file mode 100644
index 0000000..4f19fab
--- /dev/null
+++ b/babel.config.server.json
@@ -0,0 +1,5 @@
+{
+ "sourceMaps": "inline",
+ "inputSourceMap": true,
+ "plugins": [["@babel/plugin-transform-async-to-generator"]]
+}
diff --git a/docker-compose-local.yml b/docker-compose-local.yml
index a0b2361..a218567 100644
--- a/docker-compose-local.yml
+++ b/docker-compose-local.yml
@@ -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
diff --git a/docker-compose-staging.yml b/docker-compose-staging.yml
new file mode 100644
index 0000000..b6dfe4c
--- /dev/null
+++ b/docker-compose-staging.yml
@@ -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}
diff --git a/esbuild.config.admin.js b/esbuild.config.admin.js
new file mode 100644
index 0000000..454cd72
--- /dev/null
+++ b/esbuild.config.admin.js
@@ -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
diff --git a/esbuild.config.js b/esbuild.config.js
index dce28dd..7c2dfb1 100644
--- a/esbuild.config.js
+++ b/esbuild.config.js
@@ -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,
diff --git a/esbuild.config.server.js b/esbuild.config.server.js
index b84f9b6..6269fc7 100644
--- a/esbuild.config.server.js
+++ b/esbuild.config.server.js
@@ -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,
]
diff --git a/frontend/src/admin.ts b/frontend/src/admin.ts
new file mode 100644
index 0000000..69acad0
--- /dev/null
+++ b/frontend/src/admin.ts
@@ -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
+}
diff --git a/frontend/src/api.ts b/frontend/src/api.ts
new file mode 100644
index 0000000..b7093fa
--- /dev/null
+++ b/frontend/src/api.ts
@@ -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 (
+ 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 extends "medialib"
+ ? MedialibEntry
+ : T extends "content"
+ ? ContentEntry
+ : T extends "product"
+ ? ProductEntry
+ : never
+
+export async function getDBEntries(
+ collectionName: T,
+ filter?: { [key: string]: any },
+ sort: string = "sort",
+ limit?: number,
+ offset?: number,
+ projection?: string,
+ params?: { [key: string]: string }
+): Promise[]> {
+ const c = await api[]>(collectionName, {
+ filter,
+ sort,
+ limit,
+ offset,
+ projection,
+ params,
+ })
+ return c.data
+}
+
+export async function getCachedEntries(
+ collectionName: T,
+ filter?: { [key: string]: any },
+ sort: string = "sort",
+ limit?: number,
+ offset?: number,
+ projection?: string,
+ params?: { [key: string]: string }
+): Promise[]> {
+ 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(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(
+ collectionName: T,
+ filter: { [key: string]: any },
+ params?: { [key: string]: string }
+) {
+ return (await getDBEntries(collectionName, filter, "_id", null, null, null, params))?.[0]
+}
+
+export async function getCachedEntry(
+ collectionName: T,
+ filter: { [key: string]: any },
+ params?: { [key: string]: string }
+) {
+ return (await getCachedEntries(collectionName, filter, "_id", null, null, null, params))?.[0]
+}
+
+export async function postDBEntry(collectionName: T, entry: EntryTypeSwitch) {
+ return api>(collectionName, { method: "POST" }, entry)
+}
diff --git a/svelte.config.js b/svelte.config.js
index e0fae66..f864b63 100644
--- a/svelte.config.js
+++ b/svelte.config.js
@@ -9,9 +9,6 @@ module.exports = {
sveltePreprocess({
sourceMap: true,
typescript: false,
- /* scss: {
- includePaths: ["src/theme"],
- }, */
}),
],
}
diff --git a/types/global.d.ts b/types/global.d.ts
index 93fdbba..885e139 100644
--- a/types/global.d.ts
+++ b/types/global.d.ts
@@ -41,3 +41,18 @@ interface FileField {
type: string
size: number
}
+
+interface MedialibEntry {
+ id: string
+ // ...
+}
+
+interface ContentEntry {
+ id: string
+ // ...
+}
+
+interface ProductEntry {
+ id: string
+ // ...
+}