Initial commit
This commit is contained in:
222
README.md
Normal file
222
README.md
Normal 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.
|
||||
Reference in New Issue
Block a user