18 KiB
🤝 Contributing to Ospabhost 8.1
Thank you for considering contributing to Ospabhost 8.1! This document provides guidelines and instructions for contributing to the project.
📋 Table of Contents
- Code of Conduct
- Getting Started
- Development Workflow
- Coding Standards
- Commit Guidelines
- Pull Request Process
- Testing Requirements
- Documentation
- Contact
📜 Code of Conduct
Our Pledge
We pledge to make participation in our project a harassment-free experience for everyone, regardless of:
- Age, body size, disability, ethnicity
- Gender identity and expression
- Level of experience
- Nationality, personal appearance, race, religion
- Sexual identity and orientation
Our Standards
Positive behavior includes:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints
- Gracefully accepting constructive criticism
- Focusing on what is best for the project
- Showing empathy towards other community members
Unacceptable behavior includes:
- Trolling, insulting comments, personal attacks
- Public or private harassment
- Publishing others' private information without permission
- Other conduct which could reasonably be considered inappropriate
Enforcement
Violations of the Code of Conduct can be reported to:
- Email: support@ospab.host
- Telegram: @ospab_support
All complaints will be reviewed and investigated promptly and fairly.
🚀 Getting Started
Prerequisites
Before you begin, ensure you have the following installed:
# Node.js (v24.x or higher)
node --version
# npm (v10.x or higher)
npm --version
# MySQL (8.0 or higher)
mysql --version
# Git
git --version
Fork and Clone
-
Fork the repository on GitHub:
- Click "Fork" button at https://github.com/Ospab/ospabhost8.1
-
Clone your fork locally:
git clone https://github.com/YOUR_USERNAME/ospabhost8.1.git cd ospabhost8.1/ospabhost -
Add upstream remote:
git remote add upstream https://github.com/Ospab/ospabhost8.1.git git remote -v
Setup Development Environment
Backend Setup
cd backend
# Install dependencies
npm install
# Copy environment template
cp .env.example .env
# Edit .env with your local configuration
nano .env
# Generate Prisma client
npx prisma generate
# Run migrations
npx prisma migrate dev
# Seed database (optional)
npx prisma db seed
# Start development server
npm run dev
Frontend Setup
cd frontend
# Install dependencies
npm install
# Copy environment template
cp .env.example .env
# Edit .env
nano .env
# Start development server
npm run dev
Database Setup
-- Create database
CREATE DATABASE ospab CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- Create user (optional)
CREATE USER 'ospab_user'@'localhost' IDENTIFIED BY 'secure_password';
GRANT ALL PRIVILEGES ON ospab.* TO 'ospab_user'@'localhost';
FLUSH PRIVILEGES;
🔄 Development Workflow
Branch Strategy
We follow Git Flow workflow:
main # Production-ready code
├── develop # Development branch
│ ├── feature/ # New features
│ ├── fix/ # Bug fixes
│ ├── refactor/ # Code refactoring
│ └── docs/ # Documentation updates
└── hotfix/ # Critical production fixes
Creating a Feature Branch
# Ensure you're on latest develop
git checkout develop
git pull upstream develop
# Create feature branch
git checkout -b feature/your-feature-name
# Example:
git checkout -b feature/add-payment-gateway
git checkout -b fix/proxmox-connection
git checkout -b docs/api-examples
Branch Naming Convention
| Type | Pattern | Example |
|---|---|---|
| Feature | feature/<description> |
feature/panel-websocket |
| Bug Fix | fix/<description> |
fix/sso-timestamp-validation |
| Hotfix | hotfix/<description> |
hotfix/critical-security-patch |
| Refactor | refactor/<description> |
refactor/prisma-queries |
| Docs | docs/<description> |
docs/sso-integration |
| Test | test/<description> |
test/panel-api-integration |
Staying Updated
# Fetch latest changes from upstream
git fetch upstream
# Merge upstream changes into your branch
git checkout feature/your-feature
git merge upstream/develop
# Or rebase (preferred for cleaner history)
git rebase upstream/develop
📝 Coding Standards
TypeScript Style Guide
File Structure
// 1. Imports (external first, then internal)
import express from 'express';
import { PrismaClient } from '@prisma/client';
import { authenticateJWT } from '../middleware/auth';
import { validateRequest } from '../utils/validation';
// 2. Types/Interfaces
interface CreateServerRequest {
tariffId: number;
osId: number;
}
// 3. Constants
const PROXMOX_TIMEOUT = 30000;
const MAX_SERVERS_PER_USER = 10;
// 4. Main code
export class ServerService {
// Implementation
}
// 5. Helper functions
function generateServerName(userId: number): string {
return `server-${userId}-${Date.now()}`;
}
// 6. Exports
export default ServerService;
Naming Conventions
// Classes: PascalCase
class ServerService {}
class ProxmoxApi {}
// Interfaces: PascalCase with "I" prefix (optional)
interface IUser {}
interface ServerConfig {}
// Functions: camelCase
function createServer() {}
function validateEmail(email: string): boolean {}
// Variables: camelCase
const userCount = 42;
let isActive = true;
// Constants: SCREAMING_SNAKE_CASE
const API_BASE_URL = 'https://api.ospab.host';
const MAX_RETRY_ATTEMPTS = 3;
// Private properties: prefix with underscore (optional)
class Example {
private _privateField: string;
public publicField: string;
}
// Files: kebab-case
// server-service.ts
// proxmox-api.ts
// auth-middleware.ts
Code Style
// ✅ GOOD: Explicit types
function calculateTotal(price: number, quantity: number): number {
return price * quantity;
}
// ❌ BAD: Implicit any
function calculateTotal(price, quantity) {
return price * quantity;
}
// ✅ GOOD: Async/await
async function getUser(id: number): Promise<User | null> {
const user = await prisma.user.findUnique({ where: { id } });
return user;
}
// ❌ BAD: Promise chaining
function getUser(id: number) {
return prisma.user.findUnique({ where: { id } })
.then(user => user);
}
// ✅ GOOD: Error handling
async function createServer(userId: number): Promise<Server> {
try {
const server = await prisma.server.create({
data: { userId, name: generateServerName(userId) }
});
return server;
} catch (error) {
console.error('Failed to create server:', error);
throw new Error('Server creation failed');
}
}
// ✅ GOOD: Destructuring
const { id, username, email } = user;
// ❌ BAD: Multiple property access
const id = user.id;
const username = user.username;
const email = user.email;
// ✅ GOOD: Optional chaining
const serverName = user?.servers?.[0]?.name ?? 'N/A';
// ❌ BAD: Nested checks
const serverName = user && user.servers && user.servers[0]
? user.servers[0].name
: 'N/A';
React/Frontend Standards
// ✅ GOOD: Functional components with TypeScript
interface UserCardProps {
user: User;
onEdit: (id: number) => void;
}
export const UserCard: React.FC<UserCardProps> = ({ user, onEdit }) => {
const handleEdit = () => {
onEdit(user.id);
};
return (
<div className="user-card">
<h3>{user.username}</h3>
<button onClick={handleEdit}>Edit</button>
</div>
);
};
// ✅ GOOD: Custom hooks
function useServerData(userId: number) {
const [servers, setServers] = useState<Server[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchServers() {
try {
const response = await api.get(`/users/${userId}/servers`);
setServers(response.data);
} catch (error) {
console.error('Failed to fetch servers:', error);
} finally {
setLoading(false);
}
}
fetchServers();
}, [userId]);
return { servers, loading };
}
// ✅ GOOD: Tailwind CSS classes organized
<div className="
flex items-center justify-between
px-4 py-2
bg-white hover:bg-gray-50
border border-gray-200 rounded-lg
shadow-sm
">
Content
</div>
Prisma Schema Conventions
// ✅ GOOD: Explicit relations
model User {
id Int @id @default(autoincrement())
username String @unique
email String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
servers Server[] @relation("UserServers")
tickets Ticket[] @relation("UserTickets")
@@map("users")
}
model Server {
id Int @id @default(autoincrement())
name String
userId Int
user User @relation("UserServers", fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@map("servers")
}
💬 Commit Guidelines
Commit Message Format
We follow Conventional Commits specification:
<type>(<scope>): <subject>
<body>
<footer>
Types
| Type | Description | Example |
|---|---|---|
feat |
New feature | feat(api): Add Panel API endpoints |
fix |
Bug fix | fix(sso): Fix timestamp validation |
docs |
Documentation | docs(readme): Update setup instructions |
style |
Code style (formatting) | style(backend): Format with Prettier |
refactor |
Code refactoring | refactor(prisma): Optimize user queries |
test |
Add/update tests | test(panel-api): Add integration tests |
chore |
Maintenance | chore(deps): Update dependencies |
perf |
Performance improvement | perf(proxmox): Cache API responses |
ci |
CI/CD changes | ci(github): Add deployment workflow |
build |
Build system changes | build(vite): Update Vite config |
revert |
Revert previous commit | revert: Revert "feat: Add feature X" |
Scope (Optional)
Scope indicates which part of codebase is affected:
backend- Backend codefrontend- Frontend codeapi- API endpointsauth- Authenticationsso- Single Sign-Onpanel-api- Panel APIproxmox- Proxmox integrationprisma- Database/ORMdocs- Documentation
Examples
# Good commits
git commit -m "feat(panel-api): Add VPS status endpoint"
git commit -m "fix(sso): Fix HMAC signature validation"
git commit -m "docs(api): Add Panel API usage examples"
git commit -m "refactor(backend): Extract Proxmox logic to service"
# Bad commits (too vague)
git commit -m "fix stuff"
git commit -m "update code"
git commit -m "changes"
Multi-line Commits
For complex changes, add body and footer:
git commit -m "feat(panel-api): Add VPS monitoring endpoint
- Implement real-time CPU, RAM, disk stats
- Integrate with Proxmox API
- Add fallback to zeros if Proxmox unavailable
- Add caching layer for performance
Closes #42
Refs #38"
Commit Best Practices
- Atomic commits: One logical change per commit
- Present tense: "Add feature" not "Added feature"
- Imperative mood: "Fix bug" not "Fixes bug"
- Reference issues: Use
Closes #123orRefs #456 - Keep subject < 72 chars
- Use body for "why" not "what"
🔀 Pull Request Process
Before Opening PR
-
Ensure all tests pass:
# Backend cd backend npm run build npm test # Frontend cd frontend npm run build npm run lint -
Update documentation:
- If API changed, update
PANEL_API_DOCUMENTATION.md - If SSO changed, update
SSO_FINAL_SETUP.md - Update
README.mdif major feature added
- If API changed, update
-
Check code quality:
# Backend: TypeScript check cd backend npx tsc --noEmit # Frontend: ESLint cd frontend npm run lint -
Rebase on latest develop:
git fetch upstream git rebase upstream/develop
Opening Pull Request
-
Push your branch:
git push origin feature/your-feature-name -
Open PR on GitHub:
- Go to https://github.com/Ospab/ospabhost8.1/pulls
- Click "New Pull Request"
- Select your fork and branch
- Target:
developbranch (notmain)
-
Fill PR template:
## Description
Brief description of changes.
## Type of Change
- [ ] Bug fix (non-breaking change)
- [ ] New feature (non-breaking change)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work)
- [ ] Documentation update
## How Has This Been Tested?
Describe tests performed:
- [ ] Manual testing
- [ ] Unit tests
- [ ] Integration tests
## Checklist
- [ ] My code follows project style guidelines
- [ ] I have performed a self-review
- [ ] I have commented my code where necessary
- [ ] I have updated documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix works
- [ ] New and existing tests pass locally
## Screenshots (if applicable)
Add screenshots of UI changes.
## Related Issues
Closes #123
Refs #456
PR Review Process
-
Automated checks:
- CI/CD pipeline runs tests
- TypeScript compilation
- ESLint checks
-
Code review:
- At least 1 approval required
- Address reviewer feedback
- Update PR as needed
-
Merge:
- Squash commits if many small commits
- Merge to
develop - Delete feature branch
🧪 Testing Requirements
Backend Testing
// Example: Unit test with Jest
import { generateSSOLink } from './sso.service';
describe('SSO Service', () => {
test('generateSSOLink includes userId', () => {
const link = generateSSOLink(1, 'john', 'john@example.com', 'pass123');
expect(link).toContain('userId=1');
expect(link).toContain('username=john');
expect(link).toContain('email=john@example.com');
});
test('generateSSOLink creates valid HMAC signature', () => {
const link = generateSSOLink(1, 'john', 'john@example.com', 'pass123');
const url = new URL(link);
const signature = url.searchParams.get('signature');
expect(signature).toHaveLength(64); // SHA256 hex
});
});
Integration Testing
// Example: API endpoint test with Supertest
import request from 'supertest';
import app from '../src/index';
describe('Panel API', () => {
test('GET /api/panel/health returns success', async () => {
const response = await request(app).get('/api/panel/health');
expect(response.status).toBe(200);
expect(response.body.status).toBe('success');
});
test('GET /api/panel/users requires API key', async () => {
const response = await request(app).get('/api/panel/users');
expect(response.status).toBe(401);
});
test('GET /api/panel/users with API key returns users', async () => {
const response = await request(app)
.get('/api/panel/users')
.set('X-API-Key', process.env.PANEL_API_KEY);
expect(response.status).toBe(200);
expect(Array.isArray(response.body.data)).toBe(true);
});
});
Manual Testing Checklist
- Register new user
- Login with JWT
- Login with OAuth2 (Google/GitHub/Yandex)
- Order VPS
- View server list
- Open VPS terminal
- Test SSO to panel
- Upload payment check
- Create support ticket
📖 Documentation
When to Update Documentation
- New API endpoint: Update
PANEL_API_DOCUMENTATION.md - Changed endpoint: Update examples in
PANEL_API_USAGE_EXAMPLES.md - New feature: Update
README.md - Deployment change: Update
DEPLOY_BACKEND.mdorDEPLOY_NGINX_FIX.md - SSO change: Update
SSO_FINAL_SETUP.md - Database schema: Update Prisma comments
Documentation Standards
// ✅ GOOD: JSDoc comments
/**
* Creates a new VPS server for user.
*
* @param userId - The ID of the user
* @param tariffId - The tariff plan ID
* @param osId - The operating system ID
* @returns Promise resolving to created Server
* @throws {Error} If Proxmox API fails
*
* @example
* const server = await createServer(1, 2, 3);
* console.log(server.ipAddress);
*/
async function createServer(
userId: number,
tariffId: number,
osId: number
): Promise<Server> {
// Implementation
}
📞 Contact
Questions?
- Email: support@ospab.host
- Telegram: @ospab_support
- GitHub Issues: https://github.com/Ospab/ospabhost8.1/issues
Reporting Bugs
- Check existing issues: Search https://github.com/Ospab/ospabhost8.1/issues
- Create detailed report:
## Bug Description Clear description of the bug. ## Steps to Reproduce 1. Go to '...' 2. Click on '...' 3. See error ## Expected Behavior What should happen. ## Actual Behavior What actually happens. ## Environment - OS: Windows 11 - Node.js: v24.10.0 - Browser: Chrome 120 ## Screenshots Add screenshots if applicable. ## Additional Context Any other relevant information.
Feature Requests
- Check roadmap: See
README.mdroadmap section - Open discussion: https://github.com/Ospab/ospabhost8.1/discussions
- Describe use case: Why is this feature needed?
🎉 Thank You!
Thank you for contributing to Ospabhost 8.1! Every contribution, no matter how small, helps improve the project.
Contributors Hall of Fame:
- @Ospab - Lead Developer
- Your name here? 🌟
Happy Coding! 🚀
If you have questions about contributing, feel free to reach out via support@ospab.host.