diff --git a/deploy.sh b/deploy.sh index 6a2a4a3..6bfe436 100644 --- a/deploy.sh +++ b/deploy.sh @@ -1,3 +1,11 @@ -docker build -t kingArthurApp:latest . -docker save kingArthurApp:latest | gzip > kingArthurApp.tar.gz -scp kingArthurApp.tar.gz youruser@your_server_ip:~/ \ No newline at end of file +# docker build -t kingArthurApp:latest . +# docker save kingArthurApp:latest | gzip > kingArthurApp.tar.gz +# scp kingArthurApp.tar.gz youruser@your_server_ip:~/ + +# add login key first +# > docker login git.nerdyssey.de +# > Username: dockerLogin +# > Passwrord: appkey +# [DOMAIN]/[OWNER]/[IMAGE_NAME]:[TAG] +docker build -t git.nerdyssey.de/exodus/burg-stett-king-arth-web:latest . +docker push git.nerdyssey.de/exodus/burg-stett-king-arth-web:latest \ No newline at end of file diff --git a/doc/config/gitea.docker-compose.yml b/doc/config/gitea.docker-compose.yml new file mode 100644 index 0000000..e92672c --- /dev/null +++ b/doc/config/gitea.docker-compose.yml @@ -0,0 +1,46 @@ +version: '3.8' + +services: + gitea-server: + image: gitea/gitea:latest + container_name: gitea + restart: unless-stopped + environment: + - USER_UID=1000 + - USER_GID=1000 + # Database connection + - GITEA__database__DB_TYPE=postgres + - GITEA__database__HOST=gitea-db:5432 + - GITEA__database__NAME=gitea + - GITEA__database__USER=gitea + - GITEA__database__PASSWD=###SomePassword### + # Web / Domain settings + - GITEA__server__DOMAIN=git.nerdyssey.de + - GITEA__server__ROOT_URL=https://git.nerdyssey.de/ + - GITEA__server__SSH_DOMAIN=git.nerdyssey.de + - GITEA__server__SSH_PORT=2222 + # Security + - GITEA__service__DISABLE_REGISTRATION=true + - GITEA__service__REQUIRE_SIGNIN_VIEW=false + volumes: + - ./data:/data + ports: + - "2222:22" # This allows Git over SSH + depends_on: + - gitea-db + + gitea-db: + image: postgres:15-alpine + container_name: gitea-db + restart: unless-stopped + environment: + - POSTGRES_USER=gitea + - POSTGRES_PASSWORD=###SomePassword### + - POSTGRES_DB=gitea + volumes: + - ./postgres:/var/lib/postgresql/data + +networks: + default: + name: proxy_net + external: true \ No newline at end of file diff --git a/doc/config/mosquitto.docker-compose.yml b/doc/config/mosquitto.docker-compose.yml new file mode 100644 index 0000000..1157fea --- /dev/null +++ b/doc/config/mosquitto.docker-compose.yml @@ -0,0 +1,18 @@ +services: + mosquitto: + image: eclipse-mosquitto:latest + container_name: mosquitto + restart: unless-stopped + # Power are managed via private subnet "proxy_net" + # ports: + # - "1883:1883" + # - "9001:9001" + volumes: + - ./config:/mosquitto/config + - ./data:/mosquitto/data + - ./log:/mosquitto/log + +networks: + default: + name: proxy_net + external: true \ No newline at end of file diff --git a/doc/config/nginx.docker-compose.yml b/doc/config/nginx.docker-compose.yml new file mode 100644 index 0000000..cdc71a3 --- /dev/null +++ b/doc/config/nginx.docker-compose.yml @@ -0,0 +1,20 @@ +version: '3.8' + +services: + nginx-proxy-manager: + image: 'jc21/nginx-proxy-manager:latest' + container_name: nginx-proxy-manager + restart: unless-stopped + ports: + - '80:80' # Public HTTP Port (Required for Certbot challenges) + - '443:443' # Public HTTPS Port + - '81:81' # Admin Web Dashboard + - '8883:8883' # Added for Secure MQTT (MQTTS) + volumes: + - ./data:/data + - ./letsencrypt:/etc/letsencrypt + +networks: + default: + name: proxy_net + external: true \ No newline at end of file diff --git a/doc/mqtt.md b/doc/mqtt.md new file mode 100644 index 0000000..9695db1 --- /dev/null +++ b/doc/mqtt.md @@ -0,0 +1,45 @@ +# MQTT Commands + +## simple + +```sh +mosquitto_sub -h 91.99.29.8 -t outdoor/king-arthur/sword/run +``` + +## safe + +```sh +mosquitto_sub -L mqtts://nerdyssey.de:8883/outdoor/king-arthur/sword/run -d +mosquitto_sub -L mqtts://nerdyssey.de:8883/outdoor/king-arthur/sword/int -d + +mosquitto_pub -L mqtts://nerdyssey.de:8883 -t outdoor/king-arthur/sword/run -m "SetMaintanance" +``` + +- `+` Single-level wildcard: `t1/+/sub2/sub3` +- `#` Multi-level wildcard: `t1/#/sub3`, `t1/#` + +```sh +# Publishing topic with message +# -h: Host (optional) +# -t: Topic +# -m: Message +# -q: Quality of service +# -r: Retain (keep this after failed connection) +# -h: Print help info +mosquitto_pub -t someTopic -m 123 -q 1 +mosquitto_pub -r -t someTopic -m "on" +mosquitto_pub -h 91.99.29.8 -t someTopic -m 123 -q 1 + +# Subscribing to +# -h: Host (optional) +# -t: Topic s +# + is wildcard +# # is wildcard for full subtree +# -T: Exclude topic +# -q: Quality of service +mosquitto_sub -t someTopic -q 1 +mosquitto_sub -t someTopic/+/subTopic +mosquitto_sub -t bbc/# -T bbc/radio3 +mosquitto_sub -h 91.99.29.8 -t someTopic -q 1 +``` + diff --git a/doc/setup.md b/doc/setup.md new file mode 100644 index 0000000..afcd409 --- /dev/null +++ b/doc/setup.md @@ -0,0 +1,60 @@ + +```sh +# install docker +sudo apt install ca-certificates curl -y +# Add Docker's official GPG key: +sudo apt update +sudo apt install ca-certificates curl +sudo install -m 0755 -d /etc/apt/keyrings +sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc +sudo chmod a+r /etc/apt/keyrings/docker.asc +# Add the repository to Apt sources: +sudo tee /etc/apt/sources.list.d/docker.sources <=20.0.0" + "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", "vue": "^3.2.25" } }, diff --git a/package.json b/package.json index 5bcd401..30e5071 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "preview": "vite preview" }, "devDependencies": { - "@vitejs/plugin-vue": "^5.0.4", + "@vitejs/plugin-vue": "^6.0.0", "typescript": "~6.0.2", "vite": "^8.1.0", "vue-tsc": "^3.3.5" diff --git a/src/App.vue b/src/App.vue index 02bc567..308ffe2 100644 --- a/src/App.vue +++ b/src/App.vue @@ -5,5 +5,4 @@ - + diff --git a/src/services/mqtt.ts b/src/services/mqtt.ts index 4e32b90..eabb8b1 100644 --- a/src/services/mqtt.ts +++ b/src/services/mqtt.ts @@ -4,23 +4,30 @@ const mqttBrokerHost = '91.99.29.8' const mqttBrokerPort = 9001 const mqttBrokerPath = '/mqtt' const mqttUseTls = false -const mqttTopic = 'king-arthur/page/startup' +const mqttTopicRoot = 'outdoor/king-arthur/sword/' const mqttMessage = 'start-hit' let client: any = null +function getClient(): mqtt.MqttClient { + const protocol = mqttUseTls ? 'wss' : 'ws' + const brokerUrl = `${protocol}://${mqttBrokerHost}:${mqttBrokerPort}${mqttBrokerPath}` + const tempClient = mqtt.connect(brokerUrl) + return tempClient +} + export function getMqttClient(): any { return client } export function publishStartHit(): void { - const protocol = mqttUseTls ? 'wss' : 'ws' - const brokerUrl = `${protocol}://${mqttBrokerHost}:${mqttBrokerPort}${mqttBrokerPath}` - const tempClient = mqtt.connect(brokerUrl) + const tempClient = getClient() + + const mqttTopic = mqttTopicRoot + 'run' tempClient.on('connect', () => { tempClient.publish(mqttTopic, mqttMessage, { qos: 1 }, () => { - tempClient.end() + }) }) @@ -29,3 +36,131 @@ export function publishStartHit(): void { tempClient.end(true) }) } + +export function subscribeToIntTopic(callback: (response: IntTopicResponse) => void): mqtt.MqttClient { + const tempClient = getClient() + const mqttTopic = mqttTopicRoot + 'int' + + tempClient.on('connect', () => { + tempClient.subscribe(mqttTopic, { qos: 1 }, (error) => { + if (error) { + console.error('MQTT subscribe failed:', error) + } + }) + }) + + tempClient.on('message', (topic: string, message: Buffer) => { + if (topic === mqttTopic) { + const parsedMessage = parseIntMessage(message.toString()) + callback(parsedMessage) + } + }) + + tempClient.on('error', (error: any) => { + console.error('MQTT connection error:', error) + }) + + return tempClient +} + +interface IntTopicResponse { + version: string + mode: number + sE: boolean + sS: boolean + sP: boolean + lE: boolean +} + +export function parseIntMessage(message: string): IntTopicResponse { + const result: IntTopicResponse = { + version: '', + mode: 0, + sE: false, // EndPoint + sS: false, // StartPoint + sP: false, // PullSensor + lE: false // LatchEnable + } + + const versionMatch = message.match(/T([\d.]+)/) + if (versionMatch) { + result.version = versionMatch[1] + } + const modeMatch = message.match(/Mode=(\d+)/) + if (modeMatch) { + result.mode = parseInt(modeMatch[1], 10) + } + const sEMatch = message.match(/sE:(true|false)/) + if (sEMatch) { + result.sE = sEMatch[1] === 'true' + } + const sSMatch = message.match(/sS:(true|false)/) + if (sSMatch) { + result.sS = sSMatch[1] === 'true' + } + const sPMatch = message.match(/sP:(true|false)/) + if (sPMatch) { + result.sP = sPMatch[1] === 'true' + } + const lEMatch = message.match(/lE:(true|false)/) + if (lEMatch) { + result.lE = lEMatch[1] === 'true' + } + return result +} + +export function enterMaintananceMode( + callback: (sE: number, sS: number, sP: number, lE: number) => void) + : mqtt.MqttClient { + const client = getClient() + + const mqttTopic = mqttTopicRoot + 'stat' + + client.on('connect', () => { + publicMaintananceCmd(client, "SetMaintanance") + client.subscribe(mqttTopic, { qos: 1 }, (error) => { + if (error) { + console.error('MQTT subscribe failed:', error) + } + }) + }) + client.on('error', (error: any) => { + console.error('MQTT operation failed:', error) + client.end(true) + }) + + client.on('message', (topic: string, message: Buffer) => { + if (topic === mqttTopic) { + const messageStr = message.toString() + const status = parseIntMessage(message.toString()) + const sE = status.sE ? 1 : 0 + const sS = status.sS ? 1 : 0 + const sP = status.sP ? 1 : 0 + const lE = status.lE ? 1 : 0 + callback(sE, sS, sP, lE) + } + }) + + return client +} + +export function exitMaintananceMode(client: mqtt.MqttClient): void { + publicMaintananceCmd(client, "Exit") + const mqttTopic = mqttTopicRoot + 'stat' + client.unsubscribe(mqttTopic, (error) => { + if (error) { + console.error('MQTT unsubscribe failed:', error) + } + }) + client.end() +} + +export function publicMaintananceCmd(client: mqtt.MqttClient, message : string): void { + const mqttTopic = mqttTopicRoot + 'run' + console.log(`MQTT: Publish ${mqttTopic}: ${message}`) + client.publish(mqttTopic, message, { qos: 1 }, (error) => { + if (error) { + console.error('MQTT publish failed:', error) + } + }) +} \ No newline at end of file diff --git a/src/views/DirectPageLayout.vue b/src/views/DirectPageLayout.vue index 2eb28a5..0541ffa 100644 --- a/src/views/DirectPageLayout.vue +++ b/src/views/DirectPageLayout.vue @@ -2,9 +2,12 @@
diff --git a/src/views/Maintenance.vue b/src/views/Maintenance.vue index 1290eec..9578905 100644 --- a/src/views/Maintenance.vue +++ b/src/views/Maintenance.vue @@ -1,10 +1,52 @@ + + diff --git a/src/views/Players.vue b/src/views/Players.vue index 97839b1..1ecf9ae 100644 --- a/src/views/Players.vue +++ b/src/views/Players.vue @@ -2,12 +2,7 @@

Add players

- +
    @@ -15,7 +10,8 @@ {{ player }}