feat: Initial project setup with Wiki.js and management scripts

- Add Docker Compose setup for Wiki.js and PostgreSQL.
- Include setup and backup scripts for easy management.
- Create command-line scripts to create and edit wiki pages via the GraphQL API.
- Add comprehensive README and .gitignore.
This commit is contained in:
Aodhan Collins
2025-09-08 02:22:20 +01:00
parent a2adb89d93
commit d7b0f6fee0
6 changed files with 329 additions and 140 deletions

13
.env
View File

@@ -1,13 +0,0 @@
# Database Configuration
# Generate a secure password for your database
# You can use: openssl rand -base64 32
DB_PASSWORD=your_secure_database_password_here
# Wiki.js Configuration
# The domain where your wiki will be accessible
# For local development, use localhost:3000
# For production, use your actual domain
WIKI_URL=http://localhost:3000
# Optional: Set timezone (default is UTC)
TZ=Europe/London

18
.gitignore vendored Normal file
View File

@@ -0,0 +1,18 @@
# Environment variables
.env
# Python
venv/
__pycache__/
*.pyc
# Backups
backups/
# Schema file
introspectionSchema.json
# IDE / OS specific
.vscode/
.idea/
.DS_Store

202
README.md
View File

@@ -1,155 +1,103 @@
# Personal Wiki.js Setup for Home Server
# Personal Wiki.js Project
This repository contains everything you need to set up your personal Wiki.js instance on your home server.
This repository contains everything needed to set up, manage, and programmatically interact with a personal Wiki.js instance using Docker and Python scripts.
## 🚀 Quick Start
1. **Run the setup script:**
```bash
./setup.sh
```
1. **Set Up Environment**: Ensure you have `docker` and `docker-compose` installed. The `setup.sh` script will check for these.
2. **Access your wiki:**
- Open http://localhost:3000 in your browser
- Complete the initial setup wizard
- Create your administrator account
2. **Run Setup Script**: This script generates a secure database password, creates necessary directories, and starts the wiki.
```bash
./setup.sh
```
3. **Access Your Wiki**: Open your browser and navigate to the URL specified in your `.env` file (e.g., `http://localhost:3000` or `https://wiki.liveaodh.com`). Complete the initial setup wizard and create your administrator account.
## 📁 Project Structure
```
wikiaodh/
├── docker-compose.yml # Docker services configuration
├── .env # Environment variables (passwords, etc.)
├── setup.sh # Automated setup script
├── README.md # This file
── data/ # Created automatically for persistent data
├── wiki/ # Wiki application data
├── db/ # PostgreSQL database files
└── assets/ # Uploaded media and assets
├── .env # Environment variables (passwords, API key, etc.)
├── setup.sh # Automated setup script
├── backup.sh # Backup script for database and assets
── README.md # This file
├── requirements.txt # Python dependencies for scripts
├── venv/ # Python virtual environment
└── scripts/
├── create_page.py # CLI tool to create new wiki pages
└── edit_page.py # CLI tool to edit existing wiki pages
```
## 🔧 Configuration
## 🔧 Wiki Management
### Environment Variables (.env)
- `DB_PASSWORD`: Secure password for PostgreSQL database
- `WIKI_URL`: The URL where your wiki will be accessible
- `TZ`: Your timezone (default: Europe/London)
### Docker Commands
### Ports
- **3000**: Wiki.js web interface (http://localhost:3000)
- **5432**: PostgreSQL database (internal only)
- **Start the wiki**: `docker-compose up -d`
- **Stop the wiki**: `docker-compose down`
- **View logs**: `docker-compose logs -f`
- **Restart services**: `docker-compose restart`
- **Update to latest version**: `docker-compose pull && docker-compose up -d`
## 📋 Management Commands
### Backup and Restore
- **Run a full backup**: The `backup.sh` script creates a timestamped backup of your database and assets in the `backups/` directory.
```bash
./backup.sh
```
## 🤖 Command-Line Scripts
These scripts use the Wiki.js GraphQL API to manage content programmatically. They require a Python virtual environment and an API key.
### Setup
1. **Activate Virtual Environment**:
```bash
source venv/bin/activate
```
2. **Set API Key**: Generate an API key in the Wiki.js **Administration -> API Access** area and add it to your `.env` file:
```
WIKI_API_KEY=your_api_key_here
```
### `scripts/create_page.py`
Creates a new page in your wiki.
**Usage**:
```bash
# Start the wiki
docker-compose up -d
# Stop the wiki
docker-compose down
# View logs
docker-compose logs -f
# Restart services
docker-compose restart
# Update to latest version
docker-compose pull
docker-compose up -d
python3 scripts/create_page.py --path "my-folder/my-new-page" --title "My New Page" --content-file "path/to/my-content.md"
```
## 💾 Backup & Restore
**Arguments**:
- `--path` (required): The URL path for the new page.
- `--title` (required): The title of the page.
- `--content-file` (required): Path to a markdown file with the page content.
- `--tags` (optional): A list of tags (e.g., `--tags project guide`).
### Backup
### `scripts/edit_page.py`
Edits an existing page in your wiki.
**Usage**:
```bash
# Backup database
docker-compose exec db pg_dump -U wikijs wiki > backup_$(date +%Y%m%d).sql
# Update content
python3 scripts/edit_page.py --path "home" --content-file "new_content.md"
# Backup uploaded files
tar -czf assets_backup_$(date +%Y%m%d).tar.gz data/assets/
# Update title
python3 scripts/edit_page.py --path "home" --title "New Home Page Title"
# Update both
python3 scripts/edit_page.py --path "home" --title "New Title" --content-file "new_content.md"
```
### Restore
```bash
# Restore database
docker-compose exec -T db psql -U wikijs wiki < backup_YYYYMMDD.sql
# Restore uploaded files
tar -xzf assets_backup_YYYYMMDD.tar.gz
```
## 🔒 Security Considerations
1. **Change default passwords**: The setup script generates a secure database password automatically
2. **Firewall**: Consider restricting access to port 3000 to your local network only
3. **HTTPS**: For production use, set up a reverse proxy with SSL/TLS
4. **Regular updates**: Keep Docker images updated with `docker-compose pull`
## 🌐 Accessing from Other Devices
To access your wiki from other devices on your network:
1. Find your server's IP address: `ip addr show`
2. Update the `WIKI_URL` in `.env` to use your server's IP
3. Access via `http://YOUR_SERVER_IP:3000`
## 📚 Wiki.js Features
Your personal wiki includes:
- **Rich Text Editor**: WYSIWYG and Markdown support
- **Media Management**: Upload and organize images, documents, videos
- **Search**: Full-text search across all content
- **User Management**: Create accounts for family members or team
- **Themes**: Light and dark mode support
- **Mobile Friendly**: Responsive design for all devices
- **Version History**: Track changes to all pages
- **Categories & Tags**: Organize content efficiently
## 🆘 Troubleshooting
### Wiki won't start
```bash
# Check logs
docker-compose logs
# Restart services
docker-compose down
docker-compose up -d
```
### Database connection issues
```bash
# Reset database
docker-compose down
docker volume rm wikiaodh_db-data
docker-compose up -d
```
### Permission issues
```bash
# Fix permissions
sudo chown -R $USER:$USER data/
chmod -R 755 data/
```
## 🔄 Updates
To update Wiki.js to the latest version:
```bash
docker-compose down
docker-compose pull
docker-compose up -d
```
## 📞 Support
- [Wiki.js Documentation](https://docs.js.wiki/)
- [Wiki.js GitHub](https://github.com/Requarks/wiki)
- [Docker Compose Documentation](https://docs.docker.com/compose/)
**Arguments**:
- `--path` (required): The path of the page to edit.
- `--content-file` (optional): Path to a markdown file with the new content.
- `--title` (optional): A new title for the page.
---
**Enjoy your personal wiki! 📖✨**
**Enjoy your powerful, scriptable personal wiki! 📖✨**

2
requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
python-dotenv
requests

101
scripts/create_page.py Normal file
View File

@@ -0,0 +1,101 @@
import os
import requests
import argparse
from dotenv import load_dotenv
# Load environment variables from .env file
# This will search for a .env file in the current directory or parent directories
load_dotenv()
# --- Configuration ---
WIKI_URL = os.getenv("WIKI_URL")
API_KEY = os.getenv("WIKI_API_KEY")
# --- GraphQL Mutation ---
GRAPHQL_CREATE_MUTATION = """
mutation CreatePage($content: String!, $description: String!, $editor: String!, $isPrivate: Boolean!, $isPublished: Boolean!, $locale: String!, $path: String!, $tags: [String]!, $title: String!) {
pages {
create(content: $content, description: $description, editor: $editor, isPrivate: $isPrivate, isPublished: $isPublished, locale: $locale, path: $path, tags: $tags, title: $title) {
responseResult { succeeded, errorCode, slug, message }
page { id, path, title }
}
}
}
"""
def create_wiki_page(path, title, content_file, tags, locale):
"""Sends a GraphQL request to create a new wiki page."""
if not all([API_KEY, WIKI_URL]):
print("❌ Error: WIKI_API_KEY and/or WIKI_URL not found in environment variables.")
return
try:
with open(content_file, 'r') as f:
content = f.read()
except FileNotFoundError:
print(f"❌ Error: Content file not found at '{content_file}'")
return
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
variables = {
"content": content,
"description": f"Page for {title}",
"editor": "markdown",
"isPrivate": False,
"isPublished": True,
"locale": locale,
"path": path,
"tags": tags or [],
"title": title
}
graphql_query = {
"query": GRAPHQL_CREATE_MUTATION,
"variables": variables
}
print(f"🚀 Sending request to {WIKI_URL}/graphql to create page at path '{path}'...")
try:
response = requests.post(f"{WIKI_URL}/graphql", headers=headers, json=graphql_query, timeout=15)
response.raise_for_status()
result = response.json()
if 'errors' in result:
print("❌ GraphQL API returned errors:")
for error in result['errors']:
print(f" - {error.get('message')}")
return
creation_result = result.get('data', {}).get('pages', {}).get('create', {})
response_result = creation_result.get('responseResult', {})
if response_result.get('succeeded'):
page_info = creation_result.get('page', {})
print(f"✅ Success! Page '{page_info.get('title')}' created.")
print(f" - ID: {page_info.get('id')}")
print(f" - Path: {page_info.get('path')}")
print(f"🌐 View it at: {WIKI_URL}/{page_info.get('path')}")
else:
print(f"❌ API call failed: {response_result.get('message')}")
print(f" - Error Code: {response_result.get('errorCode')}")
except requests.exceptions.RequestException as e:
print(f"❌ An error occurred while connecting to the wiki: {e}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Create a new page in Wiki.js.")
parser.add_argument('--path', required=True, help='The path for the new page (e.g., \"my-folder/my-page\").')
parser.add_argument('--title', required=True, help='The title of the new page.')
parser.add_argument('--content-file', required=True, help='Path to the markdown file containing the page content.')
parser.add_argument('--tags', nargs='*', help='A list of tags to add to the page.')
parser.add_argument('--locale', default='en', help='The locale for the page (default: en).')
args = parser.parse_args()
create_wiki_page(args.path, args.title, args.content_file, args.tags, args.locale)

133
scripts/edit_page.py Normal file
View File

@@ -0,0 +1,133 @@
import os
import requests
import argparse
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# --- Configuration ---
WIKI_URL = os.getenv("WIKI_URL")
API_KEY = os.getenv("WIKI_API_KEY")
# --- GraphQL Queries & Mutations ---
GRAPHQL_LIST_ALL_PAGES_QUERY = """
query AllPages {
pages {
list {
id
path
}
}
}
"""
GRAPHQL_UPDATE_MUTATION = """
mutation UpdatePage($id: Int!, $content: String, $description: String, $tags: [String], $title: String) {
pages {
update(id: $id, content: $content, description: $description, tags: $tags, title: $title) {
responseResult { succeeded, errorCode, slug, message }
page { id, path, title }
}
}
}
"""
def run_graphql_query(query, variables):
"""Helper function to run a GraphQL query."""
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
try:
response = requests.post(f"{WIKI_URL}/graphql", headers=headers, json={"query": query, "variables": variables}, timeout=15)
response.raise_for_status()
result = response.json()
if 'errors' in result:
print("❌ GraphQL API returned errors:")
for error in result['errors']:
print(f" - {error.get('message')}")
return None
return result
except requests.exceptions.RequestException as e:
print(f"❌ An error occurred: {e}")
return None
def get_page_id(path_to_find, locale):
"""Get the ID of a page by fetching all pages and searching for its path."""
print("🔎 Fetching all pages to find the correct ID...")
result = run_graphql_query(GRAPHQL_LIST_ALL_PAGES_QUERY, {})
if not result:
print("❌ Failed to fetch the page list.")
return None
all_pages = result.get('data', {}).get('pages', {}).get('list', [])
if not all_pages:
print("❌ No pages were found on the wiki.")
return None
for page in all_pages:
if page.get('path') == path_to_find:
page_id = page.get('id')
print(f"📄 Found page with ID: {page_id} for path: '{path_to_find}'")
return page_id
print(f"❌ A page with the path '{path_to_find}' was not found.")
return None
def edit_wiki_page(path, content_file, new_title, locale):
"""Updates an existing wiki page."""
if not all([API_KEY, WIKI_URL]):
print("❌ Error: WIKI_API_KEY and/or WIKI_URL not found in environment variables.")
return
page_id = get_page_id(path, locale)
if not page_id:
return
variables = {
"id": page_id,
"description": f"Page at path {path} updated via script.",
"tags": []
}
if content_file:
try:
with open(content_file, 'r') as f:
variables['content'] = f.read()
print(f"🔄 Preparing to update content from '{content_file}'...")
except FileNotFoundError:
print(f"❌ Error: Content file not found at '{content_file}'")
return
if new_title:
variables['title'] = new_title
print(f"🔄 Preparing to update title to '{new_title}'...")
if 'content' not in variables and 'title' not in variables:
print("✨ Nothing to update. Please provide a new title or content file.")
return
result = run_graphql_query(GRAPHQL_UPDATE_MUTATION, variables)
if not result:
return
op_result = result.get('data', {}).get('pages', {}).get('update', {})
response_result = op_result.get('responseResult', {})
if response_result.get('succeeded'):
page_info = op_result.get('page', {})
print(f"✅ Success! Page '{page_info.get('title')}' updated.")
print(f"🌐 View it at: {WIKI_URL}/{page_info.get('path')}")
else:
print(f"❌ API call failed: {response_result.get('message')}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Edit an existing page in Wiki.js.")
parser.add_argument('--path', required=True, help='The path of the page to edit (e.g., \"home\").')
parser.add_argument('--content-file', help='Path to a markdown file with the new content.')
parser.add_argument('--title', help='A new title for the page.')
parser.add_argument('--locale', default='en', help='The locale of the page (default: en).')
args = parser.parse_args()
edit_wiki_page(args.path, args.content_file, args.title, args.locale)