Adds comprehensive documentation including architecture, API reference, deployment, development, and user guides with README.md. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 9111ef36-26c8-4085-84ca-a35dc1fec1b5 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7083d608-d6d3-4a6a-9a27-6286c5109627/910f6160-6716-488e-83e6-61256595c269.jpg
13 KiB
DynamoDNS Development Guide
This guide provides information for developers who want to contribute to the DynamoDNS project or extend its functionality.
Development Setup
Prerequisites
- Node.js: v18.0.0 or higher
- npm: v8.0.0 or higher
- PostgreSQL: v14.0 or higher (or Neon serverless database)
- Git: Latest version
Getting Started
-
Clone the repository:
git clone https://github.com/yourusername/dynamodns.git cd dynamodns -
Install dependencies:
npm install -
Set up environment variables: Create a
.envfile based on the.env.exampletemplate:# Database - Use a local PostgreSQL instance for development DATABASE_URL=postgresql://postgres:postgres@localhost:5432/dynamodns_dev # Server PORT=5000 NODE_ENV=development SESSION_SECRET=dev_session_secret # Auth - Enable registration for easier testing ENABLE_REGISTRATION=true -
Set up the database:
# Create the database createdb dynamodns_dev # Run migrations npm run db:push -
Start the development server:
npm run dev -
Access the application: Open your browser and navigate to http://localhost:5000
Project Structure
dynamodns/
├── client/ # Frontend React application
│ ├── src/ # Source code
│ │ ├── components/ # Reusable UI components
│ │ ├── context/ # React context providers
│ │ ├── hooks/ # Custom React hooks
│ │ ├── lib/ # Utility functions
│ │ ├── pages/ # Page components
│ │ └── App.tsx # Main application component
├── docs/ # Documentation
├── migrations/ # Database migration files
├── server/ # Backend Express application
│ ├── providers/ # DNS provider integrations
│ ├── utils/ # Utility functions
│ ├── auth.ts # Authentication logic
│ ├── db.ts # Database connection
│ ├── index.ts # Application entry point
│ ├── routes.ts # API routes
│ ├── storage.ts # Storage interface
│ └── vite.ts # Vite integration
├── shared/ # Shared code between client and server
│ └── schema.ts # Database schema with Drizzle ORM
└── various config files # Configuration files for the project
Technology Stack
Backend
- Express.js: Web server framework
- Drizzle ORM: Database ORM for TypeScript
- PostgreSQL: Relational database
- Passport.js: Authentication middleware
- WebSockets: Real-time communication
Frontend
- React: UI library
- TanStack Query: Data fetching and caching
- Shadcn UI: Component library built on Radix UI
- Tailwind CSS: Utility-first CSS framework
- Wouter: Lightweight routing
- Zod: Schema validation
- React Hook Form: Form handling
Architecture
Frontend Architecture
The frontend follows a component-based architecture with React:
- Context Providers: Manage global state (auth, customer, theme)
- Hooks: Custom hooks for data fetching and business logic
- Pages: Top-level components for different routes
- Components: Reusable UI building blocks
Data flow utilizes TanStack Query for client-side data fetching and caching:
// Example query
const { data, isLoading, error } = useQuery({
queryKey: ['/api/domains'],
enabled: !!user
});
// Example mutation
const mutation = useMutation({
mutationFn: async (data) => {
const res = await apiRequest('POST', '/api/domains', data);
return await res.json();
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['/api/domains'] });
// Handle success
},
onError: (error) => {
// Handle error
}
});
Backend Architecture
The backend follows a layered architecture:
- Routes Layer: API endpoints and request handling
- Service Layer: Business logic
- Data Access Layer: Database interactions via Drizzle ORM
- Provider Layer: Integrations with external DNS services
The application uses a storage interface pattern to abstract database operations:
// Example storage interface method
async getUser(id: string): Promise<User | undefined> {
const [user] = await db.select().from(users).where(eq(users.id, id));
return user || undefined;
}
Adding Features
Adding a New DNS Provider
-
Create a provider module: Create a new file in
server/providers/directory (e.g.,cloudflare.ts). -
Implement the provider interface:
import { DNSProvider, DNSRecord, DNSProviderError } from './types'; export class CloudflareProvider implements DNSProvider { constructor(private credentials: any) { // Initialize with provider credentials } async createRecord(record: DNSRecord): Promise<string> { // Implementation } async updateRecord(recordId: string, record: DNSRecord): Promise<void> { // Implementation } async deleteRecord(recordId: string): Promise<void> { // Implementation } async getRecord(recordId: string): Promise<DNSRecord> { // Implementation } } -
Register the provider: Add the provider to the provider factory in
server/providers/index.ts.
Adding a New API Endpoint
-
Create the route handler: Add your endpoint to
server/routes.ts:app.get('/api/your-endpoint', (req, res) => { // Implementation }); -
Add validation: Use Zod to validate request inputs:
const schema = z.object({ name: z.string().min(1), // Other fields }); app.post('/api/your-endpoint', (req, res) => { const result = schema.safeParse(req.body); if (!result.success) { return res.status(400).json({ errors: result.error.format() }); } // Implementation with validated data const data = result.data; }); -
Use the storage interface: Interact with the database using the storage interface:
app.get('/api/your-endpoint/:id', async (req, res) => { try { const item = await storage.getItem(req.params.id); if (!item) { return res.status(404).json({ message: 'Not found' }); } res.json(item); } catch (error) { res.status(500).json({ message: 'Server error' }); } });
Adding a New UI Component
-
Create the component: Create a new file in the appropriate directory under
client/src/components/. -
Implement the component:
import React from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; interface MyComponentProps { title: string; children: React.ReactNode; } export function MyComponent({ title, children }: MyComponentProps) { return ( <Card> <CardHeader> <CardTitle>{title}</CardTitle> </CardHeader> <CardContent> {children} </CardContent> </Card> ); } -
Use the component: Import and use your component in a page or another component.
Database Schema Management
The project uses Drizzle ORM with a schema defined in shared/schema.ts.
Adding a New Table
-
Define the table:
export const myNewTable = pgTable('my_new_table', { id: text('id').primaryKey().defaultRandom(), name: text('name').notNull(), createdAt: timestamp('created_at').defaultNow().notNull(), customerId: text('customer_id').references(() => customers.id, { onDelete: 'cascade' }), }); -
Define relations:
export const myNewTableRelations = relations(myNewTable, ({ one }) => ({ customer: one(customers, { fields: [myNewTable.customerId], references: [customers.id], }), })); -
Create insert schema and types:
export const insertMyNewTableSchema = createInsertSchema(myNewTable).pick({ name: true, customerId: true, }); export type InsertMyNewTable = z.infer<typeof insertMyNewTableSchema>; export type MyNewTable = typeof myNewTable.$inferSelect; -
Update the storage interface: Add methods to
server/storage.tsfor CRUD operations on your new table. -
Push the schema changes:
npm run db:push
Testing
Unit Tests
The project uses Jest for unit testing:
# Run all tests
npm test
# Run tests with coverage
npm test -- --coverage
# Run specific test file
npm test -- src/path/to/test.test.ts
Writing Tests
Example test for a utility function:
// src/utils/formatDate.test.ts
import { formatDate } from './formatDate';
describe('formatDate', () => {
it('formats a date correctly', () => {
const date = new Date('2023-01-01T12:00:00Z');
expect(formatDate(date)).toBe('Jan 1, 2023');
});
it('handles null values', () => {
expect(formatDate(null)).toBe('');
});
});
Example test for a React component using React Testing Library:
// src/components/Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from './Button';
describe('Button', () => {
it('renders correctly', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
it('calls onClick when clicked', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByText('Click me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
});
API Testing
For API endpoints, use Supertest:
// src/routes/domains.test.ts
import request from 'supertest';
import { app } from '../app';
import { db } from '../db';
describe('Domains API', () => {
beforeEach(async () => {
// Set up test data
});
afterEach(async () => {
// Clean up test data
});
it('GET /api/domains returns domains', async () => {
const response = await request(app)
.get('/api/domains')
.set('Authorization', `Bearer ${testToken}`);
expect(response.status).toBe(200);
expect(response.body).toHaveLength(2);
});
});
Code Style and Linting
The project uses ESLint and Prettier for code style and linting:
# Run ESLint
npm run lint
# Fix ESLint issues
npm run lint:fix
# Format code with Prettier
npm run format
Commit Guidelines
We follow the Conventional Commits specification:
feat: A new featurefix: A bug fixdocs: Documentation changesstyle: Changes that do not affect the meaning of the coderefactor: Code changes that neither fix a bug nor add a featureperf: Performance improvementstest: Adding or fixing testschore: Changes to the build process or auxiliary tools
Example:
feat(dns-records): add support for SRV records
Debugging
Server-side Debugging
Add logging with debug statements:
import debug from 'debug';
const log = debug('dynamodns:server');
app.get('/api/domains', (req, res) => {
log('Fetching domains for user', req.user?.id);
// Implementation
});
Run the application with debug enabled:
DEBUG=dynamodns:* npm run dev
Client-side Debugging
Use React DevTools for component inspection and debugging.
For network debugging, use the browser's developer tools and the Network tab.
Deployment
See the Deployment Guide for information on deploying the application.
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature-name - Make your changes
- Run tests:
npm test - Commit your changes following the commit guidelines
- Push to your fork:
git push origin feature/your-feature-name - Create a Pull Request
Resources
- Drizzle ORM Documentation: https://orm.drizzle.team/docs/overview
- React Documentation: https://react.dev/
- TanStack Query Documentation: https://tanstack.com/query/latest
- Shadcn UI Documentation: https://ui.shadcn.com/
- Tailwind CSS Documentation: https://tailwindcss.com/docs
- Express.js Documentation: https://expressjs.com/