Skip to content

Commit

Permalink
Initial upload
Browse files Browse the repository at this point in the history
  • Loading branch information
sameerdhoot authored Nov 4, 2020
1 parent 037af3c commit 4da94d6
Show file tree
Hide file tree
Showing 14 changed files with 729 additions and 2 deletions.
32 changes: 32 additions & 0 deletions Debian_Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# docker build -t wolweb .
FROM golang:buster AS builder

LABEL org.label-schema.vcs-url="https://github.com/sameerdhoot/wolweb" \
org.label-schema.url="https://github.com/sameerdhoot/wolweb/blob/master/README.md"

RUN mkdir /wolweb
WORKDIR /wolweb

# Install Dependecies
RUN git clone https://github.com/sameerdhoot/wolweb . && \
go get -d github.com/gorilla/handlers && \
go get -d github.com/gorilla/mux && \
go get -d github.com/ilyakaznacheev/cleanenv

# Build Source Files
RUN go build -o wolweb .

# Create 2nd Stage final image
FROM debian
WORKDIR /wolweb
COPY --from=builder /wolweb/index.html .
COPY --from=builder /wolweb/wolweb .
COPY --from=builder /wolweb/devices.json .
COPY --from=builder /wolweb/config.json .
COPY --from=builder /wolweb/static ./static

ARG WOLWEBPORT=8089

CMD ["/wolweb/wolweb"]

EXPOSE ${WOLWEBPORT}
34 changes: 34 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# docker build -t wolweb .
FROM golang:alpine AS builder

LABEL org.label-schema.vcs-url="https://github.com/sameerdhoot/wolweb" \
org.label-schema.url="https://github.com/sameerdhoot/wolweb/blob/master/README.md"

RUN mkdir /wolweb
WORKDIR /wolweb

# Install Dependecies
RUN apk update && apk upgrade && \
apk add --no-cache git && \
git clone https://github.com/sameerdhoot/wolweb . && \
go get -d github.com/gorilla/handlers && \
go get -d github.com/gorilla/mux && \
go get -d github.com/ilyakaznacheev/cleanenv

# Build Source Files
RUN go build -o wolweb .

# Create 2nd Stage final image
FROM alpine
WORKDIR /wolweb
COPY --from=builder /wolweb/index.html .
COPY --from=builder /wolweb/wolweb .
COPY --from=builder /wolweb/devices.json .
COPY --from=builder /wolweb/config.json .
COPY --from=builder /wolweb/static ./static

ARG WOLWEBPORT=8089

CMD ["/wolweb/wolweb"]

EXPOSE ${WOLWEBPORT}
134 changes: 132 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,132 @@
# wolweb
Web interface for sending Wake-on-lan (magic packet)
# 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.

I use this application behind NGINX web proxy which is secured with HTTPS certificate. It has no authentication, but it is home network and the reason I built this was to have no authentication. I have the same functionality provided in my home router, but I have to login and go through several clicks. Also, this app runs as docker image so even if it is hacked, it reduces the attack surface.

I have bookmarked direct link to device(s) on my browsers to wake them using single HTTP call for ease of access.

Things I use this for:
- to wake-up mu home laptop remotely. I use my home laptop remotely over RDP.
- this is also helpful in building routines which will wake up my server and nightly builds and when it is all done, go back to sleep. I don't keep my home lab running 24x7 as it is a waste of energy.
- to turn on my NAS and laptop to start the weekly backup from laptop to NAS.
- to turn on NAS quickly when we are watching movies stored on NAS.

> It was tricky to configure the wol feature on my Dell Laptop. NAS and Dell servers were easy to configure. Follow this article for [Dell laptop](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)

### Wake-up directly using HTTP Request

/wolweb/wake/**<hostname>** - Returns a JSON object

```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
}
```

## Configure the app

The application will use the following default values if they are not explicitly configured as explained in sections below.

| Config | Description | Default
| --- | --- | --- |
| Port | Define the port on which the webserver will listen | **8089**
| Virtual Directory | A virtual directory to mount this application under | **/wolweb**

You can override the default application configuration by using a config file or by setting environment variables. The application will first load values from config file and look for environment variables and overwrites values from the file with the values which were found in the environment.

**Using config.json:**

```json
{
"port": 8089,
"vdir":"/wolweb"
}
```
**Using Environment Variables:**

*Environment variables takes precedence over values in config.json file.*

| Variable Name | Description
| --- | --- |
| WOLWEBPORT | Override for default HTTP port
| WOLWEBVDIR | Override for default virtual directory

## Devices (targets) - devices.json format
```json
{
"devices": [
{
"name": "Server",
"mac": "34:E6:D7:33:12:71",
"ip": "192.168.1.255:9"
},
{
"name": "NAS",
"mac": "28:C6:8E:36:DC:38",
"ip": "192.168.1.255:9"
},
{
"name": "Laptop",
"mac": "18:1D:EA:70:A0:21",
"ip": "192.168.1.255:9"
}
]
}

```
## Using with Docker Container

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 applicaiton as-is, you will only need to download these two docker realted files to get started. The docker file will grab the code and compile it for your platform.

**Build Docker Image:**

```
docker build -t wolweb .
```
**Run Docker Image:**

```
docker-compose up -d
```
**Extract the compiled application files from image:**

```
docker cp wolweb:/wolweb - > wolweb.gz
```

> 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.
## Build on Windows
I use VS Code with Go extension. To build this project on windows
```
go build -o wolweb.exe .
```

## 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 .
```
Copy the file over to router and make it executable.
```sh
chmod +x wolweb
```
## 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;
}
```
> 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.
4 changes: 4 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"port": 8089,
"vdir":"/wolweb"
}
62 changes: 62 additions & 0 deletions data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package main

import (
"encoding/json"
"log"
"net/http"
"os"
"strconv"
)

func loadData() {

devicesFile, err := os.Open("devices.json")
if err != nil {
log.Fatalf("Error loading devices.json file. \"%s\"", err)
}
devicesDecoder := json.NewDecoder(devicesFile)
err = devicesDecoder.Decode(&appData)
if err != nil {
log.Fatalf("Error decoding devices.json file. \"%s\"", err)
}
log.Printf("Application data loaded from devices.json")
log.Println(" - devices defined in devices.json: ", len(appData.Devices))

}

func saveData(w http.ResponseWriter, r *http.Request) {

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 {
// http.Error(w, err.Error(), http.StatusBadRequest)
result.Success = false
result.Message = "Colud not save the data. " + err.Error()
result.ErrorObject = err
log.Printf(" - Issues decoding/saving 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)

result.Success = true
result.Message = "devices data saved to devices.json file. There are now " + strconv.Itoa(len(appData.Devices)) + " device defined in the list."
log.Printf(" - New application data saved to file devices.json")
}
json.NewEncoder(w).Encode(result)

}

func getData(w http.ResponseWriter, r *http.Request) {

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(appData)
log.Printf("Request for Application data served")

}
19 changes: 19 additions & 0 deletions devices.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"devices": [
{
"name": "Server",
"mac": "34:E6:D7:33:12:71",
"ip": "192.168.1.255:9"
},
{
"name": "NAS",
"mac": "28:C6:8E:36:DC:38",
"ip": "192.168.1.255:9"
},
{
"name": "Laptop",
"mac": "18:1D:EA:70:A0:21",
"ip": "192.168.1.255:9"
}
]
}
21 changes: 21 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
version: "3"
services:
wolweb:
container_name: wolweb
image: "wolweb"

# 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

# Have to use host mode as bridge network has issues with UDP broadcast
# https://github.com/docker/for-linux/issues/637
# ports:
# - 12345:8089
network_mode: host

# Use environment variable below to change port or virtual directory.
#environment:
#WOLWEBPORT: "8089"
#WOLWEBVDIR: "/wolweb"
74 changes: 74 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<html>

<head>
<title>Wake-on-lan Web</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.0/css/all.css">
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid.min.css" />
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid-theme.min.css" />
<link rel="stylesheet" href="static/wolweb.css">
</head>

<body>
<div class="container-fluid">
<div class="row">
<div class="col-12" role="main">
<h1 class="mb-4 mt-4"><span class="fas fa-power-off"></span> Wake-on-Lan Web Interface<small></h1>
<div id="snackbar" class="alert hideMe">
Message comes here
</div>
<div id="GridDevices"></div>
<hr>
<p>
<h3>Wake-up directly using HTTP Request</h3>
<b>{{$.VDir}}/wake/<span class="text-info">&lt;DeviceName&gt;</span></b>

<b>Returns a JSON Object</b>
<p>
<pre>
{
"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
}
</pre>
</p>
<dl class="dl-horizontal">
<dt>success</dt>
<dd>True or False if the WakeOnLan Paket was send</dd>
<dt>message</dt>
<dd>Message as string what happen</dd>
<dt>error</dt>
<dd>Encoded Jsonobject from GOLANG Error Object</dd>
</dl>
</p>
<hr>
<p>
<i class="fab fa-github"></i> Project Page: <a
href="https://github.com/sameerdhoot/wolweb">https://github.com/sameerdhoot/wolweb</a>
</p>
<p class="m-0">
<small>Build with <span class="fa fa-heart text-danger" aria-hidden="true"></span> by Sameer Dhoot, <span
class="fab fa-github"></span> <a href="https://github.com/dabondi">https://github.com/sameerdhoot</a></small>
</p>
<p class="m-0">
<small class="ml-2"><i class="fas fa-code-branch m-1"></i> from David Baumann's project <a href="https://github.com/dabondi/go-rest-wol">https://github.com/dabondi/go-rest-wol</a></small>
</p>
</div>
</div>
</div>

<script>
window.vDir = "{{$.VDir}}"
</script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid.min.js"></script>
<script src="static/wolweb.js"></script>

</body>

</html>
Loading

0 comments on commit 4da94d6

Please sign in to comment.