Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 411ee92106 | |||
| 7cfcd0f46b | |||
| 2f249a9e18 | |||
| cea4658b91 | |||
| c5a8f9f58c | |||
| 8e64a9e2ac | |||
| 348baf96bb | |||
| cb9909172b | |||
| 38cddd6dfc | |||
| cd0ed86571 | |||
| 1a1a109aa3 | |||
| b616e11bdf |
@@ -0,0 +1,21 @@
|
||||
# Smoke test: confirms Gitea Act Runner is online and accepting jobs.
|
||||
# Run manually via workflow_dispatch after runner is registered (#77).
|
||||
# Closes leeworks-agents/api-company#96
|
||||
|
||||
name: Runner Smoke Test
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
smoke:
|
||||
name: Smoke Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Confirm runner is online
|
||||
run: |
|
||||
echo "Runner is online!"
|
||||
echo "Job ID: $GITHUB_JOB"
|
||||
echo "Runner OS: $(uname -a)"
|
||||
echo "Date: $(date -u)"
|
||||
echo "Smoke test PASSED"
|
||||
@@ -31,6 +31,7 @@ const { title, description = "leeworks.dev API documentation" } = Astro.props;
|
||||
<a href="/zip-enrichment">ZIP Enrichment</a>
|
||||
<a href="/holidays">Holidays</a>
|
||||
<a href="/air-quality">Air Quality</a>
|
||||
<a href="/pricing">Pricing</a>
|
||||
<a href="/blog">Blog</a>
|
||||
<a href="https://rapidapi.com/leeworks" target="_blank" rel="noopener">RapidAPI</a>
|
||||
</nav>
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
import Base from '../layouts/Base.astro';
|
||||
---
|
||||
<Base title="API Pricing" description="API Pricing — leeworks.dev — compare plans for ZIP Enrichment, Holidays, and Air Quality APIs">
|
||||
<style>
|
||||
.pricing-hero { padding: 4rem 2rem 2rem; text-align: center; }
|
||||
.pricing-hero h1 { font-size: 2.5rem; font-weight: 800; background: linear-gradient(135deg, #90cdf4, #667eea); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 1rem; }
|
||||
.pricing-hero p { font-size: 1.15rem; color: #a0aec0; max-width: 600px; margin: 0 auto 2rem; }
|
||||
.pricing-wrap { padding: 1rem 2rem 3rem; max-width: 1100px; margin: 0 auto; overflow-x: auto; }
|
||||
table { width: 100%; border-collapse: collapse; background: #1a1d27; border-radius: 12px; overflow: hidden; min-width: 640px; }
|
||||
caption { caption-side: top; text-align: left; font-size: 0.875rem; color: #718096; padding: 0.75rem 1rem; font-style: italic; }
|
||||
thead tr { background: #2d3748; }
|
||||
th, td { padding: 0.9rem 1.25rem; text-align: left; border-bottom: 1px solid #2d3748; }
|
||||
th { font-weight: 700; color: #90cdf4; font-size: 0.9rem; text-transform: uppercase; letter-spacing: 0.04em; }
|
||||
td { color: #e2e8f0; font-size: 0.95rem; }
|
||||
tbody tr:last-child td { border-bottom: none; }
|
||||
tbody tr:hover { background: #232738; }
|
||||
.tier-name { font-weight: 600; color: #fff; }
|
||||
.tier-price { color: #9ae6b4; font-weight: 600; }
|
||||
.free-price { color: #68d391; }
|
||||
.cta-row { display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap; margin: 2.5rem 0 1rem; }
|
||||
.cta-btn { display: inline-block; background: #667eea; color: #fff; padding: 0.65rem 1.5rem; border-radius: 8px; text-decoration: none; font-weight: 600; font-size: 0.95rem; }
|
||||
.cta-btn:hover { background: #5a67d8; }
|
||||
.disclaimer { text-align: center; color: #718096; font-size: 0.85rem; margin-top: 2rem; padding: 0 2rem; }
|
||||
</style>
|
||||
|
||||
<div class="pricing-hero">
|
||||
<h1>API Pricing</h1>
|
||||
<p>Simple, transparent pricing for all three APIs. Start free — upgrade when you need more.</p>
|
||||
</div>
|
||||
|
||||
<div class="pricing-wrap">
|
||||
<table>
|
||||
<caption>All plans are monthly. Requests reset at the start of each billing cycle.</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Tier</th>
|
||||
<th scope="col">Price / mo</th>
|
||||
<th scope="col">ZIP Enrichment</th>
|
||||
<th scope="col">Holidays</th>
|
||||
<th scope="col">Air Quality</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="tier-name">Free</td>
|
||||
<td class="tier-price free-price">$0</td>
|
||||
<td>100 req/mo · 5 req/min</td>
|
||||
<td>100 req/mo · 5 req/min</td>
|
||||
<td>100 req/mo · 5 req/min</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tier-name">Basic</td>
|
||||
<td class="tier-price">$9</td>
|
||||
<td>5,000 req/mo · 60 req/min</td>
|
||||
<td>5,000 req/mo · 60 req/min</td>
|
||||
<td>5,000 req/mo · 60 req/min</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tier-name">Pro</td>
|
||||
<td class="tier-price">$19</td>
|
||||
<td>25,000 req/mo · 200 req/min</td>
|
||||
<td>20,000 req/mo · 200 req/min</td>
|
||||
<td>20,000 req/mo · 200 req/min</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tier-name">Ultra</td>
|
||||
<td class="tier-price">$49</td>
|
||||
<td>100,000 req/mo · 500 req/min</td>
|
||||
<td>100,000 req/mo · 500 req/min</td>
|
||||
<td>100,000 req/mo · 500 req/min</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="cta-row">
|
||||
<a href="#" class="cta-btn">ZIP Enrichment on RapidAPI</a>
|
||||
<a href="#" class="cta-btn">Holidays on RapidAPI</a>
|
||||
<a href="#" class="cta-btn">Air Quality on RapidAPI</a>
|
||||
</div>
|
||||
|
||||
<p class="disclaimer">Subscriptions managed via RapidAPI marketplace. Prices shown in USD. Rate limits apply per API key.</p>
|
||||
</div>
|
||||
</Base>
|
||||
@@ -0,0 +1,74 @@
|
||||
# Tooling Versions
|
||||
|
||||
> Phase 0 checklist item — documents the local agent container tooling and confirms git remote config.
|
||||
> Live cluster verification (kubectl/flux against the running cluster) requires operator confirmation.
|
||||
|
||||
## Agent Container Tooling
|
||||
|
||||
| Tool | Version | Source |
|
||||
|------|---------|--------|
|
||||
| Node.js | v22.22.2 | `node --version` |
|
||||
| Python 3 | 3.11.2 | `python3 --version` |
|
||||
| Helm | v3.21.0 | `helm version --short` |
|
||||
| Flux CLI | v2.4.0 | `flux version --client` |
|
||||
| kustomize | v5.6.0 | `kustomize version` |
|
||||
| kubectl | available | `kubectl` (cluster context not set in agent container — live commands require kubeconfig from operator) |
|
||||
|
||||
## Git Remote Config
|
||||
|
||||
```
|
||||
origin ssh://git@gitea.leeworks.dev/leeworks-agents/api-company (fetch)
|
||||
origin ssh://git@gitea.leeworks.dev/leeworks-agents/api-company (push)
|
||||
upstream ssh://git@gitea.leeworks.dev/0xWheatyz/api-company (fetch)
|
||||
upstream ssh://git@gitea.leeworks.dev/0xWheatyz/api-company (push)
|
||||
```
|
||||
|
||||
Remote `origin` confirmed pointing to `gitea.leeworks.dev/leeworks-agents/api-company`.
|
||||
|
||||
> Note: `0xWheatyz/api-company` upstream repo does not yet exist on Gitea — tracked by issue #47.
|
||||
|
||||
## Stack Summary
|
||||
|
||||
### Runtime choices
|
||||
|
||||
| API Service | Runtime | Rationale |
|
||||
|-------------|---------|-----------|
|
||||
| zip-enrichment | Node.js (Fastify) | Lightweight, fast JSON serialisation; large ZIP dataset fits SQLite well |
|
||||
| holidays | Node.js (Fastify) | Simple lookup API; Fastify handles high req/s with low memory |
|
||||
| air-quality | Python (FastAPI) | AQI ingestion benefits from Python data-science ecosystem |
|
||||
| docs-site | Astro (static) | Zero-runtime static site; built by CI, served from container |
|
||||
|
||||
### Helm chart registry
|
||||
|
||||
All HelmReleases use charts sourced from public Helm chart repositories declared in `flux/` as `HelmRepository` resources:
|
||||
|
||||
- `flux/gitea-runner/helmrepository.yaml` — Gitea Act Runner chart
|
||||
- `flux/monitoring/` — `kube-prometheus-stack` and Gatus charts
|
||||
- `flux/zip-enrichment/`, `flux/holidays/`, `flux/air-quality/` — per-API service charts
|
||||
|
||||
The in-cluster container registry is `registry.leeworks.dev` (Gitea built-in packages/container registry, documented in `docs/registry.md`).
|
||||
|
||||
### Flux version and bootstrap
|
||||
|
||||
- **Flux CLI**: v2.4.0 (available in agent container for manifest authoring)
|
||||
- **Flux controllers**: bootstrapped into `0xWheatyz/Talos` cluster (FluxCD v2.x)
|
||||
- `flux-system` namespace managed by Talos GitOps repo
|
||||
- Flux watches `0xWheatyz/Talos` → `testing1/first-cluster/cluster/`
|
||||
- `api-company-source` GitRepository + Kustomization adds this repo to Flux (issues #2, #90)
|
||||
- **Bootstrap reference**: `0xWheatyz/Talos` repo — see Talos cluster documentation
|
||||
|
||||
## Live Verification (Operator)
|
||||
|
||||
The following commands require a valid `kubeconfig` (not available in agent container):
|
||||
|
||||
```bash
|
||||
kubectl config current-context # confirm cluster context
|
||||
kubectl version --client # kubectl client version
|
||||
flux check # Flux controller health
|
||||
flux get sources git api-company # GitRepository READY status
|
||||
flux get kustomizations api-company # Kustomization READY status
|
||||
helm version # Helm client version
|
||||
kustomize version # kustomize version
|
||||
```
|
||||
|
||||
_Reference: MASTER_BUILD_PROMPT.md §Phase 0 step 2 — Closes leeworks-agents/api-company#94_
|
||||
@@ -1,7 +1,8 @@
|
||||
# This manifest is FOR REFERENCE — the live version must be committed to
|
||||
# 0xWheatyz/Talos at testing1/first-cluster/cluster/flux/api-company/
|
||||
# 0xWheatyz/Talos at testing1/first-cluster/cluster/flux/api-company-source/
|
||||
#
|
||||
# See leeworks-agents/api-company#2
|
||||
# See leeworks-agents/api-company#97
|
||||
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
@@ -17,3 +18,8 @@ spec:
|
||||
prune: true
|
||||
wait: true
|
||||
timeout: 5m
|
||||
postBuild:
|
||||
substituteFrom:
|
||||
- kind: Secret
|
||||
name: grafana-admin
|
||||
optional: false
|
||||
|
||||
@@ -2,5 +2,4 @@ apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- helmrepository.yaml
|
||||
- helmrelease.yaml
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- bedag-helmrepository.yaml
|
||||
- external-secrets
|
||||
- gitea-runner
|
||||
- monitoring
|
||||
|
||||
@@ -71,7 +71,7 @@ spec:
|
||||
- alert: APIDataStale
|
||||
expr: |
|
||||
api_data_freshness_seconds{job=~"zip|holidays|air-quality"} > 2592000
|
||||
for: 5m
|
||||
for: 30m
|
||||
labels:
|
||||
severity: warning
|
||||
team: api-company
|
||||
@@ -79,6 +79,20 @@ spec:
|
||||
summary: "Stale dataset on {{ $labels.job }} ({{ $labels.dataset }})"
|
||||
description: "{{ $labels.job }} dataset '{{ $labels.dataset }}' has not been re-seeded in {{ $value | humanizeDuration }} (threshold: 30 days). Re-seed required."
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# APIDataCriticallyStale — data freshness > 60 days
|
||||
# -------------------------------------------------------------------
|
||||
- alert: APIDataCriticallyStale
|
||||
expr: |
|
||||
api_data_freshness_seconds{job=~"zip|holidays|air-quality"} > 5184000
|
||||
for: 1h
|
||||
labels:
|
||||
severity: critical
|
||||
team: api-company
|
||||
annotations:
|
||||
summary: "API data is critically stale on {{ $labels.job }}"
|
||||
description: "{{ $labels.job }} data has not been re-seeded in more than 60 days ({{ $value | humanizeDuration }})"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# APIDown — any API job absent for 2 min
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user