docker shit
This commit is contained in:
35
.dockerignore
Normal file
35
.dockerignore
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
yarn-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Environment files
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# Version control
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Docker files
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
manage_container.sh
|
||||||
47
Dockerfile
47
Dockerfile
@@ -1,23 +1,52 @@
|
|||||||
# Use Node.js as the base image
|
# Build stage
|
||||||
FROM node:18
|
FROM node:18-alpine AS builder
|
||||||
|
|
||||||
# Set the working directory
|
# Set working directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copy package.json and package-lock.json (if available)
|
# Copy package files
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
RUN npm install
|
RUN npm ci
|
||||||
|
|
||||||
# Copy the rest of the application code
|
# Copy source code and config files
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Production stage
|
||||||
|
FROM node:18-alpine AS runner
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package files
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# Install production dependencies only
|
||||||
|
RUN npm ci --only=production
|
||||||
|
|
||||||
|
# Copy necessary files from builder
|
||||||
|
COPY --from=builder /app/.next ./.next/
|
||||||
|
COPY --from=builder /app/public ./public/
|
||||||
|
COPY --from=builder /app/next.config.js ./
|
||||||
|
|
||||||
# Create a directory for markdown files
|
# Create a directory for markdown files
|
||||||
RUN mkdir -p /markdown
|
RUN mkdir -p /markdown
|
||||||
|
|
||||||
# Expose the port your app runs on
|
# Set environment variables
|
||||||
EXPOSE 3000
|
ENV NODE_ENV=production
|
||||||
|
ENV PORT=8080
|
||||||
|
ENV HOSTNAME=0.0.0.0
|
||||||
|
|
||||||
# Command to run the application
|
# Expose the port
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
|
||||||
|
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/ || exit 1
|
||||||
|
|
||||||
|
# Start the application
|
||||||
CMD ["npm", "start"]
|
CMD ["npm", "start"]
|
||||||
78
README.md
78
README.md
@@ -4,6 +4,7 @@
|
|||||||
[](https://tailwindcss.com/)
|
[](https://tailwindcss.com/)
|
||||||
[](https://www.typescriptlang.org/)
|
[](https://www.typescriptlang.org/)
|
||||||
[](https://www.electronjs.org/)
|
[](https://www.electronjs.org/)
|
||||||
|
[](https://www.docker.com/)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
|
||||||
A modern, cross-platform blog system built with **Next.js**, **Markdown**, and **Electron**. Write posts in Markdown, manage content visually, and deploy to web or desktop.
|
A modern, cross-platform blog system built with **Next.js**, **Markdown**, and **Electron**. Write posts in Markdown, manage content visually, and deploy to web or desktop.
|
||||||
@@ -18,6 +19,7 @@ A modern, cross-platform blog system built with **Next.js**, **Markdown**, and *
|
|||||||
- 🖥️ **Electron desktop app** — Run your blog as a native desktop app
|
- 🖥️ **Electron desktop app** — Run your blog as a native desktop app
|
||||||
- 📱 **Responsive UI** — Mobile-friendly and clean design
|
- 📱 **Responsive UI** — Mobile-friendly and clean design
|
||||||
- 🛠️ **Admin dashboard** — Manage posts and folders visually
|
- 🛠️ **Admin dashboard** — Manage posts and folders visually
|
||||||
|
- 🐳 **Docker support** — Easy deployment with Docker containers
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -27,6 +29,7 @@ A modern, cross-platform blog system built with **Next.js**, **Markdown**, and *
|
|||||||
|
|
||||||
- [Node.js 18+](https://nodejs.org/)
|
- [Node.js 18+](https://nodejs.org/)
|
||||||
- [npm](https://www.npmjs.com/)
|
- [npm](https://www.npmjs.com/)
|
||||||
|
- [Docker](https://www.docker.com/) (for containerized deployment)
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
@@ -52,6 +55,70 @@ npm install
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🐳 Docker Deployment
|
||||||
|
|
||||||
|
The project includes Docker support for easy deployment. A `manage_container.sh` script is provided to simplify container management.
|
||||||
|
|
||||||
|
### Docker Setup
|
||||||
|
|
||||||
|
1. Make sure Docker is installed and running on your system
|
||||||
|
2. Update the `MARKDOWN_DIR` path in `manage_container.sh` to point to your local markdown directory
|
||||||
|
3. Make the script executable:
|
||||||
|
```bash
|
||||||
|
chmod +x manage_container.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Container Management
|
||||||
|
|
||||||
|
The `manage_container.sh` script provides several commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build the Docker image
|
||||||
|
./manage_container.sh build
|
||||||
|
|
||||||
|
# Start the container
|
||||||
|
./manage_container.sh start
|
||||||
|
|
||||||
|
# Stop the container
|
||||||
|
./manage_container.sh stop
|
||||||
|
|
||||||
|
# Restart the container
|
||||||
|
./manage_container.sh restart
|
||||||
|
|
||||||
|
# View container logs
|
||||||
|
./manage_container.sh logs
|
||||||
|
|
||||||
|
# Check container status
|
||||||
|
./manage_container.sh status
|
||||||
|
|
||||||
|
# Remove the container
|
||||||
|
./manage_container.sh remove
|
||||||
|
```
|
||||||
|
|
||||||
|
### Container Features
|
||||||
|
|
||||||
|
- **Health Checks**: Automatic health monitoring
|
||||||
|
- **Auto-restart**: Container restarts automatically if it crashes
|
||||||
|
- **Volume Mounting**: Your markdown files are mounted into the container
|
||||||
|
- **Port Mapping**: Access the blog at http://localhost:8080
|
||||||
|
|
||||||
|
### Container Status
|
||||||
|
|
||||||
|
The status command shows:
|
||||||
|
- Container running state
|
||||||
|
- Health check status
|
||||||
|
- Access URL
|
||||||
|
|
||||||
|
### Troubleshooting
|
||||||
|
|
||||||
|
If the container fails to start:
|
||||||
|
1. Check the logs: `./manage_container.sh logs`
|
||||||
|
2. Verify Docker is running
|
||||||
|
3. Ensure port 8080 is available
|
||||||
|
4. Check the markdown directory path in `manage_container.sh`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 📝 Writing Posts
|
## 📝 Writing Posts
|
||||||
|
|
||||||
Add Markdown files to the `posts/` directory. Each post should have frontmatter:
|
Add Markdown files to the `posts/` directory. Each post should have frontmatter:
|
||||||
@@ -79,7 +146,9 @@ markdownblog/
|
|||||||
│ └── lib/ # Utility functions
|
│ └── lib/ # Utility functions
|
||||||
├── electron/ # Desktop app code
|
├── electron/ # Desktop app code
|
||||||
├── public/ # Static assets (favicons, etc.)
|
├── public/ # Static assets (favicons, etc.)
|
||||||
└── ...
|
├── Dockerfile # Docker configuration
|
||||||
|
├── .dockerignore # Docker ignore rules
|
||||||
|
└── manage_container.sh # Docker management script
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -99,12 +168,7 @@ Next.js will automatically serve these at the root URL (e.g., `/favicon.ico`).
|
|||||||
- [Electron](https://www.electronjs.org/)
|
- [Electron](https://www.electronjs.org/)
|
||||||
- [Remark](https://remark.js.org/) (Markdown)
|
- [Remark](https://remark.js.org/) (Markdown)
|
||||||
- [date-fns](https://date-fns.org/)
|
- [date-fns](https://date-fns.org/)
|
||||||
|
- [Docker](https://www.docker.com/)
|
||||||
---
|
|
||||||
|
|
||||||
## 🐳 Docker
|
|
||||||
|
|
||||||
A sample `Dockerfile` and `manage_container.sh` are included for containerized deployment.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -3,39 +3,199 @@
|
|||||||
# Configuration
|
# Configuration
|
||||||
IMAGE_NAME="markdown-blog"
|
IMAGE_NAME="markdown-blog"
|
||||||
CONTAINER_NAME="markdown-blog-container"
|
CONTAINER_NAME="markdown-blog-container"
|
||||||
PORT=3000
|
PORT=8080
|
||||||
MARKDOWN_DIR="/path/to/your/markdown" # Update this to your local markdown directory
|
MARKDOWN_DIR="/home/rattatwinko/Documents/shit/markdownblog" # Update this to your local markdown directory
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Function to check if Docker is running
|
||||||
|
check_docker() {
|
||||||
|
if ! docker info > /dev/null 2>&1; then
|
||||||
|
echo -e "${RED}Error: Docker is not running${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check if container exists
|
||||||
|
container_exists() {
|
||||||
|
docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check if container is running
|
||||||
|
container_running() {
|
||||||
|
docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check container health
|
||||||
|
check_container_health() {
|
||||||
|
local health_status
|
||||||
|
health_status=$(docker inspect --format='{{.State.Health.Status}}' $CONTAINER_NAME 2>/dev/null)
|
||||||
|
if [ "$health_status" = "healthy" ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Function to build the Docker image
|
# Function to build the Docker image
|
||||||
build_image() {
|
build_image() {
|
||||||
echo "Building Docker image..."
|
echo -e "${YELLOW}Building Docker image...${NC}"
|
||||||
docker build -t $IMAGE_NAME .
|
if ! docker build -t $IMAGE_NAME .; then
|
||||||
|
echo -e "${RED}Error: Failed to build Docker image${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}Docker image built successfully${NC}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to start the container
|
# Function to start the container
|
||||||
start_container() {
|
start_container() {
|
||||||
echo "Starting container..."
|
echo -e "${YELLOW}Starting container...${NC}"
|
||||||
docker run -d --name $CONTAINER_NAME -p $PORT:3000 -v $MARKDOWN_DIR:/markdown $IMAGE_NAME
|
|
||||||
|
# Check if container already exists
|
||||||
|
if container_exists; then
|
||||||
|
if container_running; then
|
||||||
|
echo -e "${YELLOW}Container is already running${NC}"
|
||||||
|
return
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Container exists but is not running. Starting it...${NC}"
|
||||||
|
if ! docker start $CONTAINER_NAME; then
|
||||||
|
echo -e "${RED}Error: Failed to start existing container${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Create and start new container
|
||||||
|
if ! docker run -d \
|
||||||
|
--name $CONTAINER_NAME \
|
||||||
|
-p $PORT:8080 \
|
||||||
|
-v "$MARKDOWN_DIR:/markdown" \
|
||||||
|
--restart unless-stopped \
|
||||||
|
--health-cmd="wget --no-verbose --tries=1 --spider http://localhost:8080/ || exit 1" \
|
||||||
|
--health-interval=30s \
|
||||||
|
--health-timeout=30s \
|
||||||
|
--health-retries=3 \
|
||||||
|
--health-start-period=5s \
|
||||||
|
$IMAGE_NAME; then
|
||||||
|
echo -e "${RED}Error: Failed to create and start container${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Wait for container to be ready
|
||||||
|
echo -e "${YELLOW}Waiting for container to be ready...${NC}"
|
||||||
|
local max_attempts=60
|
||||||
|
local attempt=1
|
||||||
|
|
||||||
|
while [ $attempt -le $max_attempts ]; do
|
||||||
|
if check_container_health; then
|
||||||
|
echo -e "${GREEN}Container is healthy and accessible at http://localhost:$PORT${NC}"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if container is still running
|
||||||
|
if ! container_running; then
|
||||||
|
echo -e "${RED}Error: Container stopped unexpectedly${NC}"
|
||||||
|
docker logs $CONTAINER_NAME
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -n "."
|
||||||
|
sleep 1
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -e "${RED}Error: Container did not become healthy in time${NC}"
|
||||||
|
docker logs $CONTAINER_NAME
|
||||||
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to stop the container
|
# Function to stop the container
|
||||||
stop_container() {
|
stop_container() {
|
||||||
echo "Stopping container..."
|
echo -e "${YELLOW}Stopping container...${NC}"
|
||||||
docker stop $CONTAINER_NAME
|
if ! container_exists; then
|
||||||
|
echo -e "${YELLOW}Container does not exist${NC}"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! docker stop $CONTAINER_NAME; then
|
||||||
|
echo -e "${RED}Error: Failed to stop container${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}Container stopped successfully${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to remove the container
|
||||||
|
remove_container() {
|
||||||
|
echo -e "${YELLOW}Removing container...${NC}"
|
||||||
|
if ! container_exists; then
|
||||||
|
echo -e "${YELLOW}Container does not exist${NC}"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if container_running; then
|
||||||
|
stop_container
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! docker rm $CONTAINER_NAME; then
|
||||||
|
echo -e "${RED}Error: Failed to remove container${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}Container removed successfully${NC}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to restart the container
|
# Function to restart the container
|
||||||
restart_container() {
|
restart_container() {
|
||||||
echo "Restarting container..."
|
echo -e "${YELLOW}Restarting container...${NC}"
|
||||||
docker restart $CONTAINER_NAME
|
if ! container_exists; then
|
||||||
|
echo -e "${YELLOW}Container does not exist. Starting new container...${NC}"
|
||||||
|
start_container
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! docker restart $CONTAINER_NAME; then
|
||||||
|
echo -e "${RED}Error: Failed to restart container${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}Container restarted successfully${NC}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to view logs
|
# Function to view logs
|
||||||
view_logs() {
|
view_logs() {
|
||||||
echo "Viewing logs..."
|
echo -e "${YELLOW}Viewing logs...${NC}"
|
||||||
docker logs $CONTAINER_NAME
|
if ! container_exists; then
|
||||||
|
echo -e "${RED}Error: Container does not exist${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker logs -f $CONTAINER_NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Function to show container status
|
||||||
|
show_status() {
|
||||||
|
echo -e "${YELLOW}Container Status:${NC}"
|
||||||
|
if ! container_exists; then
|
||||||
|
echo -e "${RED}Container does not exist${NC}"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if container_running; then
|
||||||
|
local health_status
|
||||||
|
health_status=$(docker inspect --format='{{.State.Health.Status}}' $CONTAINER_NAME 2>/dev/null)
|
||||||
|
echo -e "${GREEN}Container is running${NC}"
|
||||||
|
echo "Health status: $health_status"
|
||||||
|
echo "Access the application at: http://localhost:$PORT"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Container exists but is not running${NC}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check Docker is running before proceeding
|
||||||
|
check_docker
|
||||||
|
|
||||||
# Main script logic
|
# Main script logic
|
||||||
case "$1" in
|
case "$1" in
|
||||||
build)
|
build)
|
||||||
@@ -53,8 +213,14 @@ case "$1" in
|
|||||||
logs)
|
logs)
|
||||||
view_logs
|
view_logs
|
||||||
;;
|
;;
|
||||||
|
status)
|
||||||
|
show_status
|
||||||
|
;;
|
||||||
|
remove)
|
||||||
|
remove_container
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Usage: $0 {build|start|stop|restart|logs}"
|
echo "Usage: $0 {build|start|stop|restart|logs|status|remove}"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
17
next.config.js
Normal file
17
next.config.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {
|
||||||
|
output: 'standalone',
|
||||||
|
reactStrictMode: true,
|
||||||
|
swcMinify: true,
|
||||||
|
// Ensure we can access the app from outside the container
|
||||||
|
experimental: {
|
||||||
|
outputFileTracingRoot: undefined,
|
||||||
|
},
|
||||||
|
// Configure the hostname and port
|
||||||
|
server: {
|
||||||
|
hostname: '0.0.0.0',
|
||||||
|
port: 8080,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = nextConfig
|
||||||
19
package-lock.json
generated
19
package-lock.json
generated
@@ -22,6 +22,7 @@
|
|||||||
"tailwindcss": "^3.4.1"
|
"tailwindcss": "^3.4.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/chokidar": "^1.7.5",
|
||||||
"@types/node": "^20.11.19",
|
"@types/node": "^20.11.19",
|
||||||
"@types/react": "^18.2.57",
|
"@types/react": "^18.2.57",
|
||||||
"@types/react-dom": "^18.2.19",
|
"@types/react-dom": "^18.2.19",
|
||||||
@@ -1112,6 +1113,17 @@
|
|||||||
"@types/responselike": "^1.0.0"
|
"@types/responselike": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/chokidar": {
|
||||||
|
"version": "1.7.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/chokidar/-/chokidar-1.7.5.tgz",
|
||||||
|
"integrity": "sha512-PDkSRY7KltW3M60hSBlerxI8SFPXsO3AL/aRVsO4Kh9IHRW74Ih75gUuTd/aE4LSSFqypb10UIX3QzOJwBQMGQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/events": "*",
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/debug": {
|
"node_modules/@types/debug": {
|
||||||
"version": "4.1.12",
|
"version": "4.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
||||||
@@ -1121,6 +1133,13 @@
|
|||||||
"@types/ms": "*"
|
"@types/ms": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/events": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/fs-extra": {
|
"node_modules/@types/fs-extra": {
|
||||||
"version": "9.0.13",
|
"version": "9.0.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
|
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
"tailwindcss": "^3.4.1"
|
"tailwindcss": "^3.4.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/chokidar": "^1.7.5",
|
||||||
"@types/node": "^20.11.19",
|
"@types/node": "^20.11.19",
|
||||||
"@types/react": "^18.2.57",
|
"@types/react": "^18.2.57",
|
||||||
"@types/react-dom": "^18.2.19",
|
"@types/react-dom": "^18.2.19",
|
||||||
|
|||||||
@@ -1 +1,72 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
const postsDirectory = path.join(process.cwd(), 'posts');
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
try {
|
||||||
|
const body = await request.json();
|
||||||
|
const { name, path: folderPath } = body;
|
||||||
|
|
||||||
|
// Create the full path for the new folder
|
||||||
|
const fullPath = path.join(postsDirectory, folderPath, name);
|
||||||
|
|
||||||
|
// Check if folder already exists
|
||||||
|
if (fs.existsSync(fullPath)) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Folder already exists' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the folder
|
||||||
|
fs.mkdirSync(fullPath, { recursive: true });
|
||||||
|
|
||||||
|
return NextResponse.json({ success: true });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating folder:', error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Error creating folder' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function DELETE(request: Request) {
|
||||||
|
try {
|
||||||
|
const body = await request.json();
|
||||||
|
const { name, path: folderPath } = body;
|
||||||
|
|
||||||
|
// Create the full path for the folder to delete
|
||||||
|
const fullPath = path.join(postsDirectory, folderPath, name);
|
||||||
|
|
||||||
|
// Check if folder exists
|
||||||
|
if (!fs.existsSync(fullPath)) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Folder does not exist' },
|
||||||
|
{ status: 404 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if folder is empty
|
||||||
|
const files = fs.readdirSync(fullPath);
|
||||||
|
if (files.length > 0) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Cannot delete non-empty folder' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the folder
|
||||||
|
fs.rmdirSync(fullPath);
|
||||||
|
|
||||||
|
return NextResponse.json({ success: true });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting folder:', error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Error deleting folder' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import matter from 'gray-matter';
|
|||||||
import { remark } from 'remark';
|
import { remark } from 'remark';
|
||||||
import html from 'remark-html';
|
import html from 'remark-html';
|
||||||
import chokidar from 'chokidar';
|
import chokidar from 'chokidar';
|
||||||
|
import type { FSWatcher } from 'chokidar';
|
||||||
|
|
||||||
export interface Post {
|
export interface Post {
|
||||||
slug: string;
|
slug: string;
|
||||||
@@ -64,8 +65,9 @@ export async function getPostsByTag(tag: string): Promise<Post[]> {
|
|||||||
const allPosts = await getAllPosts();
|
const allPosts = await getAllPosts();
|
||||||
return allPosts.filter((post) => post.tags.includes(tag));
|
return allPosts.filter((post) => post.tags.includes(tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
// File watcher setup
|
// File watcher setup
|
||||||
let watcher: chokidar.FSWatcher | null = null;
|
let watcher: FSWatcher | null = null;
|
||||||
let onChangeCallback: (() => void) | null = null;
|
let onChangeCallback: (() => void) | null = null;
|
||||||
|
|
||||||
export function watchPosts(callback: () => void) {
|
export function watchPosts(callback: () => void) {
|
||||||
|
|||||||
Reference in New Issue
Block a user