Same treatment as the sso-manager-node companion PR: replaced the generic jekyll-theme-cayman theme with a custom layout mirroring the actual app UI -- dark fixed navbar with the theta42 logo, Bootstrap 5 + Font Awesome (same stack the app uses), content in a card, dark footer matching bottom.ejs. Keeps this repo's own favicon.svg (confirmed genuinely distinct SVG artwork from the shared theta42 logo, not a duplicate) as the browser-tab icon. - New cross-page nav (Home/Installation/Architecture/API/Docker/ Contributing/Changelog). - SEO: jekyll-seo-tag + jekyll-sitemap, per-page meta description, OG/Twitter card tags, canonical URLs, JSON-LD, sitemap.xml, robots.txt. - Mobile: Bootstrap's responsive grid + collapsible navbar; the screenshot pairs in index.md stack to full-width below 576px. Verified with a real Jekyll build (jekyll/jekyll Docker image) + Playwright: desktop and mobile (375px) screenshots, mobile nav toggle, active-link highlighting, zero console/page errors, and confirmed real SEO output + the correct (non-shared) favicon via curl against the served site.
8.3 KiB
layout, title, description
| layout | title | description |
|---|---|---|
| default | Contributing | How to contribute to the proxy — dev setup, tests, and code conventions. |
Contributing Guide
Thank you for considering contributing to the Proxy project! This guide will help you get started.
Development Setup
Prerequisites
- Node.js 18+ (18.x, 20.x, or 22.x recommended)
- Redis server
- Git
Local Development
-
Clone the repository
git clone https://github.com/theta42/proxy.git cd proxy/nodejs -
Install dependencies
npm install -
Start Redis (if not already running)
redis-server -
Run in development mode
npm run devThis starts the Node.js API with nodemon for auto-reload on file changes.
-
Access the API
- API:
http://localhost:3000/api - Web UI:
http://localhost:3000
- API:
Testing
The project uses Node.js built-in test runner (requires Node 18+).
Running Tests
# Run all tests
npm test
# Run only unit tests
npm run test:unit
# Run only integration tests
npm run test:integration
# Watch mode for development
npm run test:watch
Test Structure
test/
├── unit/ # Unit tests for isolated components
│ ├── basicauth.test.js
│ ├── callback_queue.test.js
│ ├── dynamic_record.test.js
│ ├── host_features.test.js
│ ├── host_lookup.test.js
│ ├── hostname_validate.test.js
│ ├── host_sso.test.js
│ ├── oidc.test.js
│ ├── password_policy.test.js
│ ├── roles.test.js
│ ├── safe_redirect.test.js
│ ├── unix_socket.test.js
│ └── wildcard_matchany.test.js
├── integration/ # Integration tests
│ └── dns_provider.test.js
└── helpers/ # Test utilities
└── dns_provider_contract.js
Writing Tests
We test custom logic, not third-party libraries:
DO test:
- Host lookup algorithm
- Socket buffering logic
- DNS provider contracts
- Custom utility functions
DON'T test:
- Express.js routing
- Redis ORM
- External DNS APIs (use mocks instead)
Adding DNS Provider Tests
When adding a new DNS provider, you must add contract tests:
describe('NewProvider Provider', () => {
const NewProvider = require('../../models/dns_provider/newprovider');
test('should meet DNS provider contract', () => {
const mockCredentials = {api_key: 'mock-key'};
const instance = validateDnsProviderContract(NewProvider, mockCredentials);
assert.ok(instance);
});
test('should have valid method signatures', () => {
const instance = new NewProvider({api_key: 'mock'});
validateMethodSignatures(instance);
});
test('should validate key mapping', () => {
const instance = new NewProvider({api_key: 'mock'});
validateKeyMapping(instance);
});
test('should validate type checking', () => {
const instance = new NewProvider({api_key: 'mock'});
validateTypeChecking(instance);
});
});
See test/integration/dns_provider.test.js for examples.
Code Style
General Guidelines
- Use strict mode:
'use strict'; - Use tabs for indentation
- Clear, descriptive variable names
- Comment complex logic
- No trailing whitespace
File Organization
'use strict';
// 1. Node.js built-ins
const fs = require('fs');
const path = require('path');
// 2. Third-party modules
const express = require('express');
const redis = require('redis');
// 3. Local modules
const {Host} = require('./models');
const middleware = require('./middleware/auth');
// 4. Code...
Naming Conventions
- Classes:
PascalCase - Functions:
camelCase - Constants:
UPPER_SNAKE_CASE - Private methods:
__privateMethod(double underscore prefix)
Project Structure
Understanding the codebase:
nodejs/
├── conf/ # Configuration (base.js, environment overlays, secrets.js)
├── controller/ # App-level wiring (pubsub, startup)
├── migrations/ # One-off Redis data migration scripts
├── models/ # Data models (Host, User, DNS providers)
├── routes/ # API route handlers
├── services/ # Background services (lookup, scheduler)
├── middleware/ # Express middleware
├── utils/ # Utility functions
├── public/ # Static web assets
├── views/ # EJS templates
└── test/ # Test suite
Pull Request Process
Before Submitting
-
Run tests - Ensure all tests pass
npm test -
Test locally - Verify your changes work
npm run dev -
Update documentation - Keep docs in sync with code changes
-
Commit messages - Use clear, descriptive messages
Add DNS provider for Route53 - Implement Route53 DNS API client - Add contract tests for Route53 - Update documentation with Route53 setup
Submitting a PR
-
Fork the repository
-
Create a feature branch
git checkout -b feature/my-new-feature -
Make your changes
-
Commit your changes
git add . git commit -m "Description of changes" -
Push to your fork
git push origin feature/my-new-feature -
Open a Pull Request on GitHub
PR Requirements
- All tests must pass (CI/CD runs automatically)
- Tests run on Node.js 18.x, 20.x, and 22.x
- No merge conflicts with
master - Code follows project conventions
- New features include tests
- Documentation updated if needed
CI/CD Process
When you open a PR:
- GitHub Actions automatically runs tests
- Tests execute on multiple Node.js versions
- PR cannot be merged until all checks pass
- Review from maintainers
- Merge to master
Data Models
The project uses model-redis as the ORM for Redis data storage. All models extend the Table class and use a declarative schema via _keyMap.
Example Model:
const Table = require('../utils/redis_model');
class Host extends Table {
static _key = 'host'; // Primary key field
static _keyMap = {
'host': {isRequired: true, type: 'string', min: 3, max: 500},
'ip': {isRequired: true, type: 'string', min: 3, max: 500},
'targetPort': {isRequired: true, type: 'number', min: 0, max: 65535},
'forcessl': {default: true, type: 'boolean'},
'created_on': {default: () => Date.now(), type: 'number'}
};
}
Learn more: model-redis documentation
Adding Features
Adding a DNS Provider
-
Create provider file in
models/dns_provider/yourprovider.js -
Extend DnsApi base class
const {DnsApi} = require('./common'); class YourProvider extends DnsApi { static _keyMap = { api_key: {isRequired: true, type: 'string', isPrivate: true} }; // Implement required methods async listDomains() { } async getRecords(domain, options) { } async createRecord(domain, options) { } async deleteRecords(domain, options) { } } -
Add to provider list in
models/dns_provider.js -
Add contract tests in
test/integration/dns_provider.test.js -
Test your provider
npm run test:integration
Adding API Endpoints
- Add route in appropriate file (
routes/) - Update API documentation (
nodejs/api.mdanddocs/api.md— keep them in sync) - Test the endpoint manually and add integration tests if needed
Getting Help
- Questions? Open a GitHub Discussion
- Bug reports Use GitHub Issues
- Security issues Email maintainers directly (see package.json)
Code of Conduct
- Be respectful and inclusive
- Focus on constructive feedback
- Help others learn and grow
- Follow the project's technical direction
License
By contributing, you agree that your contributions will be licensed under the MIT License.