diff --git a/run-analysis.sh b/run-analysis.sh
index e487224..4149b9a 100755
--- a/run-analysis.sh
+++ b/run-analysis.sh
@@ -1,6 +1,15 @@
#!/usr/bin/env bash
set -e
+# Prevent concurrent executions of this script.
+LOCK_FILE="/tmp/$(basename "$0").lock"
+if [ -e "$LOCK_FILE" ]; then
+ echo "[WARN] $(basename "$0") is already running (lock file present)." >&2
+ exit 0
+fi
+touch "$LOCK_FILE"
+trap 'rm -f "$LOCK_FILE"' EXIT
+
# Ensure virtual environment exists
if [ ! -d ".venv" ]; then
echo "[INFO] Creating virtual environment..."
diff --git a/run-import.sh b/run-import.sh
index 22b4b31..3c79d35 100755
--- a/run-import.sh
+++ b/run-import.sh
@@ -1,6 +1,17 @@
#!/usr/bin/env bash
set -e
+# Prevent multiple simultaneous runs by using a lock file specific to this
+# script. If the lock already exists, assume another instance is running and
+# exit gracefully.
+LOCK_FILE="/tmp/$(basename "$0").lock"
+if [ -e "$LOCK_FILE" ]; then
+ echo "[WARN] $(basename "$0") is already running (lock file present)." >&2
+ exit 0
+fi
+touch "$LOCK_FILE"
+trap 'rm -f "$LOCK_FILE"' EXIT
+
# Ensure virtual environment exists
if [ ! -d ".venv" ]; then
echo "[INFO] Creating virtual environment..."
diff --git a/run-reports.sh b/run-reports.sh
index a0d718f..bfe736d 100755
--- a/run-reports.sh
+++ b/run-reports.sh
@@ -1,6 +1,15 @@
#!/usr/bin/env bash
set -e
+# Prevent concurrent executions of this script.
+LOCK_FILE="/tmp/$(basename "$0").lock"
+if [ -e "$LOCK_FILE" ]; then
+ echo "[WARN] $(basename "$0") is already running (lock file present)." >&2
+ exit 0
+fi
+touch "$LOCK_FILE"
+trap 'rm -f "$LOCK_FILE"' EXIT
+
# Ensure virtual environment exists
if [ ! -d ".venv" ]; then
echo "[INFO] Creating virtual environment..."
diff --git a/scripts/generate_reports.py b/scripts/generate_reports.py
index 4b5692a..cf06398 100644
--- a/scripts/generate_reports.py
+++ b/scripts/generate_reports.py
@@ -161,7 +161,10 @@ def _generate_interval(interval: str, domain: Optional[str] = None) -> None:
report_list.append(entry)
_save_json(out_dir / "reports.json", report_list)
- typer.echo(f"Generated {interval} reports")
+ if domain:
+ typer.echo(f"Generated {interval} reports for {domain}")
+ else:
+ typer.echo(f"Generated {interval} reports")
def _generate_all_domains(interval: str) -> None:
@@ -175,7 +178,7 @@ def _generate_root_index() -> None:
intervals = [
p.name
for p in OUTPUT_DIR.iterdir()
- if p.is_dir() and p.name.lower() not in {"domains", "global", "analysis"}
+ if p.is_dir() and p.name.lower() not in {"domains", "global"}
]
intervals.sort()
diff --git a/setup.sh b/setup.sh
new file mode 100755
index 0000000..be5087c
--- /dev/null
+++ b/setup.sh
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+set -e
+
+# Default schedules
+import_sched="*/5 * * * *"
+report_sched="0 * * * *"
+analysis_sched="0 0 * * *"
+remove=false
+
+usage() {
+ echo "Usage: $0 [--import CRON] [--reports CRON] [--analysis CRON] [--remove]"
+}
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ --import)
+ import_sched="$2"; shift 2;;
+ --reports)
+ report_sched="$2"; shift 2;;
+ --analysis)
+ analysis_sched="$2"; shift 2;;
+ --remove)
+ remove=true; shift;;
+ -h|--help)
+ usage; exit 0;;
+ *)
+ usage; exit 1;;
+ esac
+done
+
+repo_dir="$(cd "$(dirname "$0")" && pwd)"
+
+if [ "$remove" = true ]; then
+ tmp=$(mktemp)
+ sudo crontab -l 2>/dev/null | grep -v "# ngxstat import" | grep -v "# ngxstat reports" | grep -v "# ngxstat analysis" > "$tmp" || true
+ sudo crontab "$tmp"
+ rm -f "$tmp"
+ echo "[INFO] Removed ngxstat cron entries"
+ exit 0
+fi
+
+cron_entries="${import_sched} cd ${repo_dir} && ./run-import.sh # ngxstat import\n${report_sched} cd ${repo_dir} && ./run-reports.sh # ngxstat reports\n${analysis_sched} cd ${repo_dir} && ./run-analysis.sh # ngxstat analysis"
+
+( sudo crontab -l 2>/dev/null; echo -e "$cron_entries" ) | sudo crontab -
+
+echo "[INFO] Installed ngxstat cron entries"
diff --git a/templates/index.html b/templates/index.html
index 0a8abed..7b0b98f 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -286,7 +286,7 @@
Object.entries(sections).forEach(([key, section]) => {
section.classList.toggle('is-hidden', key !== name);
});
- intervalControl.classList.toggle('is-hidden', name === 'overview' || name === 'analysis');
+ intervalControl.classList.toggle('is-hidden', name === 'overview');
domainControl.classList.toggle('is-hidden', name !== 'domain');
if (name === 'overview') {
loadStats();
diff --git a/tests/test_reports.py b/tests/test_reports.py
index dbe71c2..fec898c 100644
--- a/tests/test_reports.py
+++ b/tests/test_reports.py
@@ -184,8 +184,6 @@ def test_generate_root_index(tmp_path, sample_reports, monkeypatch):
(tmp_path / "output" / "domains" / "bar.com").mkdir(parents=True)
# add an extra directory with capitalized name to ensure it's ignored
(tmp_path / "output" / "Global").mkdir(parents=True)
- # add an analysis directory to ensure it's excluded
- (tmp_path / "output" / "analysis").mkdir(parents=True)
gr._generate_root_index()
@@ -198,7 +196,6 @@ def test_generate_root_index(tmp_path, sample_reports, monkeypatch):
assert '