786 lines
18 KiB
Markdown
786 lines
18 KiB
Markdown
# 🤝 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](#code-of-conduct)
|
|
- [Getting Started](#getting-started)
|
|
- [Development Workflow](#development-workflow)
|
|
- [Coding Standards](#coding-standards)
|
|
- [Commit Guidelines](#commit-guidelines)
|
|
- [Pull Request Process](#pull-request-process)
|
|
- [Testing Requirements](#testing-requirements)
|
|
- [Documentation](#documentation)
|
|
- [Contact](#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:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
1. **Fork the repository** on GitHub:
|
|
- Click "Fork" button at https://github.com/Ospab/ospabhost8.1
|
|
|
|
2. **Clone your fork locally:**
|
|
```bash
|
|
git clone https://github.com/YOUR_USERNAME/ospabhost8.1.git
|
|
cd ospabhost8.1/ospabhost
|
|
```
|
|
|
|
3. **Add upstream remote:**
|
|
```bash
|
|
git remote add upstream https://github.com/Ospab/ospabhost8.1.git
|
|
git remote -v
|
|
```
|
|
|
|
### Setup Development Environment
|
|
|
|
#### Backend Setup
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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
|
|
|
|
```sql
|
|
-- 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// ✅ 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
|
|
|
|
```tsx
|
|
// ✅ 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
|
|
|
|
```prisma
|
|
// ✅ 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 code
|
|
- `frontend` - Frontend code
|
|
- `api` - API endpoints
|
|
- `auth` - Authentication
|
|
- `sso` - Single Sign-On
|
|
- `panel-api` - Panel API
|
|
- `proxmox` - Proxmox integration
|
|
- `prisma` - Database/ORM
|
|
- `docs` - Documentation
|
|
|
|
#### Examples
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
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
|
|
|
|
1. **Atomic commits:** One logical change per commit
|
|
2. **Present tense:** "Add feature" not "Added feature"
|
|
3. **Imperative mood:** "Fix bug" not "Fixes bug"
|
|
4. **Reference issues:** Use `Closes #123` or `Refs #456`
|
|
5. **Keep subject < 72 chars**
|
|
6. **Use body for "why" not "what"**
|
|
|
|
---
|
|
|
|
## 🔀 Pull Request Process
|
|
|
|
### Before Opening PR
|
|
|
|
1. **Ensure all tests pass:**
|
|
```bash
|
|
# Backend
|
|
cd backend
|
|
npm run build
|
|
npm test
|
|
|
|
# Frontend
|
|
cd frontend
|
|
npm run build
|
|
npm run lint
|
|
```
|
|
|
|
2. **Update documentation:**
|
|
- If API changed, update `PANEL_API_DOCUMENTATION.md`
|
|
- If SSO changed, update `SSO_FINAL_SETUP.md`
|
|
- Update `README.md` if major feature added
|
|
|
|
3. **Check code quality:**
|
|
```bash
|
|
# Backend: TypeScript check
|
|
cd backend
|
|
npx tsc --noEmit
|
|
|
|
# Frontend: ESLint
|
|
cd frontend
|
|
npm run lint
|
|
```
|
|
|
|
4. **Rebase on latest develop:**
|
|
```bash
|
|
git fetch upstream
|
|
git rebase upstream/develop
|
|
```
|
|
|
|
### Opening Pull Request
|
|
|
|
1. **Push your branch:**
|
|
```bash
|
|
git push origin feature/your-feature-name
|
|
```
|
|
|
|
2. **Open PR on GitHub:**
|
|
- Go to https://github.com/Ospab/ospabhost8.1/pulls
|
|
- Click "New Pull Request"
|
|
- Select your fork and branch
|
|
- Target: `develop` branch (not `main`)
|
|
|
|
3. **Fill PR template:**
|
|
|
|
```markdown
|
|
## 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
|
|
|
|
1. **Automated checks:**
|
|
- CI/CD pipeline runs tests
|
|
- TypeScript compilation
|
|
- ESLint checks
|
|
|
|
2. **Code review:**
|
|
- At least 1 approval required
|
|
- Address reviewer feedback
|
|
- Update PR as needed
|
|
|
|
3. **Merge:**
|
|
- Squash commits if many small commits
|
|
- Merge to `develop`
|
|
- Delete feature branch
|
|
|
|
---
|
|
|
|
## 🧪 Testing Requirements
|
|
|
|
### Backend Testing
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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.md` or `DEPLOY_NGINX_FIX.md`
|
|
- **SSO change:** Update `SSO_FINAL_SETUP.md`
|
|
- **Database schema:** Update Prisma comments
|
|
|
|
### Documentation Standards
|
|
|
|
```typescript
|
|
// ✅ 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
|
|
|
|
1. **Check existing issues:** Search https://github.com/Ospab/ospabhost8.1/issues
|
|
2. **Create detailed report:**
|
|
```markdown
|
|
## 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
|
|
|
|
1. **Check roadmap:** See `README.md` roadmap section
|
|
2. **Open discussion:** https://github.com/Ospab/ospabhost8.1/discussions
|
|
3. **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](https://github.com/Ospab) - Lead Developer
|
|
- *Your name here?* 🌟
|
|
|
|
---
|
|
|
|
**Happy Coding! 🚀**
|
|
|
|
If you have questions about contributing, feel free to reach out via support@ospab.host.
|