updated install script with cron jobs
All checks were successful
Tests / Run Go Tests (push) Successful in 1m35s

This commit is contained in:
2026-01-07 19:55:00 +01:00
parent 4b9824c714
commit 3322f7e9bc
8 changed files with 95 additions and 109 deletions

6
Cron/autoBackup.sh Executable file
View File

@@ -0,0 +1,6 @@
# cron-timing: 05 01 * * 1
container_name="arbeitszeitmessung-main-db-1"
filename=backup-$(date '+%d%m%Y').sql
database_name=__DATABASE__
docker exec $container_name pg_dump $database_name > /home/pi/arbeitszeitmessung-backup/$filename
echo "created backup file: "$filename

3
Cron/autoHolidays.sh Executable file
View File

@@ -0,0 +1,3 @@
# Calls endpoint to write all public Holidays for the current year inside a database.
port=__PORT__
curl localhost:$port/auto/feiertage

4
Cron/autoLogout.sh Executable file
View File

@@ -0,0 +1,4 @@
# cron-timing: 55 23 * * *
# Calls endpoint to log out all users, still logged in for today
port=__PORT__
curl localhost:$port/auto/logout

View File

@@ -2,11 +2,10 @@ POSTGRES_USER=root # Postgres ADMIN Nutzername
POSTGRES_PASSWORD=very_secure # Postgres ADMIN Passwort
POSTGRES_API_USER=api_nutzer # Postgres API Nutzername (für Arbeitszeitmessung)
POSTGRES_API_PASS=password # Postgres API Passwort (für Arbeitszeitmessung)
POSTGRES_PATH=../DB # Datebank Pfad (relativ zu Docker Ordner oder absoluter pfad mit /...)
LOG_PATH=../logs # Pfad für Logdatein
POSTGRES_PATH=__ROOT__/DB # Datebank Pfad (relativ zu Docker Ordner oder absoluter pfad mit /...)
LOG_PATH=__ROOT__/logs # Pfad für Audit Logs
POSTGRES_DB=arbeitszeitmessung # Postgres Datenbank Name
POSTGRES_PORT=127.0.0.1:5432 # Postgres Port will not be exposed by default. regex:^[0-9]{1,5}$
TZ=Europe/Berlin # Zeitzone
API_TOKEN=dont_access # API Token für ESP Endpoints
WEB_PORT=8000 # Port from which Arbeitszeitmessung should be accessable regex:^[0-9]{1,5}$
TYPST_CONTAINER=arbeitszeitmessung-doc-creator # Name of the pdf compiler container

View File

@@ -1,7 +0,0 @@
FROM ghcr.io/typst/typst:0.14.0
WORKDIR /app
COPY ./templates /app/templates
COPY ./static /app/static
ENTRYPOINT ["sh", "-c", "while true; do sleep 3600; done"]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,97 +0,0 @@
#let table-header(..headers) = {
table.header(
..headers.pos().map(h => strong(h))
)
}
#let abrechnung(meta, days) = {
set page(paper: "a4", margin: (x:1.5cm, y:2.25cm),
footer:[#grid(
columns: (3fr, .65fr),
align: left + horizon,
inset: .5em,
[#meta.EmployeeName -- #meta.TimeRange], grid.cell(rowspan: 2)[#image("/static/logo.png")],
[Arbeitszeitrechnung maschinell erstellt am #meta.CurrentTimestamp],
)
])
set text(font: "Noto Sans", size:10pt, fill: luma(10%))
set table(
stroke: 0.5pt + luma(10%),
inset: .5em,
align: center + horizon,
)
show text: it => {
if it.text == "0min"{
text(oklch(70.8%, 0, 0deg))[#it]
}else if it.text.starts-with("-"){
text(red)[#it]
}else{
it
}
}
[= Abrechnung Arbeitszeit -- #meta.EmployeeName]
[Zeitraum: #meta.TimeRange]
table(
columns: (1fr, 1fr, 1fr, 1fr, 1fr, 1fr, .875fr, 1.25fr),
fill: (x, y) =>
if y == 0 { oklch(87%, 0, 0deg) },
table-header(
[Datum], [Kommen], [Gehen], [Arbeitsart], [Stunden], [Kurzarbeit], [Pause], [Überstunden]
),
.. for day in days {
(
[#day.Date],
if day.DayParts.len() == 0{
table.cell(colspan: 3)[Keine Buchungen]
}else if day.DayParts.len() == 1 and not day.DayParts.first().IsWorkDay{
table.cell(colspan: 3)[#day.DayParts.first().WorkType]
}
else {
table.cell(colspan: 3, inset: 0em)[
#table(
columns: (1fr, 1fr, 1fr),
.. for Zeit in day.DayParts {
(
if Zeit.IsWorkDay{
(
table.cell()[#Zeit.BookingFrom],
table.cell()[#Zeit.BookingTo],
table.cell()[#Zeit.WorkType],
)
}else{
(table.cell(colspan: 3)[#Zeit.WorkType],)
}
)
},
)
]
},
[#day.Worktime],
[#day.Kurzarbeit],
[#day.Pausetime],
[#day.Overtime],
)
if day.IsFriday {
( table.cell(colspan: 8, fill: oklch(87%, 0, 0deg))[Wochenende], ) // note the trailing comma
}
}
)
table(
columns: (3fr, 1fr),
align: right,
inset: (x: .25em, y:.75em),
stroke: none,
table.hline(start: 0, end: 2, stroke: stroke(dash:"dashed", thickness:.5pt)),
[Arbeitszeit :], table.cell(align: left)[#meta.WorkTime],
[Kurzarbeit :], table.cell(align: left)[#meta.Kurzarbeit],
[Überstunden :], table.cell(align: left)[#meta.Overtime],
[Überstunden lfd. :],table.cell(align: left)[#meta.OvertimeTotal],
table.hline(start: 0, end: 2),
)
}

View File

@@ -4,6 +4,10 @@ set -e
envFile=Docker/.env
envExample=Docker/env.example
autoBackupScript=Cron/autoBackup.sh
autoHolidaysScript=Cron/autoHolidays.sh
autoLogoutScript=Cron/autoLogout.sh
echo "Checking Docker installation..."
if ! command -v docker >/dev/null 2>&1; then
echo "Docker not found. Install Docker? [y/N]"
@@ -18,12 +22,16 @@ else
echo "Docker is already installed."
fi
###########################################################################
echo "Checking Docker Compose..."
if ! docker compose version >/dev/null 2>&1; then
echo "Docker Compose plugin missing. You may need to update Docker."
exit 1
fi
###########################################################################
echo "Preparing .env file..."
if [ ! -f $envFile ]; then
if [ -f $envExample ]; then
@@ -44,6 +52,9 @@ if [ ! -f $envFile ]; then
raw_val=$(printf "%s" "$rest" | sed 's/ *#.*//')
default_value=$(printf "%s" "$raw_val" | sed 's/"//g')
# Replace __ROOT__ with script pwd
default_value="${default_value/__ROOT__/$(pwd)}"
regex=""
if [[ "$comment" =~ regex:(.*)$ ]]; then
regex="${BASH_REMATCH[1]}"
@@ -96,13 +107,80 @@ else
echo "Using existing .env. (found at $envFile)"
fi
###########################################################################
LOG_PATH=$(grep -E '^LOG_PATH=' $envFile | cut -d= -f2)
if [ -z "$LOG_PATH" ]; then
echo "LOG_PATH not found in .env using default $(pwd)/logs"
LOG_PATH=$(pwd)/logs
else
LOG_PATH=Docker/$LOG_PATH
fi
mkdir -p $LOG_PATH
echo "Created logs folder at $LOG_PATH"
###########################################################################
echo -e "\n\n"
echo "Start containers with docker compose up -d? [y/N]"
read -r start_containers
read -r start_containersmkdi
if [[ "$start_containers" =~ ^[Yy]$ ]]; then
cd Docker
mkdir ../logs
docker compose up -d
echo "Containers started."
else
echo "You can start them manually with: docker compose up -d"
fi
###########################################################################
echo -e "\n\n"
echo "Setup Crontab for automatic logout, backup and holiday creation? [y/N]"
read -r setup_cron
if [[ "$setup_cron" =~ ^[Yy]$ ]]; then
WEB_PORT=$(grep -E '^WEB_PORT=' $envFile | cut -d= -f2)
if [ -z "$WEB_PORT" ]; then
echo "WEB_PORT not found in .env using default 8000"
WEB_PORT=8000
fi
POSTGRES_DB=$(grep -E '^POSTGRES_DB=' $envFile | cut -d= -f2)
if [ -z "$POSTGRES_DB" ]; then
echo "arbeitszeitmessung not found in .env using default arbeitszeitmessung"
POSTGRES_DB="arbeitszeitmessung"
fi
sed -i "s/__PORT__/$WEB_PORT/" $autoHolidaysScript
sed -i "s/__PORT__/$WEB_PORT/" $autoLogoutScript
sed -i "s/__DATABASE__/$POSTGRES_DB/" $autoBackupScript
chmod +x $autoBackupScript $autoHolidaysScript $autoLogoutScript
# echo "Scripts build with PORT=$WEB_PORT and DATABSE=$POSTGRES_DB!"
echo "Adding rules to crontab."
cron_commands=$(mktemp /tmp/arbeitszeitmessung-cron.XXX)
for file in Cron/*; do
cron_timing=$(grep -E '^# cron-timing:' "$file" | sed 's/^# cron-timing:[[:space:]]*//')
if [ -z "$cron_timing" ]; then
echo "No cron-timing found in $file, so it's not added to crontab."
continue
fi
( crontab -l ; echo "$cron_timing $(pwd)/$file" )| awk '!x[$0]++' | crontab -
echo "Added entry to crontab: $cron_timing $(pwd)/$file."
done
if systemctl is-active --quiet cron.service ; then
echo "cron.service is running. Everything should be fine now."
else
echo "cron.service is not running. Please start and enable cron.service."
echo "For how to start a service, see: https://wiki.ubuntuusers.de/systemd/systemctl UNITNAME will be cron.service"
fi
else
echo "Please setup cron manually by executing crontab -e and adding all files from inside the Cron directory!"
fi