Compare commits
13 Commits
push-tlvrn
...
push-wlrpv
| Author | SHA1 | Date | |
|---|---|---|---|
| d0e107f644 | |||
| 630ceb2473 | |||
| d233c8081a | |||
| bbd8c1e40c | |||
| 57fb2febf6 | |||
| a5b9823b10 | |||
| a2a4fdd770 | |||
| 9034c23d84 | |||
| 685fe225f4 | |||
| 0e8a99a277 | |||
| 76ac71a755 | |||
| 7251753df5 | |||
| 75355c43a8 |
105
.drone.yml
@@ -61,58 +61,6 @@ trigger:
|
||||
depends_on:
|
||||
- Build
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: Publish
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: config-check
|
||||
|
||||
steps:
|
||||
- name: check-config
|
||||
image: alpine/git
|
||||
commands:
|
||||
- git fetch --no-tags --depth=2
|
||||
- |
|
||||
if git diff --quiet HEAD^ HEAD -- varnish/default.vcl; then
|
||||
echo "No changes in varnish config file, skipping..."
|
||||
exit 78 # exit code 78 = skip in Drone
|
||||
else
|
||||
echo "Changes detected in varnish config"
|
||||
fi
|
||||
|
||||
- name: Publish varnish to ghcr
|
||||
image: plugins/docker
|
||||
settings:
|
||||
registry: ghcr.io
|
||||
repo: ghcr.io/kevinmidboe/varnish-infra-map
|
||||
dockerfile: varnish/Dockerfile
|
||||
compress: true
|
||||
username:
|
||||
from_secret: GITHUB_USERNAME
|
||||
password:
|
||||
from_secret: GHCR_UPLOAD_TOKEN
|
||||
tags:
|
||||
- latest
|
||||
- ${DRONE_COMMIT_SHA}
|
||||
|
||||
trigger:
|
||||
event:
|
||||
include:
|
||||
- push
|
||||
exclude:
|
||||
- pull_request
|
||||
branch:
|
||||
- main
|
||||
- update
|
||||
depends_on:
|
||||
- Build
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
@@ -133,7 +81,7 @@ steps:
|
||||
commands:
|
||||
- mkdir -p /root/.kube
|
||||
- echo "IMAGE=ghcr.io/kevinmidboe/${DRONE_REPO_NAME}:${DRONE_COMMIT_SHA}" > /root/.kube/.env
|
||||
- echo "VARNISH_IMAGE=ghcr.io/kevinmidboe/varnish-${DRONE_REPO_NAME}" >> /root/.kube/.env
|
||||
- echo "VARNISH_IMAGE=ghcr.io/kevinmidboe/varnish-${DRONE_REPO_NAME}:latest" >> /root/.kube/.env
|
||||
- echo "NAMESPACE=${DRONE_REPO_NAME}" >> /root/.kube/.env
|
||||
- 'curl -s
|
||||
-H "X-Vault-Token: $VAULT_TOKEN"
|
||||
@@ -181,8 +129,57 @@ depends_on:
|
||||
volumes:
|
||||
- name: kube-config
|
||||
temp: {}
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: Publish varnish
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- name: Check for varnish changes
|
||||
image: alpine/git
|
||||
commands:
|
||||
- git fetch --no-tags --depth=2
|
||||
- |
|
||||
if git diff-tree --no-commit-id --name-only -r HEAD | grep -qE '(\.drone.yml|(varnish/.+(vcl|tmpl)(\n|$)))'; then
|
||||
echo "Changes detected in varnish config"
|
||||
else
|
||||
echo "No changes in varnish config file, skipping..."
|
||||
exit 78 # exit code 78 = skip in Drone
|
||||
fi
|
||||
|
||||
- name: Publish varnish image to ghcr
|
||||
image: plugins/docker
|
||||
settings:
|
||||
registry: ghcr.io
|
||||
repo: ghcr.io/kevinmidboe/varnish-infra-map
|
||||
context: varnish
|
||||
dockerfile: varnish/Dockerfile
|
||||
compress: true
|
||||
username:
|
||||
from_secret: GITHUB_USERNAME
|
||||
password:
|
||||
from_secret: GHCR_UPLOAD_TOKEN
|
||||
tags:
|
||||
- latest
|
||||
- ${DRONE_COMMIT_SHA}
|
||||
|
||||
trigger:
|
||||
event:
|
||||
include:
|
||||
- push
|
||||
exclude:
|
||||
- pull_request
|
||||
branch:
|
||||
- main
|
||||
- update
|
||||
|
||||
---
|
||||
kind: signature
|
||||
hmac: 01caa41521eac62356f6fc941cdd489dae8e2c4249bdb4e4dc1a32e101c639b7
|
||||
hmac: b4b6a98b76fdf3cf297b46cf986a3d46f3d4050e623f2c769267181c7075a6ca
|
||||
|
||||
...
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
DATABASE_URL=
|
||||
TRAEFIK_URL=
|
||||
HTTP_HEALTH_ENDPOINTS=
|
||||
PROXMOX_URL=
|
||||
PROXMOX_TOKEN=
|
||||
HOMEASSISTANT_URL=
|
||||
HOMEASSISTANT_TOKEN=
|
||||
TRAEFIK_URL=
|
||||
KUBERNETES_SERVICE_HOST=
|
||||
KUBERNETES_SA_TOKEN=
|
||||
KUBERNETES_CA_CERT_PATH=
|
||||
|
||||
|
||||
9
.kubernetes/2-config-varnish.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: varnish-config
|
||||
namespace: ${NAMESPACE}
|
||||
data:
|
||||
PROXY_HOST: ${PROXY_HOST}
|
||||
IMAGE_HOST: ${IMAGE_HOST}
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: varnish-vcl
|
||||
namespace: ${NAMESPACE}
|
||||
binaryData:
|
||||
default.vcl: dmNsIDQuMDsKCmltcG9ydCBzdGQ7CmltcG9ydCBkaWdlc3Q7CgojIERlZmluZSBiYWNrZW5kIHBvaW50aW5nIHRvIEhvbWUgQXNzaXN0YW50IElQCmJhY2tlbmQgaGFzc19iYWNrZW5kIHsKICAgIC5ob3N0ID0gIjEwLjAuMC44MiI7CiAgICAucG9ydCA9ICI4MTIzIjsKfQoKc3ViIHZjbF9yZWN2IHsKICAgICMgSGFuZGxlIENPUlMgcHJlZmxpZ2h0CiAgICBpZiAocmVxLm1ldGhvZCA9PSAiT1BUSU9OUyIpIHsKICAgICAgICByZXR1cm4gKHN5bnRoKDIwNCwgIlByZWZsaWdodCIpKTsKICAgIH0KCiAgICAjIFJld3JpdGUgaW1hZ2UgVVJMCiAgICBpZiAocmVxLnVybCB+ICJeL2ltYWdlLyIpIHsKICAgICAgICAjIEV4dHJhY3QgZXZlcnl0aGluZyBhZnRlciAvaW1hZ2UvIGFuZCBzdG9yZSBpdAogICAgICAgIHNldCByZXEuaHR0cC5YLUltYWdlLVVSTCA9IHJlZ3N1YihyZXEudXJsLCAiXi9pbWFnZS8oLiopIiwgIlwxIik7CiAgICAgICAgIyBSZXdyaXRlIHJlcS51cmwgdG8gbWF0Y2ggYmFja2VuZCBleHBlY3RhdGlvbnMKICAgICAgICBzZXQgcmVxLnVybCA9IHJlZ3N1YihyZXEuaHR0cC5YLUltYWdlLVVSTCwgIl5odHRwOi8vW14vXSsiLCAiIik7CiAgICB9CgogICAgIyBSZW1vdmUgY29va2llcyBzbyBjb250ZW50IGlzIGNhY2hlYWJsZQogICAgdW5zZXQgcmVxLmh0dHAuQ29va2llOwp9CgpzdWIgdmNsX3N5bnRoIHsKICAgIGlmIChyZXNwLnN0YXR1cyA9PSAyMDQpIHsKICAgICAgICBzZXQgcmVzcC5odHRwLkFjY2Vzcy1Db250cm9sLUFsbG93LU9yaWdpbiA9ICIqIjsKICAgICAgICBzZXQgcmVzcC5odHRwLkFjY2Vzcy1Db250cm9sLUFsbG93LU1ldGhvZHMgPSAiR0VULCBPUFRJT05TIjsKICAgICAgICBzZXQgcmVzcC5odHRwLkFjY2Vzcy1Db250cm9sLUFsbG93LUhlYWRlcnMgPSAiQ29udGVudC1UeXBlLCBYLUNhY2hlLUlEIjsKICAgICAgICBzZXQgcmVzcC5odHRwLkNvbnRlbnQtTGVuZ3RoID0gIjAiOwogICAgICAgIHJldHVybiAoZGVsaXZlcik7CiAgICB9CgogICAgaWYgKHJlc3Auc3RhdHVzID09IDMwNCkgewogICAgICAgIHNldCByZXNwLmh0dHAuRVRhZyA9IHJlcS5odHRwLklmLU5vbmUtTWF0Y2g7CiAgICAgICAgc2V0IHJlc3AuaHR0cC5Db250ZW50LUxlbmd0aCA9ICIwIjsKICAgICAgICByZXR1cm4gKGRlbGl2ZXIpOwogICAgfQp9CgpzdWIgdmNsX2JhY2tlbmRfZmV0Y2ggewogICAgIyBBbHdheXMgdXNlIHRoZSBIQVNTIGJhY2tlbmQKICAgIHNldCBiZXJlcS5iYWNrZW5kID0gaGFzc19iYWNrZW5kOwoKICAgICMgU2V0IHByb3BlciBIb3N0IGhlYWRlciBmcm9tIG9yaWdpbmFsIFVSTAogICAgIyBpZiAoYmVyZXEuaHR0cC5YLUltYWdlLVVSTCkgewogICAgIyAgICAgc2V0IGJlcmVxLmh0dHAuSG9zdCA9IHJlZ3N1YihiZXJlcS5odHRwLlgtSW1hZ2UtVVJMLCAiXmh0dHA6Ly8oW14vXSspLioiLCAiXDEiKTsKICAgICMgICAgIHNldCBiZXJlcS5odHRwLkhvc3QgPSByZWdzdWIoYmVyZXEuaHR0cC5Ib3N0LCAiOlswLTldKyQiLCAiIik7CiAgICAjIH0KfQoKc3ViIHZjbF9iYWNrZW5kX3Jlc3BvbnNlIHsKICAgIHNldCBiZXJlc3AudHRsID0gMXM7CiAgICBzZXQgYmVyZXNwLmdyYWNlID0gNjBzOwogICAgc2V0IGJlcmVzcC5rZWVwID0gNjBzOwoKICAgICMgRW5zdXJlIEVUYWcgaXMgcGFzc2VkIHRvIGNsaWVudAogICAgaWYgKGJlcmVzcC5odHRwLkVUYWcpIHsKICAgICAgICBzZXQgYmVyZXNwLmh0dHAuWC1DYWNoZS1FVGFnID0gYmVyZXNwLmh0dHAuRVRhZzsKICAgIH0gZWxzZSB7CiAgICAgICAgIyBPcHRpb25hbDogZ2VuZXJhdGUgb25lIGlmIG5vdCBwcm92aWRlZAogICAgICAgICMgc2V0IGJlcmVzcC5odHRwLkVUYWcgPSBkaWdlc3QuaGFzaF9tZDUoYmVyZXNwLmJvZHkpOwogICAgICAgIHNldCBiZXJlc3AuaHR0cC5FVGFnID0gYmVyZXNwLmh0dHAuQ29udGVudC1MZW5ndGg7CiAgICAgICAgc2V0IGJlcmVzcC5odHRwLlgtQ2FjaGUtRVRhZyA9IGJlcmVzcC5odHRwLkVUYWc7CiAgICB9Cn0KCnN1YiB2Y2xfaGl0IHsKICAgIGlmIChvYmoudHRsIDwgMHMgJiYgc3RkLmhlYWx0aHkocmVxLmJhY2tlbmRfaGludCkpIHsKICAgICAgICByZXR1cm4gKGRlbGl2ZXIpOwogICAgfQp9CgpzdWIgdmNsX2RlbGl2ZXIgewogICAgdW5zZXQgcmVzcC5odHRwLlgtSW1hZ2UtVVJMOwogICAgc2V0IHJlc3AuaHR0cC5BY2Nlc3MtQ29udHJvbC1BbGxvdy1PcmlnaW4gPSAiKiI7CgogICAgIyBIYW5kbGUgY29uZGl0aW9uYWwgcmVxdWVzdCB3aXRoIEVUYWcKICAgIGlmICgKICAgICAgICByZXEuaHR0cC5JZi1Ob25lLU1hdGNoICYmCiAgICAgICAgcmVxLmh0dHAuSWYtTm9uZS1NYXRjaCA9PSByZXNwLmh0dHAuRVRhZwogICAgKSB7CiAgICAgICAgcmV0dXJuIChzeW50aCgzMDQpKTsKICAgIH0KfQo=
|
||||
46
.kubernetes/deployment-app.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
labels:
|
||||
app: infra-map
|
||||
name: infra-map
|
||||
namespace: ${NAMESPACE}
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: infra-map
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: infra-map
|
||||
spec:
|
||||
containers:
|
||||
- name: infra-map
|
||||
image: ${IMAGE}
|
||||
imagePullPolicy: IfNotPresent
|
||||
resources:
|
||||
limits:
|
||||
cpu: 300m
|
||||
memory: 828Mi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 64Mi
|
||||
env:
|
||||
- name: ORIGIN
|
||||
value: http://infra-map.infra-map.svc.cluster.local:3000
|
||||
- name: PROTOCOL_HEADER
|
||||
value: x-forwarded-proto
|
||||
- name: HOST_HEADER
|
||||
value: x-forwarded-host
|
||||
- name: PORT_HEADER
|
||||
value: x-forwarded-port
|
||||
- name: ENV
|
||||
value: production
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: secret-env-values
|
||||
imagePullSecrets:
|
||||
- name: ghcr-login-secret
|
||||
40
.kubernetes/deployment-varnish.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
labels:
|
||||
app: varnish
|
||||
name: varnish
|
||||
namespace: ${NAMESPACE}
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: varnish
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: varnish
|
||||
spec:
|
||||
containers:
|
||||
- command:
|
||||
- /usr/local/bin/docker-entrypoint.sh
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: varnish-config
|
||||
image: ghcr.io/kevinmidboe/varnish-infra-map:latest
|
||||
imagePullPolicy: Always
|
||||
name: varnish
|
||||
resources:
|
||||
limits:
|
||||
cpu: 900m
|
||||
memory: 828Mi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 64Mi
|
||||
terminationMessagePath: /dev/termination-log
|
||||
terminationMessagePolicy: File
|
||||
imagePullSecrets:
|
||||
- name: ghcr-login-secret
|
||||
dnsPolicy: ClusterFirst
|
||||
@@ -1,56 +0,0 @@
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
labels:
|
||||
app: infra-map
|
||||
name: infra-map
|
||||
namespace: ${NAMESPACE}
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: infra-map
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: infra-map
|
||||
spec:
|
||||
containers:
|
||||
- image: ${IMAGE}
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: infra-map
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: secret-env-values
|
||||
resources:
|
||||
limits:
|
||||
cpu: 900m
|
||||
memory: 828Mi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 64Mi
|
||||
- image: ${VARNISH_IMAGE}:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: varnish
|
||||
command: ['varnishd']
|
||||
args: ['-F', '-f', '/etc/varnish/default.vcl', '-a', ':6081', '-s', 'malloc,512m']
|
||||
volumeMounts:
|
||||
- name: varnish-vcl
|
||||
mountPath: /etc/varnish/default.vcl
|
||||
subPath: default.vcl
|
||||
resources:
|
||||
limits:
|
||||
cpu: 900m
|
||||
memory: 828Mi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 64Mi
|
||||
restartPolicy: Always
|
||||
imagePullSecrets:
|
||||
- name: ghcr-login-secret
|
||||
volumes:
|
||||
- name: varnish-vcl
|
||||
configMap:
|
||||
name: varnish-vcl
|
||||
@@ -12,7 +12,7 @@ spec:
|
||||
paths:
|
||||
- backend:
|
||||
service:
|
||||
name: infra-map-service
|
||||
name: varnish
|
||||
port:
|
||||
number: 80
|
||||
path: /
|
||||
|
||||
@@ -1,19 +1,37 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: varnish
|
||||
name: varnish
|
||||
namespace: ${NAMESPACE}
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
name: http-varnish
|
||||
protocol: TCP
|
||||
targetPort: 6081
|
||||
selector:
|
||||
app: varnish
|
||||
sessionAffinity: None
|
||||
type: ClusterIP
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: infra-map
|
||||
name: infra-map-service
|
||||
name: infra-map
|
||||
namespace: ${NAMESPACE}
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
name: http-app
|
||||
protocol: TCP
|
||||
targetPort: 3000
|
||||
selector:
|
||||
app: infra-map
|
||||
sessionAffinity: None
|
||||
type: ClusterIP
|
||||
status:
|
||||
loadBalancer: {}
|
||||
|
||||
16
Dockerfile
@@ -1,20 +1,34 @@
|
||||
# --- Stage 1: Compile svelte-kit project ---
|
||||
FROM node:22-alpine3.20 AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy source files
|
||||
COPY src/ src
|
||||
COPY static/ static
|
||||
COPY package.json yarn.lock svelte.config.js tsconfig.json vite.config.ts ./
|
||||
|
||||
# Install dependencies
|
||||
RUN yarn --frozen-lockfile
|
||||
|
||||
# Build project
|
||||
ENV NODE_ENV=production
|
||||
RUN yarn build
|
||||
|
||||
# --- Stage 2: Run project with node ---
|
||||
FROM node:22-alpine3.20
|
||||
|
||||
# Copy compiled project files
|
||||
WORKDIR /opt/infra-map
|
||||
COPY --from=builder /app/build build
|
||||
|
||||
EXPOSE 3000
|
||||
ENV NODE_ENV=production
|
||||
|
||||
# Install dependencies
|
||||
RUN yarn install --frozen-lockfile
|
||||
RUN yarn add @sveltejs/kit
|
||||
|
||||
EXPOSE 3000
|
||||
ENV PORT=3000
|
||||
|
||||
CMD [ "node", "build/index.js" ]
|
||||
|
||||
46
README.md
@@ -61,20 +61,50 @@ Follow hass documentation on generating a api token: https://developers.home-ass
|
||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
|
||||
# or start the server and open the app in a new browser tab
|
||||
npm run dev -- --open
|
||||
yarn dev
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To create a production version of your app:
|
||||
To create a production version to be run from node:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
yarn build
|
||||
```
|
||||
|
||||
You can preview the production build with `npm run preview`.
|
||||
To preview either use vite to serve or execute node entrypoint:
|
||||
|
||||
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
|
||||
```bash
|
||||
yarn preview
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
node build/index.js
|
||||
```
|
||||
|
||||
## Run using docker
|
||||
|
||||
The application is configure to be used either standalone or behind a webserver. To build the svelte-application docker image run:
|
||||
|
||||
> NB! Remember to configure .env, which is automatically picked up by docker-compose. Set and override for both containers in this file.
|
||||
|
||||
Svelte-kit application:
|
||||
|
||||
```bash
|
||||
docker build -t infra-map .
|
||||
```
|
||||
|
||||
Varnish cache:
|
||||
|
||||
```bash
|
||||
cd varnish
|
||||
docker build -t infra-varnish-cache .
|
||||
```
|
||||
|
||||
Or both using docker compose:
|
||||
|
||||
```bash
|
||||
docker compose up
|
||||
```
|
||||
|
||||
@@ -1,21 +1,27 @@
|
||||
version: '3.8'
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
container_name: infra-map
|
||||
ports:
|
||||
- '3000:3000' # svelte-kit preview HTTP
|
||||
varnish:
|
||||
build: varnish
|
||||
container_name: varnish-cache
|
||||
ports:
|
||||
- '6081:6081' # Varnish HTTP
|
||||
build:
|
||||
context: varnish
|
||||
dockerfile: Dockerfile
|
||||
environment:
|
||||
- VARNISH_LISTEN_PORT=6081
|
||||
command: >
|
||||
varnishd
|
||||
-F
|
||||
-f /etc/varnish/default.vcl
|
||||
-s malloc,256m
|
||||
-a :6081
|
||||
# sets environment variables. Overridden by env, but has sane defaults
|
||||
IMAGE_HOST: ${IMAGE_HOST:-homeassistant.local}
|
||||
PROXY_HOST: ${PROXY_HOST:-app}
|
||||
ports:
|
||||
- '6081:6081'
|
||||
depends_on:
|
||||
- app
|
||||
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
env_file: .env # sets container's environment
|
||||
environment:
|
||||
- ORIGIN=http://localhost:3000
|
||||
- NODE_ENV=production
|
||||
- PROTOCOL_HEADER=x-forwarded-proto
|
||||
- HOST_HEADER=x-forwarded-host
|
||||
- PORT_HEADER=x-forwarded-port
|
||||
|
||||
@@ -31,8 +31,6 @@
|
||||
"prettier": "^3.4.2",
|
||||
"prettier-plugin-svelte": "^3.3.3",
|
||||
"sass-embedded": "^1.86.0",
|
||||
"sqlite": "^5.1.1",
|
||||
"sqlite3": "^5.1.7",
|
||||
"svelte": "^5.38.2",
|
||||
"svelte-check": "^4.0.0",
|
||||
"sveltekit-sse": "^0.13.16",
|
||||
|
||||
53
scripts/icon-converter.js
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Usage: node convert-svg-to-svelte.js [inputDir] [outputDir]
|
||||
* Defaults: ./svgs ./svelte
|
||||
*/
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const INPUT_DIR = process.argv[2] || '../svgs';
|
||||
const OUTPUT_DIR = process.argv[3] || '../src/lib/icons';
|
||||
|
||||
if (!fs.existsSync(OUTPUT_DIR)) fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
|
||||
function processSvg(svgContent) {
|
||||
// Strip XML/DOCTYPE
|
||||
let out = svgContent.replace(/<\?xml[\s\S]*?\?>\s*/i, '').replace(/<!DOCTYPE[\s\S]*?>\s*/i, '');
|
||||
|
||||
// Remove ALL comments
|
||||
out = out.replace(/<!--[\s\S]*?-->\s*/g, '');
|
||||
|
||||
// Remove <g id="icomoon-ignore"></g> with any whitespace between tags
|
||||
out = out.replace(/<g\s+id=(["'])icomoon-ignore\1\s*>\s*<\/g>\s*/gi, '');
|
||||
|
||||
// Ensure only width="100%" height="100%" on the <svg> tag
|
||||
out = out.replace(/<svg\b[^>]*>/i, (match) => {
|
||||
let tag = match
|
||||
.replace(/\s+(width|height)\s*=\s*"[^"]*"/gi, '')
|
||||
.replace(/\s+(width|height)\s*=\s*'[^']*'/gi, '');
|
||||
return tag.replace(/>$/, ' width="100%" height="100%">');
|
||||
});
|
||||
|
||||
// Prepend the single license comment
|
||||
out = '<!-- generated by icomoon.io - licensed Lindua icon -->\n' + out.replace(/^\s+/, '');
|
||||
return out;
|
||||
}
|
||||
|
||||
function convertSvgs(inputDir = INPUT_DIR, outputDir = OUTPUT_DIR) {
|
||||
if (!fs.existsSync(inputDir)) {
|
||||
console.warn(`Input directory not found: ${inputDir}`);
|
||||
return;
|
||||
}
|
||||
const files = fs.readdirSync(inputDir).filter((f) => f.toLowerCase().endsWith('.svg'));
|
||||
files.forEach((file) => {
|
||||
const src = path.join(inputDir, file);
|
||||
const dest = path.join(outputDir, file.replace(/\.svg$/i, '.svelte'));
|
||||
const svgContent = fs.readFileSync(src, 'utf8');
|
||||
const processed = processSvg(svgContent);
|
||||
fs.writeFileSync(dest, processed, 'utf8');
|
||||
console.log(`Converted: ${file} -> ${path.basename(dest)}`);
|
||||
});
|
||||
}
|
||||
|
||||
convertSvgs();
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import User from '$lib/icons/user.svelte';
|
||||
import { derived } from 'svelte/store';
|
||||
|
||||
// Create a derived store to extract breadcrumb data
|
||||
@@ -9,14 +8,6 @@
|
||||
|
||||
return segments.map((segment, index) => {
|
||||
let label = decodeURI(segment);
|
||||
|
||||
// if not uuid pattern, this is weird order of ops
|
||||
/*
|
||||
if (!segment.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)) {
|
||||
label = label.replace(/-/g, ' ')
|
||||
}
|
||||
*/
|
||||
|
||||
return {
|
||||
label,
|
||||
path: '/' + segments.slice(0, index + 1).join('/')
|
||||
@@ -40,10 +31,6 @@
|
||||
<a href={crumb.path}>{crumb.label}</a>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<User />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
import { grey400x225 } from '$lib/utils/staticImageSource';
|
||||
import Dialog from './Dialog.svelte';
|
||||
|
||||
const IMAGE_PROXY_URL = 'http://localhost:6081';
|
||||
const IMAGE_REFRESH_INTERVAL = 300;
|
||||
const IMAGE_REFRESH_INTERVAL = 1000;
|
||||
|
||||
let { imageUrl }: { imageUrl: string } = $props();
|
||||
let lastUpdated = new Date();
|
||||
@@ -17,10 +16,15 @@
|
||||
function loadBlob(blob: Blob) {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
imageSource = reader.result || '';
|
||||
const img = document.getElementById('live-image') as HTMLImageElement;
|
||||
if (!img) return;
|
||||
|
||||
imageSource = reader?.result || '';
|
||||
if (imageSource === '') {
|
||||
console.log("no image data, returning")
|
||||
return
|
||||
}
|
||||
|
||||
// set imageSource to image element
|
||||
img.src = `data:image/jpeg;base64; ${imageSource}`;
|
||||
lastUpdated = new Date();
|
||||
@@ -80,6 +84,7 @@
|
||||
{:else}
|
||||
<Dialog title="Live stream of printer" on:close={() => (fullscreen = false)}>
|
||||
<img style="width: 100%;" src={String(imageSource)} id="live-image" />
|
||||
<span>Last update {timestamp}s ago</span>
|
||||
</Dialog>
|
||||
|
||||
<img src={String(grey400x225)} />
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
</script>
|
||||
|
||||
<article class="main-container">
|
||||
<div class="header">
|
||||
<div class="title">
|
||||
<h2>{title}</h2>
|
||||
<slot name="top-left" />
|
||||
{#if title || description}
|
||||
<div class="header">
|
||||
<div class="title">
|
||||
<h2>{title}</h2>
|
||||
<slot name="top-left" />
|
||||
</div>
|
||||
<label>{description}</label>
|
||||
</div>
|
||||
<label>{description}</label>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<slot></slot>
|
||||
</article>
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
<script lang="ts">
|
||||
import External from '$lib/icons/external.svelte';
|
||||
import type { Site } from '$lib/interfaces/site.ts';
|
||||
|
||||
interface Site {
|
||||
title: string;
|
||||
image: string;
|
||||
link: string;
|
||||
background?: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
let { title, image, background, color, link }: Site = $props();
|
||||
|
||||
@@ -60,7 +54,7 @@
|
||||
h2,
|
||||
.link,
|
||||
.title {
|
||||
transition: all 0.2s ease-in-out;
|
||||
transition: all 0.18s ease-in-out;
|
||||
}
|
||||
|
||||
.title {
|
||||
@@ -90,7 +84,8 @@
|
||||
.image {
|
||||
height: 8rem;
|
||||
width: 100%;
|
||||
margin: 1.2rem 0;
|
||||
width: 8rem;
|
||||
margin: 1.2rem auto;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
@@ -105,6 +100,7 @@
|
||||
left: calc(100% - 2rem);
|
||||
top: 0;
|
||||
opacity: 0;
|
||||
fill: var(--color);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 750px) {
|
||||
@@ -130,7 +126,6 @@
|
||||
opacity: 1;
|
||||
left: calc(100% - 1rem);
|
||||
top: 2px;
|
||||
fill: var(--color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
71
src/lib/components/forms/FormSite.svelte
Normal file
@@ -0,0 +1,71 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import Input from '$lib/components/Input.svelte';
|
||||
import ColorInput from '$lib/components/ColorInput.svelte';
|
||||
import Id from '$lib/icons/id.svelte';
|
||||
import TextColor from '$lib/icons/text-color.svelte';
|
||||
import TextSize from '$lib/icons/text-size.svelte';
|
||||
import Window from '$lib/icons/window.svelte';
|
||||
import Quill from '$lib/icons/quill.svelte';
|
||||
import Tag from '$lib/icons/tag.svelte';
|
||||
import Link from '$lib/icons/link.svelte';
|
||||
import PaintRoller from '$lib/icons/paint-roller.svelte';
|
||||
import Bucket from '$lib/icons/bucket.svelte';
|
||||
import Picture from '$lib/icons/picture.svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const close = () => dispatch('close');
|
||||
</script>
|
||||
|
||||
<form method="POST">
|
||||
<div class="wrapper">
|
||||
<Input label="Name" icon={TextSize} placeholder="Website name" required />
|
||||
<Input label="Link" icon={Link} placeholder="https://site.tld" required />
|
||||
<Input label="Image" icon={Picture} placeholder="/images/site.png" required />
|
||||
<ColorInput label="Color" icon={PaintRoller} placeholder="#21ADF6" required />
|
||||
<ColorInput label="Background" icon={Bucket} />
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<button on:click={close} aria-disabled="false" type="button" tabindex="0"
|
||||
><span tabindex="-1">Cancel</span></button
|
||||
>
|
||||
<button class="affirmative" type="submit" tabindex="-1">
|
||||
<span tabindex="-1">Add connection</span>
|
||||
</button>
|
||||
</footer>
|
||||
</form>
|
||||
|
||||
<style lang="scss">
|
||||
form {
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 0.75rem 1.5rem;
|
||||
max-width: 100%;
|
||||
height: 2.5rem;
|
||||
width: auto;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
flex: 0 0 auto;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
|
||||
button {
|
||||
flex: unset;
|
||||
|
||||
span {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:global(form .wrapper div) {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
5
src/lib/icons/bucket.svelte
Normal file
@@ -0,0 +1,5 @@
|
||||
<!-- generated by icomoon.io - licensed Lindua icon -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 768 768" width="100%" height="100%">
|
||||
<path d="M671.9 418c0.6-9.2-2.8-18.1-9.3-24.6l-143.1-143.2c17.1-27.9 30.8-55.3 40.5-81.4 23.9-64.7 20.9-116.6-8.5-146.3-14.9-14.9-33.2-22.5-54.4-22.5v0c-31.1 0-68.8 16.6-115.2 50.8-10.9 8.1-22.4 17.2-34.5 27.3l-4.8-4.8c-6.5-6.5-15.5-9.9-24.6-9.3-9.2 0.6-17.6 5.1-23.3 12.3l-280.7 361.8c-19.7 25.4-17.4 61.7 5.3 84.5l194.1 194.1c12.4 12.4 28.8 18.7 45.3 18.7 13.8 0 27.6-4.4 39.2-13.4l361.7-280.7c7.3-5.7 11.8-14.1 12.3-23.3zM497.1 32v0c12.7 0 22.8 4.2 31.7 13.1 19.8 20 20.3 61 1.2 112.6-8.2 22.2-19.6 45.5-33.7 69.2l-126.3-126.1c47.6-39.6 93.5-68.8 127.1-68.8zM258.7 671.4l-194.1-194.1 30.6-39.5 202.9 202.9-39.4 30.7zM323.6 621l-208.6-208.6 208-268.1 136.8 136.7c-11.3 15.1-23.6 30.1-36.6 44.9-6.9-3.8-14.8-6-23.1-6-26.5 0-48 21.5-48 48s21.5 48 48 48c26.5 0 48-21.5 48-48 0-6.3-1.2-12.4-3.5-17.9 13.5-15.2 26.3-30.7 38.1-46.2l109 109.2-268.1 208zM416 368c0 8.8-7.2 16-16 16s-16-7.2-16-16 7.2-16 16-16 16 7.2 16 16z"></path>
|
||||
<path d="M730.3 584.5c-12.3-21.9-23.9-42.6-26.4-58.9-1.2-7.8-7.9-13.6-15.8-13.6s-14.6 5.8-15.8 13.6c-2.5 16.4-14.1 37-26.4 58.9-17.9 31.5-37.9 67.2-37.9 103.4 0 44.1 35.9 80 80 80s80-35.9 80-80c0-36.2-20-71.9-37.7-103.4zM688 735.9c-26.5 0-48-21.5-48-48 0-27.8 17.1-58.3 33.6-87.7 5-9 10-17.7 14.4-26.3 4.4 8.6 9.3 17.4 14.4 26.3 16.5 29.4 33.6 59.9 33.6 87.7 0 26.5-21.5 48-48 48z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
5
src/lib/icons/color-sampler.svelte
Normal file
@@ -0,0 +1,5 @@
|
||||
<!-- generated by icomoon.io - licensed Lindua icon -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 768 768" width="100%" height="100%">
|
||||
<path d="M480 160c-35.3 0-64 28.7-64 64s28.7 64 64 64 64-28.7 64-64-28.7-64-64-64zM480 256c-17.6 0-32-14.4-32-32s14.4-32 32-32 32 14.4 32 32-14.4 32-32 32z"></path>
|
||||
<path d="M764.8 390.4l-103.6-138.1-19.1-64.8c-0.5-1.7-1.2-3.2-2.1-4.5v-55c0-35.3-28.7-64-64-64h-512c-35.3 0-64 28.7-64 64v192c0 35.3 28.7 64 64 64h11.5l53.1 180.5c2.1 7 8.4 11.5 15.3 11.5 1.5 0 3-0.2 4.5-0.7l94.2-27.7 112.5 150c3.1 4.2 7.9 6.4 12.8 6.4 3.3 0 6.7-1 9.6-3.2l384-288c7.2-5.3 8.6-15.3 3.3-22.4zM64 128c0 0 0 0 0 0h512v192h-512v-192zM108.9 384h467.1c35.3 0 64-28.7 64-64v-26.5l28.1 95.6-513.3 151-45.9-156.1zM371.2 665.6l-95.7-127.6 417-122.7c4.1-1.2 7.5-4 9.5-7.7s2.5-8.1 1.3-12.2l-16.3-55.3 42.6 56.7-358.4 268.8z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 869 B |
9
src/lib/icons/id.svelte
Normal file
@@ -0,0 +1,9 @@
|
||||
<!-- generated by icomoon.io - licensed Lindua icon -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 768 768" height="100%" width="100%">
|
||||
<path d="M704 128h-640c-35.3 0-64 28.7-64 64v384c0 35.3 28.7 64 64 64h640c35.3 0 64-28.7 64-64v-384c0-35.3-28.7-64-64-64zM704 576h-640v-384h640v384c0.1 0 0 0 0 0z"></path>
|
||||
<path d="M416 256h256v32h-256v-32z"></path>
|
||||
<path d="M416 320h192v32h-192v-32z"></path>
|
||||
<path d="M416 384h256v32h-256v-32z"></path>
|
||||
<path d="M416 448h256v32h-256v-32z"></path>
|
||||
<path d="M298.3 402.5c-4.7-2.8-9.6-5.2-14.6-7.4 22.1-17.6 36.3-44.7 36.3-75.1 0-52.9-43.1-96-96-96s-96 43.1-96 96c0 30.4 14.2 57.5 36.3 75.1-5 2.1-9.9 4.6-14.6 7.4-20.2 11.9-37 29.3-51.4 53.3-4.5 7.4-2.2 17.1 5.1 21.8 36.6 23.1 76.1 34.4 120.6 34.4s84-11.3 120.6-34.5c7.3-4.7 9.6-14.3 5.1-21.8-14.4-23.9-31.2-41.3-51.4-53.2zM160 320c0-35.3 28.7-64 64-64s64 28.7 64 64-28.7 64-64 64-64-28.7-64-64zM224 480c-32.6 0-61.9-7-89.2-21.3 22.1-29.4 50.2-42.7 89.2-42.7s67.1 13.3 89.2 42.7c-27.3 14.3-56.6 21.3-89.2 21.3z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
5
src/lib/icons/paint-roller.svelte
Normal file
@@ -0,0 +1,5 @@
|
||||
<!-- generated by icomoon.io - licensed Lindua icon -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 768 768" width="100%" height="100%">
|
||||
<path d="M672 96h-32v-32c0-35.3-28.7-64-64-64h-416c-35.3 0-64 28.7-64 64v96c0 35.3 28.7 64 64 64h416c35.3 0 64-28.7 64-64v-32h32c17.6 0 32 14.4 32 32v96c0 17.6-14.4 32-32 32h-256c-35.3 0-64 28.7-64 64v1.6c-36.5 7.4-64 39.8-64 78.4v256c0 44.1 35.9 80 80 80s80-35.9 80-80v-256c0-38.6-27.5-71-64-78.4v-1.6c0-17.6 14.4-32 32-32h256c35.3 0 64-28.7 64-64v-96c0-35.3-28.7-64-64-64zM128 160v-96c0-17.6 14.4-32 32-32h416c17.6 0 32 14.4 32 32v32c0 17.6-14.4 32-32 32-14.2 0-21-7.9-32.8-23.1-13.4-17.3-31.7-40.9-71.2-40.9-33.3 0-49.3 20.6-62.2 37.2-13.3 17.1-21.8 26.8-41.8 26.8-26.8 0-45.1-9-62.8-17.6-21.2-10.4-43.1-21.1-71.4-9.1-18.1 7.6-29.4 16.4-35.7 27.5-6.1 10.7-6.1 21.4-6.1 30v1.2c0 17.6-14.4 32-32 32s-32-14.4-32-32zM224 160v-1.3c0-6.9 0.1-11 1.9-14.2 2.5-4.5 9.6-9.2 20.3-13.8 14.3-6 24.7-1.6 44.9 8.4 19 9.3 42.6 20.9 76.9 20.9 36.7 0 53.5-21.7 67.1-39.2 12.7-16.3 20.1-24.8 36.9-24.8 23.8 0 33.6 12.6 45.9 28.5 12.3 15.8 27.6 35.5 58.1 35.5h-352zM384 688c0 8.8-7.2 16-16 16s-16-7.2-16-16v-256c0-8.8 7.2-16 16-16s16 7.2 16 16v256z"></path>
|
||||
<path d="M159.9 301.8c-1.1-7.9-7.9-13.8-15.9-13.8s-14.8 5.9-15.9 13.8c-1.2 8.4-6.7 19.8-12.7 31.9-9.1 18.6-19.5 39.7-19.5 61.7 0 29 21.5 52.6 48 52.6s48-23.6 48-52.6c0-22-10.3-43.1-19.5-61.7-5.8-12.1-11.4-23.4-12.5-31.9zM144 416c-8.7 0-16-9.4-16-20.6 0-14.4 8.1-31.1 16-47.2 7.9 16.1 16 32.7 16 47.2 0 11.2-7.3 20.6-16 20.6z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
9
src/lib/icons/palette.svelte
Normal file
@@ -0,0 +1,9 @@
|
||||
<!-- generated by icomoon.io - licensed Lindua icon -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 768 768" width="100%" height="100%">
|
||||
<path d="M716.8 352.6c-25.9-18.5-57.8-29.9-85.9-40-15.3-5.5-29.8-10.7-40.8-16.1-9.8-4.8-13.1-8.1-14.1-9.3 0-1.9 0.3-2.9 0.4-3.4 2.7-2.2 12.2-5.2 18-7 27.1-8.6 77.6-24.6 77.6-100.8 0-45.8-29.3-85.2-82.5-111.1-43.8-21.3-103.3-33-167.4-33-102.3 0-203.8 29.1-278.4 79.8-94 63.9-143.7 158-143.7 272.3 0 51.2 9.4 99.5 28 143.5 18.1 42.8 44.4 80.4 78.1 111.9 33.2 31 72.7 55.1 117.2 71.6 44.8 16.6 93.4 25 144.6 25 50.4 0 99.9-6.7 147.1-20 47.2-13.2 89.7-32.3 126.5-56.8 81.6-54 126.5-129 126.5-211.2 0-39-17.2-71-51.2-95.4zM606.3 605.9c-63.2 42-150.1 66.1-238.3 66.1-179 0-304-118.4-304-288 0-93.4 38.9-167.2 115.6-219.4 64.3-43.7 152.7-68.8 242.5-68.8 104.2 0.1 185.9 35.3 185.9 80.2 0 29.4-8.3 32-32.9 39.8-12.4 3.9-26.5 8.4-38.6 18-11.2 8.9-24.5 25.5-24.5 54.1 0 24.2 12.9 44.3 38.3 59.7 16.7 10.1 37.3 17.4 59 25.2 57.2 20.4 94.7 36.7 94.7 75.1 0 60.1-34.7 116.2-97.7 158z"></path>
|
||||
<path d="M224 400c0-26.5-21.5-48-48-48s-48 21.5-48 48c0 26.5 21.5 48 48 48s48-21.5 48-48zM176 416c-8.8 0-16-7.2-16-16s7.2-16 16-16 16 7.2 16 16-7.2 16-16 16z"></path>
|
||||
<path d="M240 480c-26.5 0-48 21.5-48 48s21.5 48 48 48 48-21.5 48-48-21.5-48-48-48zM240 544c-8.8 0-16-7.2-16-16s7.2-16 16-16 16 7.2 16 16-7.2 16-16 16z"></path>
|
||||
<path d="M240 224c-26.5 0-48 21.5-48 48s21.5 48 48 48 48-21.5 48-48-21.5-48-48-48zM240 288c-8.8 0-16-7.2-16-16s7.2-16 16-16 16 7.2 16 16-7.2 16-16 16z"></path>
|
||||
<path d="M368 160c-26.5 0-48 21.5-48 48s21.5 48 48 48c26.5 0 48-21.5 48-48s-21.5-48-48-48zM368 224c-8.8 0-16-7.2-16-16s7.2-16 16-16 16 7.2 16 16-7.2 16-16 16z"></path>
|
||||
<path d="M542.1 417c-23.2-3.2-48.3 1.4-70.7 12.9-27.1 13.9-46.4 36.1-53 60.7-4.9 18.2-2.3 36.5 7.3 51.5 11.4 17.9 31.4 29.6 56.1 32.9 4.8 0.7 9.7 1 14.6 1 18.9 0 38.3-4.8 56.1-13.9 27.1-13.9 46.4-36.1 53-60.7 4.9-18.2 2.3-36.5-7.3-51.5-11.4-17.9-31.4-29.6-56.1-32.9zM574.7 493.1c-4.2 15.7-18 30.9-36.8 40.6-32.4 16.7-71.4 12.6-85.2-8.8-6-9.4-5.2-19.3-3.4-25.9 4.2-15.7 18-30.9 36.8-40.6 13.5-6.9 28.1-10.3 41.6-10.3 18.9 0 35.6 6.6 43.6 19.1 6 9.4 5.2 19.3 3.4 25.9z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
18
src/lib/icons/picture.svelte
Normal file
@@ -0,0 +1,18 @@
|
||||
<svg
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 768 768"
|
||||
>
|
||||
<path
|
||||
d="M704 0h-640c-35.3 0-64 28.7-64 64v640c0 35.3 28.7 64 64 64h640c35.3 0 64-28.7 64-64v-640c0-35.3-28.7-64-64-64zM704 704h-640l-0.1-640c0 0 0 0 0.1 0h640v640z"
|
||||
></path>
|
||||
<path
|
||||
d="M112 544h544c8.8 0 16-7.2 16-16v-416c0-8.8-7.2-16-16-16h-544c-8.8 0-16 7.2-16 16v416c0 8.8 7.2 16 16 16zM338.7 512l214.7-214.7c12.4-12.4 32.8-12.4 45.3 0l41.3 41.3v173.4h-301.3zM640 128v165.4l-18.7-18.7c-25-24.9-65.6-24.9-90.5 0l-237.3 237.3h-146.8l150.7-150.7c12.4-12.4 32.8-12.4 45.3 0l29.3 29.3 22.6-22.6-29.3-29.3c-25-24.9-65.6-24.9-90.5 0l-146.8 146.8v-357.5h512z"
|
||||
></path>
|
||||
<path
|
||||
d="M224 288c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zM224 192c17.6 0 32 14.4 32 32s-14.4 32-32 32-32-14.4-32-32 14.4-32 32-32z"
|
||||
></path>
|
||||
<path d="M256 608h256v32h-256v-32z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 903 B |
4
src/lib/icons/quill.svelte
Normal file
@@ -0,0 +1,4 @@
|
||||
<!-- generated by icomoon.io - licensed Lindua icon -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="100%" height="100%">
|
||||
<path d="M22.866 1.503c-0.178-0.312-0.506-0.503-0.866-0.503-2.319 0-4.803 0.503-7.178 1.45-2.463 0.984-4.803 2.45-6.769 4.237-2.163 1.969-3.866 4.294-5.066 6.906-1.319 2.881-1.987 6.047-1.987 9.406h2c0-2.288 0.328-4.319 0.869-6.113 1.131-1.9 2.109-1.906 3.856-1.919 1.644-0.012 3.687-0.028 6.244-1.688 2.822-1.831 5.731-5.359 8.894-10.778 0.181-0.309 0.184-0.691 0.003-1zM18.225 6.041c-1.266 0.112-3.844 0.525-6.012 2.050l-0.409 0.288 0.575 0.819 0.409-0.288c1.534-1.078 3.341-1.547 4.609-1.753-1.572 2.037-3.069 3.506-4.516 4.447-2.066 1.341-3.644 1.353-5.169 1.366-0.756 0.006-1.522 0.012-2.275 0.2 1.181-2.137 2.631-3.784 3.966-5 3.056-2.784 6.972-4.603 10.766-5.056-0.663 1.066-1.309 2.044-1.944 2.928z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 881 B |
6
src/lib/icons/tag.svelte
Normal file
@@ -0,0 +1,6 @@
|
||||
<!-- generated by icomoon.io - licensed Lindua icon -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="100%" height="100%">
|
||||
<path d="M24 5c0-2.756-2.244-5-5-5-2.416 0-4.434 1.722-4.9 4h-2.091c-0.534 0-1.034 0.209-1.413 0.588l-10 10c-0.781 0.781-0.781 2.050 0 2.831l5.991 5.988c0.391 0.391 0.903 0.584 1.416 0.584s1.025-0.194 1.416-0.584l10-10c0.378-0.378 0.588-0.881 0.588-1.413v-2.094c2.275-0.466 3.994-2.487 3.994-4.9zM16 9v0c0 0.55-0.45 1-1 1s-1-0.45-1-1 0.45-1 1-1c0 0 0 0 0 0 0.281 0.375 0.616 0.712 1 1zM18.003 11.994l-10 10-5.994-5.994 10-10h2.091c0.075 0.372 0.194 0.734 0.35 1.075-0.837 0.237-1.453 1.009-1.453 1.925 0 1.103 0.897 2 2 2s2-0.897 2-2c0-0.897-0.594-1.659-1.413-1.913-0.206-0.337-0.363-0.703-0.462-1.088h2.878c0.003 0 0.003 0.003 0.003 0.006v5.988zM20.003 8.872v-2.878c-0.006-1.1-0.906-1.994-2.003-1.994h-2.875c0.444-1.722 2.013-3 3.875-3 2.206 0 4 1.794 4 4 0 1.859-1.275 3.425-2.997 3.872z"></path>
|
||||
<path d="M4.793 16.5l4.707-4.707 0.707 0.707-4.707 4.707-0.707-0.707z"></path>
|
||||
<path d="M6.794 18.499l5.708-5.708 0.707 0.707-5.708 5.708-0.707-0.707z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
5
src/lib/icons/text-color.svelte
Normal file
@@ -0,0 +1,5 @@
|
||||
<!-- generated by icomoon.io - licensed Lindua icon -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 768 768" width="100%" height="100%">
|
||||
<path d="M64 736h640v32h-640v-32z"></path>
|
||||
<path d="M245.5 416h276.9l96.4 213.2 58.3-26.4-264-584c-5.1-11.4-16.5-18.8-29.1-18.8s-24 7.4-29.2 18.8l-264 584 58.3 26.4 96.4-213.2zM384 109.7l109.5 242.3h-219l109.5-242.3z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 393 B |
5
src/lib/icons/text-size.svelte
Normal file
@@ -0,0 +1,5 @@
|
||||
<!-- generated by icomoon.io - licensed Lindua icon -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 768 768" width="100%" height="100%">
|
||||
<path d="M768 64h-576v64h256v576h64v-576h256z"></path>
|
||||
<path d="M0 416h128v288h64v-288h128v-64h-320z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 277 B |
7
src/lib/icons/window.svelte
Normal file
@@ -0,0 +1,7 @@
|
||||
<!-- generated by icomoon.io - licensed Lindua icon -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="100%" height="100%">
|
||||
<path d="M22 1h-20c-1.103 0-2 0.897-2 2v18c0 1.103 0.897 2 2 2h20c1.103 0 2-0.897 2-2v-18c0-1.103-0.897-2-2-2zM22 3v4h-20v-4h20zM22 8v11h-20v-11h20zM22 21h-20v-1h20.003l-0.003 1c0.003 0 0 0 0 0z"></path>
|
||||
<path d="M5 5c0 0.552-0.448 1-1 1s-1-0.448-1-1c0-0.552 0.448-1 1-1s1 0.448 1 1z"></path>
|
||||
<path d="M8 5c0 0.552-0.448 1-1 1s-1-0.448-1-1c0-0.552 0.448-1 1-1s1 0.448 1 1z"></path>
|
||||
<path d="M11 5c0 0.552-0.448 1-1 1s-1-0.448-1-1c0-0.552 0.448-1 1-1s1 0.448 1 1z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 637 B |
9
src/lib/interfaces/site.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface Site {
|
||||
name: string;
|
||||
link: string;
|
||||
image: string;
|
||||
color: string;
|
||||
background: string;
|
||||
created?: number;
|
||||
updated?: number;
|
||||
}
|
||||
@@ -1,91 +1,5 @@
|
||||
import { currentFilament } from './filament';
|
||||
import pg from 'pg';
|
||||
import { env } from '$env/dynamic/private';
|
||||
import type { Filament } from '$lib/interfaces/printer';
|
||||
|
||||
const { Pool } = pg;
|
||||
|
||||
let pool: InstanceType<typeof Pool> | undefined;
|
||||
|
||||
async function initDb() {
|
||||
if (pool) return pool;
|
||||
|
||||
pool = new Pool({
|
||||
connectionString: env.DATABASE_URL // e.g. postgres://user:pass@localhost:5432/mydb
|
||||
});
|
||||
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
|
||||
for (const stmt of schemas) {
|
||||
await client.query(stmt);
|
||||
}
|
||||
|
||||
await client.query('COMMIT');
|
||||
} catch (err: any) {
|
||||
console.error('Failed to create tables:', err.message);
|
||||
await client.query('ROLLBACK');
|
||||
throw err;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
const schemas = [
|
||||
`
|
||||
CREATE TABLE IF NOT EXISTS filament (
|
||||
id SERIAL PRIMARY KEY,
|
||||
hex TEXT NOT NULL,
|
||||
color TEXT NOT NULL,
|
||||
material TEXT,
|
||||
weight REAL,
|
||||
link TEXT,
|
||||
added INTEGER, -- epoch seconds
|
||||
updated INTEGER, -- epoch seconds
|
||||
UNIQUE (hex, updated)
|
||||
)
|
||||
`
|
||||
];
|
||||
|
||||
async function seedData(pool: InstanceType<typeof Pool>) {
|
||||
const baseTimestamp = Math.floor(new Date('2025-04-01T05:47:01+00:00').getTime() / 1000);
|
||||
const filaments = currentFilament();
|
||||
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
|
||||
for (const f of filaments) {
|
||||
await client.query(
|
||||
`INSERT INTO filament (hex, color, material, weight, link, added, updated)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
ON CONFLICT (hex, updated) DO NOTHING`,
|
||||
[f.hex, f.color, f.material, f.weight, f.link, baseTimestamp, baseTimestamp]
|
||||
);
|
||||
}
|
||||
|
||||
await client.query('COMMIT');
|
||||
} catch (err: any) {
|
||||
console.error('Failed to seed data:', err.message);
|
||||
await client.query('ROLLBACK');
|
||||
throw err;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
// Export helper to use db elsewhere
|
||||
async function getDb() {
|
||||
if (pool) return pool;
|
||||
|
||||
const p = await initDb();
|
||||
await seedData(p);
|
||||
console.log('Database setup and seeding complete!');
|
||||
return p;
|
||||
}
|
||||
import { getDb } from '../database';
|
||||
|
||||
export async function getAllFilament(): Promise<Array<Filament>> {
|
||||
const pool = await getDb();
|
||||
100
src/lib/server/database/index.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import pg from 'pg';
|
||||
import { env } from '$env/dynamic/private';
|
||||
import type { Filament } from '$lib/interfaces/printer';
|
||||
|
||||
const { Pool } = pg;
|
||||
|
||||
let pool: InstanceType<typeof Pool> | undefined;
|
||||
|
||||
async function initDb() {
|
||||
if (pool) return pool;
|
||||
|
||||
pool = new Pool({
|
||||
connectionString: env.DATABASE_URL // e.g. postgres://user:pass@localhost:5432/mydb
|
||||
});
|
||||
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
|
||||
for (const stmt of schemas) {
|
||||
await client.query(stmt);
|
||||
}
|
||||
|
||||
await client.query('COMMIT');
|
||||
} catch (err: any) {
|
||||
console.error('Failed to create tables:', err.message);
|
||||
await client.query('ROLLBACK');
|
||||
throw err;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
const schemas = [
|
||||
`
|
||||
CREATE TABLE IF NOT EXISTS filament (
|
||||
id SERIAL PRIMARY KEY,
|
||||
hex TEXT NOT NULL,
|
||||
color TEXT NOT NULL,
|
||||
material TEXT,
|
||||
weight REAL,
|
||||
link TEXT,
|
||||
added INTEGER, -- epoch seconds
|
||||
updated INTEGER, -- epoch seconds
|
||||
UNIQUE (hex, updated)
|
||||
)
|
||||
`,
|
||||
`
|
||||
CREATE TABLE IF NOT EXISTS site (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
color TEXT NOT NULL,
|
||||
image TEXT,
|
||||
link TEXT,
|
||||
background TEXT,
|
||||
created INTEGER, -- epoch seconds
|
||||
updated INTEGER, -- epoch seconds
|
||||
UNIQUE (name, updated)
|
||||
)
|
||||
`
|
||||
];
|
||||
|
||||
async function seedFilament(pool: InstanceType<typeof Pool>) {
|
||||
const baseTimestamp = Math.floor(new Date('2025-04-01T05:47:01+00:00').getTime() / 1000);
|
||||
const filaments: Filament[] = []; // disables seed
|
||||
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
|
||||
for (const f of filaments) {
|
||||
await client.query(
|
||||
`INSERT INTO filament (hex, color, material, weight, link, added, updated)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
ON CONFLICT (hex, updated) DO NOTHING`,
|
||||
[f.hex, f.color, f.material, f.weight, f.link, baseTimestamp, baseTimestamp]
|
||||
);
|
||||
}
|
||||
|
||||
await client.query('COMMIT');
|
||||
} catch (err: any) {
|
||||
console.error('Failed to seed data:', err.message);
|
||||
await client.query('ROLLBACK');
|
||||
throw err;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
// Export helper to use db elsewhere
|
||||
export async function getDb() {
|
||||
if (pool) return pool;
|
||||
|
||||
const p = await initDb();
|
||||
await seedFilament(p);
|
||||
console.log('Database setup and seeding complete!');
|
||||
return p;
|
||||
}
|
||||
21
src/lib/server/database/sites.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { getDb } from '../database';
|
||||
import type { Site } from '$lib/interfaces/site';
|
||||
|
||||
export async function allSites(): Promise<Array<Site>> {
|
||||
const pool = await getDb();
|
||||
const query = 'SELECT * FROM site';
|
||||
const result = await pool.query(query);
|
||||
return result.rows || [];
|
||||
}
|
||||
|
||||
export async function addSite(site: Site) {
|
||||
const timestamp = Math.floor(new Date().getTime() / 1000);
|
||||
const query = `INSERT INTO site (name, link, image, color, background, updated)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING id`;
|
||||
const { name, link, image, color, background } = site;
|
||||
|
||||
const pool = await getDb();
|
||||
const result = await pool.query(query, [name, link, image, color, background, timestamp]);
|
||||
return { id: result.rows[0].id };
|
||||
}
|
||||
@@ -77,7 +77,7 @@ export async function fetchP1P(): Promise<PrinterState> {
|
||||
let hassStates = await fetchHassStates();
|
||||
|
||||
hassStates = hassStates.filter(
|
||||
(el: Entity) => el.attributes.friendly_name?.includes('P1P') === true
|
||||
(el: Entity) => el.attributes.friendly_name?.toLowerCase()?.includes('p1p') === true
|
||||
);
|
||||
return printerState(hassStates);
|
||||
} catch (error) {
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
if (counter + 1 >= colors.length) counter = 1;
|
||||
else counter += 1;
|
||||
|
||||
console.log(counter);
|
||||
|
||||
return {
|
||||
bgColor: colors[counter - 1][0],
|
||||
color: colors[counter - 1][1],
|
||||
@@ -99,7 +97,6 @@
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.4;
|
||||
line-height: 1.7;
|
||||
max-width: 80%;
|
||||
color: #333;
|
||||
|
||||
background-color: #fafafa; /* Subtle background to separate it from the rest */
|
||||
|
||||
@@ -13,7 +13,6 @@ const AVAILABLE_RESOURCES = [
|
||||
|
||||
export const load: PageServerLoad = async ({ params }) => {
|
||||
const { resource, uid } = params;
|
||||
console.log('PARAMS:', params);
|
||||
|
||||
if (!AVAILABLE_RESOURCES.includes(resource)) {
|
||||
return {
|
||||
|
||||
@@ -3,13 +3,11 @@ import { produce } from 'sveltekit-sse';
|
||||
|
||||
export function GET({ request }) {
|
||||
return produce(async function start({ emit }) {
|
||||
console.log('----- REQUEST -----');
|
||||
const url = new URL(request.url);
|
||||
const pod = url.searchParams.get('pod');
|
||||
const namespace = url.searchParams.get('namespace');
|
||||
const container = url.searchParams.get('container');
|
||||
|
||||
console.log('pod, namespace:', pod, namespace);
|
||||
const k8sLogs = createLogStream(pod, namespace, container);
|
||||
k8sLogs.start();
|
||||
const unsubscribe = k8sLogs.logEmitter.subscribe((msg: string) => {
|
||||
|
||||
@@ -23,7 +23,6 @@ async function fetchImage(src: string) {
|
||||
}
|
||||
|
||||
export const GET: RequestHandler = async ({ url }) => {
|
||||
console.log('GET');
|
||||
url.pathname = url.pathname.replace('/image/', '');
|
||||
|
||||
const res = await fetchImage(url.href);
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { fetchP1P } from '$lib/server/homeassistant';
|
||||
import { getAllFilament } from '$lib/server/database';
|
||||
import { getAllFilament } from '$lib/server/database/filament';
|
||||
import type { Filament } from '$lib/interfaces/printer';
|
||||
|
||||
interface PrinterState {
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
let open = $state(false);
|
||||
let timeLeftInterval: ReturnType<typeof setInterval>;
|
||||
|
||||
console.log("got data:", data)
|
||||
const rawFilament: Filament[] = data?.filament || [];
|
||||
let filament = $derived(
|
||||
rawFilament
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { addFilament, updateFilament } from '$lib/server/database';
|
||||
import { addFilament, updateFilament } from '$lib/server/database/filament';
|
||||
import { json } from '@sveltejs/kit';
|
||||
|
||||
export const PUT: RequestHandler = async ({ params, request }) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { getFilamentByColor } from '$lib/server/database';
|
||||
import { getFilamentByColor } from '$lib/server/database/filament';
|
||||
|
||||
export const load = async ({ params }: Parameters<PageServerLoad>[0]) => {
|
||||
let { id } = params;
|
||||
|
||||
49
src/routes/sites/+page.server.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { allSites, addSite } from '$lib/server/database/sites';
|
||||
import type { Site } from '$lib/interfaces/site';
|
||||
import type { Actions, PageServerLoad } from './$types';
|
||||
|
||||
export const load: PageServerLoad = async (): Promise<{ sites: Array<Site> }> => {
|
||||
let sites: Site[] = [];
|
||||
|
||||
try {
|
||||
sites = await allSites();
|
||||
console.log('got sites:', sites);
|
||||
} catch (error) {
|
||||
console.error('error while fetching sites server props, likely db issue');
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
return { sites };
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
default: async ({ request }) => {
|
||||
try {
|
||||
const formData = await request.formData();
|
||||
|
||||
// Extract values by input `name` attributes
|
||||
const name = formData.get('Name')?.toString().trim();
|
||||
const link = formData.get('Link')?.toString().trim();
|
||||
const image = formData.get('Image')?.toString().trim();
|
||||
const color = formData.get('Color')?.toString().trim();
|
||||
const background = formData.get('Background')?.toString().trim();
|
||||
|
||||
if (!name || !link || !image || !color || !background) {
|
||||
return { error: 'All fields are required!', success: false, statusCode: 400 };
|
||||
}
|
||||
|
||||
if (!name || !link) {
|
||||
return { error: 'name & link are required', success: false, statusCode: 400 };
|
||||
}
|
||||
|
||||
const site: Site = { name, link, image, color, background };
|
||||
await addSite(site);
|
||||
|
||||
return { success: true };
|
||||
} catch (err: unknown) {
|
||||
console.log(err);
|
||||
console.error('Failed to add site:', err.message);
|
||||
return { error: 'internal server error', success: false, statusCode: 500 };
|
||||
}
|
||||
}
|
||||
} satisfies Actions;
|
||||
@@ -1,103 +1,26 @@
|
||||
<script lang="ts">
|
||||
import PageHeader from '$lib/components/PageHeader.svelte';
|
||||
import Dialog from '$lib/components/Dialog.svelte';
|
||||
import Section from '$lib/components/Section.svelte';
|
||||
import FormSite from '$lib/components/forms/FormSite.svelte';
|
||||
import ThumbnailButton from '$lib/components/ThumbnailButton.svelte';
|
||||
import type { Site } from '$lib/interfaces/site.ts';
|
||||
|
||||
interface Site {
|
||||
title: string;
|
||||
image: string;
|
||||
link: string;
|
||||
background?: string;
|
||||
color?: string;
|
||||
}
|
||||
let { data }: { data: { site: Site } } = $props();
|
||||
let open = $state(false);
|
||||
|
||||
const sites: Array<Site> = [
|
||||
{
|
||||
title: 'Grafana',
|
||||
image: '/images/grafana.png',
|
||||
link: 'https://grafana.schleppe.cloud',
|
||||
background: '#F5E3DC',
|
||||
color: '#F05A24'
|
||||
},
|
||||
{
|
||||
title: 'Prometheus',
|
||||
image: '/images/prometheus.svg',
|
||||
link: 'http://prome.schleppe:9090',
|
||||
background: '#262221',
|
||||
color: '#F3BFA2'
|
||||
},
|
||||
{
|
||||
title: 'Traefik',
|
||||
image: '/images/traefik.png',
|
||||
link: 'https://grafana.schleppe.cloud',
|
||||
background: '#30A4C2',
|
||||
color: 'white'
|
||||
},
|
||||
{
|
||||
title: 'Kibana',
|
||||
image: '/images/kibana.svg',
|
||||
link: 'https://kibana.schleppe.cloud',
|
||||
background: '#f6cfdd',
|
||||
color: '#401C26'
|
||||
},
|
||||
{
|
||||
title: 'HASS',
|
||||
image: '/images/hass.png',
|
||||
link: 'http://homeassistant.schleppe:8123',
|
||||
background: '#1ABCF2',
|
||||
color: 'white'
|
||||
},
|
||||
{
|
||||
title: 'Vault',
|
||||
image: '/images/vault.svg',
|
||||
link: 'http://vault.schleppe:8200',
|
||||
background: 'white',
|
||||
color: 'black'
|
||||
},
|
||||
{
|
||||
title: 'Drone',
|
||||
image: '/images/drone.png',
|
||||
link: 'https://drone.schleppe.cloud',
|
||||
background: '#D8E2F0',
|
||||
color: '#1E375A'
|
||||
},
|
||||
{
|
||||
title: 'Immich',
|
||||
image: '/images/immich.png',
|
||||
link: 'http://immich.schleppe:2283',
|
||||
background: 'white',
|
||||
color: 'black'
|
||||
},
|
||||
{
|
||||
title: 'Wiki',
|
||||
image: '/images/xwiki.png',
|
||||
link: 'https://wiki.schleppe.cloud',
|
||||
background: 'white',
|
||||
color: 'black'
|
||||
},
|
||||
{
|
||||
title: 'Gitea',
|
||||
image: '/images/gitea.png',
|
||||
link: 'https://git.schleppe.cloud',
|
||||
background: '#E6E7D7',
|
||||
color: '#609925'
|
||||
},
|
||||
{
|
||||
title: 'PBS',
|
||||
image: '/images/proxmox.png',
|
||||
link: 'https://clio.schleppe:8007',
|
||||
background: '#EDE1D2',
|
||||
color: '#E66B00'
|
||||
}
|
||||
];
|
||||
const { sites } = data;
|
||||
</script>
|
||||
|
||||
<PageHeader>Sites</PageHeader>
|
||||
<PageHeader>Sites
|
||||
|
||||
<button class="add-site-btn affirmative" on:click={() => (open = true)}><span>Add new site</span></button>
|
||||
</PageHeader>
|
||||
|
||||
<div class="section-wrapper">
|
||||
{#each sites as site}
|
||||
{#each sites as site (site)}
|
||||
<ThumbnailButton
|
||||
title={site.title}
|
||||
title={site.name}
|
||||
image={site.image}
|
||||
background={site.background}
|
||||
color={site.color}
|
||||
@@ -106,27 +29,15 @@
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="section-wrapper full-width">
|
||||
<Section
|
||||
title="Expose HTTP traffic"
|
||||
description="You can reach your Application on a specific Port you configure, redirecting all your domains to it. You can make it Private by disabling HTTP traffic."
|
||||
/>
|
||||
|
||||
<Section
|
||||
title="IP restrictions"
|
||||
description="Restrict or block access to your application based on specific IP addresses or CIDR blocks."
|
||||
/>
|
||||
|
||||
<Section
|
||||
title="Expose HTTP traffic"
|
||||
description="You can reach your Application on a specific Port you configure, redirecting all your domains to it. You can make it Private by disabling HTTP traffic."
|
||||
/>
|
||||
|
||||
<Section
|
||||
title="Connected services"
|
||||
description="Connected services can communicate with your application over the private network."
|
||||
/>
|
||||
</div>
|
||||
{#if open}
|
||||
<Dialog
|
||||
on:close={() => (open = false)}
|
||||
title="Add new site"
|
||||
description="You can select anything deployed in <b>Belgium (europe-west1) datacenter</b> and create an internal connection with your service."
|
||||
>
|
||||
<FormSite on:close={() => (open = false)} />
|
||||
</Dialog>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.section-wrapper {
|
||||
@@ -149,4 +60,10 @@
|
||||
margin-top: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
:global(button.add-site-btn) {
|
||||
font-size: 1.2rem;
|
||||
float: right;
|
||||
height: 2.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
BIN
static/images/bitwarden.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
37
static/images/brew.svg
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="200.000000pt" height="200.000000pt" viewBox="0 0 200.000000 200.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)"
|
||||
fill="#F4400E" stroke="none">
|
||||
<path d="M956 1798 c-9 -12 -16 -39 -16 -60 0 -23 -5 -38 -12 -39 -166 -9
|
||||
-356 -117 -473 -269 -50 -65 -121 -206 -130 -257 -3 -18 -9 -42 -13 -55 -5
|
||||
-13 -10 -45 -11 -73 -2 -27 -5 -53 -7 -56 -5 -9 31 -40 53 -44 10 -2 30 -4 44
|
||||
-4 14 -1 26 -7 26 -13 1 -92 68 -295 107 -327 15 -12 69 -12 115 0 8 3 20 -14
|
||||
31 -47 40 -115 127 -235 230 -317 83 -66 109 -70 166 -28 123 90 227 227 264
|
||||
349 l13 43 59 -7 c42 -5 62 -3 74 7 28 23 86 173 98 255 12 78 17 93 31 88 7
|
||||
-3 31 0 53 5 35 10 40 15 44 46 2 19 -2 63 -10 97 -7 35 -15 74 -18 88 -3 14
|
||||
-27 70 -54 125 -100 205 -282 347 -492 386 l-68 12 0 42 c0 28 -6 47 -18 58
|
||||
-26 24 -68 21 -86 -5z m159 -223 c59 -10 173 -66 233 -114 109 -87 194 -227
|
||||
227 -375 l6 -29 -50 6 c-106 13 -244 77 -322 151 -48 45 -102 117 -135 179
|
||||
-32 62 -64 87 -96 74 -9 -3 -35 -40 -58 -83 -41 -75 -173 -224 -198 -224 -6 0
|
||||
-12 -4 -14 -8 -3 -10 -109 -61 -142 -69 -61 -15 -136 -26 -142 -20 -12 11 44
|
||||
166 82 228 88 145 239 258 380 283 15 3 28 7 31 9 6 6 152 1 198 -8z m-56
|
||||
-371 c29 -36 79 -86 112 -111 50 -39 59 -51 59 -77 0 -43 -28 -120 -66 -182
|
||||
-30 -47 -149 -174 -165 -174 -4 0 -38 31 -76 69 -80 80 -140 187 -149 266 l-6
|
||||
50 60 47 c33 26 84 76 113 112 29 36 55 66 59 66 4 0 30 -30 59 -66z m405
|
||||
-292 c-15 -101 -52 -205 -70 -198 -7 3 -32 10 -56 16 -81 22 -81 23 -49 88 16
|
||||
31 37 82 45 113 l17 56 59 -19 60 -20 -6 -36z m-801 41 c10 -55 16 -72 46
|
||||
-132 27 -55 28 -62 13 -70 -14 -7 -119 -41 -128 -41 -4 0 -24 64 -22 70 1 3
|
||||
-4 14 -10 26 -6 11 -14 34 -17 50 -19 101 -20 99 29 108 16 3 31 8 34 11 3 3
|
||||
15 5 28 5 16 0 23 -7 27 -27z m198 -330 c50 -48 117 -93 138 -93 23 0 68 30
|
||||
128 84 43 39 56 46 78 40 23 -6 26 -10 20 -33 -8 -32 -59 -144 -77 -168 -28
|
||||
-36 -140 -143 -150 -143 -6 0 -43 33 -82 73 -100 102 -171 251 -127 269 27 11
|
||||
31 9 72 -29z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 41 KiB |
23
static/images/influxdb.svg
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="InfluxData_Symbol_Only" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px" y="0px" width="900px" height="900px" viewBox="-173 -143 900 900" style="enable-background:new -173 -143 900 900;"
|
||||
xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;}
|
||||
.st1{fill:#22ADF6;}
|
||||
</style>
|
||||
<rect id="Background" x="-173" y="-143" class="st0" width="900" height="900"/>
|
||||
<path id="Cuboctahedron" class="st1" d="M694.1,394.9l-81-352.7C608.5,22.9,591,3.6,571.7-2L201.5-116.2c-4.6-1.8-10.1-1.8-15.7-1.8
|
||||
c-15.7,0-32.2,6.4-43.3,15.7l-265.2,246.8c-14.7,12.9-22.1,38.7-17.5,57.1l86.6,377.6c4.6,19.3,22.1,38.7,41.4,44.2l346.2,106.8
|
||||
c4.6,1.8,10.1,1.8,15.7,1.8c15.7,0,32.2-6.4,43.3-15.7L676.6,453C691.4,439.2,698.7,414.3,694.1,394.9z M240.2-32.4l254.1,78.3
|
||||
c10.1,2.8,10.1,7.4,0,10.1L360.8,86.4c-10.1,2.8-23.9-1.8-31.3-9.2l-93-100.4C228.2-31.4,230-35.1,240.2-32.4z M398.5,423.5
|
||||
c2.8,10.1-3.7,15.7-13.8,12.9l-274.4-84.7c-10.1-2.8-12-11.1-4.6-18.4L315.7,138c7.4-7.4,15.7-4.6,18.4,5.5L398.5,423.5z
|
||||
M-53.6,174.8L169.3-32.4c7.4-7.4,19.3-6.4,26.7,0.9L307.4,89.2c7.4,7.4,6.4,19.3-0.9,26.7L83.6,323.1c-7.4,7.4-19.3,6.4-26.7-0.9
|
||||
L-54.5,201.6C-61.9,193.3-60.9,181.3-53.6,174.8z M0.8,503.6l-58.9-258.8c-2.8-10.1,1.8-12,8.3-4.6l93,100.4
|
||||
c7.4,7.4,10.1,22.1,7.4,32.2L10,503.6C7.2,513.7,2.6,513.7,0.8,503.6z M326.7,654.6l-291-89.3c-10.1-2.8-15.7-13.8-12.9-23.9
|
||||
l48.8-156.6c2.8-10.1,13.8-15.7,23.9-12.9l291,89.3c10.1,2.8,15.7,13.8,12.9,23.9l-48.8,156.6C347,651.9,336.9,657.4,326.7,654.6z
|
||||
M584.5,442.8L390.3,623.3c-7.4,7.4-11,4.6-8.3-5.5L422.5,487c2.8-10.1,13.8-20.3,23.9-22.1l133.5-30.4
|
||||
C590.1,431.8,591.9,436.4,584.5,442.8z M605.7,404.2L445.5,441c-10.1,2.8-20.3-3.7-23-13.8l-68.1-296.5c-2.8-10.1,3.7-20.3,13.8-23
|
||||
l160.2-36.8c10.1-2.8,20.3,3.7,23,13.8l68.1,296.5C622.3,392.2,615.9,402.3,605.7,404.2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
BIN
static/images/ollama.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
static/images/part-db.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
static/images/request.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
static/images/sonarr.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
static/images/spoolman.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
static/images/tautulli.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
@@ -1,4 +1,5 @@
|
||||
FROM debian:bullseye
|
||||
# --- Stage 1: Compile varnish w/ vmods ---
|
||||
FROM debian:bullseye-slim AS builder
|
||||
|
||||
# Install dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
@@ -21,9 +22,31 @@ RUN git clone https://github.com/varnish/libvmod-digest.git /opt/libvmod-digest
|
||||
./configure VARNISHSRC=/usr/include/varnish && \
|
||||
make && make install
|
||||
|
||||
COPY . /etc/varnish/
|
||||
# --- Stage 2: Runtime image ---
|
||||
FROM debian:bullseye-slim
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl gnupg apt-transport-https lsb-release && \
|
||||
curl -fsSL https://packagecloud.io/varnishcache/varnish73/gpgkey | gpg --dearmor -o /usr/share/keyrings/varnish.gpg && \
|
||||
echo "deb [signed-by=/usr/share/keyrings/varnish.gpg] https://packagecloud.io/varnishcache/varnish73/debian/ $(lsb_release -cs) main" \
|
||||
> /etc/apt/sources.list.d/varnish.list && \
|
||||
apt-get update && apt-get install -y varnish libmhash2 && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy VMOD from builder
|
||||
COPY --from=builder /usr/lib/varnish/vmods/* /usr/lib/varnish/vmods/
|
||||
|
||||
# Copy gomplate from official image
|
||||
COPY --from=hairyhenderson/gomplate:stable /gomplate /bin/gomplate
|
||||
|
||||
# Copy configuration
|
||||
COPY default.vcl.tmpl /etc/varnish/
|
||||
COPY *.vcl /etc/varnish/
|
||||
COPY includes /etc/varnish/includes
|
||||
|
||||
# Create entrypoint script
|
||||
COPY docker-entrypoint.sh /usr/local/bin/
|
||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
|
||||
EXPOSE 6081
|
||||
|
||||
CMD ["varnishd", "-F", "-f", "/etc/varnish/default.vcl", "-a", ":6081", "-s", "malloc,512m"]
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
||||
|
||||
@@ -2,21 +2,20 @@ vcl 4.0;
|
||||
|
||||
import std;
|
||||
import digest;
|
||||
import directors;
|
||||
|
||||
# include "handlers/ttl-override-handler.vcl";
|
||||
include "includes/x-cache-header.vcl";
|
||||
|
||||
include "vcl_deliver.vcl";
|
||||
|
||||
# Define backend pointing to Home Assistant IP
|
||||
backend hass_backend {
|
||||
.host = "10.0.0.82";
|
||||
.host = "{{ getenv `IMAGE_HOST` `homeassistant.local` }}";
|
||||
.port = "8123";
|
||||
}
|
||||
|
||||
backend app_frontend {
|
||||
.host = "host.docker.internal";
|
||||
.port = "5173";
|
||||
.host = "{{ getenv `PROXY_HOST` `localhost` }}";
|
||||
.port = "3000";
|
||||
}
|
||||
|
||||
sub vcl_recv {
|
||||
8
varnish/docker-entrypoint.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# Generate VCL at runtime
|
||||
gomplate -f /etc/varnish/default.vcl.tmpl -o /etc/varnish/default.vcl
|
||||
|
||||
# Execute startup CMD
|
||||
exec varnishd -F -f /etc/varnish/default.vcl -a :6081 -s malloc,512m
|
||||