Files
event-uploader/README.md
2026-02-03 10:38:46 +01:00

223 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.