diff --git a/API_DOCUMENTATION.md b/API_DOCUMENTATION.md new file mode 100644 index 000000000..5b95be6e8 --- /dev/null +++ b/API_DOCUMENTATION.md @@ -0,0 +1,600 @@ +# Node Hello World - API Documentation + +## Overview + +This is a dual-purpose Node.js application that can operate in two modes: + +1. **HTTP Server Mode**: A simple HTTP server that serves a "Hello World" message +2. **Pushover Mode**: A notification tool that sends one Pushover notification and exits + +The application is built using Node.js's built-in `http` module and is designed for testing simple deployments to the cloud or sending one-time notifications. + +## Project Structure + +``` +node-hello/ +├── index.js # Main application entry point +├── package.json # Project configuration and dependencies +├── package-lock.json # Dependency lock file +├── README.md # Basic project information +├── .gitignore # Git ignore rules +├── .prettierrc # Code formatting configuration +└── .vscode/ # VS Code configuration + └── settings.json # Editor settings +``` + +## Public APIs and Components + +### 1. HTTP Server (`index.js`) + +The main component of the application is an HTTP server that responds to all requests with a "Hello Node!" message. + +#### Server Configuration + +```javascript +const http = require('http'); +const port = process.env.PORT || 3000; +``` + +- **Port**: Configurable via `PORT` environment variable, defaults to `3000` +- **Protocol**: HTTP (not HTTPS) +- **Dependencies**: Node.js built-in `http` module + +#### Server Instance + +```javascript +const server = http.createServer((req, res) => { + res.statusCode = 200; + const msg = 'Hello Node!\n' + res.end(msg); +}); +``` + +**Function Signature**: `http.createServer(requestListener)` + +**Parameters**: +- `req` (http.IncomingMessage): The HTTP request object +- `res` (http.ServerResponse): The HTTP response object + +**Response**: +- **Status Code**: 200 (OK) +- **Content-Type**: Plain text (default) +- **Body**: "Hello Node!\n" + +#### Server Startup + +```javascript +server.listen(port, () => { + console.log(`Server running on http://localhost:${port}/`); +}); +``` + +**Function Signature**: `server.listen(port, callback)` + +**Parameters**: +- `port` (number): The port number to listen on +- `callback` (function): Callback function executed when server starts + +## API Endpoints + +### GET / (Root Endpoint) + +**Description**: Returns a simple "Hello Node!" message + +**HTTP Method**: GET (responds to all HTTP methods) + +**URL**: `http://localhost:3000/` (or configured port) + +**Request Parameters**: None + +**Response**: +``` +Status: 200 OK +Content-Type: text/plain +Body: Hello Node! +``` + +**Example Request**: +```bash +curl http://localhost:3000/ +``` + +**Example Response**: +``` +Hello Node! +``` + +### All Other Endpoints + +**Description**: The server responds to all paths with the same "Hello Node!" message + +**HTTP Method**: Any (GET, POST, PUT, DELETE, etc.) + +**URL**: `http://localhost:3000/*` (any path) + +**Response**: Same as root endpoint + +**Example Requests**: +```bash +curl http://localhost:3000/api/users +curl -X POST http://localhost:3000/login +curl -X PUT http://localhost:3000/data/123 +``` + +All return the same "Hello Node!" response. + +## Configuration + +### Environment Variables + +| Variable | Description | Default | Required | +|----------|-------------|---------|----------| +| `PORT` | Port number for the server to listen on | `3000` | No | + +### Package.json Configuration + +```json +{ + "name": "node-hello", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node index.js" + } +} +``` + +**Key Properties**: +- **name**: `node-hello` - Package name +- **version**: `1.0.0` - Semantic version +- **main**: `index.js` - Entry point +- **scripts.start**: `node index.js` - Start command +- **scripts.notify**: `node index.js --pushping --userkey YOUR_USER_KEY --apikey YOUR_API_KEY` - Send notification +- **scripts.help**: `node index.js --help` - Show help message + +## Application Modes + +### HTTP Server Mode (Default) + +When run without the `--pushping` parameter, the application operates as a continuous HTTP server: + +- Serves "Hello Node!" response to all HTTP requests +- Runs continuously until manually stopped +- Listens on configurable port (default: 3000) + +### Pushover Mode + +When run with the `--pushping` parameter, the application operates as a one-time notification tool: + +- Sends a single notification to Pushover +- Exits immediately after sending the notification +- Does not start the HTTP server +- Useful for scripts, cron jobs, or one-time alerts + +### Command Line Parameters + +| Parameter | Description | Required | Example | +|-----------|-------------|----------|---------| +| `--pushping` | Enable pushover notifications | No | `--pushping` | +| `--userkey` | Pushover user key | Yes (with --pushping) | `--userkey abcd1234` | +| `--apikey` | Pushover application API key | Yes (with --pushping) | `--apikey efgh5678` | +| `--title` | Custom notification title | No | `--title "My Server"` | +| `--message` | Custom notification message | No | `--message "Custom alert"` | +| `--port` | Server port | No | `--port 8080` | +| `--help` | Show help message | No | `--help` | + +### Pushover Configuration + +To use Pushover notifications, you need: + +1. **Pushover Account**: Sign up at https://pushover.net/ +2. **User Key**: Found in your Pushover dashboard +3. **API Token**: Create an application at https://pushover.net/apps/build + +### Pushover Notification Behavior + +When running in Pushover mode, the application: + +1. **Parses command line arguments** +2. **Validates required parameters** (userkey and apikey) +3. **Sends one notification** with the specified or default message +4. **Exits immediately** after notification is sent + +#### Default Notification +- **Default Title**: "Node Hello Server" +- **Default Message**: "Node Hello application notification" + +#### Message Priority +The notification content is determined in this order: +1. **Custom message** (if `--message` parameter is provided) +2. **Default message** ("Node Hello application notification") + +#### Title Priority +The notification title is determined in this order: +1. **Custom title** (if `--title` parameter is provided) +2. **Default title** ("Node Hello Server") + +### Custom Notifications + +You can customize the notification content using command line parameters: + +- **Custom Title**: Use `--title "Your Title"` to set a custom notification title +- **Custom Message**: Use `--message "Your Message"` to set a custom notification message + +**Examples**: +```bash +# Default notification +node index.js --pushping --userkey your_user_key --apikey your_api_key + +# Custom title only +node index.js --pushping --userkey your_user_key --apikey your_api_key --title "Production Alert" + +# Custom message only +node index.js --pushping --userkey your_user_key --apikey your_api_key --message "Deployment completed successfully" + +# Custom title and message +node index.js --pushping --userkey your_user_key --apikey your_api_key --title "Deploy Status" --message "Application deployed to production" +``` + +## Usage Examples + +### HTTP Server Mode + +#### Basic HTTP Server + +1. **Start the server**: +```bash +npm start +``` + +2. **Access the server**: +```bash +curl http://localhost:3000/ +``` + +3. **Expected output**: +``` +Hello Node! +``` + +#### Custom Port + +```bash +node index.js --port 8080 +``` + +#### Production Deployment + +```bash +# Set production port via environment variable +export PORT=80 +npm start + +# Or via command line +node index.js --port 80 +``` + +#### Using with PM2 + +```bash +# Install PM2 +npm install -g pm2 + +# Start with PM2 +pm2 start index.js --name "node-hello" + +# Monitor +pm2 monit +``` + +#### Docker Deployment + +```dockerfile +FROM node:18-alpine + +WORKDIR /app + +COPY package*.json ./ +RUN npm install + +COPY . . + +EXPOSE 3000 + +CMD ["npm", "start"] +``` + +### Pushover Mode + +#### Basic Notification + +```bash +node index.js --pushping --userkey YOUR_USER_KEY --apikey YOUR_API_KEY +``` + +**Expected output**: +``` +Sending pushover notification... +Pushover notification sent successfully +Notification sent. Exiting... +``` + +#### Custom Notifications + +```bash +# Custom message +node index.js --pushping --userkey YOUR_USER_KEY --apikey YOUR_API_KEY --message "Build completed successfully" + +# Custom title +node index.js --pushping --userkey YOUR_USER_KEY --apikey YOUR_API_KEY --title "CI/CD Pipeline" + +# Both custom title and message +node index.js --pushping --userkey YOUR_USER_KEY --apikey YOUR_API_KEY --title "Deployment Alert" --message "Application deployed to production" +``` + +#### Use in Scripts + +```bash +#!/bin/bash +# deploy.sh + +echo "Starting deployment..." +# ... deployment commands ... + +if [ $? -eq 0 ]; then + node index.js --pushping --userkey $PUSHOVER_USER --apikey $PUSHOVER_API --title "Deployment Success" --message "Application deployed successfully" +else + node index.js --pushping --userkey $PUSHOVER_USER --apikey $PUSHOVER_API --title "Deployment Failed" --message "Deployment encountered errors" +fi +``` + +#### Use in Cron Jobs + +```bash +# Add to crontab for daily health check notification +0 9 * * * /usr/bin/node /path/to/index.js --pushping --userkey YOUR_USER_KEY --apikey YOUR_API_KEY --title "Daily Health Check" --message "System is running normally" +``` + +## Error Handling + +The current implementation does not include explicit error handling. The server will: + +- Return `200 OK` for all requests +- Not handle server errors explicitly +- Rely on Node.js default error handling + +### Recommendations for Production + +```javascript +// Enhanced error handling example +const server = http.createServer((req, res) => { + try { + res.statusCode = 200; + res.setHeader('Content-Type', 'text/plain'); + const msg = 'Hello Node!\n'; + res.end(msg); + } catch (error) { + console.error('Server error:', error); + res.statusCode = 500; + res.end('Internal Server Error'); + } +}); + +// Handle server errors +server.on('error', (error) => { + console.error('Server error:', error); +}); +``` + +## Performance Considerations + +### Current Implementation + +- **Single-threaded**: Uses Node.js event loop +- **No caching**: Generates response for each request +- **No compression**: Serves plain text without compression +- **No rate limiting**: Accepts unlimited requests + +### Optimization Recommendations + +1. **Add compression**: +```javascript +const zlib = require('zlib'); +// Add gzip compression for responses +``` + +2. **Add caching headers**: +```javascript +res.setHeader('Cache-Control', 'public, max-age=3600'); +``` + +3. **Add rate limiting**: +```javascript +// Use express-rate-limit or similar +``` + +## Testing + +### Manual Testing + +```bash +# Test basic functionality +curl http://localhost:3000/ + +# Test different HTTP methods +curl -X POST http://localhost:3000/ +curl -X PUT http://localhost:3000/api/test +curl -X DELETE http://localhost:3000/users/1 +``` + +### Load Testing + +```bash +# Using curl for basic load testing +for i in {1..100}; do curl http://localhost:3000/ & done + +# Using Apache Bench (ab) +ab -n 1000 -c 10 http://localhost:3000/ +``` + +### Integration Testing Example + +```javascript +// test.js +const http = require('http'); +const assert = require('assert'); + +const options = { + hostname: 'localhost', + port: 3000, + path: '/', + method: 'GET' +}; + +const req = http.request(options, (res) => { + assert.strictEqual(res.statusCode, 200); + + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + + res.on('end', () => { + assert.strictEqual(data, 'Hello Node!\n'); + console.log('Test passed!'); + }); +}); + +req.on('error', (error) => { + console.error('Test failed:', error); +}); + +req.end(); +``` + +## Deployment + +### Local Development + +```bash +# Install dependencies (none in this case) +npm install + +# Start development server +npm start + +# Server will be available at http://localhost:3000/ +``` + +### Production Deployment + +#### AWS EC2 + +```bash +# Transfer files to EC2 instance +scp -r . ec2-user@your-instance:/home/ec2-user/node-hello + +# SSH into instance +ssh ec2-user@your-instance + +# Install Node.js and start application +sudo yum update -y +sudo yum install -y nodejs npm +cd node-hello +npm start +``` + +#### Heroku + +```bash +# Create Heroku app +heroku create your-app-name + +# Deploy +git push heroku main +``` + +#### Docker + +```bash +# Build image +docker build -t node-hello . + +# Run container +docker run -p 3000:3000 node-hello +``` + +## Security Considerations + +### Current Security Status + +- **No authentication**: Open to all requests +- **No input validation**: Accepts all requests without validation +- **No HTTPS**: Uses HTTP only +- **No security headers**: Missing security headers + +### Security Recommendations + +1. **Add HTTPS**: +```javascript +const https = require('https'); +const fs = require('fs'); + +const options = { + key: fs.readFileSync('private-key.pem'), + cert: fs.readFileSync('certificate.pem') +}; + +const server = https.createServer(options, (req, res) => { + // ... handler code +}); +``` + +2. **Add security headers**: +```javascript +res.setHeader('X-Content-Type-Options', 'nosniff'); +res.setHeader('X-Frame-Options', 'DENY'); +res.setHeader('X-XSS-Protection', '1; mode=block'); +``` + +3. **Add rate limiting and input validation for production use** + +## Contributing + +### Code Style + +The project uses Prettier for code formatting with the following configuration: + +```json +{ + "singleQuote": true, + "semi": true, + "tabWidth": 2, + "trailingComma": "es5", + "printWidth": 80, + "bracketSpacing": true, + "arrowParens": "avoid" +} +``` + +### Development Workflow + +1. Make changes to `index.js` +2. Test locally with `npm start` +3. Format code with `npx prettier --write .` +4. Commit changes + +## License + +ISC License (as specified in package.json) + +## Support + +For issues and questions, please refer to the GitHub repository: +- **Repository**: https://github.com/johnpapa/node-hello +- **Issues**: https://github.com/johnpapa/node-hello/issues + +--- + +*This documentation covers all public APIs, functions, and components of the Node Hello World application. The application is intentionally simple and serves as a foundation for more complex Node.js applications.* diff --git a/README.md b/README.md index b84b3924e..509d264e1 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,86 @@ # Node Hello World -Simple node.js app that servers "hello world" +Simple node.js app that serves "hello world" with optional Pushover notification support -Great for testing simple deployments to the cloud +Great for testing simple deployments to the cloud or sending one-time notifications + +## Features + +- **HTTP Server Mode**: Lightweight HTTP server with configurable port +- **Pushover Mode**: Send one notification and exit +- Command line parameter support +- Dual functionality: server or notification tool ## Run It -`npm start` +### HTTP Server Mode (Default) + +```bash +# Start HTTP server on port 3000 +npm start + +# Or with custom port +node index.js --port 8080 +``` + +### Pushover Mode (Send notification and exit) + +```bash +# Send notification with default message +node index.js --pushping --userkey YOUR_USER_KEY --apikey YOUR_API_KEY + +# Send custom notification +node index.js --pushping --userkey YOUR_USER_KEY --apikey YOUR_API_KEY --title "Alert" --message "Something happened" +``` + +## Command Line Options + +| Parameter | Description | Required | Example | +|-----------|-------------|----------|---------| +| `--pushping` | Enable pushover notifications | No | `--pushping` | +| `--userkey` | Pushover user key | Yes (with --pushping) | `--userkey abcd1234` | +| `--apikey` | Pushover application API key | Yes (with --pushping) | `--apikey efgh5678` | +| `--title` | Custom notification title | No | `--title "My Server"` | +| `--message` | Custom notification message | No | `--message "Custom alert"` | +| `--port` | Server port | No | `--port 8080` | +| `--help` | Show help message | No | `--help` | + +## Pushover Setup + +1. Create account at [pushover.net](https://pushover.net/) +2. Get your User Key from the dashboard +3. Create an application to get an API Token +4. Use these keys with the `--userkey` and `--apikey` parameters + +## Modes + +### HTTP Server Mode +When run without `--pushping`, the application starts an HTTP server that: +- Serves "Hello Node!" on all requests +- Runs continuously until stopped +- Listens on configurable port (default: 3000) + +### Pushover Mode +When run with `--pushping`, the application: +- Sends one notification to Pushover +- Exits immediately after sending +- Does not start HTTP server + +## Help + +```bash +node index.js --help +``` + +## Examples + +```bash +# HTTP Server Mode +npm start # Start server on port 3000 +node index.js --port 8080 # Start server on port 8080 + +# Pushover Mode (sends notification and exits) +node index.js --pushping --userkey u123abc --apikey a456def +node index.js --pushping --userkey u123abc --apikey a456def --title "Alert" --message "Task completed" +node index.js --pushping --userkey u123abc --apikey a456def --message "Deployment finished" +``` diff --git a/index.js b/index.js index 54e5fef1f..8ec861db2 100644 --- a/index.js +++ b/index.js @@ -1,12 +1,184 @@ const http = require('http'); -const port = process.env.PORT || 3000; +const https = require('https'); +const querystring = require('querystring'); -const server = http.createServer((req, res) => { - res.statusCode = 200; - const msg = 'Hello Node!\n' - res.end(msg); -}); +// Parse command line arguments +const args = process.argv.slice(2); +const config = { + port: process.env.PORT || 3000, + pushping: false, + userkey: null, + apikey: null, + title: 'Node Hello Server', + message: null +}; -server.listen(port, () => { - console.log(`Server running on http://localhost:${port}/`); -}); +// Parse command line arguments +for (let i = 0; i < args.length; i++) { + switch (args[i]) { + case '--pushping': + config.pushping = true; + break; + case '--userkey': + config.userkey = args[i + 1]; + i++; // Skip next argument as it's the value + break; + case '--apikey': + config.apikey = args[i + 1]; + i++; // Skip next argument as it's the value + break; + case '--title': + config.title = args[i + 1]; + i++; // Skip next argument as it's the value + break; + case '--message': + config.message = args[i + 1]; + i++; // Skip next argument as it's the value + break; + case '--port': + config.port = parseInt(args[i + 1]); + i++; // Skip next argument as it's the value + break; + } +} + +// Function to show help +function showHelp() { + console.log(` +Usage: node index.js [options] + +Modes: + HTTP Server Mode (default): Runs a continuous HTTP server + Pushover Mode: Sends one notification and exits + +Options: + --pushping Enable pushover mode (sends one notification and exits) + --userkey Pushover user key (required with --pushping) + --apikey Pushover API key (required with --pushping) + --title Custom title for notification (optional) + --message <message> Custom message for notification (optional) + --port <port> Port to run server on (default: 3000, ignored in pushover mode) + --help Show this help message + +Examples: + # Run HTTP server + node index.js + node index.js --port 8080 + + # Send pushover notification and exit + node index.js --pushping --userkey YOUR_USER_KEY --apikey YOUR_API_KEY + node index.js --pushping --userkey YOUR_USER_KEY --apikey YOUR_API_KEY --title "Alert" --message "Something happened" +`); +} + +// Check for help flag +if (args.includes('--help') || args.includes('-h')) { + showHelp(); + process.exit(0); +} + +// Validate pushover configuration +if (config.pushping && (!config.userkey || !config.apikey)) { + console.error('Error: --pushping requires both --userkey and --apikey parameters'); + console.error('Use --help for usage information'); + process.exit(1); +} + +// Function to send pushover notification +function sendPushoverNotification(message, title = null, callback = null) { + if (!config.pushping) { + if (callback) callback(); + return; + } + + // Use custom message if provided, otherwise use the passed message + const finalMessage = config.message || message; + // Use custom title if provided, otherwise use the passed title or default + const finalTitle = title || config.title; + + const postData = querystring.stringify({ + token: config.apikey, + user: config.userkey, + message: finalMessage, + title: finalTitle + }); + + const options = { + hostname: 'api.pushover.net', + port: 443, + path: '/1/messages.json', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': Buffer.byteLength(postData) + } + }; + + const req = https.request(options, (res) => { + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + res.on('end', () => { + if (res.statusCode === 200) { + console.log('Pushover notification sent successfully'); + } else { + console.error('Failed to send pushover notification:', res.statusCode, data); + } + if (callback) callback(); + }); + }); + + req.on('error', (error) => { + console.error('Error sending pushover notification:', error); + if (callback) callback(); + }); + + req.write(postData); + req.end(); +} + +// If pushover is enabled, just send notification and exit +if (config.pushping) { + console.log('Sending pushover notification...'); + + // Use custom message or default message + const notificationMessage = config.message || 'Node Hello application notification'; + + sendPushoverNotification(notificationMessage, config.title, () => { + console.log('Notification sent. Exiting...'); + process.exit(0); + }); +} else { + // Run as HTTP server if pushover is not enabled + let requestCount = 0; + + const server = http.createServer((req, res) => { + requestCount++; + + res.statusCode = 200; + const msg = 'Hello Node!\n'; + res.end(msg); + }); + + server.listen(config.port, () => { + const startMessage = `Server running on http://localhost:${config.port}/`; + console.log(startMessage); + }); + + // Handle graceful shutdown + process.on('SIGINT', () => { + console.log('\nShutting down server...'); + + server.close(() => { + console.log('Server closed'); + process.exit(0); + }); + }); + + // Handle uncaught exceptions + process.on('uncaughtException', (error) => { + console.error('Uncaught Exception:', error); + process.exit(1); + }); +} diff --git a/package.json b/package.json index b0d12dfc6..2349bd687 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "description": "", "main": "index.js", "scripts": { - "start": "node index.js" + "start": "node index.js", + "notify": "node index.js --pushping --userkey YOUR_USER_KEY --apikey YOUR_API_KEY", + "help": "node index.js --help" }, "repository": { "type": "git",