Personal portfolio and small admin CMS: public pages (index.php, about.php, contact.php), contact form, and an /admin area for projects and profile content.
- Docker and Docker Compose v2
From the repository root:
docker compose up -d| Service | URL / port |
|---|---|
| Website | http://localhost:8080 |
| Admin | http://localhost:8080/admin/ |
| MySQL (host) | localhost:3306 — user root, password root, database ayatsuba_mmdd |
If the site does not load but docker compose ps shows web as running, check docker compose logs web. If you see endless Waiting for MySQL at db..., the web image’s MySQL client was failing TLS to the DB container (fixed in docker/docker-php-entrypoint.sh with --skip-ssl). Recreate the web service after pulling the latest repo: docker compose up -d web.
Default admin user (seeded on first DB init only):
- Email:
admin@local.test - Password:
admin
Stop containers:
docker compose downRemove containers and the MySQL volume (fresh database, docker/init.sql runs again on next up):
docker compose down -v-
First-time MySQL startup loads
docker/init.sql(schema + sample categories + admin user). -
If you already have a dump from production, import it instead, for example:
docker compose exec -T db mysql -uroot -proot ayatsuba_mmdd < your-backup.sql
-
Uploads go to
upload/. If the admin uploader fails with permission errors, fix ownership or permissions on that folder on your machine (the project directory is bind-mounted into the container).
On each start, the web container runs docker/ensure_portfolio_users_columns.sql against MySQL (idempotent). That adds any missing portfolio_users columns (resume, linkedin_url, instagram_url, x_url, github_url, about_skills) so older volumes stay compatible after you docker compose build web && docker compose up -d.
To patch the database without rebuilding (e.g. you are on the old image without the entrypoint), run once from the project root:
docker compose exec -T db mysql -uroot -proot ayatsuba_mmdd < docker/ensure_portfolio_users_columns.sqlThe app also runs a small self-migration after each DB connection (includes/self_migrate_portfolio_users.php): if optional columns such as about_skills are missing on portfolio_users, it issues ALTER TABLE automatically (requires DB user permission to alter that table).
Legacy one-off migrations (docker/migration_add_resume.sql, docker/migration_add_social_urls.sql) are optional if you use the ensure script above.
The contact form and password reset use PHP mail(). In Docker, mail usually does not send unless you add an MTA or a dev mail catcher. Inserts into portfolio_contacts may still succeed when the form validates.
Use any PHP 7+ / 8+ stack with Apache (or nginx + php-fpm), MySQL, and the mysqli extension. Connection settings live in includes/database.php:
- With no environment variables set, it uses
localhost,root,root, and databaseayatsuba_mmdd(typical local MAMP-style defaults). - To override, set
DB_HOST,DB_USER,DB_PASSWORD, andDB_NAME(the Docker Compose file sets these for thewebservice).
Create the database and tables yourself, or import a SQL dump. The Docker docker/init.sql file is a reference schema if you need one.
Choose a category when creating a project; the form requires it and the server rejects a missing category.
Under My Page (admin/user_edit.php) you can edit your name, profile photo, about HTML, upload a resume file (stored in upload/), set LinkedIn, Instagram, X, and GitHub profile URLs, and choose which skill icons appear on the About page (HTML, CSS, JavaScript, etc.). The About page shows a resume download link only when a resume file is saved. Footer social icons on the public site use these URLs (user id 1); icons are hidden for empty fields.
For non-Docker MySQL, run the statements in docker/ensure_portfolio_users_columns.sql once (they are safe to repeat), or copy the ALTER/information_schema logic into your migration tool.
Note: project GitHub links in the CMS are stored on each project (portfolio_projects.github_url), separate from your profile GitHub URL in the footer.
Project content belongs to the site owner; use this codebase according to your own policies.