feat: cron done, admin unfinished
This commit is contained in:
+11
-4
@@ -1,9 +1,16 @@
|
|||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
|
||||||
RUN apk add --no-cache docker-cli bash findutils postgresql-client
|
RUN apk add --no-cache docker-cli bash findutils postgresql-client cronie
|
||||||
|
|
||||||
COPY backup.sh /backup.sh
|
WORKDIR /app
|
||||||
RUN chmod +x /backup.sh
|
|
||||||
|
|
||||||
CMD ["/backup.sh"]
|
COPY backup.sh crontab.txt ./
|
||||||
|
|
||||||
|
RUN chmod +x backup.sh
|
||||||
|
|
||||||
|
RUN crontab crontab.txt
|
||||||
|
|
||||||
|
RUN touch /var/log/cron.log
|
||||||
|
|
||||||
|
CMD ["crond", "-f"]
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -2,29 +2,29 @@
|
|||||||
|
|
||||||
echo "[INFO] Backup entry success"
|
echo "[INFO] Backup entry success"
|
||||||
|
|
||||||
# === Configuration ===
|
# Configuration
|
||||||
PG_CONTAINER_NAME=$(docker ps --filter "name=_db" --format "{{.Names}}" | head -n 1)
|
PG_CONTAINER_NAME=$(docker ps --filter "name=_db" --format "{{.Names}}" | head -n 1)
|
||||||
PG_USER="user"
|
PG_USER="user"
|
||||||
PG_PASSWORD="password"
|
PG_PASSWORD="password"
|
||||||
PG_DB="mydatabase"
|
PG_DB="mydatabase"
|
||||||
BACKUP_DIR="/backups"
|
BACKUP_DIR="/backups"
|
||||||
LOG_DIR="/backups/logs"
|
LOG_DIR="/backups/logs"
|
||||||
RETENTION_DAYS=7
|
RETENTION_DAYS=1
|
||||||
TIMESTAMP=$(date +"%Y%m%d-%H%M%S")
|
TIMESTAMP=$(date +"%Y%m%d-%H%M%S")
|
||||||
FILENAME="$BACKUP_DIR/backup-$TIMESTAMP.sql"
|
FILENAME="$BACKUP_DIR/backup-$TIMESTAMP.sql"
|
||||||
LOGFILE="$LOG_DIR/backup-$TIMESTAMP.log"
|
LOGFILE="$LOG_DIR/backup-$TIMESTAMP.log"
|
||||||
|
|
||||||
# === Setup Directories ===
|
# Setup Directories
|
||||||
mkdir -p "$BACKUP_DIR"
|
mkdir -p "$BACKUP_DIR"
|
||||||
mkdir -p "$LOG_DIR"
|
mkdir -p "$LOG_DIR"
|
||||||
|
|
||||||
# === Begin Logging ===
|
# Begin Logging
|
||||||
exec > "$LOGFILE" 2>&1
|
exec > "$LOGFILE" 2>&1
|
||||||
|
|
||||||
echo "[INFO] Backup script started at $TIMESTAMP"
|
echo "[INFO] Backup script started at $TIMESTAMP"
|
||||||
echo "[INFO] Backing up database '$PG_DB' from container '$PG_CONTAINER_NAME'..."
|
echo "[INFO] Backing up database '$PG_DB' from container '$PG_CONTAINER_NAME'..."
|
||||||
|
|
||||||
# === Perform Backup ===
|
# Perform Backup
|
||||||
PGPASSWORD="$PG_PASSWORD" pg_dump -h db -U "$PG_USER" -d "$PG_DB" > "$FILENAME"
|
PGPASSWORD="$PG_PASSWORD" pg_dump -h db -U "$PG_USER" -d "$PG_DB" > "$FILENAME"
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
@@ -34,11 +34,11 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# === Rotate Old Backups ===
|
# Rotate Old Backups
|
||||||
echo "[INFO] Removing backups older than $RETENTION_DAYS days..."
|
echo "[INFO] Removing backups older than $RETENTION_DAYS days..."
|
||||||
find "$BACKUP_DIR" -type f -name "backup-*.sql" -mtime +$RETENTION_DAYS -exec rm -f {} \;
|
find "$BACKUP_DIR" -type f -name "backup-*.sql" -mtime +$RETENTION_DAYS -exec rm -f {} \;
|
||||||
|
|
||||||
# === Rotate Old Logs ===
|
# Rotate Old Logs
|
||||||
echo "[INFO] Removing logs older than $RETENTION_DAYS days..."
|
echo "[INFO] Removing logs older than $RETENTION_DAYS days..."
|
||||||
find "$LOG_DIR" -type f -name "backup-*.log" -mtime +$RETENTION_DAYS -exec rm -f {} \;
|
find "$LOG_DIR" -type f -name "backup-*.log" -mtime +$RETENTION_DAYS -exec rm -f {} \;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
*/15 * * * * /app/backup.sh
|
||||||
@@ -9,6 +9,9 @@ import { useState, useEffect } from "react";
|
|||||||
import ReactMarkdown from "react-markdown";
|
import ReactMarkdown from "react-markdown";
|
||||||
import { BlogViewer } from "./utils/BlogViewer";
|
import { BlogViewer } from "./utils/BlogViewer";
|
||||||
import { BlogList } from "./utils/BlogList";
|
import { BlogList } from "./utils/BlogList";
|
||||||
|
import { RequireAdmin } from "./utils/RouteGuard";
|
||||||
|
import { AdminPage } from "./utils/AdminPage";
|
||||||
|
import Unauthorized from "./utils/UnauthorizedPage";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [darkMode, setDarkMode] = useState(true);
|
const [darkMode, setDarkMode] = useState(true);
|
||||||
@@ -25,6 +28,15 @@ function App() {
|
|||||||
<Route path="/blog/:slug" element={<BlogViewer />} />
|
<Route path="/blog/:slug" element={<BlogViewer />} />
|
||||||
<Route path="/about" element={<About />} />
|
<Route path="/about" element={<About />} />
|
||||||
<Route path="/contact" element={<Contact />} />
|
<Route path="/contact" element={<Contact />} />
|
||||||
|
<Route
|
||||||
|
path="/admin"
|
||||||
|
element={
|
||||||
|
<RequireAdmin>
|
||||||
|
<AdminPage />
|
||||||
|
</RequireAdmin>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route path="/unauthorized" element={<Unauthorized />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export const getCurrentUser = async () => {
|
||||||
|
// Simulate fetching user info from an API
|
||||||
|
return new Promise<{ username: string; isAdmin: boolean }>((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve({
|
||||||
|
username: "john_doe",
|
||||||
|
isAdmin: true, // change to false to simulate non-admin
|
||||||
|
});
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export function AdminPage() {
|
||||||
|
return <div>{"adminpage"}</div>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { useEffect, useState, type JSX } from "react";
|
||||||
|
import { Navigate, useLocation } from "react-router-dom";
|
||||||
|
import { getCurrentUser } from "../helper/auth";
|
||||||
|
|
||||||
|
export const RequireAdmin = ({ children }: { children: JSX.Element }) => {
|
||||||
|
const [isAdmin, setIsAdmin] = useState<boolean | null>(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkAdmin = async () => {
|
||||||
|
const user = await getCurrentUser();
|
||||||
|
setIsAdmin(user.isAdmin);
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
checkAdmin();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (loading) return <div>Checking permissions...</div>;
|
||||||
|
|
||||||
|
if (!isAdmin) {
|
||||||
|
return <Navigate to="/unauthorized" state={{ from: location }} replace />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return children;
|
||||||
|
};
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
const Unauthorized = () => {
|
||||||
|
return (
|
||||||
|
<div className="p-4 text-red-600">
|
||||||
|
<h1 className="text-xl font-bold">403 - Unauthorized</h1>
|
||||||
|
<p>You do not have permission to view this page.</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Unauthorized;
|
||||||
Reference in New Issue
Block a user