A Node.js-based job scheduler that executes Python scripts in isolated virtual environments with CRON-like scheduling capabilities.
- Isolated Python Environments: Each job runs in its own virtual environment
- CRON Scheduling: Schedule jobs using standard CRON patterns
- Security: Python jobs are restricted to API calls and logging only
- NoSQL Storage: MongoDB-based storage for job configurations
- REST API: Full CRUD API for managing jobs
- Authentication: JWT tokens, API keys, and Basic Auth support
- User Management: Role-based access control (admin/user)
- Automatic Cleanup: Virtual environments are automatically removed after execution
- Comprehensive Logging: Detailed logging of job execution and errors
- Manual Execution: Execute jobs on-demand via API
- Rate Limiting: Built-in protection against abuse
- Search & Statistics: Advanced job search and system statistics
- Node.js 18+
- Python 3.7+
- MongoDB
- npm or yarn
- Clone the repository:
git clone <repository-url>
cd pyjob- Install dependencies:
npm install- Set up environment variables:
cp env.example .env
# Edit .env with your configuration- Start MongoDB (if running locally):
mongod- Create an admin user:
npm run create-admin- Start the application:
# Development
npm run dev
# Production
npm start| Variable | Description | Default |
|---|---|---|
MONGODB_URI |
MongoDB connection string | mongodb://localhost:27017/pyjob |
PORT |
Server port | 3000 |
NODE_ENV |
Environment mode | development |
PYTHON_BASE_PATH |
Base path for virtual environments | /tmp/pyjob-envs |
MAX_CONCURRENT_JOBS |
Maximum concurrent job executions | 5 |
LOG_LEVEL |
Logging level | info |
POST /api/auth/register- Register new userPOST /api/auth/login- Login with username/passwordPOST /api/auth/token- Get JWT token (Basic Auth)GET /api/auth/me- Get current user infoPOST /api/auth/api-keys- Generate API keyGET /api/auth/api-keys- List API keysDELETE /api/auth/api-keys/:name- Revoke API key
GET /api/jobs- List all jobs (public read)GET /api/jobs/:id- Get job by ID (public read)POST /api/jobs- Create new job (auth required)PUT /api/jobs/:id- Update job (auth required)DELETE /api/jobs/:id- Delete job (auth required)POST /api/jobs/:id/execute- Execute job manually (auth required)PATCH /api/jobs/:id/toggle- Toggle job status (auth required)GET /api/jobs/:id/executions- Get job execution history (auth required)GET /api/jobs/:id/logs- Get job logs (auth required)GET /api/jobs/search- Search jobs (public)GET /api/jobs/stats/overview- Get statistics (public)GET /api/jobs/scheduled/status- Get scheduled jobs status (auth required)GET /api/jobs/export- Export jobs data (admin only)
GET /health- Health checkGET /api/jobs/health/status- System health statusGET /- API information
{
"name": "my-python-job",
"description": "A sample Python job",
"cronPattern": "0 */6 * * *",
"pythonScript": "print('Hello from Python!')",
"requirements": ["requests", "numpy"],
"environmentVariables": {
"API_KEY": "your-api-key",
"DEBUG": "true"
},
"isActive": true,
"timeout": 300000,
"maxRetries": 3
}Standard 5-field CRON pattern:
* * * * *
β β β β β
β β β β ββββ Day of week (0-6)
β β β ββββββ Month (1-12)
β β ββββββββ Day of month (1-31)
β ββββββββββ Hour (0-23)
ββββββββββββ Minute (0-59)
Examples:
0 */6 * * *- Every 6 hours0 9 * * 1-5- Every weekday at 9 AM*/15 * * * *- Every 15 minutes
This comprehensive guide covers all aspects of using PyJob, from account creation to advanced job management with different authentication methods.
PyJob supports three authentication methods:
- JWT Token Authentication - For web applications and API clients
- API Key Authentication - For programmatic access and integrations
- Basic Authentication - For simple HTTP clients
The easiest way to get started is to create an admin account using the provided script:
# Run the admin creation script
npm run create-adminThis will create an admin user with the following default credentials:
- Username:
admin - Email:
admin@pyjob.local - Password:
admin123 - Role:
admin
You can register new users through the API. Here are examples for all authentication methods:
# 1. Register a new user
curl -X POST http://localhost:3000/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"username": "john_doe",
"email": "john@example.com",
"password": "securepassword123",
"role": "user"
}'
# Response:
# {
# "message": "User registered successfully",
# "user": {
# "id": "68ca6ea99ad4c9a6fa530bee",
# "username": "john_doe",
# "email": "john@example.com",
# "role": "user",
# "isActive": true,
# "createdAt": "2025-09-17T08:15:02.560Z"
# }
# }# Register using Basic Auth (username:password in base64)
curl -X POST http://localhost:3000/api/auth/register \
-H "Content-Type: application/json" \
-H "Authorization: Basic $(echo -n 'admin:admin123' | base64)" \
-d '{
"username": "jane_doe",
"email": "jane@example.com",
"password": "securepassword123",
"role": "user"
}'# Login and get JWT token
curl -X POST http://localhost:3000/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "john_doe",
"password": "securepassword123"
}'
# Response:
# {
# "message": "Login successful",
# "user": {
# "id": "68ca6ea99ad4c9a6fa530bee",
# "username": "john_doe",
# "email": "john@example.com",
# "role": "user",
# "lastLogin": "2025-09-17T08:15:32.490Z"
# },
# "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
# }# Get JWT token using Basic Auth
curl -X POST http://localhost:3000/api/auth/token \
-H "Authorization: Basic $(echo -n 'john_doe:securepassword123' | base64)"
# Response:
# {
# "message": "Token generated successfully",
# "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
# "expiresIn": "24h"
# }API keys are perfect for programmatic access and integrations:
# Generate a new API key (requires authentication)
curl -X POST http://localhost:3000/api/auth/api-keys \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "my-integration-key"
}'
# Response:
# {
# "message": "API key generated successfully",
# "apiKey": {
# "name": "my-integration-key",
# "key": "pyjob_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
# "createdAt": "2025-09-17T08:15:32.490Z"
# }
# }# List all your API keys
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
http://localhost:3000/api/auth/api-keys
# Response:
# {
# "apiKeys": [
# {
# "name": "my-integration-key",
# "key": "pyjob_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
# "createdAt": "2025-09-17T08:15:32.490Z"
# }
# ]
# }# Revoke an API key
curl -X DELETE http://localhost:3000/api/auth/api-keys/my-integration-key \
-H "Authorization: Bearer YOUR_JWT_TOKEN"PyJob now supports comprehensive JWT token management, allowing users to create, manage, and track multiple tokens with descriptions and expiration control.
# Generate a new JWT token with description
curl -X POST http://localhost:3000/api/auth/tokens \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"description": "API integration token for mobile app"
}'
# Response:
# {
# "message": "JWT token generated successfully",
# "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
# "tokenInfo": {
# "tokenId": "18019b9a-0eb9-48ba-ab49-104d4df531cb",
# "description": "API integration token for mobile app",
# "expiresAt": "2025-09-18T08:33:58.342Z",
# "createdAt": "2025-09-17T08:33:58.347Z"
# }
# }# List all your JWT tokens
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
http://localhost:3000/api/auth/tokens
# Response:
# {
# "tokens": [
# {
# "tokenId": "18019b9a-0eb9-48ba-ab49-104d4df531cb",
# "description": "API integration token for mobile app",
# "createdAt": "2025-09-17T08:33:58.343Z",
# "lastUsed": "2025-09-17T10:34:11.000Z",
# "expiresAt": "2025-09-20T08:33:58.342Z",
# "isActive": true,
# "isExpired": false
# }
# ]
# }# Get details for a specific token
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
http://localhost:3000/api/auth/tokens/18019b9a-0eb9-48ba-ab49-104d4df531cb
# Response:
# {
# "tokenId": "18019b9a-0eb9-48ba-ab49-104d4df531cb",
# "description": "API integration token for mobile app",
# "createdAt": "2025-09-17T08:33:58.343Z",
# "lastUsed": "2025-09-17T10:34:11.000Z",
# "expiresAt": "2025-09-20T08:33:58.342Z",
# "isActive": true,
# "isExpired": false
# }# Update token description
curl -X PUT http://localhost:3000/api/auth/tokens/18019b9a-0eb9-48ba-ab49-104d4df531cb/description \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"description": "Updated description - Production API token"
}'# Extend token expiration by 48 hours
curl -X POST http://localhost:3000/api/auth/tokens/18019b9a-0eb9-48ba-ab49-104d4df531cb/extend \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"additionalHours": 48
}'
# Response:
# {
# "message": "Token expiration extended successfully",
# "newExpiresAt": "2025-09-20T08:33:58.342Z"
# }# Revoke a JWT token
curl -X DELETE http://localhost:3000/api/auth/tokens/18019b9a-0eb9-48ba-ab49-104d4df531cb \
-H "Authorization: Bearer YOUR_JWT_TOKEN"# Clean up expired tokens
curl -X POST http://localhost:3000/api/auth/tokens/cleanup \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
# Response:
# {
# "message": "Token cleanup completed",
# "cleanedCount": 3,
# "remainingTokens": 2
# }Jobs can be created using any of the three authentication methods. Here are complete examples:
# Set your JWT token (replace with actual token)
export JWT_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
# Create a simple Python job
curl -X POST http://localhost:3000/api/jobs \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Hello World Job",
"description": "A simple job that prints hello world every 5 minutes",
"cronPattern": "*/5 * * * *",
"pythonScript": "print(\"Hello from PyJob!\")\nprint(\"Current time:\", __import__(\"datetime\").datetime.now())",
"requirements": [],
"environmentVariables": {
"ENVIRONMENT": "production"
},
"isActive": true,
"timeout": 300000,
"maxRetries": 3
}'
# Response:
# {
# "name": "Hello World Job",
# "description": "A simple job that prints hello world every 5 minutes",
# "cronPattern": "*/5 * * * *",
# "pythonScript": "print(\"Hello from PyJob!\")\nprint(\"Current time:\", __import__(\"datetime\").datetime.now())",
# "requirements": [],
# "environmentVariables": {
# "ENVIRONMENT": "production"
# },
# "isActive": true,
# "runCount": 0,
# "successCount": 0,
# "failureCount": 0,
# "timeout": 300000,
# "maxRetries": 3,
# "_id": "68ca6ea99ad4c9a6fa530bee",
# "createdAt": "2025-09-17T08:17:45.473Z",
# "updatedAt": "2025-09-17T08:17:45.474Z",
# "__v": 0
# }# Set your API key (replace with actual key)
export API_KEY="pyjob_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
# Create a data processing job
curl -X POST http://localhost:3000/api/jobs \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Data Processor",
"description": "Process data from external API every hour",
"cronPattern": "0 * * * *",
"pythonScript": "import requests\nimport json\n\n# Fetch data from API\nresponse = requests.get(\"https://api.example.com/data\")\ndata = response.json()\n\n# Process the data\nprocessed_data = {\n \"timestamp\": __import__(\"datetime\").datetime.now().isoformat(),\n \"count\": len(data),\n \"status\": \"processed\"\n}\n\nprint(json.dumps(processed_data, indent=2))",
"requirements": ["requests"],
"environmentVariables": {
"API_URL": "https://api.example.com",
"API_KEY": "your-secret-api-key"
},
"isActive": true,
"timeout": 600000,
"maxRetries": 2
}'# Create a monitoring job using Basic Auth
curl -X POST http://localhost:3000/api/jobs \
-H "Authorization: Basic $(echo -n 'john_doe:securepassword123' | base64)" \
-H "Content-Type: application/json" \
-d '{
"name": "System Monitor",
"description": "Monitor system health every 15 minutes",
"cronPattern": "*/15 * * * *",
"pythonScript": "import psutil\nimport json\n\n# Get system information\ncpu_percent = psutil.cpu_percent(interval=1)\nmemory = psutil.virtual_memory()\ndisk = psutil.disk_usage(\"/\")\n\n# Create monitoring report\nreport = {\n \"timestamp\": __import__(\"datetime\").datetime.now().isoformat(),\n \"cpu_usage\": cpu_percent,\n \"memory_usage\": memory.percent,\n \"disk_usage\": disk.percent,\n \"status\": \"healthy\" if cpu_percent < 80 and memory.percent < 80 else \"warning\"\n}\n\nprint(json.dumps(report, indent=2))",
"requirements": ["psutil"],
"environmentVariables": {
"MONITORING_ENABLED": "true"
},
"isActive": true,
"timeout": 300000,
"maxRetries": 1
}'# Create a job that uses multiple Python packages
curl -X POST http://localhost:3000/api/jobs \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Data Analysis Job",
"description": "Analyze data using pandas and numpy",
"cronPattern": "0 2 * * *",
"pythonScript": "import pandas as pd\nimport numpy as np\nimport json\n\n# Generate sample data\ndata = {\n \"values\": np.random.normal(100, 15, 1000).tolist(),\n \"categories\": np.random.choice([\"A\", \"B\", \"C\"], 1000).tolist()\n}\n\ndf = pd.DataFrame(data)\n\n# Perform analysis\nanalysis = {\n \"mean\": float(df[\"values\"].mean()),\n \"std\": float(df[\"values\"].std()),\n \"count_by_category\": df[\"categories\"].value_counts().to_dict(),\n \"timestamp\": __import__(\"datetime\").datetime.now().isoformat()\n}\n\nprint(json.dumps(analysis, indent=2))",
"requirements": ["pandas", "numpy"],
"environmentVariables": {
"ANALYSIS_TYPE": "statistical",
"SAMPLE_SIZE": "1000"
},
"isActive": true,
"timeout": 600000,
"maxRetries": 2
}'# Create a job with robust error handling
curl -X POST http://localhost:3000/api/jobs \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Robust API Job",
"description": "Job with comprehensive error handling",
"cronPattern": "0 */6 * * *",
"pythonScript": "import requests\nimport json\nimport sys\n\ntry:\n # Attempt to fetch data\n response = requests.get(\"https://api.example.com/data\", timeout=30)\n response.raise_for_status()\n \n data = response.json()\n \n # Process data\n result = {\n \"status\": \"success\",\n \"data_count\": len(data) if isinstance(data, list) else 1,\n \"timestamp\": __import__(\"datetime\").datetime.now().isoformat()\n }\n \n print(json.dumps(result, indent=2))\n \nexcept requests.exceptions.RequestException as e:\n print(f\"Request failed: {e}\", file=sys.stderr)\n sys.exit(1)\n \nexcept json.JSONDecodeError as e:\n print(f\"JSON decode error: {e}\", file=sys.stderr)\n sys.exit(1)\n \nexcept Exception as e:\n print(f\"Unexpected error: {e}\", file=sys.stderr)\n sys.exit(1)",
"requirements": ["requests"],
"environmentVariables": {
"API_TIMEOUT": "30",
"RETRY_COUNT": "3"
},
"isActive": true,
"timeout": 300000,
"maxRetries": 3
}'# List jobs (public endpoint - no authentication required)
curl http://localhost:3000/api/jobs
# List jobs with pagination
curl "http://localhost:3000/api/jobs?page=1&limit=10"
# List jobs with authentication for additional details
curl -H "Authorization: Bearer $JWT_TOKEN" http://localhost:3000/api/jobs# Get basic job details (public - filtered data)
curl http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee
# Get complete job details including sensitive info (requires authentication)
curl -H "Authorization: Bearer $JWT_TOKEN" \
http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee/details
# Using API key
curl -H "X-API-Key: $API_KEY" \
http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee/details# Get only the Python script
curl -H "Authorization: Bearer $JWT_TOKEN" \
http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee/script
# Response:
# {
# "jobId": "68ca6ea99ad4c9a6fa530bee",
# "jobName": "Test Python Job",
# "pythonScript": "print(\"Hello from PyJob!\")\nprint(\"Current time:\", __import__(\"datetime\").datetime.now())"
# }
# Get only the requirements
curl -H "Authorization: Bearer $JWT_TOKEN" \
http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee/requirements
# Response:
# {
# "jobId": "68ca6ea99ad4c9a6fa530bee",
# "jobName": "Test Python Job",
# "requirements": ["requests"]
# }
# Get only the environment variables
curl -H "Authorization: Bearer $JWT_TOKEN" \
http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee/env
# Response:
# {
# "jobId": "68ca6ea99ad4c9a6fa530bee",
# "jobName": "Test Python Job",
# "environmentVariables": {
# "TEST_VAR": "test_value"
# }
# }# Update job configuration
curl -X PUT http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Updated Job Name",
"description": "Updated description",
"cronPattern": "0 */2 * * *",
"isActive": true
}'# Enable/disable a job
curl -X PATCH http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee/toggle \
-H "Authorization: Bearer $JWT_TOKEN"
# Using API key
curl -X PATCH http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee/toggle \
-H "X-API-Key: $API_KEY"# Execute job manually (JWT Token)
curl -X POST http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee/execute \
-H "Authorization: Bearer $JWT_TOKEN"
# Execute job manually (API Key)
curl -X POST http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee/execute \
-H "X-API-Key: $API_KEY"
# Execute job manually (Basic Auth)
curl -X POST http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee/execute \
-H "Authorization: Basic $(echo -n 'john_doe:securepassword123' | base64)"# Get execution history
curl -H "Authorization: Bearer $JWT_TOKEN" \
http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee/executions
# Response:
# {
# "executions": [
# {
# "_id": "68ca6ebb9ad4c9a6fa530bf8",
# "jobId": "68ca6ea99ad4c9a6fa530bee",
# "jobName": "Test Python Job",
# "status": "completed",
# "retryCount": 0,
# "triggeredBy": "manual",
# "startedAt": "2025-09-17T08:18:03.836Z",
# "completedAt": "2025-09-17T08:18:09.059Z",
# "duration": 5223,
# "exitCode": 0,
# "stderr": "",
# "stdout": "Hello from PyJob!\nCurrent time: 2025-09-17 10:18:09.057612"
# }
# ],
# "pagination": {
# "page": 1,
# "limit": 10,
# "total": 1,
# "pages": 1
# }
# }# Get job logs
curl -H "Authorization: Bearer $JWT_TOKEN" \
http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee/logs# Delete a job (JWT Token)
curl -X DELETE http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee \
-H "Authorization: Bearer $JWT_TOKEN"
# Delete a job (API Key)
curl -X DELETE http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee \
-H "X-API-Key: $API_KEY"
# Delete a job (Basic Auth)
curl -X DELETE http://localhost:3000/api/jobs/68ca6ea99ad4c9a6fa530bee \
-H "Authorization: Basic $(echo -n 'john_doe:securepassword123' | base64)"# Search jobs by name or description
curl "http://localhost:3000/api/jobs/search?q=python"
# Search with filters
curl "http://localhost:3000/api/jobs/search?q=data&status=active&page=1&limit=5"# Get job statistics
curl http://localhost:3000/api/jobs/stats/overview
# Response:
# {
# "totalJobs": 5,
# "activeJobs": 3,
# "inactiveJobs": 2,
# "totalExecutions": 150,
# "successfulExecutions": 145,
# "failedExecutions": 5,
# "averageExecutionTime": 2500,
# "lastExecution": "2025-09-17T08:15:30.000Z"
# }# Get scheduled jobs status (requires authentication)
curl -H "Authorization: Bearer $JWT_TOKEN" \
http://localhost:3000/api/jobs/scheduled/status# Export all jobs data
curl -H "Authorization: Bearer $ADMIN_JWT_TOKEN" \
http://localhost:3000/api/jobs/export
# Export with filters
curl -H "Authorization: Bearer $ADMIN_JWT_TOKEN" \
"http://localhost:3000/api/jobs/export?format=json&includeExecutions=true"# Basic health check (public)
curl http://localhost:3000/health
# Detailed system health (public)
curl http://localhost:3000/api/jobs/health/status
# Response:
# {
# "status": "healthy",
# "timestamp": "2025-09-17T08:15:30.000Z",
# "scheduler": {
# "isRunning": true,
# "scheduledJobs": 3
# },
# "environments": {
# "active": 0,
# "maxConcurrent": 5
# },
# "database": {
# "connected": true
# },
# "redis": {
# "enabled": false,
# "connected": false,
# "available": false
# },
# "cache": {
# "redisAvailable": false,
# "totalCachedResults": 0,
# "totalCachedConfigs": 0
# }
# }* * * * *
β β β β β
β β β β ββββ Day of week (0-6, Sunday = 0)
β β β ββββββ Month (1-12)
β β ββββββββ Day of month (1-31)
β ββββββββββ Hour (0-23)
ββββββββββββ Minute (0-59)
| Pattern | Description | Example |
|---|---|---|
*/5 * * * * |
Every 5 minutes | 0, 5, 10, 15, 20... |
0 * * * * |
Every hour | At minute 0 of every hour |
0 */2 * * * |
Every 2 hours | At minute 0 of every 2nd hour |
0 9 * * * |
Daily at 9 AM | Every day at 09:00 |
0 9 * * 1-5 |
Weekdays at 9 AM | Monday to Friday at 09:00 |
0 0 * * 0 |
Weekly on Sunday | Every Sunday at midnight |
0 0 1 * * |
Monthly | First day of every month at midnight |
0 0 1 1 * |
Yearly | January 1st at midnight |
| Pattern | Description |
|---|---|
15,45 * * * * |
At 15 and 45 minutes past every hour |
0 9-17 * * 1-5 |
Every hour from 9 AM to 5 PM, Monday to Friday |
0 0 */3 * * |
Every 3 days at midnight |
0 0 1,15 * * |
1st and 15th of every month at midnight |
- Use Strong Passwords: Minimum 8 characters with mixed case, numbers, and symbols
- Rotate API Keys: Regularly generate new API keys and revoke old ones
- Use HTTPS: Always use HTTPS in production environments
- Token Expiration: JWT tokens expire after 24 hours by default
- Role-based Access: Use appropriate roles (admin/user) for different access levels
- Environment Variables: Store sensitive data in environment variables, not in the script
- Timeout Limits: Set appropriate timeout values to prevent runaway jobs
- Resource Limits: Configure maximum concurrent jobs to prevent resource exhaustion
- Input Validation: Validate all job inputs and parameters
- Error Handling: Implement proper error handling in your Python scripts
curl -X POST http://localhost:3000/api/jobs \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Secure Data Job",
"description": "Secure job with proper error handling",
"cronPattern": "0 */6 * * *",
"pythonScript": "import os\nimport sys\nimport json\n\ntry:\n # Use environment variables for sensitive data\n api_key = os.getenv(\"API_KEY\")\n if not api_key:\n print(\"Error: API_KEY not found\", file=sys.stderr)\n sys.exit(1)\n \n # Your secure logic here\n result = {\"status\": \"success\", \"timestamp\": __import__(\"datetime\").datetime.now().isoformat()}\n print(json.dumps(result))\n \nexcept Exception as e:\n print(f\"Error: {e}\", file=sys.stderr)\n sys.exit(1)",
"requirements": [],
"environmentVariables": {
"API_KEY": "your-secret-api-key-here"
},
"isActive": true,
"timeout": 300000,
"maxRetries": 2
}'# Check if user exists
curl -H "Authorization: Bearer $JWT_TOKEN" http://localhost:3000/api/auth/me
# If token is expired, login again
curl -X POST http://localhost:3000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"your_username","password":"your_password"}'# Check job execution history
curl -H "Authorization: Bearer $JWT_TOKEN" \
http://localhost:3000/api/jobs/JOB_ID/executions
# Check job logs
curl -H "Authorization: Bearer $JWT_TOKEN" \
http://localhost:3000/api/jobs/JOB_ID/logs# Check system health
curl http://localhost:3000/api/jobs/health/status
# Check if MongoDB is connected
curl http://localhost:3000/health| Code | Description | Solution |
|---|---|---|
| 401 | Unauthorized | Check authentication credentials |
| 403 | Forbidden | Check user permissions/role |
| 404 | Not Found | Verify job ID or endpoint |
| 400 | Bad Request | Check request format and validation |
| 500 | Internal Error | Check server logs and system health |
Logs are stored in the logs/ directory:
combined.log- All application logserror.log- Error logs only
# System health
curl http://localhost:3000/api/jobs/health/status
# Job statistics
curl http://localhost:3000/api/jobs/stats/overview
# Scheduled jobs status
curl -H "Authorization: Bearer $JWT_TOKEN" \
http://localhost:3000/api/jobs/scheduled/statusThis comprehensive guide covers all aspects of using PyJob effectively. Whether you're a developer integrating with the API or an administrator managing the system, these examples will help you get the most out of PyJob's powerful job scheduling capabilities.
- Isolated Execution: Each job runs in its own virtual environment
- Restricted File Access: Python scripts cannot write to the local file system
- Environment Variables: Secure handling of sensitive data
- Timeout Protection: Jobs are automatically terminated if they exceed the timeout
- Resource Limits: Configurable limits on concurrent job executions
- Authentication: Multiple auth methods (JWT, API keys, Basic Auth)
- Rate Limiting: Protection against abuse and brute force attacks
- Role-based Access: Admin and user roles with different permissions
- Secure Password Storage: Bcrypt hashing for user passwords
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β REST API β β Job Scheduler β β Virtual Env β
β (Express) βββββΊβ (node-cron) βββββΊβ Manager β
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β MongoDB β β Job Execution β β Python Scripts β
β (NoSQL) β β Tracking β β (Isolated) β
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
npm testnpm run devLogs are stored in the logs/ directory:
combined.log- All logserror.log- Error logs only
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
MIT License - see LICENSE file for details.