mirror of
https://github.com/Dvorinka/Primora.git
synced 2026-10-28 07:41:23 +00:00
feat: ship phase 1 surface area (v0.2.0)
- primora CLI + @primora/mcp server over generated client - auth users admin panel, mobile bottom nav - release workflow: GHCR images, compiled binaries, npm tarballs - fix gzip Content-Length truncation, nginx upstream re-resolution Authored By: TDvorak <[email protected]> Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
f77823ad90
commit
420502eec1
90 files changed
+9172
-9718
No files matched your search
+71
-486
@@ -1,530 +1,115 @@
|
||||
# Primora Platform - Quick Start Guide
|
||||
# Primora — Quick Start
|
||||
|
||||
## 🚀 Getting Started
|
||||
## Prerequisites
|
||||
|
||||
### Prerequisites
|
||||
- Go 1.21+
|
||||
- Node.js 18+
|
||||
- PostgreSQL 14+
|
||||
- Docker (optional)
|
||||
- Docker with the Compose plugin (recommended path), or
|
||||
- Go 1.26+, Node.js 20+, and a local PostgreSQL + Redis-compatible instance
|
||||
|
||||
### Installation
|
||||
## Docker (recommended)
|
||||
|
||||
#### 1. Clone the Repository
|
||||
```bash
|
||||
git clone https://github.com/your-org/primora.git
|
||||
cd primora
|
||||
./scripts/setup.sh
|
||||
```
|
||||
|
||||
#### 2. Backend Setup
|
||||
The script creates `.env` from `.env.example`, generates `JWT_SECRET` and
|
||||
`BETTER_AUTH_SECRET`, prompts for a domain (default `localhost`), and runs
|
||||
`docker compose up -d`.
|
||||
|
||||
Manual equivalent:
|
||||
|
||||
```bash
|
||||
cd apps/backend
|
||||
|
||||
# Install dependencies
|
||||
go mod download
|
||||
|
||||
# Set up environment variables
|
||||
cp .env.example .env
|
||||
# Edit .env with your database credentials
|
||||
|
||||
# Run migrations
|
||||
go run cmd/migrate/main.go up
|
||||
|
||||
# Start the server
|
||||
go run cmd/server/main.go
|
||||
# edit .env: set JWT_SECRET and BETTER_AUTH_SECRET
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
The backend will be available at `http://localhost:8080`
|
||||
Open `http://localhost`. Sign up, create an organization, create a project.
|
||||
|
||||
#### 3. Frontend Setup
|
||||
```bash
|
||||
cd apps/frontend
|
||||
Mailpit (local email capture) is at `http://localhost/mailpit/`.
|
||||
|
||||
# Install dependencies
|
||||
npm install
|
||||
### Port conflicts
|
||||
|
||||
# Set up environment variables
|
||||
cp .env.example .env
|
||||
# Edit .env with your API URL
|
||||
Only host bindings are configurable. If `5432`/`6379`/`80` are taken:
|
||||
|
||||
# Start development server
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The frontend will be available at `http://localhost:3000`
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Key Features
|
||||
|
||||
### For Users
|
||||
|
||||
**Dashboard**
|
||||
- Project overview with statistics
|
||||
- Quick actions for common tasks
|
||||
- Usage charts and analytics
|
||||
|
||||
**Projects**
|
||||
- Create and manage projects
|
||||
- Search and filter
|
||||
- Role-based access control
|
||||
|
||||
**Members**
|
||||
- Invite team members
|
||||
- Manage roles and permissions
|
||||
- Track pending invitations
|
||||
|
||||
**Storage**
|
||||
- Create buckets
|
||||
- Upload and manage files
|
||||
- Preview images and text files
|
||||
|
||||
**Settings**
|
||||
- Generate API keys
|
||||
- Configure organization
|
||||
- Manage preferences
|
||||
|
||||
**Audit Logs**
|
||||
- Track all activities
|
||||
- Filter and search logs
|
||||
- Export for compliance
|
||||
|
||||
### For Developers
|
||||
|
||||
**Command Palette** (Ctrl/Cmd + K)
|
||||
- Quick navigation
|
||||
- Search commands
|
||||
- Keyboard shortcuts
|
||||
|
||||
**Notifications**
|
||||
- Success/error feedback
|
||||
- Action prompts
|
||||
- Auto-dismiss
|
||||
|
||||
**Data Export**
|
||||
- JSON export
|
||||
- CSV export
|
||||
- Clipboard copy
|
||||
|
||||
**Advanced Search**
|
||||
- Fuzzy matching
|
||||
- Multi-field search
|
||||
- Relevance sorting
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Configuration
|
||||
|
||||
### Backend Configuration
|
||||
|
||||
**Environment Variables:**
|
||||
```env
|
||||
# Database
|
||||
DATABASE_URL=postgresql://user:password@localhost:5432/primora
|
||||
|
||||
# Server
|
||||
PORT=8080
|
||||
HOST=0.0.0.0
|
||||
|
||||
# JWT
|
||||
JWT_SECRET=your-secret-key
|
||||
JWT_EXPIRY=24h
|
||||
|
||||
# Rate Limiting
|
||||
RATE_LIMIT_REQUESTS=100
|
||||
RATE_LIMIT_WINDOW=1m
|
||||
|
||||
# Storage
|
||||
STORAGE_PATH=./storage
|
||||
MAX_UPLOAD_SIZE=10485760
|
||||
|
||||
# Logging
|
||||
LOG_LEVEL=info
|
||||
LOG_FORMAT=json
|
||||
POSTGRES_PORT=5433
|
||||
DRAGONFLY_PORT=6380
|
||||
NGINX_PORT=8081
|
||||
```
|
||||
|
||||
**Rate Limiting:**
|
||||
```go
|
||||
// Global rate limit: 100 requests per minute
|
||||
rateLimiter := middleware.NewRateLimiter(100, time.Minute)
|
||||
e.Use(rateLimiter.Middleware())
|
||||
Internal service addresses (`postgres:5432`, `dragonfly:6379`) never change.
|
||||
|
||||
// Per-user rate limit: 1000 requests per hour
|
||||
keyLimiter := middleware.NewKeyRateLimiter(1000, time.Hour)
|
||||
e.Use(keyLimiter.Middleware(func(c echo.Context) string {
|
||||
return c.Get("user_id").(string)
|
||||
}))
|
||||
```
|
||||
## Demo mode (no backend required)
|
||||
|
||||
### Frontend Configuration
|
||||
- Append `?demo=true` to the dashboard URL, or
|
||||
- set `VITE_DEMO_MODE=true`, or
|
||||
- click **launch demo mode** on the login page.
|
||||
|
||||
**Environment Variables:**
|
||||
```env
|
||||
# API
|
||||
VITE_API_BASE_URL=http://localhost:8080/api/v1
|
||||
Demo mode is fully client-side: organizations, projects, members, buckets,
|
||||
objects, collections, API keys, and audit events all work and persist for the
|
||||
session. A "Demo — exit" pill in the topbar leaves demo mode.
|
||||
|
||||
# Features
|
||||
VITE_ENABLE_ANALYTICS=false
|
||||
VITE_ENABLE_DEBUG=true
|
||||
## Configuration
|
||||
|
||||
# Limits
|
||||
VITE_MAX_FILE_SIZE=10485760
|
||||
VITE_PAGE_SIZE=25
|
||||
```
|
||||
All configuration lives in `.env` (see `.env.example` for the annotated list).
|
||||
The important ones:
|
||||
|
||||
---
|
||||
| Variable | Purpose |
|
||||
|---|---|
|
||||
| `JWT_SECRET` | Signing secret for API tokens — required, generate with `openssl rand -base64 32` |
|
||||
| `BETTER_AUTH_SECRET` | Better Auth secret — required, same generation |
|
||||
| `POSTGRES_*` / `DATABASE_URL` | Postgres connection |
|
||||
| `DRAGONFLY_URL` | Cache/rate-limit store |
|
||||
| `SMTP_*` / `MAIL_FROM` | Transactional email (Mailpit in dev, or Resend via `RESEND_API_KEY`) |
|
||||
| `GITHUB_CLIENT_ID` / `GOOGLE_CLIENT_ID` / `DISCORD_*` / `MICROSOFT_*` | Optional OAuth providers |
|
||||
| `USER_RATE_LIMIT_PER_MINUTE` / `API_KEY_RATE_LIMIT_PER_MINUTE` | Throttling, `0` disables |
|
||||
| `VITE_DEMO_MODE` | Enable the client-side demo workspace |
|
||||
|
||||
## 📝 Usage Examples
|
||||
|
||||
### Using the Command Palette
|
||||
|
||||
```tsx
|
||||
// Define commands
|
||||
const commands = [
|
||||
{
|
||||
id: "new-project",
|
||||
label: "Create New Project",
|
||||
description: "Start a new project",
|
||||
category: "Projects",
|
||||
keywords: ["add", "create", "new"],
|
||||
action: () => navigate("/projects/new")
|
||||
},
|
||||
{
|
||||
id: "settings",
|
||||
label: "Open Settings",
|
||||
category: "General",
|
||||
action: () => navigate("/settings")
|
||||
}
|
||||
];
|
||||
|
||||
// Add to your app
|
||||
<CommandPaletteEnhanced
|
||||
commands={commands}
|
||||
isOpen={showPalette()}
|
||||
onClose={() => setShowPalette(false)}
|
||||
/>
|
||||
|
||||
// Trigger with keyboard shortcut
|
||||
useKeyboardShortcuts([
|
||||
{
|
||||
key: "k",
|
||||
ctrl: true,
|
||||
meta: true,
|
||||
action: () => setShowPalette(true)
|
||||
}
|
||||
]);
|
||||
```
|
||||
|
||||
### Using Notifications
|
||||
|
||||
```tsx
|
||||
import { notify } from "./components/NotificationCenter";
|
||||
|
||||
// Success notification
|
||||
notify.success("Project Created", "Your project is ready");
|
||||
|
||||
// Error notification
|
||||
notify.error("Upload Failed", "Please try again");
|
||||
|
||||
// With action
|
||||
notify.warning("Unsaved Changes", "You have unsaved changes", {
|
||||
action: {
|
||||
label: "Save Now",
|
||||
onClick: () => saveChanges()
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Exporting Data
|
||||
|
||||
```tsx
|
||||
import { exportJSON, exportCSV } from "./utils/export";
|
||||
|
||||
// Export as JSON
|
||||
const handleExportJSON = () => {
|
||||
exportJSON(auditLogs, "audit-logs");
|
||||
};
|
||||
|
||||
// Export as CSV
|
||||
const handleExportCSV = () => {
|
||||
exportCSV(projects, "projects", ["name", "slug", "created_at"]);
|
||||
};
|
||||
```
|
||||
|
||||
### Using the Data Table
|
||||
|
||||
```tsx
|
||||
<DataTable
|
||||
data={projects}
|
||||
keyField="id"
|
||||
columns={[
|
||||
{
|
||||
key: "name",
|
||||
label: "Project Name",
|
||||
sortable: true,
|
||||
filterable: true
|
||||
},
|
||||
{
|
||||
key: "created_at",
|
||||
label: "Created",
|
||||
sortable: true,
|
||||
render: (date) => new Date(date).toLocaleDateString()
|
||||
},
|
||||
{
|
||||
key: "status",
|
||||
label: "Status",
|
||||
render: (status) => <Badge>{status}</Badge>
|
||||
}
|
||||
]}
|
||||
searchable
|
||||
exportable
|
||||
onExport={(data) => exportCSV(data, "projects")}
|
||||
onRowClick={(project) => navigate(`/projects/${project.id}`)}
|
||||
pageSize={25}
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Customization
|
||||
|
||||
### Theming
|
||||
|
||||
The platform uses Tailwind CSS for styling. Customize the theme in `tailwind.config.js`:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#eff6ff',
|
||||
// ... your colors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Adding Custom Commands
|
||||
|
||||
```tsx
|
||||
const customCommands = [
|
||||
{
|
||||
id: "custom-action",
|
||||
label: "My Custom Action",
|
||||
description: "Does something cool",
|
||||
category: "Custom",
|
||||
icon: <MyIcon />,
|
||||
action: () => {
|
||||
// Your custom logic
|
||||
}
|
||||
}
|
||||
];
|
||||
```
|
||||
|
||||
### Custom Validation Rules
|
||||
|
||||
```go
|
||||
// Backend
|
||||
validator := validation.New()
|
||||
validator.Custom("field", "Custom error message")
|
||||
|
||||
// Or create a reusable validator
|
||||
func ValidateProjectName(name string) error {
|
||||
if strings.Contains(name, "forbidden") {
|
||||
return errors.New("name contains forbidden word")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Testing
|
||||
|
||||
### Backend Tests
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
go test ./...
|
||||
|
||||
# Run with coverage
|
||||
go test -cover ./...
|
||||
|
||||
# Run specific package
|
||||
go test ./internal/middleware
|
||||
curl http://localhost/api/v1/health/liveness # {"status":"ok"}
|
||||
curl http://localhost/api/v1/health/readiness # db + cache + storage checks
|
||||
curl http://localhost/auth-health # auth service + db + cache
|
||||
```
|
||||
|
||||
### Frontend Tests
|
||||
## Quality checks
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
npm test
|
||||
|
||||
# Run with coverage
|
||||
npm test -- --coverage
|
||||
|
||||
# Run in watch mode
|
||||
npm test -- --watch
|
||||
npm run check # everything: go test, tsc, builds, codegen drift
|
||||
npm test # vitest across workspaces
|
||||
cd apps/backend && go test ./...
|
||||
cd apps/frontend && npx tsc -p tsconfig.json --noEmit
|
||||
```
|
||||
|
||||
---
|
||||
## Regenerating code
|
||||
|
||||
## 🐛 Debugging
|
||||
|
||||
### Backend Debugging
|
||||
|
||||
**Enable Debug Logging:**
|
||||
```env
|
||||
LOG_LEVEL=debug
|
||||
```
|
||||
|
||||
**View Logs:**
|
||||
```bash
|
||||
# Follow logs
|
||||
tail -f logs/app.log
|
||||
|
||||
# Search logs
|
||||
grep "ERROR" logs/app.log
|
||||
```
|
||||
|
||||
### Frontend Debugging
|
||||
|
||||
**Enable Debug Mode:**
|
||||
```env
|
||||
VITE_ENABLE_DEBUG=true
|
||||
```
|
||||
|
||||
**Browser DevTools:**
|
||||
- Open DevTools (F12)
|
||||
- Check Console for errors
|
||||
- Use Network tab for API calls
|
||||
- Use React DevTools for component inspection
|
||||
|
||||
---
|
||||
|
||||
## 📦 Deployment
|
||||
|
||||
### Docker Deployment
|
||||
Generated code is checked in but must never be hand-edited.
|
||||
|
||||
```bash
|
||||
# Build images
|
||||
docker-compose build
|
||||
|
||||
# Start services
|
||||
docker-compose up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
|
||||
# Stop services
|
||||
docker-compose down
|
||||
npm run generate:sqlc # after changing db queries/migrations
|
||||
npm run generate:client # after changing apps/backend/openapi/openapi.yaml
|
||||
```
|
||||
|
||||
### Production Checklist
|
||||
`npm run check` fails CI if generated output drifts from the sources.
|
||||
|
||||
- [ ] Set strong JWT secret
|
||||
- [ ] Configure rate limiting
|
||||
- [ ] Enable HTTPS
|
||||
- [ ] Set up database backups
|
||||
- [ ] Configure monitoring
|
||||
- [ ] Set up error tracking
|
||||
- [ ] Enable CORS properly
|
||||
- [ ] Optimize database indexes
|
||||
- [ ] Set up CDN for static assets
|
||||
- [ ] Configure log rotation
|
||||
## Troubleshooting
|
||||
|
||||
---
|
||||
**`docker compose up` fails on port binding** — another service owns the port.
|
||||
Set `POSTGRES_PORT`/`DRAGONFLY_PORT`/`NGINX_PORT` in `.env`.
|
||||
|
||||
## 🔍 Troubleshooting
|
||||
**Login works but every API call 401s** — the backend can't reach the auth
|
||||
service's JWKS. Confirm `AUTH_INTERNAL_BASE_URL=http://auth:3001` and that the
|
||||
auth container is up (`docker compose ps`).
|
||||
|
||||
### Common Issues
|
||||
**Emails never arrive** — they go to Mailpit in development:
|
||||
`http://localhost/mailpit/`. For real delivery configure `SMTP_*` or
|
||||
`RESEND_API_KEY`.
|
||||
|
||||
**Backend won't start:**
|
||||
- Check database connection
|
||||
- Verify environment variables
|
||||
- Check port availability
|
||||
- Review logs for errors
|
||||
**Frontend dev server can't reach the API** — Vite does not proxy. Either run
|
||||
the full compose stack and use `http://localhost`, or set
|
||||
`VITE_API_BASE_URL=http://localhost/api/v1` and
|
||||
`VITE_AUTH_BASE_URL=http://localhost/auth` in `apps/frontend/.env.local`.
|
||||
|
||||
**Frontend won't connect:**
|
||||
- Verify API URL in .env
|
||||
- Check CORS configuration
|
||||
- Verify backend is running
|
||||
- Check browser console
|
||||
|
||||
**Rate limit errors:**
|
||||
- Increase rate limits in config
|
||||
- Check if IP is correct
|
||||
- Verify rate limit middleware
|
||||
|
||||
**Upload failures:**
|
||||
- Check file size limits
|
||||
- Verify storage path exists
|
||||
- Check file permissions
|
||||
- Review storage configuration
|
||||
|
||||
---
|
||||
|
||||
## 📚 Additional Resources
|
||||
|
||||
### Documentation
|
||||
- [API Documentation](./docs/api.md)
|
||||
- [Component Guide](./apps/frontend/COMPONENT_GUIDE.md)
|
||||
- [Architecture Overview](./docs/architecture.md)
|
||||
|
||||
### Community
|
||||
- GitHub Issues
|
||||
- Discord Server
|
||||
- Stack Overflow Tag
|
||||
|
||||
### Learning
|
||||
- [Video Tutorials](https://youtube.com/primora)
|
||||
- [Blog Posts](https://blog.primora.dev)
|
||||
- [Example Projects](https://github.com/primora/examples)
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
We welcome contributions! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
|
||||
|
||||
### Development Workflow
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Make your changes
|
||||
4. Write tests
|
||||
5. Submit a pull request
|
||||
|
||||
### Code Style
|
||||
|
||||
**Backend (Go):**
|
||||
- Follow Go conventions
|
||||
- Use `gofmt` for formatting
|
||||
- Write meaningful comments
|
||||
- Add tests for new features
|
||||
|
||||
**Frontend (TypeScript):**
|
||||
- Follow TypeScript best practices
|
||||
- Use Prettier for formatting
|
||||
- Write JSDoc comments
|
||||
- Add tests for components
|
||||
|
||||
---
|
||||
|
||||
## 📄 License
|
||||
|
||||
MIT License - see [LICENSE](./LICENSE) for details
|
||||
|
||||
---
|
||||
|
||||
## 🎉 You're Ready!
|
||||
|
||||
You now have everything you need to start building with Primora. Happy coding! 🚀
|
||||
|
||||
For questions or support, reach out to:
|
||||
- Email: support@primora.dev
|
||||
- Discord: discord.gg/primora
|
||||
- Twitter: @primoradev
|
||||
**`npm run check` reports generated drift** — run `npm run generate:sqlc`
|
||||
and/or `npm run generate:client` and commit the regenerated files.
|
||||
Reference in new issue
Block a user