Author SHA1 Message Date
ztimson daaa53790a Update src/server.mjs
Code review / review (pull_request) Successful in 32s
2026-09-14 16:10:55 -04:00
ztimson a2afcfa7bd Merge pull request 'ztimson-patch-5' (#5) from ztimson-patch-5 into master
Reviewed-on: #5
2026-09-14 16:06:33 -04:00
ztimson b9ebb149ef Update package.json
Code review / review (pull_request) Successful in 1m21s
2026-09-14 16:06:24 -04:00
ztimson cfcbccf152 Update install.sh 2026-09-14 16:06:11 -04:00
ztimson 9c8c108da0 Merge pull request 'Update README.md' (#4) from ztimson-patch-4 into master
Reviewed-on: #4
2026-09-12 22:46:03 -04:00
ztimson d54d318166 Update README.md
Code review / review (pull_request) Successful in 32s
2026-09-12 22:45:56 -04:00
ztimson 4e70c78a7a Merge pull request 'Add install.sh' (#3) from ztimson-patch-3 into master
Reviewed-on: #3
2026-09-12 22:45:13 -04:00
ztimson fd6e2b07dc Add install.sh
Code review / review (pull_request) Successful in 29s
2026-09-12 22:45:04 -04:00
ztimson 240119ee5f Merge pull request 'ztimson-patch-2' (#2) from ztimson-patch-2 into master
Reviewed-on: #2
2026-09-12 22:41:03 -04:00
ztimson 4817089056 Update public/logo.png
Code review / review (pull_request) Successful in 35s
2026-09-12 22:40:40 -04:00
ztimson 169ffad863 Upload files to "public" 2026-09-12 22:40:19 -04:00
ztimson 4bfecdeacc Add public/index.html 2026-09-12 22:39:16 -04:00
ztimson c84008cf5c Add src/server.mjs 2026-09-12 22:38:26 -04:00
ztimson e24ad685fe Add package.json 2026-09-12 22:36:53 -04:00
ztimson e08bc14634 Update README.md 2026-09-12 22:34:58 -04:00
ztimson 063a228134 Merge pull request 'Update README.md' (#1) from ztimson-patch-1 into master
Reviewed-on: #1
2026-09-12 22:32:41 -04:00
ztimson 15e0003958 Update README.md
Code review / review (pull_request) Successful in 30s
2026-09-12 22:32:32 -04:00
6 changed files with 280 additions and 77 deletions
+60 -77
View File
@@ -1,96 +1,75 @@
<!-- Header -->
<div id="top" align="center">
<br />
<!-- Logo -->
<img src="https://git.zakscode.com/repo-avatars/2b4ee6ba1f2e2618bf7694e4a52fb56d1d0ea6abafa2dcbe496ab786b86d5a76" alt="Logo" width="200" height="200">
<img src="https://git.zakscode.com/repo-avatars/8be45ee4b7c1acd45c73cfc7e9c189f139d69a1c9ec9aba29849ae6a8a0cfb40" alt="Logo" width="200" height="200">
<!-- Title -->
### Template
### Homecast
<!-- Description -->
Simple repository template
A homebrew Chromecast
<!-- Repo badges -->
[![Version](https://img.shields.io/badge/dynamic/json.svg?label=Version&style=for-the-badge&url=https://git.zakscode.com/api/v1/repos/ztimson/template/tags&query=$[0].name)](https://git.zakscode.com/ztimson/template/tags)
[![Pull Requests](https://img.shields.io/badge/dynamic/json.svg?label=Pull%20Requests&style=for-the-badge&url=https://git.zakscode.com/api/v1/repos/ztimson/template&query=open_pr_counter)](https://git.zakscode.com/ztimson/template/pulls)
[![Issues](https://img.shields.io/badge/dynamic/json.svg?label=Issues&style=for-the-badge&url=https://git.zakscode.com/api/v1/repos/ztimson/template&query=open_issues_count)](https://git.zakscode.com/ztimson/template/issues)
[![Version](https://img.shields.io/badge/dynamic/json.svg?label=Version\&style=for-the-badge\&url=https://git.zakscode.com/api/v1/repos/ztimson/homecast/tags\&query=$\[0\].name)](https://git.zakscode.com/ztimson/homecast/tags)
[![Pull Requests](https://img.shields.io/badge/dynamic/json.svg?label=Pull%20Requests\&style=for-the-badge\&url=https://git.zakscode.com/api/v1/repos/ztimson/homecast\&query=open_pr_counter)](https://git.zakscode.com/ztimson/homecast/pulls)
[![Issues](https://img.shields.io/badge/dynamic/json.svg?label=Issues\&style=for-the-badge\&url=https://git.zakscode.com/api/v1/repos/ztimson/homecast\&query=open_issues_count)](https://git.zakscode.com/ztimson/homecast/issues)
<!-- Links -->
---
---
<div>
<a href="https://git.zakscode.com/ztimson/template/wiki" target="_blank">Documentation</a>
• <a href="https://git.zakscode.com/ztimson/template/releases" target="_blank">Release Notes</a>
• <a href="https://git.zakscode.com/ztimson/template/issues/new?template=.github%2fissue_template%2fbug.md" target="_blank">Report a Bug</a>
• <a href="https://git.zakscode.com/ztimson/template/issues/new?template=.github%2fissue_template%2fenhancement.md" target="_blank">Request a Feature</a>
<a href="https://git.zakscode.com/ztimson/homecast/wiki" target="_blank">Documentation</a>
• <a href="https://git.zakscode.com/ztimson/homecast/releases" target="_blank">Release Notes</a>
• <a href="https://git.zakscode.com/ztimson/homecast/issues/new?template=.github%2fissue_template%2fbug.md" target="_blank">Report a Bug</a>
• <a href="https://git.zakscode.com/ztimson/homecast/issues/new?template=.github%2fissue_template%2fenhancement.md" target="_blank">Request a Feature</a>
</div>
---
---
</div>
## Table of Contents
- [Template](#top)
- [About](#about)
- [Demo](#demo)
- [Built With](#built-with)
- [Setup](#setup)
- [Production](#production)
- [Development](#development)
- [License](#license)
* [Homecast](#top)
* [About](#about)
* [Built With](#built-with)
* [Setup](#setup)
* [Production](#production)
* [Development](#development)
* [License](#license)
## About
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Homecast is a homebrew, Chromecast-style streaming appliance built around a Raspberry Pi connected to a television.
### Demo
The goal is to provide a simple, self-hosted casting and media experience while retaining complete control over the underlying hardware and software.
Website: https://git.zakscode.com
Features include:
- Installs on-top of any Raspberry Pi OS + Wayland
- Allows you to fully customize and use as your own media center
- Remote desktop into the pi via the browser on any device
- Mirrors to the TV
- Install the PWA via the browser to get the remote control app
- Use the share button built into any site / app to cast media to Homecast
- Custom screen savers, automatic background music, and AI harness integration
### Built With
[![Angular](https://img.shields.io/badge/Angular-DD0031?style=for-the-badge&logo=angular)](https://angular.io/)
[![Android](https://img.shields.io/badge/android-34A853?style=for-the-badge&logo=android&logoColor=ffffff)](https://www.android.com/)
[![Arduino](https://img.shields.io/badge/Arduino-00878F?style=for-the-badge&logo=arduino&logoColor=white)](https://www.arduino.cc/)
[![Bootstrap](https://img.shields.io/badge/Bootstrap-563D7C?style=for-the-badge&logo=bootstrap&logoColor=white)](https://getbootstrap.com)
[![C](https://img.shields.io/badge/C-A8B9CC?style=for-the-badge&logo=c&logoColor=ffffff)](https://en.cppreference.com/w/c/language)
[![C++](https://img.shields.io/badge/C%2B%2B-00599C?style=for-the-badge&logo=cplusplus)](https://cplusplus.com/)
[![C#](https://img.shields.io/badge/C%23-239120?style=for-the-badge&logo=csharp)](https://dotnet.microsoft.com/)
[![CSS](https://img.shields.io/badge/CSS-1572B6?style=for-the-badge&logo=css3)](https://www.w3.org/Style/CSS/Overview.en.html)
[![Django](https://img.shields.io/badge/django-0C4B33?style=for-the-badge&logo=django)](https://www.djangoproject.com/)
[![Docker](https://img.shields.io/badge/Docker-384d54?style=for-the-badge&logo=docker)](https://docker.com/)
[![Electron](https://img.shields.io/badge/Electron-47848F?style=for-the-badge&logo=electron&logoColor=white)](https://www.electronjs.org/)
[![Firebase](https://img.shields.io/badge/Firebase-FFFFFF?style=for-the-badge&logo=firebase)](https://firebase.google.com/)
[![Go](https://img.shields.io/badge/Go-00ADD8?style=for-the-badge&logo=go&logoColor=ffffff)](https://go.dev/)
[![GraphQL](https://img.shields.io/badge/GraphQL-E10098?style=for-the-badge&logo=graphql)](https://graphql.org/)
[![HTML](https://img.shields.io/badge/HTML-FFFFFF?style=for-the-badge&logo=html5)](https://developer.mozilla.org/en-US/docs/Glossary/HTML)
[![Java](https://img.shields.io/badge/Java-5382A1?style=for-the-badge&logo=coffeescript&logoColor=F8981D)](https://java.com/)
[![JavaScript](https://img.shields.io/badge/JavaScript-000000?style=for-the-badge&logo=javascript)](https://javascript.com/)
[![JQuery](https://img.shields.io/badge/jQuery-0769AD?style=for-the-badge&logo=jquery)](https://jquery.com )
[![Laravel](https://img.shields.io/badge/Laravel-6C6C6C?style=for-the-badge&logo=laravel)](https://laravel.com)
[![Linux](https://img.shields.io/badge/Linux-eeeeee?style=for-the-badge&logo=linux&logoColor=000000)](https://www.linux.org/)
[![Momentum](https://img.shields.io/badge/Momentum-000000?style=for-the-badge&logo=data:image/svg%2bxml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDIwMDEwOTA0Ly9FTiIgImh0dHA6Ly93d3cudzMub3JnL1RSLzIwMDEvUkVDLVNWRy0yMDAxMDkwNC9EVEQvc3ZnMTAuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI1MHB0IiBoZWlnaHQ9IjI1MHB0IiB2aWV3Qm94PSIwIDAgMjUwIDI1MCIgIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIG1lZXQiPgoJPHN0eWxlPgoJCUBtZWRpYSAocHJlZmVycy1jb2xvci1zY2hlbWU6IGxpZ2h0KSB7CgkJZyB7IGZpbGw6ICMwMDAwMDA7IH0KCQl9CgkJQG1lZGlhIChwcmVmZXJzLWNvbG9yLXNjaGVtZTogZGFyaykgewoJCWcgeyBmaWxsOiAjZmZmZmZmOyB9CgkJfQoJPC9zdHlsZT4KCTxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsMjUwKSBzY2FsZSgwLjEsLTAuMSkiPgoJCTxwYXRoIGQ9Ik0xMTY1IDIyOTkgYy0yNDUgLTE4IC00NzAgLTEyMyAtNjUxIC0zMDMgLTExNyAtMTE4IC0yMDAgLTI1MCAtMjUyIC00MDEgLTcxIC0yMDkgLTcxIC00NjggLTEgLTY3OSAzNyAtMTA4IDExOCAtMjU0IDE3OCAtMzE5IGwyNSAtMjggLTEzMiAtMTMyIC0xMzIgLTEzMiA1MiAtNTMgNTMgLTUyIDEzMiAxMzIgMTMyIDEzMiAyOCAtMjUgYzQxIC0zOCAxMDkgLTgyIDE4OCAtMTIxIDE1MyAtNzcgMjkyIC0xMDkgNDcwIC0xMDkgMjkxIDAgNTM2IDEwMSA3NDEgMzA1IDE1MyAxNTQgMjQ0IDMyNCAyODkgNTQwIDIxIDEwMyAyMSAyOTkgLTEgNDAyIC0zMiAxNTkgLTEwNSAzMjQgLTE4NyA0MjYgbC00OSA2MSAxMjIgMTIyIDEyMyAxMjMgLTIyIDMxIGMtMTIgMTYgLTM2IDQwIC01MiA1MiBsLTMxIDIyIC0xMjEgLTEyMSAtMTIyIC0xMjEgLTYwIDQ2IGMtODEgNjEgLTI2MSAxNDcgLTM2NSAxNzMgLTEwNCAyNyAtMjM1IDM3IC0zNTUgMjl6IG0yNzQgLTE2NCBjMTAyIC0yMSAyMTcgLTY4IDI5NyAtMTIxIDEwOSAtNzIgMTA2IC02NiA0OSAtMTI0IGwtNTAgLTUwIC01MCA5IGMtODMgMTYgLTE2OSAtNSAtMzA1IC03NCBsLTc1IC0zOCA0NSAtMzggYzI1IC0yMiA1MyAtNDIgNjMgLTQ1IDExIC0zIDQ1IDYgNzcgMjAgNjggMzEgMTM1IDQ1IDE3MCAzNiAyMiAtNSAyNSAtMTIgMjggLTU2IDEyIC0yMDQgLTM2MCAtNjQ1IC02NzQgLTgwMCAtMTA3IC01MyAtMTgwIC02NiAtMjA4IC0zOCAtMjcgMjcgLTE4IDExMyAyMiAxOTggMTggMzcgMjkgNzMgMjUgODAgLTE0IDI0IC03NiA5NiAtODIgOTYgLTEyIDAgLTc4IC0xMzIgLTEwMSAtMTk5IC0xNiAtNDkgLTIxIC04NCAtMTkgLTE0OCBsMyAtODQgLTQxIC00MSBjLTUwIC01MCAtNTIgLTQ5IC0xMTkgNTggLTU1IDg4IC05OCAxOTYgLTExOSAyOTQgLTE5IDkxIC0xOSAyNzkgMCAzNjkgNzIgMzQzIDM0NyA2MjEgNjg4IDY5NSA4NSAxOSAyODkgMTkgMzc2IDF6IG01NjkgLTM5MyBjNjMgLTk1IDEwNSAtMTk1IDEyOCAtMzA1IDE4IC04OSAxOCAtMjc3IC0xIC0zNjcgLTczIC0zNDYgLTM0OSAtNjIyIC02OTUgLTY5NSAtOTEgLTE5IC0yNzggLTE5IC0zNjggMCAtMTMwIDI3IC0yNzggOTUgLTM2NyAxNjkgbC0zMCAyNSA0NSA0NSBjNDQgNDUgNDUgNDYgMTEwIDQ2IDE3MyAwIDM5MiAxMjQgNjI1IDM1NSAxMzAgMTI5IDIwNCAyMjIgMjc4IDM1MCA2MyAxMDkgODkgMTg1IDk0IDI4MCBsNSA4MCA1MSA1MyBjMjggMjggNTUgNTIgNTkgNTIgNSAwIDM1IC00MCA2NiAtODh6Ii8+CgkJPHBhdGggZD0iTTc1MiAxODM5IGMtMTk1IC05NyAtOTMgLTQzMyAyMzggLTc5MSA3MSAtNzcgNzIgLTc4IDEwMCAtNjMgMTUgOCA0MiAyNiA2MCA0MSBsMzEgMjcgLTY5IDcxIGMtMTc2IDE4MSAtMjk3IDM3NCAtMzE3IDUwOCAtMTQgOTIgMzIgMTEwIDE1OCA2NSAxMjAgLTQ0IDI2OCAtMTQ3IDQwOSAtMjg3IGw4OCAtODcgMTkgMjEgYzEwIDEyIDI4IDM2IDM5IDU1IGwyMSAzNCAtMjYgMzEgYy00NyA1NyAtMTk3IDE4NiAtMjg1IDI0NyAtMTk2IDEzNCAtMzYxIDE4MCAtNDY2IDEyOHoiLz4KCQk8cGF0aCBkPSJNMTAxMiAxNDk3IGMtMjkgLTI5IC01MiAtNTggLTUyIC02NiAwIC0xMyA1NCAtOTYgNjggLTEwNSA1IC0zIDQ0IDMwIDg2IDczIGw3OCA3OSAtNTMgMzYgYy0zMCAyMCAtNTggMzYgLTY0IDM2IC02IDAgLTM0IC0yNCAtNjMgLTUzeiIvPgoJCTxwYXRoIGQ9Ik0xNjcyIDExMzkgYy00NiAtNTYgLTQ5IC02OSAtMjggLTEwMiAyMyAtMzUgNTYgLTE0MyA1NiAtMTgxIDAgLTc4IC01OSAtODMgLTIwNiAtMjAgbC04MyAzNiAtNTEgLTQ0IGMtMjcgLTI0IC01MCAtNDYgLTUwIC01MCAwIC0xNCAxODYgLTk5IDI1MiAtMTE0IDIwMCAtNDkgMzE3IDc4IDI2MyAyODYgLTE2IDYzIC03MCAxODYgLTk3IDIyMyAtMTMgMTcgLTE3IDE0IC01NiAtMzR6Ii8+Cgk8L2c+Cjwvc3ZnPgo=)](https://git.zakscode.com/ztimson/momentum)
[![MongoDB](https://img.shields.io/badge/mongodb-000000?style=for-the-badge&logo=mongodb)](https://www.mongodb.com/)
[![MySQL](https://img.shields.io/badge/MySQL-4479A1?style=for-the-badge&logo=mysql&logoColor=ffffff)](https://www.mysql.com/)
[![Nest](https://img.shields.io/badge/nestjs-E0234E?style=for-the-badge&logo=nestjs)](https://nestjs.com/)
[![.NET](https://img.shields.io/badge/.NET-512BD4?style=for-the-badge&logo=dotnet)](https://dotnet.microsoft.com/)
[![Next](https://img.shields.io/badge/next.js-000000?style=for-the-badge&logo=nextdotjs)](https://nextjs.org/)
[![NGINX](https://img.shields.io/badge/NGINX-009639?style=for-the-badge&logo=nginx)](https://www.nginx.com/)
[![Node](https://img.shields.io/badge/Node.js-000000?style=for-the-badge&logo=nodedotjs)](https://nodejs.org/)
[![p5.js](https://img.shields.io/badge/p5.js-ed225d?style=for-the-badge&logo=p5dotjs&logoColor=white)](https://p5js.org/)
[![PHP](https://img.shields.io/badge/PHP-474A8A?style=for-the-badge&logo=php&logoColor=white)](https://www.php.net/)
[![PostgreSQL](https://img.shields.io/badge/PostgreSQl-212121?style=for-the-badge&logo=postgresql)](https://www.postgresql.org/)
[![Python](https://img.shields.io/badge/Python-FFD43B?style=for-the-badge&logo=python)](https://www.python.org/)
[![React](https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react)](https://reactjs.org/)
[![Redis](https://img.shields.io/badge/Redis-ffffff?style=for-the-badge&logo=redis)](https://redis.com/)
[![SASS](https://img.shields.io/badge/SASS-CC6699?style=for-the-badge&logo=sass&logoColor=ffffff)](https://sass-lang.com/)
[![Shell](https://img.shields.io/badge/Shell-000000?style=for-the-badge&logo=windowsterminal&logoColor=00ff00)](https://en.wikipedia.org/wiki/Shell_script)
[![SQL Server](https://img.shields.io/badge/SQL%20Server-CC2927?style=for-the-badge&logo=microsoftsqlserver)](https://www.microsoft.com/en-ca/sql-server)
[![SQLite](https://img.shields.io/badge/SQLITE-003B57?style=for-the-badge&logo=sqlite)](https://www.sqlite.org/index.html)
[![Svelte](https://img.shields.io/badge/Svelte-4A4A55?style=for-the-badge&logo=svelte)](https://svelte.dev/)
[![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white)](https://typescriptlang.org/)
[![Windows](https://img.shields.io/badge/Windows-0078D4?style=for-the-badge&logo=windows)](https://microsoft.com/windows)
[![Vite](https://img.shields.io/badge/vite-1b1b1b?style=for-the-badge&logo=vite)](https://vitejs.dev/)
[![Vue](https://img.shields.io/badge/Vue.js-35495E?style=for-the-badge&logo=vuedotjs)](https://vuejs.org/)
[![Linux](https://img.shields.io/badge/Linux-eeeeee?style=for-the-badge\&logo=linux\&logoColor=000000)](https://www.linux.org/)
[![Node](https://img.shields.io/badge/Node.js-000000?style=for-the-badge\&logo=nodedotjs)](https://nodejs.org/)
[![JavaScript](https://img.shields.io/badge/JavaScript-000000?style=for-the-badge\&logo=javascript)](https://javascript.com/)
[![HTML](https://img.shields.io/badge/HTML-FFFFFF?style=for-the-badge\&logo=html5)](https://developer.mozilla.org/en-US/docs/Glossary/HTML)
[![CSS](https://img.shields.io/badge/CSS-1572B6?style=for-the-badge\&logo=css3)](https://www.w3.org/Style/CSS/Overview.en.html)
[![Raspberry Pi](https://img.shields.io/badge/Raspberry%20Pi-A22846?style=for-the-badge\&logo=raspberrypi\&logoColor=white)](https://www.raspberrypi.com/)
[![Wayland](https://img.shields.io/badge/Wayland-FFBC00?style=for-the-badge\&logo=wayland\&logoColor=black)](https://wayland.freedesktop.org/)
## Setup
@@ -102,11 +81,14 @@ Website: https://git.zakscode.com
</summary>
#### Prerequisites
- [Docker](https://docs.docker.com/install/)
- Raspberry Pi 4/5 (2GB min/4GB ideal)
- Updated version of Raspberry Pi OS
#### Instructions
1. Run the docker image: `docker run -p 80:80 git.zakscode.com/ztimson/template:latest`
2. Open [http://localhost](http://localhost)
1. Install homecast: `curl -sL https://git.zakscode.com/ztimson/homecast/raw/branch/master/install.sh | sudo bash`
</details>
<details>
@@ -117,16 +99,17 @@ Website: https://git.zakscode.com
</summary>
#### Prerequisites
- [Node.js](https://nodejs.org/en/download)
* Node.js
#### Instructions
1. Install the dependencies: `npm install`
2. Start the Angular server: `npm run start`
3. Open [http://localhost:4200](http://localhost:4200)
1. Clone the repository.
2. Install the dependencies: `npm install`
3. Start the development server: `npm run start`
</details>
## License
Copyright © 2023 Zakary Timson | All Rights Reserved | Available under MIT Licensing
See the [license](./LICENSE) for more information.
Copyright © 2026 Zakary Timson | Available under MIT Licensing
+28
View File
@@ -0,0 +1,28 @@
#!/bin/bash
set -e
# Fallback AP
curl -sL https://git.zakscode.com/ztimson/fallback-ap/raw/branch/master/install.sh | sudo bash
# VNC
sudo raspi-config nonint do_vnc 0
# Node
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.7/install.sh | bash
source ~/.bashrc
nvm install stable && nvm use stable
# Install Homecast
mkdir /app && cd /app
git clone https://git.zakscode.com/ztimson/homecast.git .
npm i
mkdir -p certs
openssl req -x509 -newkey rsa:2048 \
-keyout certs/key.pem \
-out certs/cert.pem \
-days 3650 \
-nodes \
-subj "/CN=homecast"
# TODO: Create service
+17
View File
@@ -0,0 +1,17 @@
{
"name": "homecast",
"version": "0.1.0",
"description": "The Homebrew Chromecast",
"main": "src/server.mjs",
"scripts": {
"start": "node src/server.mjs"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "module",
"dependencies": {
"@novnc/novnc": "^1.7.0",
"ws": "^8.21.3"
}
}
+46
View File
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Remote Desktop</title>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
background: #000;
}
#screen {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="screen"></div>
<script type="module">
import RFB from '/novnc/core/rfb.js';
const screen = document.querySelector('#screen');
const rfb = new RFB(
screen,
`wss://${location.host}/websockify`
);
rfb.addEventListener('connect', () => console.log('RFB connected'));
rfb.addEventListener('disconnect', event => console.log('RFB disconnected', event.detail));
rfb.scaleViewport = true;
rfb.resizeSession = false;
rfb.showDotCursor = true;
rfb.addEventListener('connect', () => console.log('Connected'));
rfb.addEventListener('disconnect', event => console.log('Disconnected', event.detail));
</script>
</body>
</html>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

+129
View File
@@ -0,0 +1,129 @@
import http from 'node:http';
import https from 'node:https';
import net from 'node:net';
import fs from 'node:fs';
import {readFile} from 'node:fs/promises';
import path from 'node:path';
import {fileURLToPath} from 'node:url';
import {WebSocketServer} from 'ws';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const NOVNC = path.join(__dirname, '../node_modules/@novnc/novnc');
const PORT = process.env['PORT'] || 3000;
const VNC_HOST = '127.0.0.1';
const VNC_PORT = 5900;
const MIME_TYPES = {
'.html': 'text/html; charset=utf-8',
'.js': 'text/javascript',
'.css': 'text/css',
'.json': 'application/json',
'.svg': 'image/svg+xml',
'.png': 'image/png',
'.ico': 'image/x-icon',
'.wasm': 'application/wasm'
};
const server = https.createServer({
key: fs.readFileSync('./certs/key.pem'),
cert: fs.readFileSync('./certs/cert.pem')
}, async (req, res) => {
let file;
if (req.url === '/') file = path.join(__dirname, '../public/index.html');
else if (req.url.startsWith('/novnc/')) file = path.join(NOVNC, req.url.slice(7));
else {
res.writeHead(404);
res.end('Not found');
return;
}
try {
const data = await readFile(file);
const type = MIME_TYPES[path.extname(file)] || 'application/octet-stream';
res.writeHead(200, {'Content-Type': type});
res.end(data);
} catch (error) {
console.error('HTTP error:', error.message);
res.writeHead(404);
res.end('Not found');
}
});
const wss = new WebSocketServer({
server,
path: '/websockify',
handleProtocols: protocols => {
console.log('WebSocket protocols:', [...protocols]);
if (protocols.has('binary')) return 'binary';
return false;
}
});
wss.on('connection', ws => {
console.log('VNC client connected');
const socket = net.createConnection(VNC_PORT, VNC_HOST);
socket.on('connect', () => {
console.log(`Connected to VNC ${VNC_HOST}:${VNC_PORT}`);
});
socket.on('data', data => {
console.log(`VNC -> browser: ${data.length} bytes`);
if (data.length <= 64) {
console.log(` hex: ${data.toString('hex')}`);
console.log(` text: ${JSON.stringify(data.toString())}`);
}
if (ws.readyState === ws.OPEN) {
ws.send(data, {binary: true});
}
});
ws.on('message', (data, isBinary) => {
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
console.log(`Browser -> VNC: ${buffer.length} bytes (${isBinary ? 'binary' : 'text'})`);
if (buffer.length <= 64) {
console.log(` hex: ${buffer.toString('hex')}`);
console.log(` text: ${JSON.stringify(buffer.toString())}`);
}
if (socket.writable) {
socket.write(buffer);
}
});
ws.on('error', error => {
console.error('WebSocket error:', error.message);
socket.destroy();
});
ws.on('close', (code, reason) => {
console.log(`Browser disconnected: ${code}${reason?.length ? ` ${reason}` : ''}`);
socket.destroy();
});
socket.on('error', error => {
console.error('VNC socket error:', error.message);
if (ws.readyState === ws.OPEN) ws.close();
});
socket.on('close', () => {
console.log('VNC socket closed');
if (ws.readyState === ws.OPEN) ws.close();
});
});
server.on('error', error => {
console.error('Server error:', error);
});
server.listen(PORT, '0.0.0.0', () => {
console.log(`https://0.0.0.0:${PORT}`);
})