--- layout: default title: Contributing --- # Contributing Guide [← Back to Home](index.html) 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 1. **Clone the repository** ```bash git clone https://github.com/theta42/proxy.git cd proxy/nodejs ``` 2. **Install dependencies** ```bash npm install ``` 3. **Start Redis** (if not already running) ```bash redis-server ``` 4. **Run in development mode** ```bash npm run dev ``` This starts the Node.js API with nodemon for auto-reload on file changes. 5. **Access the API** - API: `http://localhost:3000/api` - Web UI: `http://localhost:3000` ## Testing The project uses Node.js built-in test runner (requires Node 18+). ### Running Tests ```bash # 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: ```javascript 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 ```javascript '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 1. **Run tests** - Ensure all tests pass ```bash npm test ``` 2. **Test locally** - Verify your changes work ```bash npm run dev ``` 3. **Update documentation** - Keep docs in sync with code changes 4. **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 1. **Fork the repository** 2. **Create a feature branch** ```bash git checkout -b feature/my-new-feature ``` 3. **Make your changes** 4. **Commit your changes** ```bash git add . git commit -m "Description of changes" ``` 5. **Push to your fork** ```bash git push origin feature/my-new-feature ``` 6. **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: 1. GitHub Actions automatically runs tests 2. Tests execute on multiple Node.js versions 3. PR cannot be merged until all checks pass 4. Review from maintainers 5. Merge to master ## Data Models The project uses [model-redis](https://www.npmjs.com/package/model-redis) as the ORM for Redis data storage. All models extend the `Table` class and use a declarative schema via `_keyMap`. **Example Model:** ```javascript 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](https://www.npmjs.com/package/model-redis) ## Adding Features ### Adding a DNS Provider 1. **Create provider file** in `models/dns_provider/yourprovider.js` 2. **Extend DnsApi base class** ```javascript 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) { } } ``` 3. **Add to provider list** in `models/dns_provider.js` 4. **Add contract tests** in `test/integration/dns_provider.test.js` 5. **Test your provider** ```bash npm run test:integration ``` ### Adding API Endpoints 1. **Add route** in appropriate file (`routes/`) 2. **Update API documentation** (`nodejs/api.md` and `docs/api.md` — keep them in sync) 3. **Test the endpoint** manually and add integration tests if needed ## Getting Help - **Questions?** Open a [GitHub Discussion](https://github.com/theta42/proxy/discussions) - **Bug reports** Use [GitHub Issues](https://github.com/theta42/proxy/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. --- [← Back to Home](index.html) | [View on GitHub](https://github.com/theta42/proxy)