This commit is contained in:
parent
4ec39882a8
commit
d6acf825c8
@ -89,11 +89,11 @@ jobs:
|
||||
run: |
|
||||
yarn build
|
||||
|
||||
#- name: build ssr
|
||||
# env:
|
||||
# FORCE_COLOR: "true"
|
||||
# run: |
|
||||
# yarn build:server
|
||||
- name: build ssr
|
||||
env:
|
||||
FORCE_COLOR: "true"
|
||||
run: |
|
||||
yarn build:server
|
||||
|
||||
- name: build legacy
|
||||
env:
|
||||
@ -101,58 +101,58 @@ jobs:
|
||||
run: |
|
||||
yarn build:legacy
|
||||
|
||||
- name: Wait for Live Server
|
||||
run: |
|
||||
attempts=0
|
||||
max_attempts=2
|
||||
while ! curl --output /dev/null --silent --head --fail http://live-server:8081; do
|
||||
if [ $attempts -eq $max_attempts ]; then
|
||||
echo "Live server not ready after $max_attempts attempts"
|
||||
echo "${{ toJson(job) }}"
|
||||
curl -v http://live-server:8081
|
||||
exit 1
|
||||
fi
|
||||
attempts=$((attempts+1))
|
||||
echo "Waiting for live-server to be ready... attempt $attempts"
|
||||
sleep 5
|
||||
done
|
||||
#- name: Wait for Live Server
|
||||
# run: |
|
||||
# attempts=0
|
||||
# max_attempts=2
|
||||
# while ! curl --output /dev/null --silent --head --fail http://live-server:8081; do
|
||||
# if [ $attempts -eq $max_attempts ]; then
|
||||
# echo "Live server not ready after $max_attempts attempts"
|
||||
# echo "${{ toJson(job) }}"
|
||||
# curl -v http://live-server:8081
|
||||
# exit 1
|
||||
# fi
|
||||
# attempts=$((attempts+1))
|
||||
# echo "Waiting for live-server to be ready... attempt $attempts"
|
||||
# sleep 5
|
||||
# done
|
||||
|
||||
- name: Test HTTP Request
|
||||
run: |
|
||||
echo "Live server not ready after $max_attempts attempts"
|
||||
echo "${{ toJson(job) }}"
|
||||
echo "${{ job.services.live-server.id }}"
|
||||
echo "${{ job.services.tibi-server.id }}"
|
||||
echo "${{ job.services.mongo.id }}"
|
||||
#- name: Test HTTP Request
|
||||
# run: |
|
||||
# echo "Live server not ready after $max_attempts attempts"
|
||||
# echo "${{ toJson(job) }}"
|
||||
# echo "${{ job.services.live-server.id }}"
|
||||
# echo "${{ job.services.tibi-server.id }}"
|
||||
## echo "${{ job.services.mongo.id }}"
|
||||
|
||||
docker logs "${{ job.services.tibi-server.id }}"
|
||||
docker logs "${{ job.services.live-server.id }}"
|
||||
curl -v http://live-server:8081
|
||||
# docker logs "${{ job.services.tibi-server.id }}"
|
||||
# docker logs "${{ job.services.live-server.id }}"
|
||||
# curl -v http://live-server:8081
|
||||
|
||||
- name: Install Chrome
|
||||
run: |
|
||||
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
|
||||
sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list'
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y google-chrome-stable
|
||||
#- name: Install Chrome
|
||||
# run: |
|
||||
# wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
|
||||
# sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list'
|
||||
#
|
||||
# sudo apt-get update
|
||||
# sudo apt-get install -y google-chrome-stable
|
||||
|
||||
# Lighthouse Analysis Step
|
||||
- name: Lighthouse Analysis
|
||||
run: |
|
||||
yarn add lighthouse
|
||||
npx lighthouse http://127.0.0.1:8081 --output json --output-path /tmp/lighthouse-report.json --chrome-flags="--headless --no-sandbox --disable-dev-shm-usage"
|
||||
#- name: Lighthouse Analysis
|
||||
# run: |
|
||||
# yarn add lighthouse
|
||||
# npx lighthouse http://127.0.0.1:8081 --output json --output-path /tmp/lighthouse-report.json --chrome-flags="--headless --no-sandbox --disable-dev-shm-usage"
|
||||
|
||||
# Notify-Lighthouse Step
|
||||
- name: Notify Lighthouse
|
||||
run: |
|
||||
docker run --rm \
|
||||
-e PLUGIN_FROM=noreply@gitbase.de \
|
||||
-e PLUGIN_HOST=smtp.basehosts.de \
|
||||
-e PLUGIN_RECIPIENT=recipient@example.com \
|
||||
-e PLUGIN_SUBJECT="Lighthouse Report" \
|
||||
-v ${{ github.workspace }}/tmp:/lighthouse-reports \
|
||||
drillster/drone-email /tmp/lighthouse-report.json
|
||||
#- name: Notify Lighthouse
|
||||
# run: |
|
||||
# docker run --rm \
|
||||
# -e PLUGIN_FROM=noreply@gitbase.de \
|
||||
# -e PLUGIN_HOST=smtp.basehosts.de \
|
||||
# -e PLUGIN_RECIPIENT=recipient@example.com \
|
||||
# -e PLUGIN_SUBJECT="Lighthouse Report" \
|
||||
# -v ${{ github.workspace }}/tmp:/lighthouse-reports \
|
||||
# drillster/drone-email /tmp/lighthouse-report.json
|
||||
|
||||
- name: deploy
|
||||
if: github.ref == 'refs/heads/master'
|
||||
|
@ -7,6 +7,7 @@ meta:
|
||||
label: { de: "SSR Dummy", en: "ssr dummy" }
|
||||
muiIcon: server
|
||||
rowIdentTpl: { twig: "{{ id }}" }
|
||||
|
||||
views:
|
||||
- type: simpleList
|
||||
mediaQuery: "(max-width: 600px)"
|
||||
@ -55,6 +56,7 @@ fields:
|
||||
- name: path
|
||||
type: string
|
||||
index: [single, unique]
|
||||
|
||||
- name: content
|
||||
type: string
|
||||
meta:
|
||||
|
@ -38,6 +38,7 @@ collections:
|
||||
- !include collections/banner.yml
|
||||
- !include collections/forms.yml
|
||||
- !include collections/backups.yml
|
||||
- !include collections/ssr.yml
|
||||
|
||||
jobs:
|
||||
- cron: "0 * * * *"
|
||||
|
@ -1,5 +1,5 @@
|
||||
const release = "tibi-docs.dirty"
|
||||
|
||||
const apiClientBaseURL = "/api/"
|
||||
// @ts-ignore
|
||||
if (release && typeof context !== "undefined") {
|
||||
context.response.header("X-Release", release)
|
||||
@ -7,4 +7,5 @@ if (release && typeof context !== "undefined") {
|
||||
|
||||
module.exports = {
|
||||
release,
|
||||
apiClientBaseURL,
|
||||
}
|
||||
|
180
api/hooks/lib/ssr.js
Normal file
180
api/hooks/lib/ssr.js
Normal file
@ -0,0 +1,180 @@
|
||||
const { apiClientBaseURL } = require("../config-client")
|
||||
|
||||
/**
|
||||
* convert object to string
|
||||
* @param {any} obj object
|
||||
*/
|
||||
function obj2str(obj) {
|
||||
if (Array.isArray(obj)) {
|
||||
return JSON.stringify(
|
||||
obj.map(function (idx) {
|
||||
return obj2str(idx)
|
||||
})
|
||||
)
|
||||
} else if (typeof obj === "object" && obj !== null) {
|
||||
var elements = Object.keys(obj)
|
||||
.sort()
|
||||
.map(function (key) {
|
||||
var val = obj2str(obj[key])
|
||||
if (val) {
|
||||
return key + ":" + val
|
||||
}
|
||||
})
|
||||
|
||||
var elementsCleaned = []
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
if (elements[i]) elementsCleaned.push(elements[i])
|
||||
}
|
||||
|
||||
return "{" + elementsCleaned.join("|") + "}"
|
||||
}
|
||||
|
||||
if (obj) return obj
|
||||
}
|
||||
|
||||
// fetch polyfill
|
||||
// [MIT License](LICENSE.md) © [Jason Miller](https://jasonformat.com/)
|
||||
const _f = function (
|
||||
/** @type {string | URL} */ url,
|
||||
/** @type {{ method?: any; credentials?: any; headers?: any; body?: any; }} */ options
|
||||
) {
|
||||
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 = () => ({
|
||||
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
|
||||
|
||||
/**
|
||||
* api request via client or server
|
||||
* server function ssrRequest is called via context.ssrRequest, binded in ssr hook
|
||||
*
|
||||
* @param {string} endpoint
|
||||
* @param {ApiOptions} options
|
||||
* @param {any} body
|
||||
* @returns {Promise<ApiResult<any>>}
|
||||
*/
|
||||
function apiRequest(endpoint, options, body) {
|
||||
// TODO cache only for GET
|
||||
|
||||
// first check cache if on client
|
||||
const cacheKey = obj2str({ endpoint: endpoint, options: options })
|
||||
|
||||
// @ts-ignore
|
||||
if (typeof window !== "undefined" && window.__SSR_CACHE__ && options?.method === "GET") {
|
||||
// @ts-ignore
|
||||
const cache = window.__SSR_CACHE__[cacheKey]
|
||||
console.log("SSR:", cacheKey, cache)
|
||||
if (cache) {
|
||||
return Promise.resolve(cache)
|
||||
}
|
||||
}
|
||||
|
||||
let method = options?.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?.lookup) query += "&lookup=" + options.lookup
|
||||
|
||||
if (options?.params) {
|
||||
Object.keys(options.params).forEach((p) => {
|
||||
query += "&" + p + "=" + encodeURIComponent(options.params[p])
|
||||
})
|
||||
}
|
||||
|
||||
let headers = {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
if (options?.headers) headers = { ...headers, ...options.headers }
|
||||
|
||||
if (typeof window === "undefined" && method === "GET") {
|
||||
// server
|
||||
|
||||
// reference via context from get hook to tree shake in client
|
||||
// @ts-ignore
|
||||
const d = context.ssrRequest(cacheKey, endpoint, query, Object.assign({}, options, { method, headers }))
|
||||
return d
|
||||
} else {
|
||||
// client
|
||||
let url = endpoint + (query ? "?" + query : "")
|
||||
const requestOptions = {
|
||||
method,
|
||||
mode: "cors",
|
||||
headers,
|
||||
}
|
||||
|
||||
if (method === "POST" || method === "PUT") {
|
||||
requestOptions.body = JSON.stringify(body)
|
||||
}
|
||||
|
||||
return _fetch(apiClientBaseURL + url, requestOptions).then((response) => {
|
||||
return response?.json().then((json) => {
|
||||
if (response?.status < 200 || response?.status >= 400) {
|
||||
return Promise.reject({ response, data: json })
|
||||
}
|
||||
return Promise.resolve({ data: json || null, count: response.headers?.get("x-results-count") || 0 })
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
obj2str,
|
||||
apiRequest,
|
||||
}
|
@ -4,7 +4,7 @@ const { obj2str, log } = require("../lib/utils")
|
||||
;(function () {
|
||||
/** @type {HookResponse} */
|
||||
var response = null
|
||||
|
||||
console.log("SSR GET READ")
|
||||
var request = context.request()
|
||||
var url = request.query("url")
|
||||
var noCache = request.query("noCache")
|
||||
|
@ -1,3 +1,5 @@
|
||||
const fs = require("fs")
|
||||
|
||||
const resolvePlugin = {
|
||||
name: "resolvePlugin",
|
||||
setup(build) {
|
||||
@ -68,7 +70,9 @@ 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", {
|
||||
target: apiBase,
|
||||
@ -77,12 +81,39 @@ if (process.argv[2] == "start") {
|
||||
logLevel: "debug",
|
||||
})
|
||||
)
|
||||
|
||||
// if SSR env variable is set
|
||||
console.log(process.env)
|
||||
if (process.env.SSR) {
|
||||
// read api/config.yml.env and read SSR_TOKEN variable from it
|
||||
const configEnv = fs.readFileSync(__dirname + "/api/config.yml.env", "utf8")
|
||||
const SSR_TOKEN = configEnv.match(/SSR_TOKEN=(.*)/)[1]
|
||||
|
||||
// redirect all other requests to /api/ssr?token=owshwerNwoa&url=...
|
||||
bsMiddleware.push(
|
||||
createProxyMiddleware(
|
||||
function (path, req) {
|
||||
return !path.match(/\./)
|
||||
},
|
||||
{
|
||||
target: apiBase,
|
||||
changeOrigin: true,
|
||||
logLevel: "debug",
|
||||
pathRewrite: function (path, req) {
|
||||
console.log(path)
|
||||
return "/ssr?token=" + SSR_TOKEN + "&url=" + encodeURIComponent(path)
|
||||
},
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sveltePlugin: sveltePlugin,
|
||||
resolvePlugin: resolvePlugin,
|
||||
options: options,
|
||||
distDir,
|
||||
watch: {
|
||||
path: [__dirname + "/" + frontendDir + "/src/**/*"],
|
||||
},
|
||||
@ -103,7 +134,6 @@ module.exports = {
|
||||
}),
|
||||
],
|
||||
},
|
||||
ghostMode: false,
|
||||
open: false,
|
||||
// logLevel: "debug",
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
AddType application/javascript .mjs
|
||||
|
||||
DirectoryIndex spa.html
|
||||
#DirectoryIndex noindex
|
||||
#DirectoryIndex spa.html
|
||||
DirectoryIndex noindex
|
||||
|
||||
<ifModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
@ -11,5 +11,6 @@ DirectoryIndex spa.html
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule (.*) /spa.html [QSA,L]
|
||||
RewriteRule ^/?(.*)$ http://tibi-server:8080/api/v1/_/allkids_erfurt/ssr?token=owshwerNwoa&url=/$1 [P,QSA,L]
|
||||
#RewriteRule (.*) /spa.html [QSA,L]
|
||||
</ifModule>
|
||||
|
@ -1,121 +1,12 @@
|
||||
import { apiBaseURL } from "./config"
|
||||
|
||||
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
|
||||
import { apiRequest } from "../../api/hooks/lib/ssr"
|
||||
|
||||
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
|
||||
}
|
||||
},
|
||||
options?: ApiOptions,
|
||||
body?: any
|
||||
): Promise<{ data: T; count: number } | any> => {
|
||||
if (typeof window === "undefined") {
|
||||
// ssr
|
||||
// @ts-ignore
|
||||
return context.ssrFetch(endpoint, options)
|
||||
}
|
||||
|
||||
let method = options?.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 requestOptions: any = {
|
||||
method,
|
||||
mode: "cors",
|
||||
headers,
|
||||
}
|
||||
|
||||
if (method === "POST" || method === "PUT") {
|
||||
requestOptions.body = JSON.stringify(body)
|
||||
}
|
||||
|
||||
let response = await _fetch(url, requestOptions)
|
||||
if (response.status == 409 || response.status == 401) return response
|
||||
let data = (await response?.json()) || null
|
||||
let data = await apiRequest(endpoint, options, body)
|
||||
// @ts-ignore
|
||||
return { data }
|
||||
console.log(data, "data")
|
||||
return data
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
import App from "./components/App.svelte"
|
||||
import App from "./App.svelte"
|
||||
|
||||
export default App
|
||||
|
21
types/global.d.ts
vendored
21
types/global.d.ts
vendored
@ -2,6 +2,27 @@ interface Sites {
|
||||
[key: string]: Site
|
||||
}
|
||||
|
||||
interface ApiResult<T> {
|
||||
data: T
|
||||
count: number
|
||||
}
|
||||
|
||||
interface ApiOptions {
|
||||
method?: string
|
||||
filter?: any
|
||||
sort?: string
|
||||
lookup?: string
|
||||
limit?: number
|
||||
offset?: number
|
||||
projection?: string
|
||||
headers?: {
|
||||
[key: string]: string
|
||||
}
|
||||
params?: {
|
||||
[key: string]: string
|
||||
}
|
||||
}
|
||||
|
||||
interface Site {
|
||||
path: string
|
||||
showTeaser: boolean
|
||||
|
Loading…
Reference in New Issue
Block a user