Skip to content

Commit

Permalink
Merge pull request #28 from ellisdickinson46/main
Browse files Browse the repository at this point in the history
Refinements
  • Loading branch information
sameerdhoot committed Aug 7, 2023
2 parents 9c1c9d9 + 9e0a103 commit 496b449
Show file tree
Hide file tree
Showing 20 changed files with 1,863 additions and 2,457 deletions.
49 changes: 33 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
[![made-with-Go](https://img.shields.io/badge/Made%20with-Go-orange)](http://golang.org) [![proc-arch](https://img.shields.io/badge/Arch-x86%20%7C%20AMD64%20%7C%20ARM5%20%7C%20ARM7-blue)](http://golang.org) [![os](https://img.shields.io/badge/OS-Linux%20%7C%20Windows%20%7C%20Darwin-yellowgreen)](http://golang.org)
[![made-with-Go](https://img.shields.io/badge/Made%20with-Go-orange)](http://golang.org)
[![proc-arch](https://img.shields.io/badge/Arch-x86%20%7C%20AMD64%20%7C%20ARM5%20%7C%20ARM7-blue)](http://golang.org)
[![os](https://img.shields.io/badge/OS-Linux%20%7C%20Windows%20%7C%20Darwin-yellowgreen)](http://golang.org)


# Web interface for sending Wake-on-LAN (Magic Packet)

A GoLang based HTTP server which will send a Wake-on-LAN package (magic packet) on local network. The request can be send using web interface or directly using HTTP request with mapped device name in the URL. The only computing device I have running 24x7 is handy-dandy Raspberry Pi 4 (4gb) with docker containers. All other devices like server, laptop and NAS as powered only when I need them. I needed a way to easily turn them on specifically when trying to automate things like nightly builds.
A GoLang based HTTP server which will send a Wake-on-LAN package (magic packet) on a local network. The request can be send using web interface or directly using HTTP request with the mapped device name in the URL. The only computing device I have running 24x7 is handy-dandy Raspberry Pi 4 (4GB) with docker containers. All other devices like server, laptop and NAS as powered only when I need them. I needed a way to easily turn them on specifically when trying to automate things like nightly builds.

This application is intended do be used in conjunction with a reverse proxy and secured with an SSL certificate. As the intended use case was with home networks, the application has no in-built authentication. While this could pose a slight security risk even if this was hacked to application is intended to be containerised so the attack surface if limited.

Expand All @@ -13,11 +15,13 @@ Use cases:
- Wake-up my home computers remotely, for access remotely over RDP.
- Integration with automated routines to allow parts of a home lab to sleep instead of running 24x7 to save energy.


## Configuring WOL

On some devices WOL can be difficult to correctly configure and have work reliably.
> Follow this article for [Dell Laptops](https://www.dell.com/support/article/en-us/sln305365/how-to-setup-wake-on-lan-wol-on-your-dell-system?lang=en).

## Bootstrap UI with JS Grid for editing data

![Screenshot](wolweb_ui.png)
Expand All @@ -30,9 +34,9 @@ The UI features CRUD operation implemented using [js-grid.com](https://github.co

```json
{
"success":true,
"message":"Sent magic packet to device Server with Mac 34:E6:D7:33:12:71 on Broadcast IP 192.168.1.255:9",
"error":null
"success": true,
"message": "Sent magic packet to device Server with Mac 34:E6:D7:33:12:71 on Broadcast IP 192.168.1.255:9",
"error": null
}
```

Expand All @@ -42,6 +46,7 @@ The application will use the following default values if they are not explicitly

| Config | Description | Default
| --- | --- | --- |
| Host | Define the host address on which the webserver will listen | **0.0.0.0**
| Port | Define the port on which the webserver will listen | **8089**
| Virtual Directory | A virtual directory to mount this application under | **/wolweb**
| Broadcast IP and Port | This is broadcast IP address and port for the local network. *Please include the port :9* | **192.168.1.255:9**
Expand All @@ -52,6 +57,7 @@ You can override the default application configuration by using a config file or

```json
{
"host": "0.0.0.0",
"port": 8089,
"vdir":"/wolweb",
"bcastip":"192.168.1.255:9"
Expand All @@ -63,6 +69,7 @@ You can override the default application configuration by using a config file or

| Variable Name | Description
| --- | --- |
| WOLWEBHOST | Override for default HTTP host
| WOLWEBPORT | Override for default HTTP port
| WOLWEBVDIR | Override for default virtual directory
| WOLWEBBCASTIP | Override for broadcast IP address and port
Expand Down Expand Up @@ -91,10 +98,8 @@ You can override the default application configuration by using a config file or

```

## Requirement

## Usage with Docker

This project includes [Dockerfile (based on Alpine)](./Dockerfile) and [docker-compose.yml](./docker-compose.yml) files which you can use to build the image for your platform and run it using the docker compose file. If interested, I also have alternate [Dockerfile (based on Debian)](.Debian_Dockerfile). Both of these Dockerfile are tested to run on Raspberry Pi Docker CE. If you want to use this application as-is, you will only need to download these two docker-related files to get started. The docker file will grab the code and compile it for your platform.

> I could not get this to run using Docker's bridged network. The only way I was able to make it work was to use host network for the docker container. See this [https://github.com/docker/for-linux/issues/637](https://github.com/docker/for-linux/issues/637) for details.
Expand All @@ -115,21 +120,22 @@ docker run --network host -it wolweb
docker cp wolweb:/wolweb - > wolweb.gz
```


## Build
You need Go 1.20 to build binary for any OS.

```powershell
# Windows
go build -o wolweb.exe .
```

```shell
# Linux&MacOS
# Linux/macOS
go build -o wolweb .
```

## Build for ASUS Routers (ARM v5)
### Build for ASUS Routers (ARM v5)
I initially thought of running this application on my router, so I needed to build the application without having to install build tool on my router. I use the following **PowerShell** one liner to build targeting the ARM v5 platform on my Windows machine with VS Code:

```powershell
$Env:GOOS = "linux"; $Env:GOARCH = "arm"; $Env:GOARM = "5"; go build -o wolweb .
```
Expand All @@ -138,16 +144,27 @@ Copy the file over to router and make it executable.
chmod +x wolweb
```

To see detailed instructions on how to run this application as service on ASUS router with custom firmware [asuswrt-merlin](https://www.asuswrt-merlin.net/) see this [Wiki guide](https://github.com/sameerdhoot/wolweb/wiki/Run-on-asuswrt-merlin)
## NGiNX Config
> To see detailed instructions on how to run this application as service on ASUS router with custom firmware [asuswrt-merlin](https://www.asuswrt-merlin.net/) see this [Wiki guide](https://github.com/sameerdhoot/wolweb/wiki/Run-on-asuswrt-merlin)

## NGiNX Config
I am already using NGiNX as web-proxy for accessing multiple services (web interfaces) from single IP and port 443 using free Let's Encrypt HTTPS certificate. For accessing this service, I just added the following configuration under my existing server node.
```
location /wolweb {
proxy_pass http://192.168.1.4:8089/wolweb;
}
location /wolweb {
proxy_pass http://192.168.1.4:8089/wolweb;
}
```
> This is also the reason why I have an option in this application to use virtual directory **/wolweb** as I can easily map all requests for this application. My / is already occupied for other web application in my network.

## Credits
Thank you to David Baumann's project https://github.com/dabondi/go-rest-wol for providing the framework which I modified a little to work within constraints of environment.
This project is based on a couple of framworks and provided below:

* https://github.com/dabondi/go-rest-wol - Provided the initial project framework
* https://github.com/sabhiram/go-wol - Provided the functionality to the REST

Thank you to the developers behind both projects, David Baumann and Shaba Abhiram!


## License
Distributed with GNU General Public License (c) 2023
1 change: 1 addition & 0 deletions config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"host": "0.0.0.0",
"port": 8089,
"vdir": "/wolweb",
"bcastip": "192.168.1.255:9"
Expand Down
54 changes: 38 additions & 16 deletions data.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,58 @@ import (
)

func loadData() {
// Read data from 'devices.json' file

devicesFile, err := os.Open("devices.json")
if err != nil {
log.Fatalf("Error loading devices.json file. \"%s\"", err)
devicesFile, fileErr := os.Open("devices.json")
if fileErr != nil {
log.Fatalf("Error loading devices.json file. \"%s\"", fileErr)
}

devicesDecoder := json.NewDecoder(devicesFile)
err = devicesDecoder.Decode(&appData)
if err != nil {
log.Fatalf("Error decoding devices.json file. \"%s\"", err)
decodeErr := devicesDecoder.Decode(&appData)
if decodeErr != nil {
log.Fatalf("Error decoding devices.json file. \"%s\"", decodeErr)
}

log.Printf("Application data loaded from devices.json")
log.Println(" - devices defined in devices.json: ", len(appData.Devices))

devicesFile.Close()
}

func saveData(w http.ResponseWriter, r *http.Request) {
// Wrtie data to 'devices.json' file

w.Header().Set("Content-Type", "application/json")
var result HTTPResponseObject

log.Printf("New Application data received for saving to disk")
err := json.NewDecoder(r.Body).Decode(&appData)
if err != nil {
file, fileErr := os.OpenFile("devices.json", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm)
decoderErr := json.NewDecoder(r.Body).Decode(&appData)

baseErrStr := "Unable to save data to disk. "

if fileErr != nil {
result.Success = false
result.ErrorObject = fileErr
// http.Error(w, err.Error(), http.StatusBadRequest)

log.Printf(" - Issues saving application data")

// Attempt to match raised exception against common errors
if os.IsPermission(fileErr) {
result.Message = baseErrStr + "Ensure you have appropriate permissions to write to 'device.json'."
} else if os.IsNotExist(fileErr) {
result.Message = baseErrStr + "Ensure you have a 'devices.json' file present to store data."
} else {
// Return generic error object if no matching case was found
result.Message = baseErrStr + fileErr.Error()
}
} else if decoderErr != nil {
result.Success = false
result.Message = "Unable to save the data. " + err.Error()
result.ErrorObject = err
log.Printf(" - Issues decoding/saving application data")
result.ErrorObject = decoderErr
result.Message = baseErrStr + "The device data received by the API was invalid."
log.Printf(" - Issues decoding application data")
} else {
file, _ := os.OpenFile("devices.json", os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm)
defer file.Close()

encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
encoder.Encode(appData)
Expand All @@ -49,8 +69,10 @@ func saveData(w http.ResponseWriter, r *http.Request) {
result.Message = "Devices data saved to devices.json file. There are now " + strconv.Itoa(len(appData.Devices)) + " device(s) defined in the list."
log.Printf(" - New application data saved to file devices.json")
}
json.NewEncoder(w).Encode(result)

//
json.NewEncoder(w).Encode(result)
file.Close()
}

func getData(w http.ResponseWriter, r *http.Request) {
Expand Down
5 changes: 3 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ services:
image: "ghcr.io/sameerdhoot/wolweb"
restart: always

# make sure that the file exists in local directory from where you are running the compose file
# Or, initialize empty json file by running command "echo '{}' > devices.json"
# Make sure that the file exists in local directory from where you are running the compose file;
# or initialize empty json file by running command "echo '{}' > devices.json".
volumes:
- ./devices.json:/wolweb/devices.json

Expand All @@ -18,6 +18,7 @@ services:

# Use environment variable below to change port or virtual directory.
environment:
WOLWEBHOST: "0.0.0.0"
WOLWEBPORT: "8089"
#WOLWEBVDIR: "/wolweb"
WOLWEBBCASTIP: "192.168.1.255:9"
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (

require (
github.com/BurntSushi/toml v1.1.0 // indirect
github.com/NYTimes/gziphandler v1.1.1
github.com/felixge/httpsnoop v1.0.1 // indirect
github.com/joho/godotenv v1.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
Expand All @@ -10,6 +13,9 @@ github.com/ilyakaznacheev/cleanenv v1.4.2 h1:nRqiriLMAC7tz7GzjzUTBHfzdzw6SQ7XvTa
github.com/ilyakaznacheev/cleanenv v1.4.2/go.mod h1:i0owW+HDxeGKE0/JPREJOdSCPIyOnmh6C0xhWAkF/xA=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down
Loading

0 comments on commit 496b449

Please sign in to comment.