Important
Built this in a weekend as a quick custom playground for my own site. Code is intentionally hyper-focused on my personal setup and highly unoptimized, but it gets the job done!
My developer portfolio and blog built with React, Next.js, Markdown content, and a MySQL-backed admin/auth system that works well on Node.js hosting such as Plesk.
- React
- Next.js App Router
- TypeScript
- Tailwind CSS
- Markdown articles with frontmatter
- MySQL
- Hashed passwords with
bcryptjs
Numbered Dev is intentionally designed to feel:
- dark
- sharp
- flat
- modern
- technical
That means:
- sharp corners
- minimal visual effects
- no fluffy glow
- no soft card-heavy styling
- clean borders
- strong typography
- restrained accent color
- Portfolio-style landing page
- Blog powered by Markdown files in
content/posts - Individual article pages
- Lightweight admin panel
- MySQL-backed admin accounts, sessions, links, assets, post views, and reactions
- Hashed admin password storage
- Draft and published post support
- Flat, modern developer-focused UI
blog/
├── content/
│ └── posts/ # Markdown blog posts
├── public/ # Static assets
└── src/
├── app/ # Next.js routes and API handlers
├── components/ # UI components
└── lib/ # Content and database helpers
Install dependencies:
npm installUse the included Compose file instead of a custom MySQL Dockerfile:
docker compose up -d
docker compose logs -f mysqlIf you previously started the container with an older broken config, remove the old container and volume first:
docker compose down -v
docker compose up -dThe local MySQL container uses these defaults:
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_DATABASE=numbered_dev
MYSQL_USER=numbered_dev_user
MYSQL_PASSWORD=change-thisThe included Compose setup is based on mysql:8.4 and does not require the deprecated default-authentication-plugin=mysql_native_password option.
Run the development server:
npm run devOpen:
http://localhost:3000
Create a .env.local file in the project root.
Example:
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_DATABASE=numbered_dev
MYSQL_USER=numbered_dev_user
MYSQL_PASSWORD=change-this
SESSION_COOKIE_NAME=numbered-dev-admin-session
ADMIN_USERNAME=admin
ADMIN_PASSWORD=change-this-immediatelyNEXT_PUBLIC_SITE_URL=http://localhost:3000- The app uses MySQL for runtime persistence.
- The database schema is created automatically when the app starts and connects successfully.
- The initial admin user is created automatically from
ADMIN_USERNAMEandADMIN_PASSWORDif that user does not already exist. - If the configured admin already exists, the app leaves it unchanged.
- You should use a strong MySQL password and a strong admin password in production.
Open:
http://localhost:3000/admin
Sign in with the credentials from your environment variables.
The admin area lets you:
- create posts
- edit posts
- delete posts
- manage projects
- manage links
- upload and manage assets
Posts are stored as Markdown files under:
content/posts
Each post uses frontmatter like this:
---
title: "My Post Title"
excerpt: "Short summary for cards and previews."
date: "2026-03-09"
published: true
tags:
- react
- architecture
cover: "/optional-image.svg"
---
# Heading
Write your article here.You can manage content in two ways:
Use the admin panel to create and update posts without editing files manually.
Edit files in content/posts if you prefer a file-based workflow.
MySQL is used for application persistence.
Current usage includes:
- admin accounts
- admin sessions
- social/profile links
- uploaded asset metadata
- post views
- post reactions
The schema is created automatically by the application at runtime when the database connection is available.
If you use the local Docker MySQL setup above, make sure the container is running before starting the app. If MySQL fails to start because of an old data directory or an invalid server option from an earlier config, run:
docker compose down -v
docker compose up -dThis removes the old local MySQL volume and recreates the database cleanly.
Admin authentication uses:
- hashed passwords
- session tokens
- expiring admin sessions
This keeps the setup lightweight while still covering the basics expected from a private admin surface.
npm run dev
npm run build
npm run start
npm run lintA typical Plesk deployment looks like this:
- Create a Node.js app in Plesk
- Create a MySQL database in Plesk
- Add the required environment variables in Plesk
- Install dependencies
- Build the app
- Start or restart the app
- Open
/adminand sign in with the admin credentials from the environment
Make sure the runtime can write to:
content/posts/public/assets/
If your hosting setup restricts filesystem writes, admin content creation and uploads will not work until those directories are writable.
- Set
NODE_ENV=production - Use HTTPS
- Use a strong MySQL password
- Use a strong admin password
- Restrict access to the admin area where possible
- Back up both the MySQL database and your Markdown/content files
This project is optimized for a solo developer workflow:
- Markdown keeps writing portable and versionable
- MySQL fits shared hosting and Plesk environments better than SQLite
- Next.js keeps the public site and admin in one codebase
- The UI stays focused on clean presentation instead of decorative effects
Numbered Dev is meant to feel like software built by an engineer:
- precise
- structured
- readable
- modern
- useful
No weird shadows. No glowing noise. Just a clean, sharp, dark system.
Private project unless you choose otherwise.