diff --git a/.env.docker b/.env.docker index 778f842..e384e45 100644 --- a/.env.docker +++ b/.env.docker @@ -6,13 +6,13 @@ PORT=3000 # Database (MySQL container) MYSQL_ROOT_PASSWORD=rootpassword -MYSQL_DATABASE=openplace -MYSQL_USER=openplace -MYSQL_PASSWORD=openplacepassword +MYSQL_DATABASE=FurryPlace +MYSQL_USER=FurryPlace +MYSQL_PASSWORD=FurryPlacepassword MYSQL_PORT=3306 # Database URL (used by Prisma) -DATABASE_URL="mysql://openplace:openplacepassword@mysql:3306/openplace" +DATABASE_URL="mysql://FurryPlace:FurryPlacepassword@mysql:3306/FurryPlace" # JWT Secret (CHANGE THIS IN PRODUCTION!) JWT_SECRET="your-secret-key-change-in-production" diff --git a/.env.example b/.env.example index de178b9..1693ebd 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,9 @@ PORT=3000 -DATABASE_URL="mysql://root:password@localhost/openplace" +DATABASE_URL="mysql://root:password@localhost/FurryPlace" JWT_SECRET="your-secret-key" + +# Google OAuth +GOOGLE_CLIENT_ID="your-google-client-id" +GOOGLE_CLIENT_SECRET="your-google-client-secret" +GOOGLE_CALLBACK_URL="http://localhost:3000/auth/google/callback" diff --git a/.gitmodules b/.gitmodules index 52e7841..678fbc3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "frontend"] path = frontend - url = https://github.com/openplaceteam/frontend + url = https://github.com/FurryPlaceteam/frontend diff --git a/ADMIN-CONTENT-EDITOR.md b/ADMIN-CONTENT-EDITOR.md new file mode 100644 index 0000000..6ebbc01 --- /dev/null +++ b/ADMIN-CONTENT-EDITOR.md @@ -0,0 +1,84 @@ +# Admin Content Editor Access + +## Quick Access + +The site content management editor is now available at: + +**URL:** `http://localhost:3000/_app/admin-content-editor.html` + +Or for production: + +**URL:** `https://yourdomain.com/_app/admin-content-editor.html` + +## Requirements + +- Must be logged in as an admin user +- User account must have `role='admin'` in the database +- Valid JWT authentication cookie + +## Features + +✅ Edit modal content (rules, instructions, help text) +✅ Update footer links (Discord, GitHub, Instagram, etc.) +✅ Manage multiple locales (English, Chinese, etc.) +✅ Add/remove/edit individual content items +✅ Bulk save changes +✅ Initialize default content with one click + +## File Locations + +**Important:** The `frontend/` directory is regenerated on every build! + +- **Source (for frontend-src builds):** [frontend-src/static/_app/admin-content-editor.html](frontend-src/static/_app/admin-content-editor.html) +- **Backup (for USE_FRONTEND_BACKUP=true):** [frontend-backup/_app/admin-content-editor.html](frontend-backup/_app/admin-content-editor.html) +- **Active (auto-generated):** `frontend/_app/admin-content-editor.html` (copied during build - DO NOT EDIT) + +### Which File to Edit? + +- **If using frontend-src builds:** Edit `frontend-src/static/_app/admin-content-editor.html` +- **If using frontend-backup:** Edit `frontend-backup/_app/admin-content-editor.html` +- **Never edit** files in `frontend/` - they get deleted on every build! + +## Integration + +The content editor is: +1. A standalone HTML page (no build required) +2. Uses vanilla JavaScript (no framework dependencies) +3. Authenticates using existing JWT cookies +4. Calls the site-content API endpoints + +## Adding to Admin Panel Navigation + +Since the admin panel is a compiled SvelteKit app, to add a navigation link: + +1. Edit the source Svelte file in `frontend-src/src/routes/admin/+page.svelte` +2. Add a link/button to `/_app/admin-content-editor.html` +3. Rebuild the frontend: `cd frontend-src && npm run build` + +**Or** simply bookmark/share the direct URL with your admin team! + +## Build Process + +### When using frontend-src: +```bash +cd frontend-src +npm run build +# Copies files from frontend-src/static/_app/ to frontend/_app/ +``` + +### When using frontend-backup (Docker): +```bash +docker-compose up --build -d +# With USE_FRONTEND_BACKUP=true in docker-compose.yml +# Copies files from frontend-backup/_app/ to frontend/_app/ +``` + +## API Endpoints Used + +- `GET /api/admin/site-content` - Fetch all content +- `POST /api/admin/site-content` - Create/update item +- `POST /api/admin/site-content/bulk` - Bulk update +- `DELETE /api/admin/site-content/:key` - Delete item +- `POST /api/admin/site-content/initialize` - Load defaults + +See [CONTENT-MANAGEMENT.md](CONTENT-MANAGEMENT.md) for detailed documentation. diff --git a/CLAUDE.md b/CLAUDE.md index b1d03e8..7491946 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -Openplace is an unofficial open-source backend for wplace (a collaborative pixel art canvas), built with TypeScript, tinyhttp, Prisma, and MySQL. The system manages user authentication, pixel painting with charge-based rate limiting, alliances, leaderboards, and moderation features. +FurryPlace is an unofficial open-source backend for wplace (a collaborative pixel art canvas), built with TypeScript, tinyhttp, Prisma, and MySQL. The system manages user authentication, pixel painting with charge-based rate limiting, alliances, leaderboards, and moderation features. ## Development Commands diff --git a/CONTENT-MANAGEMENT.md b/CONTENT-MANAGEMENT.md new file mode 100644 index 0000000..a1c1508 --- /dev/null +++ b/CONTENT-MANAGEMENT.md @@ -0,0 +1,220 @@ +# Site Content Management System + +This document explains how to manage site content (modals, rules, etc.) using the new database-driven system. + +## Overview + +The site content management system allows administrators to modify: +- Modal content (welcome modal, rules, instructions) +- Help text (paint faster, map lagging) +- Footer links and contact information +- Site title and branding +- Any other text content on the site + +Content is stored in MySQL and can be edited through an admin interface, supporting multiple locales (English, Chinese, etc.). + +## Architecture + +### Database Schema + +**Table: `SiteContent`** +- `id`: Auto-incrementing primary key +- `key`: Unique string identifier (e.g., `modal.rules.title`) +- `value`: Text content +- `locale`: Language code (en, zh, etc.) +- `createdAt`: Creation timestamp +- `updatedAt`: Last update timestamp + +### Backend API Endpoints + +**Public Endpoint:** +- `GET /api/site-content?locale=en` - Fetch all content for a locale (used by frontend) + +**Admin Endpoints (require authentication & admin role):** +- `GET /api/admin/site-content` - Get all content items +- `POST /api/admin/site-content` - Create or update a single item +- `POST /api/admin/site-content/bulk` - Bulk update multiple items +- `DELETE /api/admin/site-content/:key` - Delete an item +- `POST /api/admin/site-content/initialize` - Initialize default content + +### Frontend Integration + +**File: `frontend/_app/info.js`** +- Automatically loads content from API on page load +- Falls back to hardcoded defaults if API fails +- Uses MutationObserver to patch modals when they appear +- Supports locale switching + +## Setup Instructions + +### 1. Database Migration + +Run the Prisma migration to create the `SiteContent` table: + +```bash +pnpm db:push +``` + +### 2. Initialize Default Content + +After the database is set up, initialize the default content by making a POST request: + +```bash +curl -X POST http://localhost:3000/api/admin/site-content/initialize \ + -H "Cookie: j=YOUR_ADMIN_JWT_TOKEN" +``` + +Or use the admin UI (see below). + +### 3. Access the Admin UI + +Navigate to: `http://localhost:3000/_app/admin-content-editor.html` + +**Note:** You must be logged in as an admin with a valid JWT token in cookies. + +## Using the Admin UI + +### Interface Features + +1. **Locale Selector** - Switch between languages (en, zh) +2. **Reload Button** - Refresh content from database +3. **Add New Item** - Create new content entries +4. **Initialize Defaults** - Populate database with default content +5. **Save All Changes** - Bulk save modified items +6. **Delete Button** - Remove individual items + +### Content Keys + +Content is organized using dot-notation keys: + +#### Modal Content Keys + +**Overview Section:** +- `modal.overview.title` - "Overview" heading +- `modal.overview.videoUrl` - YouTube embed URL + +**Paint Faster Section:** +- `modal.paintFaster.title` - Section heading +- `modal.paintFaster.mobile` - Instructions for mobile users +- `modal.paintFaster.desktop` - Instructions for desktop users + +**Map Lagging Section:** +- `modal.mapLagging.title` - Section heading +- `modal.mapLagging.text` - Help text +- `modal.mapLagging.link` - Link to hardware acceleration guide + +**Rules Section:** +- `modal.rules.title` - "Rules" heading +- `modal.rules.badge` - Badge text (e.g., "Important") +- `modal.rules.item.0` - First rule (with emoji) +- `modal.rules.item.1` - Second rule +- `modal.rules.item.N` - Additional rules (add as needed) +- `modal.rules.footer` - Footer warning text + +**Footer Section:** +- `modal.footer.email` - Contact email +- `modal.footer.discord.url` - Discord invite URL +- `modal.footer.discord.text` - Discord link text +- `modal.footer.github.url` - GitHub organization URL +- `modal.footer.github.text` - GitHub link text +- `modal.footer.instagram.url` - Instagram URL +- `modal.footer.instagram.text` - Instagram link text +- `modal.footer.terms.url` - Terms of service URL +- `modal.footer.terms.text` - Terms link text +- `modal.footer.privacy.url` - Privacy policy URL +- `modal.footer.privacy.text` - Privacy link text + +**Site-wide:** +- `site.title` - Site title/branding + +### Adding New Rules + +To add a new rule to the rules modal: + +1. Click "Add New Item" +2. Set key to `modal.rules.item.4` (or next available number) +3. Set value to `🚀 Your new rule text here` +4. Click "Add Item" +5. Repeat for other locales (en, zh, etc.) + +The frontend will automatically display all numbered rules in order. + +### Workflow Example + +1. **Login as admin** to your FurryPlace instance +2. **Navigate to** `/_app/admin-content-editor.html` +3. **Select locale** (e.g., English) +4. **Click "Initialize Defaults"** (first time only) +5. **Edit content** directly in the text fields +6. **Click "Save All Changes"** to persist to database +7. **Refresh your main site** - changes appear immediately! + +## Development Notes + +### Adding New Content Fields + +To add new editable content: + +1. Choose a unique key (e.g., `modal.newSection.title`) +2. Add to the initialize defaults in `src/routes/site-content.ts` +3. Update the monkey patch in `frontend/_app/info.js` to use the new key +4. Add documentation to this file + +### Locale Support + +Currently supports: +- `en` - English +- `zh` - Chinese + +To add a new locale: +1. Add option to locale selector in admin UI +2. Initialize default content for that locale +3. Update frontend locale detection in `info.js` + +### Caching Considerations + +- Frontend caches API response per page load +- No server-side caching (content always fresh from DB) +- Consider adding Redis/memory cache for production + +## Troubleshooting + +### Content Not Updating + +1. Check browser console for `[WPLACE_INFO]` logs +2. Verify API endpoint returns data: `GET /api/site-content?locale=en` +3. Clear browser cache and reload +4. Check database for content entries: `SELECT * FROM SiteContent;` + +### Admin UI Not Loading + +1. Verify you're logged in as admin +2. Check browser cookies for `j` JWT token +3. Verify `role` in User table is set to `admin` +4. Check browser console for authentication errors + +### Default Content Missing + +Run initialization endpoint: +```bash +curl -X POST http://localhost:3000/api/admin/site-content/initialize \ + -H "Cookie: j=YOUR_JWT_TOKEN" +``` + +## Security Notes + +- Admin endpoints require authentication AND `role='admin'` +- Input validation prevents injection attacks +- Keys restricted to alphanumeric + dots + underscores +- No HTML rendering (XSS protection via text replacement) + +## Future Enhancements + +- [ ] Add wysiwyg editor for formatted text +- [ ] Support for images/media uploads +- [ ] Version history and rollback +- [ ] Multi-tenant support (different content per domain) +- [ ] Import/export functionality (JSON/CSV) +- [ ] Preview changes before publishing +- [ ] Scheduled content changes +- [ ] Content approval workflow diff --git a/DOCKER.md b/DOCKER.md index cc31160..8dc96c7 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -1,6 +1,6 @@ # Docker Setup Guide -This guide explains how to build and run Openplace using Docker. +This guide explains how to build and run FurryPlace using Docker. ## Quick Start @@ -117,7 +117,7 @@ docker-compose ps ```bash # Access MySQL shell -docker-compose exec mysql mysql -u openplace -p +docker-compose exec mysql mysql -u FurryPlace -p # Run migrations docker-compose exec app pnpm db:push @@ -126,10 +126,10 @@ docker-compose exec app pnpm db:push docker-compose exec app pnpm seed # Backup database -docker-compose exec mysql mysqldump -u openplace -popenplacepassword openplace > backup.sql +docker-compose exec mysql mysqldump -u FurryPlace -pFurryPlacepassword FurryPlace > backup.sql # Restore database -docker-compose exec -T mysql mysql -u openplace -popenplacepassword openplace < backup.sql +docker-compose exec -T mysql mysql -u FurryPlace -pFurryPlacepassword FurryPlace < backup.sql ``` ### Maintenance @@ -142,7 +142,7 @@ docker-compose restart docker-compose restart app # View resource usage -docker stats openplace-app openplace-mysql +docker stats FurryPlace-app FurryPlace-mysql # Clean up unused images docker image prune @@ -154,15 +154,15 @@ If you prefer to build without docker-compose: ```bash # Build image -docker build -t openplace:latest . +docker build -t FurryPlace:latest . # Run container (requires existing MySQL) docker run -d \ - --name openplace \ + --name FurryPlace \ -p 3000:3000 \ - -e DATABASE_URL="mysql://user:pass@host:3306/openplace" \ + -e DATABASE_URL="mysql://user:pass@host:3306/FurryPlace" \ -e JWT_SECRET="your-secret" \ - openplace:latest + FurryPlace:latest ``` ## Development with Docker @@ -213,10 +213,10 @@ docker-compose up -d --build The Docker setup consists of: -- **openplace-app**: Node.js application container running the backend -- **openplace-mysql**: MySQL 8.0 database container +- **FurryPlace-app**: Node.js application container running the backend +- **FurryPlace-mysql**: MySQL 8.0 database container - **mysql-data**: Persistent volume for database storage -- **openplace-network**: Bridge network for container communication +- **FurryPlace-network**: Bridge network for container communication The application automatically runs database migrations on startup. diff --git a/Dockerfile b/Dockerfile index cfe174a..d12b80e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ WORKDIR /app COPY package.json pnpm-lock.yaml ./ # Install backend dependencies -RUN pnpm install --frozen-lockfile +RUN pnpm install # Copy source code and prisma schema COPY . . @@ -54,7 +54,7 @@ WORKDIR /app COPY package.json pnpm-lock.yaml ./ # Install production dependencies only -RUN pnpm install --frozen-lockfile --prod +RUN pnpm install --prod # Copy prisma schema for migrations COPY prisma ./prisma diff --git a/README.md b/README.md index 745f07e..294bc10 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ -# openplace -Openplace (styled lowercase) is a free unofficial open source backend for [wplace.](https://wplace.live) We aim to give the freedom and flexibility for all users to be able to make their own private wplace experience for themselves, their friends, or even their community. +# FurryPlace +FurryPlace (styled lowercase) is a free unofficial open source backend for [wplace.](https://wplace.live) We aim to give the freedom and flexibility for all users to be able to make their own private wplace experience for themselves, their friends, or even their community. > [!WARNING] > This is a work-in-progress. Expect unfinished features and bugs. Please help us by posting issues in #help-n-support on our [Discord server](https://discord.gg/ZRC4DnP9Z2) or by contributing pull requests. Thanks! ## macOS ### Getting Started -This is where you will be preparing your machine to run openplace. +This is where you will be preparing your machine to run FurryPlace. 1. install brew, node and git -2. run `git clone --recurse-submodules https://github.com/openplaceteam/openplace` -3. cd into the openplace directory +2. run `git clone --recurse-submodules https://github.com/FurryPlaceteam/FurryPlace` +3. cd into the FurryPlace directory 4. run ``npm i && brew install mariadb caddy nss`` 5. brew will then spit out a command to inform you on how to start it. if it doesn't, run `brew services start mariadb && brew services start caddy` #### Configuring and building the database @@ -29,7 +29,7 @@ This is where you will be preparing your machine to run openplace. 14. in another terminal, cd to the same root directory and run `caddy run --config Caddyfile` #### Spinning up your server -You will be required to configure an SSL certificate if you plan to use this in production. However, if you are only using this with you and your friends, you can simply navigate to `https://{IP}:8080` NOTE: openplace is only hosted over HTTPS. you will run into HTTP error 400 if you attempt to load the website over HTTP. +You will be required to configure an SSL certificate if you plan to use this in production. However, if you are only using this with you and your friends, you can simply navigate to `https://{IP}:8080` NOTE: FurryPlace is only hosted over HTTPS. you will run into HTTP error 400 if you attempt to load the website over HTTP. #### Updating your database In the event that the database schematic changes, you simply need to run `npm run db:push` to update your database schema. diff --git a/docker-compose.yml b/docker-compose.yml index 7e51464..677e4ca 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,13 +3,13 @@ version: '3.8' services: mysql: image: mysql:8.0 - container_name: openplace-mysql + container_name: FurryPlace-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpassword} - MYSQL_DATABASE: ${MYSQL_DATABASE:-openplace} - MYSQL_USER: ${MYSQL_USER:-openplace} - MYSQL_PASSWORD: ${MYSQL_PASSWORD:-openplacepassword} + MYSQL_DATABASE: ${MYSQL_DATABASE:-FurryPlace} + MYSQL_USER: ${MYSQL_USER:-FurryPlace} + MYSQL_PASSWORD: ${MYSQL_PASSWORD:-FurryPlacepassword} # No ports exposed - only accessible within Docker network volumes: - mysql-data:/var/lib/mysql @@ -19,11 +19,11 @@ services: timeout: 5s retries: 5 networks: - - openplace-network + - FurryPlace-network adminer: image: adminer:latest - container_name: openplace-adminer + container_name: FurryPlace-adminer restart: unless-stopped ports: - "${ADMINER_PORT:-8080}:8080" @@ -32,7 +32,7 @@ services: depends_on: - mysql networks: - - openplace-network + - FurryPlace-network app: build: @@ -40,26 +40,29 @@ services: dockerfile: Dockerfile args: USE_FRONTEND_BACKUP: ${USE_FRONTEND_BACKUP:-false} - container_name: openplace-app + container_name: FurryPlace-app restart: unless-stopped ports: - "${PORT:-3000}:3000" environment: PORT: 3000 - DATABASE_URL: "mysql://${MYSQL_USER:-openplace}:${MYSQL_PASSWORD:-openplacepassword}@mysql:3306/${MYSQL_DATABASE:-openplace}" + DATABASE_URL: "mysql://${MYSQL_USER:-FurryPlace}:${MYSQL_PASSWORD:-FurryPlacepassword}@mysql:3306/${MYSQL_DATABASE:-FurryPlace}" JWT_SECRET: ${JWT_SECRET:-change-this-secret-in-production} NODE_ENV: production + GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} + GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET} + GOOGLE_CALLBACK_URL: ${GOOGLE_CALLBACK_URL:-http://localhost:3000/auth/google/callback} depends_on: mysql: condition: service_healthy networks: - - openplace-network + - FurryPlace-network command: > sh -c " echo 'Waiting for database to be ready...' && sleep 5 && echo 'Running database migrations...' && - pnpm db:push && + pnpm db:push -- --accept-data-loss && echo 'Starting application...' && node dist/index.js " @@ -68,5 +71,5 @@ volumes: mysql-data: networks: - openplace-network: + FurryPlace-network: driver: bridge diff --git a/frontend-backup/.gitignore b/frontend-backup/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/frontend-backup/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/frontend-backup/26/2025/08/12/horse.png b/frontend-backup/26/2025/08/12/horse.png new file mode 100644 index 0000000..99d09f5 Binary files /dev/null and b/frontend-backup/26/2025/08/12/horse.png differ diff --git a/frontend-backup/404.html b/frontend-backup/404.html index 09390d1..496a7f5 100644 --- a/frontend-backup/404.html +++ b/frontend-backup/404.html @@ -5,124 +5,26 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - + - -
- - - - +