Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
472c646
feat: add language selection to project creation and settings
mvoof Mar 14, 2026
0f39e46
docs: add CC BY-NC 4.0 license and contributing guidelines
mvoof Mar 14, 2026
770568d
refactor: extract Glossary and Review from Settings into standalone p…
mvoof Mar 15, 2026
8f592e1
chore: fix dependencies
mvoof Mar 15, 2026
08d3279
fix: replace antd Typography with plain HTML to prevent EllipsisMeasu…
mvoof Mar 15, 2026
e93122b
chore: scope eslint to js/jsx/ts/tsx files and add global ignores
mvoof Mar 15, 2026
bbb2e05
fix: resolve all ESLint errors (a11y, mobx/missing-observer, unused i…
mvoof Mar 15, 2026
babc05f
fix: correct InputRef type in CommandPalette to fix frontend build
mvoof Mar 15, 2026
968df20
fix: add port 5174 to CORS origins for Vite dev server
mvoof Mar 15, 2026
510e22a
fix: resolve timezone-aware datetime crash in invitation creation
mvoof Mar 15, 2026
97e6a57
fix: use Vite dev proxy to eliminate CORS and port issues
mvoof Mar 15, 2026
fcdfd50
fix: use CORS origin regex to allow any localhost port in dev
mvoof Mar 15, 2026
dc7664e
fix: resolve CORS and trailing-slash redirect issues breaking auth flow
mvoof Mar 15, 2026
80b72e4
fix: resolve members not loading and improve invitations management
mvoof Mar 15, 2026
7ffb917
feat: enhance members management, wire audit logging, improve passwor…
mvoof Mar 15, 2026
714ebbb
fix: resolve CI test failures from trailing slashes and audit DB sess…
mvoof Mar 15, 2026
c8742ad
feat: add member role change and replace theme switcher with toggle
mvoof Mar 15, 2026
1607b0d
feat: complete RBAC v2 redesign with simplified 2-tier role system
mvoof Mar 16, 2026
f7cec4e
feat: restrict EDITOR role from project management UI
mvoof Mar 16, 2026
0f68e06
feat: hide Settings button on dashboard for EDITOR role
mvoof Mar 16, 2026
aaa4e23
fix: prevent non-admin managers from modifying/removing other managers
mvoof Mar 16, 2026
c826439
refactor: remove legacy code
mvoof Mar 16, 2026
b4280de
refactor: Frontend Anti-Pattern Refactoring
mvoof Mar 16, 2026
3d6e942
refactor: datetime.now(timezone.utc) instead deprecated datetime.ut…
mvoof Mar 16, 2026
dfc4d01
chore: @playwright/test package is a testing framework and should be …
mvoof Mar 16, 2026
cea9c63
fix: resolve all ESLint errors and replace any/unknown with proper Ty…
mvoof Mar 17, 2026
4b1b507
refactor: migrate eslint config to defineConfig API from eslint/config
mvoof Mar 17, 2026
839f0e2
refactor: use strong types
mvoof Mar 18, 2026
e86abf6
refactor: centralize business logic in MobX stores, eliminate React C…
mvoof Mar 18, 2026
281a3b6
chore: update antd to version 6.3.3 and successfully resolved the bui…
mvoof Mar 19, 2026
f4f5c15
feat(auth): add invitation token verification and auto-fill
mvoof Mar 19, 2026
11ec92f
feat: use antd notification
mvoof Mar 19, 2026
e92cdb4
refactor: rename notificationStore to inboxStore, update related imports
mvoof Mar 19, 2026
b134508
refactor: app theme
mvoof Mar 21, 2026
433b572
refactor(auth): fix error handling, improve toUserError, replace raw …
mvoof Mar 21, 2026
375fc6a
fix(theme): replace hardcoded navy blue with algorithm-derived theme …
mvoof Mar 21, 2026
f8e41f4
fix(theme): replace hardcoded colors with algorithm-derived theme tokens
mvoof Mar 21, 2026
c953db9
refactor(frontend): decompose monolithic components into composable s…
mvoof Mar 22, 2026
27fcd60
feat(docker): add frontend service and optimize dev environment
mvoof Mar 27, 2026
334118e
feat: implement single source of truth for environment variables and …
mvoof Mar 27, 2026
25eac03
docs: update README.md with production-ready instructions
mvoof Mar 27, 2026
b16146d
refactor(frontend): centralize all theme colors and accents in themeC…
mvoof Mar 27, 2026
525d48b
refactor: remove framer-motion dependency and motion components
mvoof Mar 27, 2026
3f73239
refactor: set theme color
mvoof Mar 27, 2026
eae0f9d
style: add horizontal spaces
mvoof Mar 27, 2026
a9961bd
fix: unify PresenceUser type and resolve task_id type mismatch
mvoof Mar 27, 2026
298438b
feat: remove global role management functionality
mvoof Mar 27, 2026
f4d0072
docs: update contributor instructions
mvoof Apr 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,8 +1,35 @@
# Copy to .env and fill in values
# VerseLab Environment Variables Template
# Copy this file to .env and fill in the values for production deployment.

# Database credentials (used by both postgres container and backend)
# --- API Versioning ---
# The prefix for all API endpoints. Must match on both frontend and backend.
# Default is /api/v1. Changing this will update both services.
API_V1_STR=/api/v1

# --- Database ---
# Credentials used by both the PostgreSQL container and the Backend service.
DB_USER=postgres
DB_PASSWORD=change-me-strong-password

# JWT secret (generate with: python -c "import secrets; print(secrets.token_urlsafe(64))")
# --- Security ---
# JWT signing key. CRITICAL: Generate a unique random string for production!
# Command: python3 -c "import secrets; print(secrets.token_urlsafe(64))"
SECRET_KEY=change-me-generate-random-string

# --- CORS (Cross-Origin Resource Sharing) ---
# List of allowed origins for the API.
# In production, set this to your domain (e.g., https://verselab.io).
# You can use a comma-separated list or a JSON array.
CORS_ORIGINS=https://your-domain.com

# --- Rate Limiting ---
# General limit for all endpoints per IP.
RATE_LIMIT_DEFAULT=60/minute

# Stricter limit for authentication endpoints (login/register).
RATE_LIMIT_AUTH=10/minute

# --- Frontend specific (passed during build) ---
# For Docker/Nginx, these usually use the same API_V1_STR prefix.
VITE_API_URL=${API_V1_STR}
VITE_WS_URL=
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ Thumbs.db
.agent
CLAUDE.md
GEMINI.md
.playwright-mcp
77 changes: 77 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
> [!NOTE]
> Please prefer English language for all communication.

# Contributing to VerseLab

Thank you for considering contributing to VerseLab! Before opening an issue, please check that a similar one hasn't already been reported.

## How to Contribute

1. **Fork** the repository
2. **Clone** your fork: `git clone https://github.com/your-username/VerseLab.git && cd VerseLab`
3. **Create** a feature branch: `git checkout -b feat/my-feature`
4. **Install dependencies:**

```bash
# Backend
cd backend && uv sync

# Frontend
cd frontend && npm install
```

5. **Make your changes** and test them locally

6. **Format and lint** your code to ensure it follows the project style:
```bash
# Frontend
cd frontend && npm run format && npm run lint
```

7. **Commit** your changes using the conventional commit format (see below).

8. **Submit a pull request** with a clear description of what you changed and why

## Commit Messages

We follow [Conventional Commits](https://www.conventionalcommits.org/):

```
<type>[optional scope]: <description>
```

Allowed types:

| Type | Description |
| ---------- | ------------------------------------ |
| `feat` | New feature |
| `fix` | Bug fix |
| `refactor` | Code change (no new feature or fix) |
| `perf` | Performance improvement |
| `docs` | Documentation only |
| `style` | Formatting, missing semicolons, etc. |
| `test` | Adding or correcting tests |
| `ci` | CI/CD changes |
| `chore` | Repository maintenance |
| `revert` | Revert a previous commit |

## Development

```bash
# Run backend
cd backend && uv run uvicorn app.main:app --reload

# Run frontend
cd frontend && npm run dev

# Run tests
cd backend && uv run pytest
```

## Guidelines

- Keep pull requests focused — one feature or fix per PR
- Add tests for new backend functionality when possible
- Ensure `tsc --noEmit` passes before submitting frontend changes

If you have questions, feel free to open an issue or start a discussion.
32 changes: 32 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Creative Commons Attribution-NonCommercial 4.0 International License

Copyright (c) 2026 mvoof

This work is licensed under the Creative Commons Attribution-NonCommercial 4.0
International License. To view a copy of this license, visit
https://creativecommons.org/licenses/by-nc/4.0/ or send a letter to Creative
Commons, PO Box 1866, Mountain View, CA 94042, USA.

You are free to:

Share — copy and redistribute the material in any medium or format
Adapt — remix, transform, and build upon the material

Under the following terms:

Attribution — You must give appropriate credit, provide a link to the license,
and indicate if changes were made. You may do so in any reasonable manner, but
not in any way that suggests the licensor endorses you or your use.

NonCommercial — You may not use the material for commercial purposes.

No additional restrictions — You may not apply legal terms or technological
measures that legally restrict others from doing anything the license permits.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
33 changes: 13 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ cd frontend && npm install && npm run dev

### Production (VPS / remote server)

The production setup uses a "Single Source of Truth" architecture. All configuration for Database, Backend, and Frontend is managed through a single `.env` file in the project root.

```bash
# 1. Clone the repo
git clone <repo-url> && cd VerseLab
Expand All @@ -45,32 +47,23 @@ cp .env.example .env
nano .env
```

**.env contents:**

```env
DB_PASSWORD=your-strong-database-password
SECRET_KEY=generate-with-python-see-below
```

Generate a secret key:
**Key Production Variables in root `.env`:**

```bash
python3 -c "import secrets; print(secrets.token_urlsafe(64))"
```
| Variable | Description |
|----------|-------------|
| `API_V1_STR` | **API Version Contract.** Defines the prefix for all routes (e.g., `/api/v1`). Synced across both services. |
| `DB_PASSWORD` | Strong password for PostgreSQL. |
| `SECRET_KEY` | JWT signing key (generate with: `python3 -c "import secrets; print(secrets.token_urlsafe(64))"`) |
| `CORS_ORIGINS` | Set to your domain (e.g., `https://verselab.io`) to restrict API access. |
| `VITE_API_URL` | Set to `${API_V1_STR}` to ensure the frontend matches the backend version. |

```bash
# 4. Build and start all services (first time)
# 4. Build and start all services
# Note: Changing VITE_* variables requires a rebuild:
docker compose up -d --build

# Restart after code changes (rebuild without recreating network)
docker compose build && docker compose up -d

# Stop / start without destroying network
docker compose stop
docker compose start
```

The app will be available at `http://<your-server-ip>` (port 80).
The app will be available at `http://<your-server-ip>` (port 80). Nginx handles the routing and security headers.

### Accessing from other machines

Expand Down
27 changes: 24 additions & 3 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
# Copy to .env and fill in your values
DATABASE_URL=postgresql+asyncpg://postgres:YOUR_PASSWORD@localhost:5432/verselab_db
SECRET_KEY=generate-a-strong-random-string-here
# VerseLab Backend Environment Variables Template
# Copy this file to .env and fill in the values.

# --- Database ---
# Replace with your actual database credentials.
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/verselab_db

# --- Security ---
# JWT signing key. CRITICAL: Generate a unique random string for production!
# Command: python3 -c "import secrets; print(secrets.token_urlsafe(64))"
SECRET_KEY=change-me-generate-random-string

# --- CORS (Cross-Origin Resource Sharing) ---
# List of allowed origins for the API.
# You can use a comma-separated list or a JSON array.
# If empty, the backend defaults to allowing localhost (dev mode).
# CORS_ORIGINS=https://your-domain.com

# --- Rate Limiting ---
# General limit for all endpoints per IP.
RATE_LIMIT_DEFAULT=60/minute

# Stricter limit for authentication endpoints (login/register).
RATE_LIMIT_AUTH=10/minute
27 changes: 20 additions & 7 deletions backend/app/config.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import re
import secrets

Expand All @@ -12,6 +13,9 @@ class Settings(BaseSettings):
extra="ignore",
)

# API Versioning
API_V1_STR: str = "/api/v1"

# Database
DATABASE_URL: str = "postgresql+asyncpg://postgres:postgres@localhost:5432/verselab_db"

Expand All @@ -21,13 +25,22 @@ class Settings(BaseSettings):
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24
REFRESH_TOKEN_EXPIRE_DAYS: int = 30

# CORS
CORS_ORIGINS: list[str] = [
"http://localhost:3000",
"http://localhost:5173",
"http://127.0.0.1:3000",
"http://127.0.0.1:5173",
]
# CORS — in dev, allow any localhost port via regex (Vite picks a free port).
# In prod, set CORS_ORIGINS to explicit origins (comma-separated or JSON array).
CORS_ORIGINS: list[str] = []
CORS_ORIGIN_REGEX: str = r"^https?://(localhost|127\.0\.0\.1)(:\d+)?$"

@field_validator("CORS_ORIGINS", mode="before")
@classmethod
def parse_cors_origins(cls, v: str | list[str]) -> list[str]:
if isinstance(v, list):
return v
if not v or not v.strip():
return []
v = v.strip()
if v.startswith("["):
return json.loads(v)
return [origin.strip() for origin in v.split(",") if origin.strip()]

# Rate limiting
RATE_LIMIT_DEFAULT: str = "60/minute"
Expand Down
20 changes: 0 additions & 20 deletions backend/app/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,6 @@ def __init__(self, message: str, status_code: int = 400, details: dict | None =
super().__init__(message)


class NotFoundError(AppError):
def __init__(self, resource: str = "Resource"):
super().__init__(f"{resource} not found", status_code=404)


class ForbiddenError(AppError):
def __init__(self, message: str = "Access denied"):
super().__init__(message, status_code=403)


class ConflictError(AppError):
def __init__(self, message: str = "Conflict"):
super().__init__(message, status_code=409)


class ValidationError(AppError):
def __init__(self, message: str = "Validation error"):
super().__init__(message, status_code=422)


async def app_error_handler(request: Request, exc: AppError) -> JSONResponse:
return JSONResponse(
status_code=exc.status_code,
Expand Down
9 changes: 9 additions & 0 deletions backend/app/limiter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from slowapi import Limiter
from slowapi.util import get_remote_address

from .config import settings

# Global limiter instance
limiter = Limiter(
key_func=get_remote_address, default_limits=[settings.RATE_LIMIT_DEFAULT]
)
Loading
Loading