Initial commit

This commit is contained in:
2026-02-03 10:38:46 +01:00
commit eb304f4b14
144 changed files with 22605 additions and 0 deletions

222
README.md Normal file
View File

@@ -0,0 +1,222 @@
# Event Video Upload Application
Web application for event organizers to collect video uploads from attendees. Admins create events with shareable URLs; attendees upload videos to Google Drive folders per event.
## Stack
- **Backend**: Laravel 12 (PHP 8.3+), MySQL 8.0+, Sanctum, Google Drive API v3, Queue (database)
- **Admin**: Vue 3 + TypeScript + Vite + Bootstrap 5
- **Upload**: Vue 3 + TypeScript + Vite + Tailwind CSS + Uppy
## Getting Started
1. **Clone and install**
- Backend: `cd api && composer install`
- Admin: `cd admin && npm install`
- Upload: `cd upload && npm install`
2. **Configure**
- Copy `api/.env.example` to `api/.env`
- Set MySQL (`DB_*`), `SANCTUM_STATEFUL_DOMAINS=localhost:5173,localhost:5174`, `SESSION_DOMAIN=localhost`
- Set Google OAuth: `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `GOOGLE_REDIRECT_URI`
- Run `php artisan key:generate` in `api/`
3. **Database**
- Create MySQL database (e.g. `event_uploader`)
- In `api/`: `php artisan migrate`
4. **Admin user**
- `cd api && php artisan make:admin --email=admin@example.com --password=secret`
5. **Run** (requires 4 separate terminal windows)
- API: `cd api && php artisan serve`
- **Queue Worker** (REQUIRED for uploads): `cd api && php artisan queue:work`
- Admin: `cd admin && npm run dev` (port 5173)
- Upload: `cd upload && npm run dev` (port 5174)
⚠️ **Important**: The queue worker MUST be running for file uploads to process to Google Drive. Without it, uploads will stay in "pending" status.
6. **Use**
- Open http://localhost:5173, log in, connect Google Drive, create an event, copy the upload URL
- Share the upload URL (http://localhost:5174/events/{slug}); attendees upload there
## MySQL: Docker vs Homebrew
| | **Docker** | **Homebrew** |
|---|---|---|
| **Setup** | Requires Docker Desktop. One `docker run` or `docker compose up`. | `brew install mysql` and create DB. |
| **Best for** | Same MySQL version everywhere; no system install; easy reset. | Quick local use; no Docker needed. |
| **Reset** | `docker compose down -v` for a clean DB. | Manual drop/recreate or reinstall. |
**Recommendation:** Use **Docker** if you already have it or want a reproducible setup; use **Homebrew** if you prefer a single local MySQL and no containers.
---
## Option A: MySQL with Docker
A `docker-compose.yml` in the project root runs only MySQL (API and frontends run on your machine).
1. **Start MySQL** (from project root):
```bash
docker compose up -d
```
Wait for the healthcheck (a few seconds), then continue.
2. **Configure the API** (`api/.env`):
```env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=event_uploader
DB_USERNAME=root
DB_PASSWORD=secret
APP_URL=http://localhost:8000
SESSION_DOMAIN=localhost
SANCTUM_STATEFUL_DOMAINS=localhost:5173,localhost:5174
```
3. **Then:** `cd api && php artisan migrate && php artisan make:admin --email=admin@test.com --password=secret`, and run API, queue, admin, and upload (see step 4 in Option B).
**Stop:** `docker compose down`
**Stop and wipe DB:** `docker compose down -v`
---
## Option B: MySQL with Homebrew (macOS)
1. **Install MySQL** (if needed):
```bash
brew install mysql
brew services start mysql
```
Or run once: `mysql.server start`.
2. **Create the database and user** (optional; root with no password works for local):
```bash
mysql -u root -e "CREATE DATABASE IF NOT EXISTS event_uploader;"
```
If MySQL has a root password, use `-p` and enter it.
3. **Configure the API** (`api/.env`):
```env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=event_uploader
DB_USERNAME=root
DB_PASSWORD=
APP_URL=http://localhost:8000
SESSION_DOMAIN=localhost
SANCTUM_STATEFUL_DOMAINS=localhost:5173,localhost:5174
```
Set `DB_PASSWORD` if your MySQL user has a password.
4. **Bootstrap and run** (from project root):
```bash
cd api && composer install && php artisan key:generate && php artisan migrate
php artisan make:admin --email=admin@test.com --password=secret
php artisan serve &
php artisan queue:work &
cd ../admin && npm install && npm run dev &
cd ../upload && npm install && npm run dev
```
Or run each in a separate terminal: API (port 8000), queue worker, admin (5173), upload (5174).
5. **Test the flow**:
- Open http://localhost:5173, log in as `admin@test.com` / `secret`.
- Create an event (Google Drive can be skipped at first; you can set it up later).
- Copy the upload URL and open it in another tab (e.g. http://localhost:5174/events/your-slug).
- Upload a small test file; the queue worker will process it (Google Drive upload will fail until OAuth is configured).
**Tip:** Keep the queue worker running in a dedicated terminal so uploads are processed. Without it, files stay in "pending" until the job runs.
## Start everything (one command)
From the project root:
```bash
./start.sh
```
Starts API (8000), queue worker, admin (5173), and upload (5174). Press **Ctrl+C** to stop all.
If you use Docker for MySQL:
```bash
./start.sh --with-docker
```
(Starts `docker compose up -d` first, then the four apps.)
**First time:** run `chmod +x start.sh` if the script is not executable.
## Development Commands
```bash
# Backend
cd api && composer install && php artisan migrate && php artisan serve
# Admin
cd admin && npm install && npm run dev
# Upload
cd upload && npm install && npm run dev
# Queue
cd api && php artisan queue:work
# Create admin
cd api && php artisan make:admin --email=admin@example.com --password=secret
# Tests
cd api && ./vendor/bin/pest
```
## Pushing to Gitea
This repo pushes to Gitea (1Password SSH key). One-time setup:
1. Run from project root: `./scripts/setup-gitea-ssh.sh` (creates `~/.ssh/gitea-1password-only`).
2. In 1Password: **Settings → Developer** → enable **Use the SSH agent**.
Then you can push from Cursor or run `./gitea-push.sh`. For commit signing and first-push tips, see [GITEA-SETUP.md](GITEA-SETUP.md).
## Project Structure
- `api/` Laravel API (auth, events, Google Drive, public upload)
- `admin/` Vue 3 admin SPA (events, uploads, Google Drive)
- `upload/` Vue 3 public upload SPA (event page, password gate, Uppy)
## Troubleshooting
### Uploads stuck in "pending" status
**Problem**: Files upload successfully but stay in "pending" status and never appear in Google Drive.
**Solution**: The queue worker is not running. Start it with:
```bash
cd api && php artisan queue:work
```
You can verify the queue worker is processing jobs by checking the logs or running:
```bash
cd api && php artisan queue:failed # Check for failed jobs
```
### 419 Page Expired error on upload
**Problem**: Getting a 419 error when uploading files from the public upload page.
**Solution**: This was fixed by excluding public upload endpoints from CSRF verification. Make sure your `api/bootstrap/app.php` includes the CSRF exception configuration.
### Google Drive callback fails with "Route [login] not defined"
**Problem**: After authorizing Google Drive, you get redirected to an error page.
**Solution**: This was fixed by moving the Google Drive callback route outside the `auth:sanctum` middleware group and using `web` middleware instead. The callback now handles authentication internally.
### No folders appear when selecting a Shared Drive
**Problem**: When you click on a Shared Drive, you see all folders from all drives instead of just that drive's folders.
**Solution**: This was fixed by updating the Google Drive API query to use `'{$driveId}' in parents` when listing root folders of a Shared Drive.