HTTP Tiger Blueprint
· 5 min · httptiger.com
Project Architecture
Frontend: Tailwind CSS + Vanilla JS → S3/CloudFront
Backend: API Gateway → Lambda → DynamoDB
Config: SSM Parameters
Auth: Google OAuth
Tailwind CSS Structure
/* Main layout */
.container { @apply max-w-7xl mx-auto px-4 sm:px-6 lg:px-8; }
.hero-section { @apply bg-gradient-to-br from-blue-900 to-purple-900; }
/* URL checker form */
.url-input {
@apply w-full px-4 py-3 border border-gray-300
rounded-lg focus:ring-2 focus:ring-blue-500
focus:border-transparent transition-all;
}
.check-button {
@apply bg-blue-600 hover:bg-blue-700 text-white
font-medium py-3 px-6 rounded-lg transition-colors
disabled:opacity-50 disabled:cursor-not-allowed;
}
/* Results display */
.result-grid { @apply grid gap-4 mt-6; }
.result-item {
@apply bg-white rounded-lg shadow-md p-4
border-l-4 border-green-500 hover:shadow-lg;
}
.status-badge {
@apply inline-flex items-center px-2.5 py-0.5
rounded-full text-xs font-medium;
}
.status-success { @apply bg-green-100 text-green-800; }
.status-error { @apply bg-red-100 text-red-800; }
Frontend JavaScript Core
class URLChecker {
constructor() {
this.apiEndpoint = 'https://api.httptiger.com';
this.maxBatchSize = 100;
this.init();
}
init() {
this.setupEventListeners();
this.loadUserSession();
}
async checkUrls(urls) {
const results = [];
const chunks = this.chunkArray(urls, 10); // Process in batches
for (const chunk of chunks) {
const promises = chunk.map(url => this.checkSingleUrl(url));
const chunkResults = await Promise.allSettled(promises);
results.push(...chunkResults);
this.updateProgress(results.length, urls.length);
await this.delay(1000); // Rate limiting
}
return results;
}
async checkSingleUrl(url) {
try {
const response = await fetch(`${this.apiEndpoint}/check`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url, userId: this.getUserId() })
});
return await response.json();
} catch (error) {
return { url, status: 'error', message: error.message };
}
}
}
Lambda Functions
URL Check Function
exports.checkUrl = async (event) => {
const { url, userId } = JSON.parse(event.body);
// Rate limiting check
await checkUserLimits(userId);
try {
const response = await fetch(url, {
method: 'HEAD',
timeout: 10000,
follow: 5
});
// Log check to DynamoDB
await dynamoDB.put({
TableName: 'url_checks',
Item: {
id: uuidv4(),
url,
userId,
status: response.status,
timestamp: Date.now(),
responseTime: Date.now() - startTime
}
}).promise();
return {
statusCode: 200,
body: JSON.stringify({
url,
status: response.status,
statusText: response.statusText,
responseTime: Date.now() - startTime
})
};
} catch (error) {
return {
statusCode: 200,
body: JSON.stringify({
url,
status: 'error',
message: error.message
})
};
}
};
User Management
exports.getUserLimits = async (event) => {
const userId = event.requestContext.authorizer.principalId;
const user = await dynamoDB.get({
TableName: 'users',
Key: { userId }
}).promise();
const plan = user.Item?.plan || 'free';
const usage = await getMonthlyUsage(userId);
const limits = {
free: { monthly: 500, batch: 10 },
pro: { monthly: 10000, batch: 50 },
business: { monthly: 100000, batch: 100 }
};
return {
statusCode: 200,
body: JSON.stringify({
plan,
usage,
limits: limits[plan],
remaining: limits[plan].monthly - usage
})
};
};
DynamoDB Tables
users:
- userId (string) - partition key
- email (string)
- plan (string) - free/pro/business
- createdAt (string)
- lastLogin (string)
url_checks:
- id (string) - partition key
- userId (string) - GSI partition key
- url (string)
- status (number)
- timestamp (number) - GSI sort key
- responseTime (number)
rate_limits:
- key (string) - partition key (userId#date)
- count (number)
- ttl (number) - auto-expire
SSM Configuration
/urlchecker/google-oauth-client - OAuth client ID
/urlchecker/google-oauth-secret - OAuth secret
/urlchecker/jwt-secret - JWT signing key
/urlchecker/stripe-key - Payment processing
/urlchecker/database-url - DynamoDB table prefix
Key Features
Batch Processing
function processBatch(urls) {
const batches = chunkArray(urls, 10);
const results = [];
batches.forEach((batch, index) => {
setTimeout(async () => {
const batchResults = await Promise.all(
batch.map(url => checkUrl(url))
);
results.push(...batchResults);
updateUI(results);
}, index * 1000); // 1 second between batches
});
}
Real-time Progress
function updateProgress(completed, total) {
const percentage = (completed / total) * 100;
document.getElementById('progress-bar').style.width = `${percentage}%`;
document.getElementById('progress-text').textContent =
`${completed}/${total} URLs checked`;
}
Export Results
function exportToCSV(results) {
const headers = ['URL', 'Status', 'Response Time', 'Checked At'];
const rows = results.map(r => [
r.url,
r.status,
r.responseTime + 'ms',
new Date(r.timestamp).toISOString()
]);
const csvContent = [headers, ...rows]
.map(row => row.join(','))
.join('\n');
downloadFile(csvContent, 'url-check-results.csv', 'text/csv');
}
Deployment
# Build and deploy
npm run build
aws s3 sync dist/ s3://urlstatuschecker-frontend
aws cloudfront create-invalidation --distribution-id E123 --paths "/*"
# Lambda deployment
zip -r function.zip .
aws lambda update-function-code --function-name urlchecker-api --zip-file fileb://function.zip
Monitoring
CloudWatch Alarms:
- Lambda errors > 1%
- DynamoDB throttling
- API Gateway 5xx > 2%
- Frontend 404s > 5%
Custom Metrics:
- URLs checked per minute
- Average response time
- User plan distribution
- Error rate by URL pattern