- JavaScript 63.4%
- CSS 18.3%
- C# 16.6%
- Dockerfile 1.2%
- HTML 0.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| elliebot.net | ||
| .gitignore | ||
| CHANGELOG.md | ||
| compose.yaml | ||
| DEPLOY.md | ||
| Dockerfile | ||
| elliebot.net.conf | ||
| elliebot.net.sln | ||
| global.json | ||
| LICENSE | ||
| README.md | ||
elliebot.net
A website for the EllieBot Discord bot. Built with ASP.NET Core 8 on the backend and Vue 3 (via ES modules, no build step) on the frontend.
Features
Public
- Command reference — searchable table of all bot commands loaded from
commandlist.json, with expandable rows showing usage examples, options, requirements, and submodule info. Commands display in original file order and are grouped by module via a sidebar filter. - Blog — published posts with full Markdown rendering, author cards with avatars, and clean slug-based URLs (
/blog/my-post-title) - About — includes a version history timeline and a commands-per-module bar chart
- Developers — team cards with optional uploaded avatars; single card is centred automatically
- 404 page — unknown routes show a proper not-found page
- Dark mode — calm dark theme throughout
- URL routing — all pages have clean URLs via the History API; refreshing or sharing a link always lands on the correct page
Staff
- Login — JWT-authenticated login; sessions persist across page refreshes
- Post editor — write posts in Markdown with a live preview tab; converts to HTML on save. Supports headings, bold, italic, inline code, fenced code blocks, links, images, blockquotes, and lists
- Staff dashboard — manage all posts (edit, delete, publish/draft status); admins also have a Team tab to add and remove staff accounts
- User settings — each staff member can update their display name, change their password, and upload a profile avatar. Avatars appear on developer cards and blog post author cards
- Role system — two roles:
author(own posts only) andadmin(all posts + team management)
Tech Stack
| Layer | Technology |
|---|---|
| Backend | ASP.NET Core 8 minimal API |
| Frontend | Vue 3 (ES modules, CDN — no bundler) |
| Auth | JWT (HS256, 7-day tokens) |
| Storage | Flat JSON files (posts, staff accounts, avatars) |
| Markdown | Custom parser (md.js) — no dependencies |
| Reverse proxy | nginx (host) with Let's Encrypt |
| Container | Docker + Docker Compose |
Project Structure
elliebot.net/
├── Program.cs # All API routes and backend logic
├── elliebot.net.csproj
├── Dockerfile
├── appsettings.json
├── libs/ # Local JWT library DLLs
│ ├── Microsoft.IdentityModel.Abstractions.dll
│ ├── Microsoft.IdentityModel.JsonWebTokens.dll
│ ├── Microsoft.IdentityModel.Logging.dll
│ ├── Microsoft.IdentityModel.Tokens.dll
│ └── System.IdentityModel.Tokens.Jwt.dll
├── data/
│ └── staff.json # Staff accounts (created on first run)
└── wwwroot/
├── index.html # SPA shell
├── commandlist.json # Bot command data
├── posts/ # Blog posts — one .json file per post
├── avatars/ # Uploaded staff avatars
├── css/
│ ├── variables.css # Design tokens (:root CSS variables)
│ ├── layout.css # Nav, page shell, footer, transitions
│ ├── components.css # Buttons, badges, cards, module colours
│ └── pages.css # Per-page styles including version timeline
└── js/
├── app.js # Vue app, URL routing, nav
├── data.js # Static content (features, devs, about sections)
├── md.js # Markdown → HTML parser
└── pages/
├── Home.js
├── Commands.js
├── About.js
├── Developers.js
├── Blog.js
├── BlogPost.js
├── Login.js
├── StaffDashboard.js
├── PostEditor.js
├── Settings.js
└── NotFound.js
docker-compose.yml
nginx-elliebot.conf # Drop into /etc/nginx/sites-available/
DEPLOY.md # Full deployment instructions
URL Scheme
| URL | Page |
|---|---|
/ |
Home |
/commands |
Command reference |
/commands/:module |
Commands filtered to a module |
/about |
About |
/developers |
Developers |
/blog |
Blog list |
/blog/:slug |
Individual blog post |
/staff/login |
Staff login |
/staff/dashboard |
Post management + team admin |
/staff/editor |
New post |
/staff/editor/:id |
Edit existing post |
/staff/settings |
Account settings (display name, password, avatar) |
API Reference
Public
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/commands |
Full command list JSON |
GET |
/api/posts |
All published posts (summary fields — no body) |
GET |
/api/posts/{slug} |
Single published post with full body. Also accepts id for backwards compatibility |
GET |
/api/staff/members |
Public staff list — id, displayName, role, avatarUrl only |
Auth
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/auth/login |
Login — returns JWT token and user info |
GET |
/api/auth/me |
Currently authenticated user including avatarUrl |
PUT |
/api/auth/me |
Update display name and/or password — returns a fresh token |
POST |
/api/auth/avatar |
Upload avatar image (jpg/png/gif/webp, max 2 MB) |
Staff (requires valid JWT)
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/staff/posts |
All posts — admins see all, authors see only their own |
GET |
/api/staff/posts/{id} |
Single post including unpublished |
POST |
/api/staff/posts |
Create a new post |
PUT |
/api/staff/posts/{id} |
Update a post |
DELETE |
/api/staff/posts/{id} |
Delete a post (authors can only delete their own unpublished drafts) |
Admin (requires JWT with admin role)
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/admin/staff |
List all staff accounts |
POST |
/api/admin/staff |
Create a new staff account |
DELETE |
/api/admin/staff/{id} |
Remove a staff account |
POST |
/api/admin/staff/{id}/password |
Reset a staff member's password |
Staff Roles
| Role | Permissions |
|---|---|
author |
Create posts, edit and delete their own unpublished drafts, manage their own avatar and display name |
admin |
Everything an author can do, plus publish/unpublish any post, delete any post, and manage staff accounts |
Admins cannot delete their own account.
Markdown Support
Posts are written in Markdown in the editor and converted to HTML on save. Supported syntax:
| Syntax | Output |
|---|---|
# Heading — ###### Heading |
<h1> — <h6> |
**bold** or __bold__ |
<strong> |
*italic* or _italic_ |
<em> |
`code` |
<code> |
```lang ... ``` |
<pre><code> fenced block |
~~strikethrough~~ |
<del> |
[text](url) |
<a> |
 |
<img> |
> quote |
<blockquote> |
- item / * item / + item |
<ul> |
1. item |
<ol> |
--- |
<hr> |
Legacy posts saved as raw HTML before Markdown support was added are loaded and displayed as-is.
Docker Volumes
Three named volumes persist data across container rebuilds:
| Volume | Mount path | Contents |
|---|---|---|
posts_data |
/app/wwwroot/posts |
Blog post JSON files |
avatars_data |
/app/wwwroot/avatars |
Staff avatar images |
staff_data |
/app/data |
Staff accounts (staff.json) |
Running Locally
cd elliebot.net
dotnet run
The site is available at http://localhost:5000.
On first run with no data/staff.json present, create one manually:
[
{
"id": "1",
"username": "user1",
"displayName": "Example User",
"role": "admin",
"passwordHash": "demo:password123"
}
]
The demo: prefix stores the password in plain text for development. All passwords set through the UI are stored as SHA-256 hashes.
Deployment
See DEPLOY.md for full instructions. The short version:
# 1. Set a strong JWT secret in docker-compose.yml
# 2. Start the container
docker compose up -d --build
# 3. Configure nginx
sudo cp nginx-elliebot.conf /etc/nginx/sites-available/elliebot
sudo ln -s /etc/nginx/sites-available/elliebot /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
# 4. Get a certificate
sudo certbot --nginx -d yourdomain.com
Configuration
| Setting | Where | Default | Description |
|---|---|---|---|
Jwt__Secret |
docker-compose.yml env |
Random on startup | JWT signing secret — set a fixed value in production or all sessions are invalidated on every restart |
ASPNETCORE_ENVIRONMENT |
Dockerfile / docker-compose.yml |
Production |
Set to Development for detailed error pages |
ASPNETCORE_URLS |
Dockerfile | http://+:8080 |
Port the app listens on inside the container |
Important: always set a fixed
Jwt__Secretin production. If it is left unset, a new random key is generated on each startup and all existing login sessions will be invalidated.
Updating the Command List
Replace wwwroot/commandlist.json with the updated file. The structure must be an object keyed by module name, each containing an array of command objects:
{
"ModuleName": [
{
"Aliases": ["commandname", "alias"],
"Description": "What this command does",
"Usage": [".commandname <arg>"],
"Submodule": "optional",
"Module": "ModuleName",
"Options": [],
"Requirements": ["ManageMessages"]
}
]
}
Commands are displayed in the same order they appear in the file. If running in Docker, copy the new file into the container or mount it as a volume.