This commit is contained in:
Sebastian Frank 2021-03-22 16:54:31 +01:00
parent 2ee7f650db
commit 0aca310a5e
5 changed files with 294 additions and 0 deletions

5
scripts/init.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
files=`find .drone.yml api src -type f -name "config*" -or -name "*drone*"`
grep -E "__.*__" $files | grep -v TIMESTAMP

165
src/api.ts Normal file
View File

@ -0,0 +1,165 @@
import { apiBaseURL } from "./config"
import * as sentry from "./sentry"
import * as SSR from "../api/hooks/lib/utils.js"
// [MIT License](LICENSE.md) © [Jason Miller](https://jasonformat.com/)
const _f = function (url, options): Promise<Response> {
if (typeof XMLHttpRequest === "undefined") {
return Promise.resolve(null)
}
options = options || {}
return new Promise((resolve, reject) => {
const request = new XMLHttpRequest()
const keys = []
const all = []
const headers = {}
const response = (): 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,
get: (n) => headers[n.toLowerCase()],
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])
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)
})
}
const _fetch =
typeof fetch === "undefined"
? typeof window === "undefined"
? _f
: window.fetch || _f
: fetch
export const api = async <T>(
endpoint: string,
options?: {
method?: string
filter?: any
sort?: string
limit?: number
offset?: number
projection?: string
headers?: {
[key: string]: string
}
params?: {
[key: string]: string
}
}
): Promise<{ data: T; count: number }> => {
if (typeof window === "undefined") {
// ssr
// @ts-ignore
return context.ssrFetch(endpoint, options)
}
// try ssr cache
const key = SSR.obj2str({ endpoint, options })
// @ts-ignore
if (window.__SSR_CACHE__) {
// @ts-ignore
const ssrVal = window.__SSR_CACHE__[key]
console.log("SSR:", key, ssrVal)
if (ssrVal) {
return ssrVal
}
}
let method = "GET"
let query = "&count=1"
if (options?.filter)
query += "&filter=" + encodeURIComponent(JSON.stringify(options.filter))
if (options?.sort) query += "&sort=" + options.sort + "&sort=_id"
if (options?.limit) query += "&limit=" + options.limit
if (options?.offset) query += "&offset=" + options.offset
if (options?.projection) query += "&projection=" + options.projection
if (options?.params) {
Object.keys(options.params).forEach((p) => {
query += "&" + p + "=" + encodeURIComponent(options.params[p])
})
}
let headers: any = {
"Content-Type": "application/json",
}
if (options?.headers) headers = { ...headers, ...options.headers }
let url = apiBaseURL + endpoint + (query ? "?" + query : "")
const span = sentry.currentTransaction()?.startChild({
op: "fetch",
description: method + " " + url,
data: Object.assign({}, options, { url }),
})
const trace_id = span?.toTraceparent()
if (trace_id) {
headers["sentry-trace"] = trace_id
}
const response = await _fetch(url, {
method,
mode: "cors",
headers,
})
span?.finish()
// @ts-ignore
let data = (await response?.json()) || null
if (response?.status < 200 || response?.status >= 400)
throw { response, data }
// @ts-ignore
return { data, count: response?.headers?.get("x-results-count") || 0 }
}
export const getContent = async (path: string): Promise<Content> => {
const c = await api<Content[]>("content", { limit: 1, filter: { path } })
if (c?.data?.length) {
return c.data[0]
}
return null
}

View File

@ -1,5 +1,17 @@
<h1>__PROJECT_TITLE__</h1>
<div use:links>
<a href="/test1">1</a>
<a href="/test2">2</a>
<a href="/test3">3</a>
<a href="/test4">4</a>
<Router url="{url}">
<Route path="/*path" let:params>
<Content path="/{params.path}" />
</Route>
</Router>
</div>
<style lang="less">
h1 {
color: red;
@ -7,8 +19,10 @@
</style>
<script lang="typescript">
import { Router, Route, links } from "svelte-routing"
import { scrollToTop } from "svelte-scrollto"
import { location } from "../store"
import Content from "./Content.svelte"
export let url = ""
if (url) {

View File

@ -0,0 +1,5 @@
<h2>{path}</h2>
<script lang="typescript">
export let path: string
</script>

View File

@ -0,0 +1,105 @@
{#if blocks?.length}
<section class="section_padding">
<div class="container">
{#each blocks as box, idx}
<!-- Teaserbox -->
<div class="row center_row">
{#if box.images?.length && (box.layout == 1 || box.layout == 3)}
<div class="col-md-{box.layout < 3 ? 6 : 12}">
<img
loading="lazy"
src="{imageBase + box.images[0].file.src}"
alt="{box.images[0].label || ''}"
/>
</div>
{/if}
{#if box.text || box.title || (box.button_text && box.button_url)}
<div class="col-md-{box.layout < 3 ? 6 : 12}">
{#if box.subtitle}
<div class="subline">{box.subtitle}</div>
{/if}
{#if box.title}
{#if accordeon == true}
<h2
class="h2_nooffset acc_trigger"
class:active="{activeAccordeons[idx]}"
on:click="{() => {
activeAccordeons[idx] = !activeAccordeons[idx]
}}"
>
{box.title}
<span class="icon">\/</span>
</h2>
{:else}
<h2 class="h2_nooffset">{box.title}</h2>
{/if}
{/if}
<div
class="boxText"
class:hideText="{accordeon && box.title && !activeAccordeons[idx]}"
>
{@html box.text}
{#if box.button_text && box.button_url}
<a
href="{box.button_url}"
class="btn btn_blue"
>{box.button_text}</a>
{/if}
</div>
</div>
{/if}
{#if box.images?.length && (box.layout == 2 || box.layout == 4)}
<div class="col-md-{box.layout < 3 ? 6 : 12}">
<img
loading="lazy"
src="{imageBase + box.images[0].file.src}"
alt="{box.images[0].label || ''}"
/>
</div>
{/if}
</div>
{/each}
</div>
</section>
{/if}
<style lang="less">
.acc_trigger {
display: flex;
display: -webkit-flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
border-bottom: solid 1px #ccc;
padding-bottom: 10px;
.icon {
display: flex;
display: -webkit-flex;
align-items: center;
justify-content: center;
font-size: 30px;
transform: rotate(0deg);
transition: all 0.3s;
}
&.active {
.icon {
transform: rotate(180deg);
}
}
}
.hideText {
display: none;
}
</style>
<script lang="typescript">
export let blocks: ContentBlock[]
export let imageBase: string
export let accordeon = false
let activeAccordeons: {
[key: number]: boolean
} = {}
</script>