docs: move Antora site content into repo (#1012)
|
|
@ -0,0 +1,34 @@
|
||||||
|
name: Antora docs
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'docs/**'
|
||||||
|
- 'local-antora-playbook.yml'
|
||||||
|
- 'local-antora-playbook-ci.yml'
|
||||||
|
- '.github/workflows/docs-antora.yml'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'docs/**'
|
||||||
|
- 'local-antora-playbook.yml'
|
||||||
|
- 'local-antora-playbook-ci.yml'
|
||||||
|
- '.github/workflows/docs-antora.yml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
antora:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Install Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: Install Antora toolchain
|
||||||
|
run: npm i antora@3.1.14 asciidoctor-kroki@0.18.1 @asciidoctor/tabs@1.0.0-beta.6
|
||||||
|
|
||||||
|
- name: Generate docs site (smoke)
|
||||||
|
run: npx antora local-antora-playbook-ci.yml --log-level info
|
||||||
|
|
@ -20,3 +20,5 @@ integration-tests/configs/authRequireGuestsToLogin/sessions.yaml
|
||||||
webui
|
webui
|
||||||
webui.dev
|
webui.dev
|
||||||
sessions.yaml
|
sessions.yaml
|
||||||
|
docs/build/
|
||||||
|
build/
|
||||||
|
|
@ -58,6 +58,7 @@ make
|
||||||
The project layout is reasonably straightforward;
|
The project layout is reasonably straightforward;
|
||||||
|
|
||||||
* See the `Makefile` for common targets. This project was originally created on top of Fedora, but it should be usable on Debian/your faveourite distro with minor changes (if any).
|
* See the `Makefile` for common targets. This project was originally created on top of Fedora, but it should be usable on Debian/your faveourite distro with minor changes (if any).
|
||||||
|
* End-user documentation (AsciiDoc for link:https://docs.olivetin.app[docs.olivetin.app]) lives in `docs/` as an Antora component; the published site is built from the separate link:https://github.com/OliveTin/docs.olivetin.app[docs.olivetin.app] repository.
|
||||||
* The API is defined in protobuf+Connect RPC - you will need to `make proto`.
|
* The API is defined in protobuf+Connect RPC - you will need to `make proto`.
|
||||||
* The Go daemon is built from the `cmd` and `internal` directories mostly.
|
* The Go daemon is built from the `cmd` and `internal` directories mostly.
|
||||||
* The webui is just a single page application with a bit of Javascript in the `webui` directory. This can happily be hosted on another webserver.
|
* The webui is just a single page application with a bit of Javascript in the `webui` directory. This can happily be hosted on another webserver.
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@
|
||||||
|
|
||||||
All documentation can be found at [docs.olivetin.app](https://docs.olivetin.app). This includes installation and usage guide, etc.
|
All documentation can be found at [docs.olivetin.app](https://docs.olivetin.app). This includes installation and usage guide, etc.
|
||||||
|
|
||||||
|
The AsciiDoc sources for that site live in this repository under [`docs/`](docs/) (Antora component). The [docs.olivetin.app](https://github.com/OliveTin/docs.olivetin.app) repository contains the Antora playbook, theme supplemental files, and the workflow that publishes GitHub Pages.
|
||||||
|
|
||||||
## Use cases
|
## Use cases
|
||||||
|
|
||||||
**Safely** give access to commands, for less technical people;
|
**Safely** give access to commands, for less technical people;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
name: ROOT
|
||||||
|
title: OliveTin
|
||||||
|
version: ''
|
||||||
|
display_version: 'Version 3k'
|
||||||
|
start_page: index.adoc
|
||||||
|
asciidoc:
|
||||||
|
attributes:
|
||||||
|
source-language: asciidoc@
|
||||||
|
table-caption: false
|
||||||
|
toclevels: 2
|
||||||
|
nav:
|
||||||
|
- modules/ROOT/nav.adoc
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import re
|
||||||
|
|
||||||
|
nav_file = open('nav.adoc', 'r')
|
||||||
|
nav_string = nav_file.read()
|
||||||
|
|
||||||
|
adoc_files = glob.glob('pages/**/*.adoc', recursive=True)
|
||||||
|
|
||||||
|
filelist = dict()
|
||||||
|
|
||||||
|
for file in adoc_files:
|
||||||
|
with open(file, 'r') as handle:
|
||||||
|
content = handle.read()
|
||||||
|
|
||||||
|
matches = re.findall(r'<<(.*?),?([\w\- ]+)>>', content)
|
||||||
|
|
||||||
|
for match in matches:
|
||||||
|
m = match
|
||||||
|
|
||||||
|
if match[0] == "":
|
||||||
|
m = match[1]
|
||||||
|
else:
|
||||||
|
m = match[0]
|
||||||
|
|
||||||
|
if content.count("#" + m) != 1:
|
||||||
|
if file not in filelist:
|
||||||
|
filelist[file] = list()
|
||||||
|
|
||||||
|
filelist[file].append(m)
|
||||||
|
|
||||||
|
|
||||||
|
print("Files:", len(filelist))
|
||||||
|
|
||||||
|
for file in filelist.keys():
|
||||||
|
print(file)
|
||||||
|
|
||||||
|
for match in filelist[file]:
|
||||||
|
print("\t", match)
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import re
|
||||||
|
|
||||||
|
adoc_files = glob.glob('pages/**/*.adoc', recursive=True)
|
||||||
|
|
||||||
|
filelist = list()
|
||||||
|
|
||||||
|
for file in adoc_files:
|
||||||
|
with open(file, 'r') as handle:
|
||||||
|
content = handle.read()
|
||||||
|
|
||||||
|
matches = re.findall('^= ', content, re.MULTILINE)
|
||||||
|
|
||||||
|
if len(matches) == 0:
|
||||||
|
filelist.append(file)
|
||||||
|
|
||||||
|
|
||||||
|
print("Files:", len(filelist))
|
||||||
|
|
||||||
|
for file in filelist:
|
||||||
|
print(file)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# find .adoc files that are not navigable from the nav.adoc file
|
||||||
|
|
||||||
|
import glob
|
||||||
|
|
||||||
|
nav_file = open('nav.adoc', 'r')
|
||||||
|
nav_string = nav_file.read()
|
||||||
|
|
||||||
|
adoc_files = glob.glob('pages/**/*.adoc', recursive=True)
|
||||||
|
|
||||||
|
unnavigable_files = []
|
||||||
|
|
||||||
|
for file in adoc_files:
|
||||||
|
filename = file.replace("pages/", "")
|
||||||
|
|
||||||
|
if filename not in nav_string:
|
||||||
|
unnavigable_files.append(filename)
|
||||||
|
|
||||||
|
|
||||||
|
unnavigable_files = sorted(unnavigable_files)
|
||||||
|
|
||||||
|
print("Unnavigable files:", len(unnavigable_files))
|
||||||
|
for file in unnavigable_files:
|
||||||
|
print(file)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
actions:
|
||||||
|
- title: Unicode (emoji) alias icon
|
||||||
|
shell: echo "Hello!"
|
||||||
|
icon: smile
|
||||||
|
|
||||||
|
- title: Unicode (emoji) icon
|
||||||
|
shell: echo "Hello!"
|
||||||
|
icon: "😎"
|
||||||
|
|
||||||
|
- title: Iconify Icon
|
||||||
|
icon: <iconify-icon icon="ant-design:bug-filled"></iconify-icon>
|
||||||
|
|
||||||
|
- title: HTML Image (jpg/png/gif/etc) icon
|
||||||
|
shell: echo "Hello!"
|
||||||
|
icon: '<img src = "custom-webui/icons/mrgreen.gif" style = "width: 1em;" />'
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: olivetin-config
|
||||||
|
data:
|
||||||
|
config.yaml: |
|
||||||
|
actions:
|
||||||
|
- title: "Hello world!"
|
||||||
|
shell: echo 'Hello World!'
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: olivetin
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: olivetin
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: olivetin
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: olivetin
|
||||||
|
image: docker.io/jamesread/olivetin:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 1337
|
||||||
|
volumeMounts:
|
||||||
|
- name: olivetin-config
|
||||||
|
mountPath: "/config"
|
||||||
|
readOnly: true
|
||||||
|
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- curl
|
||||||
|
- localhost:1337
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 30
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: olivetin-config
|
||||||
|
configMap:
|
||||||
|
name: olivetin-config
|
||||||
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: olivetin-ingress
|
||||||
|
spec:
|
||||||
|
defaultBackend:
|
||||||
|
service:
|
||||||
|
name: olivetin
|
||||||
|
port:
|
||||||
|
number: 1337
|
||||||
|
rules:
|
||||||
|
- host: olivetin.apps.ocp.teratan.net
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: olivetin
|
||||||
|
port:
|
||||||
|
number: 1337
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: 'jc21/nginx-proxy-manager:latest'
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- '80:80'
|
||||||
|
- '81:81'
|
||||||
|
- '443:443'
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
- ./letsencrypt:/etc/letsencrypt
|
||||||
|
olivetin:
|
||||||
|
container_name: olivetin
|
||||||
|
image: jamesread/olivetin
|
||||||
|
volumes:
|
||||||
|
- ./OliveTin:/config # replace host path or volume as needed
|
||||||
|
ports:
|
||||||
|
- "1337:1337"
|
||||||
|
restart: unless-stopped
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
|
||||||
|
ssl_certificate "/etc/nginx/conf.d/server.crt";
|
||||||
|
ssl_certificate_key "/etc/nginx/conf.d/server.key";
|
||||||
|
|
||||||
|
access_log /var/log/nginx/ot.access.log main;
|
||||||
|
error_log /var/log/nginx/ot.error.log notice;
|
||||||
|
|
||||||
|
server_name olivetin.example.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:1337/;
|
||||||
|
proxy_redirect http://localhost:1337/ http://localhost/OliveTin/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /websocket {
|
||||||
|
proxy_set_header Upgrade "websocket";
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_pass http://localhost:1337/websocket;
|
||||||
|
proxy_read_timeout 600s;
|
||||||
|
proxy_send_timeout 600s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
# This config has two actions which are applied to all "container" entities
|
||||||
|
# found in the entity file.
|
||||||
|
#
|
||||||
|
# Docs: http://localhost/docs.olivetin.app/docs/entities.html
|
||||||
|
actions:
|
||||||
|
- title: Start {{ container.Names }}
|
||||||
|
icon: box
|
||||||
|
shell: docker start {{ container.Names }}
|
||||||
|
entity: container
|
||||||
|
triggers:
|
||||||
|
- Update container entity file
|
||||||
|
|
||||||
|
- title: Stop {{ container.Names }}
|
||||||
|
icon: box
|
||||||
|
shell: docker stop {{ container.Names }}
|
||||||
|
entity: container
|
||||||
|
triggers:
|
||||||
|
- Update container entity file
|
||||||
|
|
||||||
|
# This is a hidden action, that is run on startup, and every 5 minutes, and
|
||||||
|
# when the above start/stop commands are run (see the `triggers` property).
|
||||||
|
|
||||||
|
- title: Update container entity file
|
||||||
|
shell: 'docker ps -a --format json > /etc/OliveTin/entities/containers.json'
|
||||||
|
hidden: true
|
||||||
|
execOnStartup: true
|
||||||
|
execOnCron: '*/5 * * * *'
|
||||||
|
|
||||||
|
# Docs: http://docs.olivetin.app/entities.html
|
||||||
|
entities:
|
||||||
|
- file: /etc/OliveTin/entities/containers.json
|
||||||
|
name: container
|
||||||
|
|
||||||
|
# The only way to properly use entities, are to use them with a `fieldset` on
|
||||||
|
# a dashboard.
|
||||||
|
dashboards:
|
||||||
|
# This is the second dashboard.
|
||||||
|
- title: My Containers
|
||||||
|
contents:
|
||||||
|
- title: 'Container {{ container.Names }} ({{ container.Image }})'
|
||||||
|
entity: container
|
||||||
|
type: fieldset
|
||||||
|
contents:
|
||||||
|
- type: display
|
||||||
|
title: |
|
||||||
|
{{ container.RunningFor }} <br /><br /><strong>{{ container.State }}</strong>
|
||||||
|
- title: 'Start {{ container.Names }}'
|
||||||
|
- title: 'Stop {{ container.Names }}'
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
{"Command":"\"/bin/bash\"","CreatedAt":"2024-02-28 22:33:35 +0000 GMT","ID":"fcf468e18a0e","Image":"fedora","Labels":"maintainer=Clement Verna \u003ccverna@fedoraproject.org\u003e","LocalVolumes":"0","Mounts":"","Names":"minecraft","Networks":"bridge","Ports":"","RunningFor":"3 minutes ago","Size":"0B","State":"created","Status":"Created"}
|
||||||
|
{"Command":"\"/bin/bash\"","CreatedAt":"2024-02-23 23:18:57 +0000 GMT","ID":"442dd6fe316a","Image":"fedora","Labels":"maintainer=Clement Verna \u003ccverna@fedoraproject.org\u003e","LocalVolumes":"0","Mounts":"","Names":"brave_shirley","Networks":"bridge","Ports":"","RunningFor":"4 days ago","Size":"0B","State":"created","Status":"Created"}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
actions:
|
||||||
|
- title: check log directory
|
||||||
|
hidden: true
|
||||||
|
shell: |
|
||||||
|
function addDirectory {
|
||||||
|
COUNT=$(ls -l $1 | wc -l)
|
||||||
|
echo "- directory: $1" >> /etc/OliveTin/entities/directories.yaml
|
||||||
|
echo " count: $COUNT" >> /etc/OliveTin/entities/directories.yaml
|
||||||
|
}
|
||||||
|
|
||||||
|
truncate -s 0 /etc/OliveTin/entities/directories.yaml
|
||||||
|
addDirectory /var/log/
|
||||||
|
addDirectory /home/xconspirisist/logs
|
||||||
|
execOnStartup: true
|
||||||
|
execOnCron: "* * * * *"
|
||||||
|
|
||||||
|
- title: clean {{ log_directory.directory }} ({{log_directory.count }} files)
|
||||||
|
shell: |
|
||||||
|
echo "Removing all files in {{ log_directory.directory }}"
|
||||||
|
entity: log_directory
|
||||||
|
|
||||||
|
entities:
|
||||||
|
- name: log_directory
|
||||||
|
file: /etc/OliveTin/entities/directories.yaml
|
||||||
|
|
||||||
|
dashboards:
|
||||||
|
- title: Log Actions
|
||||||
|
contents:
|
||||||
|
- entity: log_directory
|
||||||
|
type: fieldset
|
||||||
|
contents:
|
||||||
|
- title: clean {{ log_directory.directory }} ({{log_directory.count }} files)
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
logLevel: "INFO"
|
||||||
|
|
||||||
|
actions:
|
||||||
|
- title: Turn heating up
|
||||||
|
icon: '🔼'
|
||||||
|
shell: /opt/heating.sh up
|
||||||
|
|
||||||
|
- title: Turn heating down
|
||||||
|
icon: '🔽'
|
||||||
|
shell: /opt/heating.sh down
|
||||||
|
|
||||||
|
entities:
|
||||||
|
- file: /etc/OliveTin/entities/heating.yaml
|
||||||
|
name: heating
|
||||||
|
|
||||||
|
dashboards:
|
||||||
|
- title: Heating Control Panel
|
||||||
|
contents:
|
||||||
|
- title: "{{ heater.title }}"
|
||||||
|
entity: heating
|
||||||
|
type: fieldset
|
||||||
|
contents:
|
||||||
|
- type: display
|
||||||
|
title: |
|
||||||
|
<span class = "icon">🌡</span> <br />{{ heating.temperature }}
|
||||||
|
|
||||||
|
- title: Turn heating up
|
||||||
|
- title: Turn heating down
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
- title: Main heater
|
||||||
|
temperature: 20 degrees
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
const myPassword = 'sekrit'
|
||||||
|
|
||||||
|
const domMain = document.getElementsByTagName('main')[0]
|
||||||
|
domMain.style.display = 'none'
|
||||||
|
|
||||||
|
const domPassword = document.createElement('input')
|
||||||
|
const domLogin = document.createElement('button')
|
||||||
|
|
||||||
|
function checkPassword () {
|
||||||
|
if (domPassword.value === myPassword) {
|
||||||
|
domMain.style.display = 'block'
|
||||||
|
domPassword.remove()
|
||||||
|
domLogin.remove()
|
||||||
|
} else {
|
||||||
|
window.alert('Incorrect password. Please try again.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupPasswordForm () {
|
||||||
|
domPassword.setAttribute('type', 'password')
|
||||||
|
domPassword.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
checkPassword()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
domLogin.innerText = 'Login'
|
||||||
|
domLogin.onclick = checkPassword
|
||||||
|
|
||||||
|
const domHeader = document.querySelector('header')
|
||||||
|
domHeader.appendChild(domPassword)
|
||||||
|
domHeader.appendChild(domLogin)
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', setupPasswordForm)
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
actions:
|
||||||
|
- title: Stop {{ systemd_unit.unit }}
|
||||||
|
shell: systemctl stop {{ systemd_unit.unit }}
|
||||||
|
icon: <iconify-icon icon="zondicons:hand-stop"></iconify-icon>
|
||||||
|
entity: systemd_unit
|
||||||
|
triggers:
|
||||||
|
- Update services file
|
||||||
|
|
||||||
|
- title: Start {{ systemd_unit.unit }}
|
||||||
|
shell: systemctl start {{ systemd_unit.unit }}
|
||||||
|
icon: <iconify-icon icon="ic:round-directions-run"></iconify-icon>
|
||||||
|
entity: systemd_unit
|
||||||
|
triggers:
|
||||||
|
- Update services file
|
||||||
|
|
||||||
|
- title: Update services file
|
||||||
|
shell: systemctl list-units -a -o json --no-pager | jq -c 'map(select (.unit | contains ("upsilon", "podman", "boot.mount"))) | .[]' > /etc/OliveTin/entities/systemd_units.json
|
||||||
|
hidden: true
|
||||||
|
execOnStartup: true
|
||||||
|
|
||||||
|
entities:
|
||||||
|
- file: /etc/OliveTin/entities/systemd_units.json
|
||||||
|
name: systemd_unit
|
||||||
|
|
||||||
|
dashboards:
|
||||||
|
- title: My Services
|
||||||
|
contents:
|
||||||
|
- title: '{{ systemd_unit.description }}'
|
||||||
|
type: fieldset
|
||||||
|
entity: systemd_unit
|
||||||
|
contents:
|
||||||
|
- title: 'Status: {{ systemd_unit.sub }}'
|
||||||
|
type: display
|
||||||
|
|
||||||
|
- title: Start {{ systemd_unit.unit }}
|
||||||
|
- title: Stop {{ systemd_unit.unit }}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
{"unit":"boot.mount","load":"loaded","active":"active","sub":"mounted","description":"/boot"}
|
||||||
|
{"unit":"podman.service","load":"loaded","active":"inactive","sub":"dead","description":"Podman API Service"}
|
||||||
|
{"unit":"upsilon-drone.service","load":"loaded","active":"active","sub":"running","description":"upsilon-drone"}
|
||||||
|
{"unit":"podman.socket","load":"loaded","active":"active","sub":"listening","description":"Podman API Socket"}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
actions:
|
||||||
|
- title: WakeOnLan Server1
|
||||||
|
shell: ether-wake A8:5E:45:E4:FF:2A
|
||||||
|
icon: ping
|
||||||
|
|
||||||
|
- title: Install ether-wake on startup
|
||||||
|
shell: microdnf install -y net-tools
|
||||||
|
hidden: true
|
||||||
|
execOnStartup: true
|
||||||
|
timeout: 120
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
- title: WakeOnLan Server1
|
||||||
|
# The r0gger/docker-wake-on-lan is a minimal container for WOL
|
||||||
|
# that can be run on the host network.
|
||||||
|
# It is not required to run the OliveTin container on the host network.
|
||||||
|
shell: |
|
||||||
|
docker run --rm --name wake-on-lan --net=host -e MAC='A8:5E:45:E4:FF:2A' r0gger/docker-wake-on-lan
|
||||||
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 343 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 23 KiB |