Skip to content

Commit dcd311f

Browse files
committed
first commit
0 parents  commit dcd311f

11 files changed

Lines changed: 349 additions & 0 deletions

File tree

.dockerignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.gitignore
2+
.git
3+
.github
4+
.env
5+
docker-compose.yaml
6+
LICENSE
7+
*.md
8+
Dockerfile
9+
.venv
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/bin/bash
2+
3+
# Usage: ./replace_placeholders.sh source_file destination_file
4+
5+
SOURCE_FILE="$1"
6+
DEST_FILE="$2"
7+
8+
# Check if source file exists
9+
if [[ ! -f "$SOURCE_FILE" ]]; then
10+
echo "Source file '$SOURCE_FILE' does not exist!"
11+
exit 1
12+
fi
13+
14+
# Read the entire source file into a variable
15+
content=$(<"$SOURCE_FILE")
16+
17+
# Extract unique placeholders: match { { file.xxx } } with arbitrary spaces
18+
placeholders=$(grep -oP '\{\s*{\s*file\.(.*)\s*}\s*}' "$SOURCE_FILE" | sed -E 's/\{\s*\{\s*file\.//;s/\s*\}\s*\}//' | sort -u)
19+
20+
for placeholder in $placeholders; do
21+
if [[ -f "$placeholder" ]]; then
22+
file_content=$(<"$placeholder")
23+
24+
# Escape special characters
25+
escaped_content=$(printf '%s' "$file_content" | perl -pe 's/([\\\/])/\\$1/g; s/\n/\\n/g;')
26+
escaped_placeholder=$(printf '%s' "$placeholder" | perl -pe 's/([\\\/])/\\$1/g; s/\n/\\n/g;')
27+
28+
content=$(printf '%s' "$content" | perl -pe "s/{\s*{\s*file\.${escaped_placeholder}\s*}\s*}/$escaped_content/g")
29+
else
30+
echo "Warning: File '$placeholder' not found, skipping."
31+
fi
32+
done
33+
34+
# Write to destination file
35+
printf '%b\n' "$content" > "$DEST_FILE"
36+
37+
echo "Replacements complete. Output written to '$DEST_FILE'."
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# WoL Client
2+
3+
WoL Client is a Wake-on-LAN Docker Container, that wakes up queried Machines.
4+
5+
## Installation
6+
7+
```yaml
8+
{ { file.docker-compose.yaml } }
9+
```
10+
11+
## Usage
12+
13+
Send Wakeup-Packtes to host at `192.168.1.1` [`00:00:00:00:00:00`]
14+
15+
```curl
16+
curl -X POST "Content-Type: application/json" -d '{ "ip":"192.168.1.1", "mac": "00:00:00:00:00:00", "startupTime": 5 }' http://wol-client
17+
```
18+
19+
## Contributing
20+
21+
Want to change something, have suggestions?
22+
Fell free to open up an issue or to create a Pull Request.
23+
24+
This project is rather small so there might not be many additions anytime soon.
25+
26+
## License
27+
28+
[MIT](https://choosealicense.com/licenses/mit/)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Build & Push Dev Image
2+
3+
on:
4+
push:
5+
branches:
6+
- dev
7+
8+
env:
9+
USERNAME: ${{ github.repository_owner }}
10+
IMAGE_NAME: ${{ github.repository }}
11+
REGISTRY: ghcr.io
12+
13+
jobs:
14+
update:
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout Repository
19+
uses: actions/checkout@v4
20+
21+
- name: Login to Registry
22+
uses: docker/login-action@v3
23+
with:
24+
registry: ${{ env.REGISTRY }}
25+
username: ${{ env.USERNAME }}
26+
password: ${{ secrets.GH_PCKG_TOKEN }}
27+
28+
- name: Setup QEMU
29+
uses: docker/setup-qemu-action@v3
30+
31+
- name: Setup Buildx
32+
uses: docker/setup-buildx-action@v3
33+
34+
- name: Extract Labels and Tags
35+
id: meta
36+
uses: docker/metadata-action@v5
37+
with:
38+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
39+
flavor: |
40+
latest=false
41+
tags: |
42+
type=raw,value=latest-dev
43+
44+
- name: Build and Push Image
45+
uses: docker/build-push-action@v6
46+
with:
47+
context: .
48+
platforms: linux/amd64, linux/arm64
49+
tags: ${{ steps.meta.outputs.tags }}
50+
labels: ${{ steps.meta.outputs.labels }}
51+
push: true

.github/workflows/docker-image.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Build & Push Image
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
env:
8+
USERNAME: ${{ github.repository_owner }}
9+
IMAGE_NAME: ${{ github.repository }}
10+
REGISTRY: ghcr.io
11+
12+
jobs:
13+
update:
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: Checkout Repository
18+
uses: actions/checkout@v4
19+
20+
- name: Login to Registry
21+
uses: docker/login-action@v3
22+
with:
23+
registry: ${{ env.REGISTRY }}
24+
username: ${{ env.USERNAME }}
25+
password: ${{ secrets.GH_PCKG_TOKEN }}
26+
27+
- name: Setup QEMU
28+
uses: docker/setup-qemu-action@v3
29+
30+
- name: Setup Buildx
31+
uses: docker/setup-buildx-action@v3
32+
33+
- name: Extract Labels and Tags
34+
id: meta
35+
uses: docker/metadata-action@v5
36+
with:
37+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
38+
flavor: |
39+
latest=false
40+
tags: |
41+
type=semver,pattern=v{{major}}
42+
type=semver,pattern=v{{version}}
43+
type=semver,pattern=v{{major}}.{{minor}}
44+
type=semver,pattern=latest
45+
46+
- name: Build and Push Image
47+
uses: docker/build-push-action@v6
48+
with:
49+
context: .
50+
platforms: linux/amd64, linux/arm64
51+
tags: ${{ steps.meta.outputs.tags }}
52+
labels: ${{ steps.meta.outputs.labels }}
53+
push: true
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Update README
2+
3+
on:
4+
push:
5+
paths:
6+
- "docker-compose.yaml"
7+
- ".github/templates/README.template.md"
8+
9+
jobs:
10+
update-readme:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout repository
15+
uses: actions/checkout@v4
16+
17+
- name: Replace File Placeholders in README
18+
run: |
19+
bash .github/helper-scripts/replace_placeholders.sh .github/templates/README.template.md README.md
20+
21+
- name: Commit & Push README.md
22+
env:
23+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
24+
run: |
25+
git config user.name "github-actions[bot]"
26+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
27+
28+
git add README.md
29+
if git diff --cached --quiet; then
30+
echo "No changes to commit."
31+
else
32+
git commit -m "Update README.md"
33+
git push
34+
fi

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.env
2+
.venv

Dockerfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM python:3.9-alpine
2+
3+
WORKDIR /app
4+
5+
RUN pip install flask wakeonlan pythonping
6+
7+
COPY . .
8+
9+
ENV PORT=5555
10+
11+
EXPOSE ${PORT}
12+
13+
CMD ["python", "app.py"]

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 CodeShell
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

app.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from flask import Flask, Response, request, jsonify
2+
from wakeonlan import send_magic_packet as wakeup
3+
from pythonping import ping
4+
from time import sleep
5+
import logging
6+
7+
app = Flask("WoL Client")
8+
9+
app.logger.setLevel(logging.INFO)
10+
11+
@app.route("/", methods=["POST"])
12+
def listen():
13+
data = request.json
14+
15+
response = {
16+
"output": "",
17+
"success": False
18+
}
19+
20+
if data:
21+
macAddr = data.get("mac", None)
22+
ipAddr = data.get("ip", None)
23+
startupTime = data.get("startupTime", None)
24+
25+
if macAddr and ipAddr:
26+
output = ""
27+
28+
infoLog(f"Pinging {ipAddr}.")
29+
initPong = ping(ipAddr)
30+
31+
if (initPong.success()):
32+
infoLog(f"Host at {ipAddr} is reachable.")
33+
34+
output += "Host is reachable.\n"
35+
response["output"] = output
36+
response["success"] = True
37+
else:
38+
infoLog(f"Host at {ipAddr} is unreachable.")
39+
40+
output += "Host is unreachable.\n"
41+
42+
infoLog(f"Sending Wakeup Packets to {macAddr}.")
43+
44+
output += "Sending Wakeup Packets.\n"
45+
wakeup(macAddr, ip_address=ipAddr)
46+
47+
output += "Waiting for Host to Turn On.\n"
48+
49+
if (startupTime):
50+
infoLog(f"Waiting {startupTime}s for Host to Turn On.")
51+
52+
sleep(startupTime)
53+
54+
infoLog(f"Pinging {ipAddr} again.")
55+
finalPong = ping(ipAddr)
56+
57+
if (finalPong.success()):
58+
infoLog(f"Host at {ipAddr} is reachable.")
59+
60+
output += "Host is now reachable.\n"
61+
62+
response["success"] = True
63+
response["output"] = output
64+
else:
65+
infoLog(f"Host at {ipAddr} is still unreachable.")
66+
67+
output += "Host is still unreachable.\n"
68+
else:
69+
infoLog(f"Waiting for Host to Turn On.")
70+
71+
for i in range(1, 8):
72+
sleep(4)
73+
74+
infoLog(f"Pinging {ipAddr} again. ({i}x)")
75+
finalPong = ping(ipAddr, count=3)
76+
77+
if (finalPong.success()):
78+
infoLog(f"Host at {ipAddr} is reachable.")
79+
80+
output += "Host is now reachable.\n"
81+
82+
response["success"] = True
83+
response["output"] = output
84+
85+
return jsonify(message=response)
86+
87+
infoLog(f"Host at {ipAddr} is still unreachable.")
88+
89+
output += "Host is still unreachable.\n"
90+
91+
return jsonify(message=response)
92+
93+
def infoLog(msg):
94+
app.logger.info(msg)
95+
96+
if __name__ == '__main__':
97+
app.run(debug=False, port=5555, host='0.0.0.0')

0 commit comments

Comments
 (0)