From 999bdf7222a68995dd40ec7da967b360f67d354a Mon Sep 17 00:00:00 2001 From: chiko Date: Tue, 21 Oct 2025 00:52:32 +0000 Subject: [PATCH] fix-docker (#1) Reviewed-on: https://git.n8x.sx/chiko/77th_eventcalenderntfy/pulls/1 --- Crontab | 2 ++ Dockerfile | 31 ++++++++++++-------- docker-compose.yml | 36 +++++++++++++++++++++++ package.json | 3 +- requirements.txt | 3 +- src/app.ts | 28 ++++++++++++++---- src/notification.py | 64 ++++++++++++++++++++++------------------- src/sendNotification.ts | 12 ++++---- src/sql.ts | 4 ++- 9 files changed, 128 insertions(+), 55 deletions(-) create mode 100644 Crontab create mode 100644 docker-compose.yml diff --git a/Crontab b/Crontab new file mode 100644 index 0000000..c1dd39e --- /dev/null +++ b/Crontab @@ -0,0 +1,2 @@ +1 * * * * bun run ./src/app.ts --today > /dev/null 2>&1 +0 * * * * bun run ./src/app.ts > /dev/null 2>&1 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index c4a8a55..19ef720 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,44 +1,51 @@ FROM debian:12 AS base WORKDIR /opt/app RUN apt-get update && \ - apt-get install -y curl unzip ca-certificates python3 python3-pip && \ + # apt-get install -y curl unzip cron ca-certificates python3 python3-pip && \ + apt-get install -y curl unzip cron ca-certificates python3 python3-pip && \ rm -rf /var/lib/apt/lists/* # install BunJs RUN curl -fsSL https://bun.com/install | bash ENV PATH="/root/.bun/bin:$PATH" -# symlink python3 to python -RUN ln -s /usr/bin/python3 /usr/bin/python - # install dependencies into temp directory # this will cache them and speed up future builds FROM base AS install RUN mkdir -p /temp/dev COPY package.json bun.lock /temp/dev/ RUN cd /temp/dev && bun install --frozen-lockfile -# and install python dependencies -COPY ./requirements.txt . -RUN pip3 install --break-system-packages -r ./requirements.txt # install with --production (exclude devDependencies) RUN mkdir -p /temp/prod COPY package.json bun.lock /temp/prod/ RUN cd /temp/prod && bun install --frozen-lockfile --production +# and install python dependencies +# COPY ./requirements.txt . +# RUN python3 -m pip install --break-system-packages -r requirements.txt +# RUN python3 -m pip install -U python-dotenv + # copy node_modules from temp directory # then copy all (non-ignored) project files into the image FROM base AS prerelease COPY --from=install /temp/dev/node_modules node_modules -COPY . . +COPY . ./ # [optional] tests & build ENV NODE_ENV=production # copy production dependencies and source code into final image FROM base AS release +WORKDIR /opt/app COPY --from=install /temp/prod/node_modules node_modules COPY --from=prerelease /opt/app/src/app.ts . COPY --from=prerelease /opt/app/package.json . -VOLUME ["/opt/app/data/db"] -# run the app -USER bun -ENTRYPOINT ["./entrypoint.sh"] \ No newline at end of file +#COPY --from=prerelease .entrypoint.sh . +COPY Crontab /etc/cron.d/ +RUN chmod 0644 /etc/cron.d/Crontab +COPY . ./ +# USER bun +RUN touch /var/log/cron.log +# RUN chmod +x entrypoint.sh +# ENTRYPOINT ["./entrypoint.sh"] +VOLUME /opt/app/data/db +CMD bun run ./src/app.ts --today && cron && tail -f /var/log/cron.log \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..a0fca37 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,36 @@ +services: + app: + build: . + volumes: + - ./data/db:/opt/app/data/db + env_file: + - path: ./.env + required: true + depends_on: + apprise: + condition: service_healthy + links: + - apprise + apprise: + image: caronc/apprise:latest + hostname: apprise + environment: + - APPRISE_WORKER_COUNT=1 + - APPRISE_STATEFUL_MODE=simple + # - PUID=$(id -u) + # - PGID=$(id -g) + volumes: + - ./data/apprise/config:/config + - ./data/apprise/plugin:/plugin + - ./data/apprise/attach:/attach + ports: + - 8000:8000 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/status"] + interval: 30s + timeout: 10s + retries: 5 +# networks: +# default: +# external: true +# name: npm \ No newline at end of file diff --git a/package.json b/package.json index 32b5b91..434a70f 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "dev:init": "bun run ./src/app.ts --init", "db:init": "bun run ./run/db_init.ts", "db:deleteall": "bun run ./run/db_deleteall.ts", - "build": "bun build ./src/app.ts --compile --outfile ./build/77th_event_calendar_notification", + "build": "bun build --compile --minify --sourcemap ./src/app.ts --outfile ./build/77th_event_calendar_notification", + "build:linux": "bun build --compile --minify --sourcemap --target=bun-linux-arm64 ./src/app.ts --outfile ./build/77th_event_calendar_notification", "docker:build": "docker build -t chiko/77th_eventcalendarntfy:0.1.0 ." }, "peerDependencies": { diff --git a/requirements.txt b/requirements.txt index 356ca44..b4d8f3e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -apprise \ No newline at end of file +apprise +python-dotenv \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index 70c6df5..86f295e 100644 --- a/src/app.ts +++ b/src/app.ts @@ -112,11 +112,29 @@ async function main( ) { } return false; })( ev ); - sendNotification( - `${today_prefix ? "TODAY " : ""}${notification_prefix ? notification_prefix + ": " : ""} ${ev.title} (${ TEventType[ ev.event_type ] })`, - `${body}` - // `${ev.link || "https://77th-jsoc.com/#/events"}` - ); + const title = `${today_prefix ? "TODAY " : ""}${notification_prefix ? notification_prefix + ": " : ""} ${ev.title} (${ TEventType[ ev.event_type ] })`; + + await fetch("http://apprise:8000/notify", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + urls: [ + `ntfys://${process.env.ntfy_username}:${process.env.ntfy_password}@${process.env.ntfy_host}/${process.env.ntfy_topic}${ ev.link ? `?click=${ev.link}`: "?click=https://77th-jsoc.com/#/events" }`, + `discord://${process.env.dc_webhook}?avatar_url=${process.env.dc_avatar_url}&botname=${process.env.dc_botname}` + ].join(","), + title: title, + body: body, + format: "text" + }) + }); + + // await sendNotification( + // title, + // body + // // `${ev.link || "https://77th-jsoc.com/#/events"}` + // ); ev.set_notification("done", db); } }; diff --git a/src/notification.py b/src/notification.py index cda75a9..13a3dfb 100644 --- a/src/notification.py +++ b/src/notification.py @@ -1,37 +1,41 @@ from dotenv import load_dotenv import os -load_dotenv() # Load environment variables from .env file -ntfy_username = os.getenv('ntfy_username') -ntfy_password = os.getenv('ntfy_password') -ntfy_host = os.getenv('ntfy_host') -ntfy_topic = os.getenv('ntfy_topic') -dc_webhook = os.getenv('dc_webhook') -dc_botname = os.getenv('dc_botname') -dc_avatar_url = os.getenv('dc_avatar_url') +def main(): + load_dotenv() # Load environment variables from .env file + ntfy_username = os.getenv('ntfy_username') + ntfy_password = os.getenv('ntfy_password') + ntfy_host = os.getenv('ntfy_host') + ntfy_topic = os.getenv('ntfy_topic') + dc_webhook = os.getenv('dc_webhook') + dc_botname = os.getenv('dc_botname') + dc_avatar_url = os.getenv('dc_avatar_url') -from argparse import ArgumentParser -import apprise + from argparse import ArgumentParser + import apprise -parser = ArgumentParser() -parser.add_argument("--title") -parser.add_argument("--body") -parser.add_argument("--click") -args = parser.parse_args() -print(args) + parser = ArgumentParser() + parser.add_argument("--title") + parser.add_argument("--body") + parser.add_argument("--click") + args = parser.parse_args() + print(args) -apobj = apprise.Apprise() -# config = apprise.AppriseConfig() -# config.add('https://myserver:8080/path/to/config') -if ntfy_host and ntfy_topic: - ntfy_link = f"ntfys://{ntfy_username}:{ntfy_password}@{ntfy_host}/{ntfy_topic}" - if args.click: - ntfy_link = ntfy_link + "?click=" + args.click - apobj.add(ntfy_link) -if dc_webhook: - apobj.add(f"discord://{dc_webhook}?avatar_url={dc_avatar_url}&botname={dc_botname}"); + apobj = apprise.Apprise() + # config = apprise.AppriseConfig() + # config.add('https://myserver:8080/path/to/config') + if ntfy_host and ntfy_topic: + ntfy_link = f"ntfys://{ntfy_username}:{ntfy_password}@{ntfy_host}/{ntfy_topic}" + if args.click: + ntfy_link = ntfy_link + "?click=" + args.click + apobj.add(ntfy_link) + if dc_webhook: + apobj.add(f"discord://{dc_webhook}?avatar_url={dc_avatar_url}&botname={dc_botname}"); -apobj.notify( - body=args.body, - title=args.title -) \ No newline at end of file + apobj.notify( + body=args.body, + title=args.title + ) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/src/sendNotification.ts b/src/sendNotification.ts index cb0a754..639ff21 100644 --- a/src/sendNotification.ts +++ b/src/sendNotification.ts @@ -1,14 +1,16 @@ import * as Bun from "bun"; -export function sendNotification(title: string, body: string, click?: string | null) { +export async function sendNotification(title: string, body: string, click?: string | null) { const command = [ - "python", - "./app/notification.py", + "python3", + "./src/notification.py", `--title=${title}`, `--body=${body}`, ]; - if (click) { + if ( click ) { command.push(`--click=${click}`); } - Bun.spawn(command); + const proc = Bun.spawn(command); + const text = await proc.stdout.text(); + console.log("sendNotification: " + text); } diff --git a/src/sql.ts b/src/sql.ts index 04be71c..6d55878 100644 --- a/src/sql.ts +++ b/src/sql.ts @@ -10,4 +10,6 @@ export const db = new Database(db_filepath); export function init () { Event.createTable(db); -} \ No newline at end of file +} + +init(); \ No newline at end of file