env: deploy script & minor ui changes

This commit is contained in:
Anton Shubin 2024-01-31 13:39:46 +08:00
parent 18b11cb413
commit 7b9799ac11
20 changed files with 181 additions and 112 deletions

3
apps/front/.env.example Normal file
View File

@ -0,0 +1,3 @@
SSH_TO_SERVER=user@server
PATH_APPS=~/apps
HOSTNAME_GB_APP=gb.example.com

20
apps/front/Makefile Normal file
View File

@ -0,0 +1,20 @@
.PHONY: *
-include .env.prod
export $(shell sed 's/=.*//' .env.prod)
# Replace env variables in index.html
build:
pnpm run build
# Copy files to server
copy-files:
rsync -avhzru -P -e ssh --include-from='prod.deploy.files.txt' --exclude '*' ./ $(SSH_TO_SERVER):$(PATH_APPS)/gb
# Deploy Immich
up-gb:
ssh homelab "cd $(PATH_APPS)/gb && docker compose --env-file ../.env --env-file .env.prod -f prod.compose.yml up -d"
deploy:
make build
make copy-files
make up-gb

View File

@ -11,15 +11,15 @@
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"test": "vitest", "test": "vitest",
"lint": "prettier --check . && eslint .", "lint": "prettier --check . && eslint .",
"format": "prettier --write ." "format": "prettier --write .",
"prod:deploy": "make deploy"
}, },
"dependencies": { "dependencies": {
"chart.js": "^4.4.1", "chart.js": "^4.4.1",
"pnpm": "^8.15.0",
"svelte-chartjs": "^3.1.2" "svelte-chartjs": "^3.1.2"
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/kit": "^2.0.0", "@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0",
"@tailwindcss/forms": "^0.5.7", "@tailwindcss/forms": "^0.5.7",

View File

@ -8,17 +8,14 @@ dependencies:
chart.js: chart.js:
specifier: ^4.4.1 specifier: ^4.4.1
version: 4.4.1 version: 4.4.1
pnpm:
specifier: ^8.15.0
version: 8.15.0
svelte-chartjs: svelte-chartjs:
specifier: ^3.1.2 specifier: ^3.1.2
version: 3.1.2(chart.js@4.4.1)(svelte@4.2.9) version: 3.1.2(chart.js@4.4.1)(svelte@4.2.9)
devDependencies: devDependencies:
'@sveltejs/adapter-auto': '@sveltejs/adapter-static':
specifier: ^3.0.0 specifier: ^3.0.1
version: 3.1.1(@sveltejs/kit@2.4.3) version: 3.0.1(@sveltejs/kit@2.4.3)
'@sveltejs/kit': '@sveltejs/kit':
specifier: ^2.0.0 specifier: ^2.0.0
version: 2.4.3(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.9)(vite@5.0.12) version: 2.4.3(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.9)(vite@5.0.12)
@ -551,13 +548,12 @@ packages:
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
dev: true dev: true
/@sveltejs/adapter-auto@3.1.1(@sveltejs/kit@2.4.3): /@sveltejs/adapter-static@3.0.1(@sveltejs/kit@2.4.3):
resolution: {integrity: sha512-6LeZft2Fo/4HfmLBi5CucMYmgRxgcETweQl/yQoZo/895K3S9YWYN4Sfm/IhwlIpbJp3QNvhKmwCHbsqQNYQpw==} resolution: {integrity: sha512-6lMvf7xYEJ+oGeR5L8DFJJrowkefTK6ZgA4JiMqoClMkKq0s6yvsd3FZfCFvX1fQ0tpCD7fkuRVHsnUVgsHyNg==}
peerDependencies: peerDependencies:
'@sveltejs/kit': ^2.0.0 '@sveltejs/kit': ^2.0.0
dependencies: dependencies:
'@sveltejs/kit': 2.4.3(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.9)(vite@5.0.12) '@sveltejs/kit': 2.4.3(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.9)(vite@5.0.12)
import-meta-resolve: 4.0.0
dev: true dev: true
/@sveltejs/kit@2.4.3(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.9)(vite@5.0.12): /@sveltejs/kit@2.4.3(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.9)(vite@5.0.12):
@ -2068,12 +2064,6 @@ packages:
pathe: 1.1.2 pathe: 1.1.2
dev: true dev: true
/pnpm@8.15.0:
resolution: {integrity: sha512-6kVRfVKF0SPqwCw3k1Bfof1tqQovxg0ejZ4MHpKSiG7Pr/UT8GK50cyAIbuGFQM7GsW+o7LuP8FlptcDS75rAw==}
engines: {node: '>=16.14'}
hasBin: true
dev: false
/postcss-import@15.1.0(postcss@8.4.33): /postcss-import@15.1.0(postcss@8.4.33):
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}

View File

@ -0,0 +1,31 @@
version: '3.8'
networks:
proxy:
external: true
services:
gb-app:
container_name: gb-app
image: nginx:alpine
volumes:
- ./build:/usr/share/nginx/html:ro
restart: unless-stopped
networks:
- proxy
- default
deploy:
resources:
limits:
memory: 256M
cpus: '0.25'
security_opt:
- no-new-privileges:true
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.routers.gb-app.rule=Host(`${HOSTNAME_GB_APP}`)"
- "traefik.http.routers.gb-app.entrypoints=websecure"
- "traefik.http.routers.gb-app.tls=true"
- "traefik.http.routers.gb-app.tls.certresolver=myresolver"
- "traefik.http.services.gb-app.loadbalancer.server.port=80"

View File

@ -0,0 +1,3 @@
build/***
prod.compose.yml
.env.prod

View File

@ -18,7 +18,7 @@
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [ datasets: [
{ {
label: 'Motion detection', label: 'Motion detection, # of people',
fill: true, fill: true,
lineTension: 0.3, lineTension: 0.3,
backgroundColor: 'rgba(225, 204,230, .3)', backgroundColor: 'rgba(225, 204,230, .3)',
@ -36,7 +36,7 @@
pointHoverBorderWidth: 2, pointHoverBorderWidth: 2,
pointRadius: 1, pointRadius: 1,
pointHitRadius: 10, pointHitRadius: 10,
data: [65, 59, 80, 81, 56, 55, 40] data: [900, 850, 1000, 1200, 1500, 1300, 1100]
} }
] ]
}; };

View File

@ -18,7 +18,7 @@
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [ datasets: [
{ {
label: 'Power consumption', label: 'Power consumption, kWh',
fill: true, fill: true,
lineTension: 0.3, lineTension: 0.3,
backgroundColor: 'rgba(225, 204,230, .3)', backgroundColor: 'rgba(225, 204,230, .3)',
@ -36,7 +36,7 @@
pointHoverBorderWidth: 2, pointHoverBorderWidth: 2,
pointRadius: 1, pointRadius: 1,
pointHitRadius: 10, pointHitRadius: 10,
data: [65, 59, 80, 81, 56, 55, 40] data: [1200, 1100, 1850, 1500, 1600, 1550, 1450]
} }
] ]
}; };

View File

@ -18,7 +18,7 @@
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [ datasets: [
{ {
label: 'Voltage', label: 'Voltage, V',
fill: true, fill: true,
lineTension: 0.3, lineTension: 0.3,
backgroundColor: 'rgba(225, 204,230, .3)', backgroundColor: 'rgba(225, 204,230, .3)',
@ -36,7 +36,7 @@
pointHoverBorderWidth: 2, pointHoverBorderWidth: 2,
pointRadius: 1, pointRadius: 1,
pointHitRadius: 10, pointHitRadius: 10,
data: [65, 59, 80, 81, 56, 55, 40] data: [224, 223, 221, 220, 222, 224, 223]
} }
] ]
}; };

View File

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import Badge from '$lib/client/components/badgeStatus.svelte'; import Badge from '$lib/client/components/badgeStatus.svelte';
export let title = 'Failed devices'; export let title = 'Faulty devices';
export let description = export let description =
'Lamp is not working / too much voltage / no signal from device for a long time.'; 'Lamp is not working / too much voltage / no signal from device for a long time.';
export let link = '#'; export let link = '#';

View File

@ -0,0 +1 @@
export const prerender = true;

View File

@ -128,10 +128,8 @@
</ul> </ul>
</div> </div>
<Table />
<ul <ul
class="grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 xl:gap-x-8 class="grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 xl:gap-x-8
*:overflow-hidden *:rounded-xl *:border *:border-gray-200 *:bg-white" *:overflow-hidden *:rounded-xl *:border *:border-gray-200 *:bg-white"
> >
<li> <li>
@ -144,4 +142,6 @@
<ChartVoltage /> <ChartVoltage />
</li> </li>
</ul> </ul>
<Table />
</section> </section>

View File

@ -0,0 +1,3 @@
<h1 class="text-3xl font-bold text-center text-gray-900">
To be discussed...
</h1>

View File

@ -1,94 +1,103 @@
<script lang="ts"> <script lang="ts">
import Map from '$lib/client/components/map.svelte'; import Map from '$lib/client/components/map.svelte';
import Table from '$lib/client/components/table.svelte'; import Table from '$lib/client/components/table.svelte';
import ToggleSwitch from '$lib/client/components/toggle.svelte'; import ToggleSwitch from '$lib/client/components/toggle.svelte';
import { import {
IconArrowUturnLeft, IconArrowUturnLeft,
IconArrowUturnRight, IconArrowUturnRight,
IconCheck, IconCheck,
IconCursorArrowRays, IconCursorArrowRays,
IconXMark IconXMark
} from '../../lib/client/components/icons'; } from '../../lib/client/components/icons';
let initialValueForSwitch: boolean = true; let initialValueForSwitch: boolean = true;
</script> </script>
<section class="space-y-4 lg:space-y-8"> <section class="space-y-4 lg:space-y-8">
<div class="flex relative overflow-hidden rounded-xl border border-gray-200 bg-white"> <h1 class="text-2xl font-semibold text-right text-gray-400">Create new zones with edit buttons:</h1>
<Map /> <div class="flex relative overflow-hidden rounded-xl border border-gray-200 bg-white">
<div class="top-2 absolute bg-white block right-2 rounded-xl shadow z-10">
<div class="flex flex-col gap-4 p-2">
<div class="flex flex-col gap-0.5">
<button type="button" class="btn btn-primary">
<IconArrowUturnLeft />
</button>
<button type="button" class="btn btn-primary"><IconArrowUturnRight /></button> <Map/>
</div>
<button type="button" class="btn btn-primary"><IconCursorArrowRays /></button> <div class="top-2 absolute bg-white block right-2 rounded-xl shadow z-10">
<div class="flex flex-col gap-4 p-2">
<div class="flex flex-col gap-0.5">
<button class="btn btn-primary" type="button">
<IconArrowUturnLeft/>
</button>
<div class="flex-col gap-0.5 hidden"> <button class="btn btn-primary" type="button">
<button type="button" class="btn btn-primary"><IconCheck /></button> <IconArrowUturnRight/>
<button type="button" class="btn btn-primary"> </button>
<IconXMark /> </div>
</button>
</div>
<div class="flex flex-col gap-0.5"> <button class="btn btn-primary" type="button">
<button type="button" class="btn btn-primary"> <IconCursorArrowRays/>
<span class="size-6 border-2 border-white rounded block"></span> </button>
</button>
<button type="button" class="btn btn-primary"> <div class="flex-col gap-0.5 hidden">
<span class="size-6 border-2 border-white rounded-full block"></span> <button class="btn btn-primary" type="button">
</button> <IconCheck/>
</div> </button>
</div> <button class="btn btn-primary" type="button">
</div> <IconXMark/>
</div> </button>
<div class="mx-auto max-w-6xl"> </div>
<div class="flex flex-col">
<div class="min-w-full overflow-hidden overflow-x-auto align-middle shadow sm:rounded-lg"> <div class="flex flex-col gap-0.5">
<table class="min-w-full divide-y divide-gray-200"> <button class="btn btn-primary" type="button">
<thead> <span class="size-6 border-2 border-white rounded block"></span>
<tr </button>
class="*:whitespace-nowrap *:bg-gray-50 *:px-6 *:py-3 text-sm font-medium text-gray-900" <button class="btn btn-primary" type="button">
> <span class="size-6 border-2 border-white rounded-full block"></span>
<th class="text-left" scope="col">Zone name</th> </button>
<th class="text-right" scope="col">Gateways</th> </div>
<th class="text-right" scope="col">Lamps</th> </div>
<th class="text-right" scope="col">Sensors</th> </div>
<th class="text-center" scope="col">Schedule</th> </div>
<th class="text-right" scope="col">On/Off</th> <div class="mx-auto max-w-6xl">
<th class="text-right" scope="col">Actions</th> <div class="flex flex-col">
</tr> <div class="min-w-full overflow-hidden overflow-x-auto align-middle shadow sm:rounded-lg">
</thead> <table class="min-w-full divide-y divide-gray-200">
<tbody class="divide-y divide-gray-200 bg-white"> <thead>
{#each { length: 10 } as item, index} <tr
<tr class="bg-white text-sm *:px-6 *:py-4"> class="*:whitespace-nowrap *:bg-gray-50 *:px-6 *:py-3 text-sm font-medium text-gray-900"
<td class="text-gray-900"> >
Zone #{index + 1} <th class="text-left" scope="col">Zone name</th>
</td> <th class="text-right" scope="col">Gateways</th>
<td class="text-right">12</td> <th class="text-right" scope="col">Lamps</th>
<td class="text-right"> 20 </td> <th class="text-right" scope="col">Sensors</th>
<td class="text-right">40</td> <th class="text-center" scope="col">Schedule</th>
<td class="text-center"> <a href="/" class="text-purple-800">View</a></td> <th class="text-right" scope="col">On/Off</th>
<td class="text-right"> <th class="text-right" scope="col">Actions</th>
<ToggleSwitch {initialValueForSwitch} /> </tr>
</td> </thead>
<td class="text-right"> <tbody class="divide-y divide-gray-200 bg-white">
<div class="flex gap-4 items-center justify-end"> {#each {length: 10} as item, index}
<a href="/" class="text-purple-800">Edit</a> <tr class="bg-white text-sm *:px-6 *:py-4">
<a href="/" class="text-red-600">Delete</a> <td class="text-gray-900">
</div> Zone #{index + 1}
</td> </td>
</tr> <td class="text-right">12</td>
{/each} <td class="text-right"> 20</td>
<!-- More transactions... --> <td class="text-right">40</td>
</tbody> <td class="text-center"><a href="/" class="text-purple-800">View</a></td>
</table> <td class="text-right">
</div> <ToggleSwitch {initialValueForSwitch}/>
</div> </td>
</div> <td class="text-right">
<div class="flex gap-4 items-center justify-end">
<a href="/" class="text-purple-800">Edit</a>
<a href="/" class="text-red-600">Delete</a>
</div>
</td>
</tr>
{/each}
<!-- More transactions... -->
</tbody>
</table>
</div>
</div>
</div>
</section> </section>

View File

@ -0,0 +1,3 @@
<h1 class="text-3xl font-bold text-center text-gray-900">
To be discussed...
</h1>

View File

@ -0,0 +1,3 @@
<h1 class="text-3xl font-bold text-center text-gray-900">
To be discussed...
</h1>

View File

@ -0,0 +1,3 @@
<h1 class="text-3xl font-bold text-center text-gray-900">
To be discussed...
</h1>

View File

@ -1,4 +1,4 @@
import adapter from '@sveltejs/adapter-auto'; import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */ /** @type {import('@sveltejs/kit').Config} */