Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 137cdb3b01 | |||
| b4bd508170 | |||
| ad679ddc97 | |||
| 8518878358 | |||
| be9f80419b | |||
| 9370d2c898 | |||
| 411ee92106 |
@@ -0,0 +1,58 @@
|
|||||||
|
# Validate and publish OpenAPI specs to RapidAPI when apis/*/openapi.yaml changes on main.
|
||||||
|
# Requires secrets (configured once RapidAPI listings are live):
|
||||||
|
# RAPIDAPI_PLATFORM_KEY, RAPIDAPI_ZIP_API_ID, RAPIDAPI_ZIP_VERSION_ID,
|
||||||
|
# RAPIDAPI_HOLIDAYS_API_ID, RAPIDAPI_HOLIDAYS_VERSION_ID,
|
||||||
|
# RAPIDAPI_AQI_API_ID, RAPIDAPI_AQI_VERSION_ID
|
||||||
|
#
|
||||||
|
# If RAPIDAPI_PLATFORM_KEY is not set the publish step exits 0 with a skip message.
|
||||||
|
|
||||||
|
name: Validate and Publish OpenAPI Specs
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'apis/*/openapi.yaml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
name: Lint and publish specs
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout (with history for diff)
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
|
||||||
|
- name: Set up Node.js 20
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: Install Redocly CLI
|
||||||
|
run: npm install -g @redocly/cli@latest
|
||||||
|
|
||||||
|
- name: Lint OpenAPI specs
|
||||||
|
run: |
|
||||||
|
echo "Linting all OpenAPI specs..."
|
||||||
|
npx @redocly/cli lint apis/*/openapi.yaml
|
||||||
|
|
||||||
|
- name: Detect changed specs
|
||||||
|
id: changed
|
||||||
|
run: |
|
||||||
|
changed=$(git diff --name-only HEAD~1 HEAD | grep 'openapi\.yaml' || true)
|
||||||
|
echo "Changed specs: ${changed:-none}"
|
||||||
|
echo "files=${changed}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Publish specs to RapidAPI
|
||||||
|
env:
|
||||||
|
RAPIDAPI_KEY: ${{ secrets.RAPIDAPI_PLATFORM_KEY }}
|
||||||
|
RAPIDAPI_ZIP_API_ID: ${{ secrets.RAPIDAPI_ZIP_API_ID }}
|
||||||
|
RAPIDAPI_ZIP_VERSION_ID: ${{ secrets.RAPIDAPI_ZIP_VERSION_ID }}
|
||||||
|
RAPIDAPI_HOLIDAYS_API_ID: ${{ secrets.RAPIDAPI_HOLIDAYS_API_ID }}
|
||||||
|
RAPIDAPI_HOLIDAYS_VERSION_ID: ${{ secrets.RAPIDAPI_HOLIDAYS_VERSION_ID }}
|
||||||
|
RAPIDAPI_AQI_API_ID: ${{ secrets.RAPIDAPI_AQI_API_ID }}
|
||||||
|
RAPIDAPI_AQI_VERSION_ID: ${{ secrets.RAPIDAPI_AQI_VERSION_ID }}
|
||||||
|
run: node scripts/publish-openapi.js
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Company Status
|
# Company Status
|
||||||
|
|
||||||
_Last updated: 2026-05-28 (agent cycle — cycle-2 triage)_
|
_Last updated: 2026-05-30 (sprint planning cycle)_
|
||||||
|
|
||||||
## APIs
|
## APIs
|
||||||
| API | Spec | Code | Deployed | Listed on RapidAPI | Paying Users | MRR |
|
| API | Spec | Code | Deployed | Listed on RapidAPI | Paying Users | MRR |
|
||||||
@@ -21,7 +21,37 @@ Legend: [x]=done, [~]=in-progress, [ ]=not started
|
|||||||
- **API service manifests:** `flux/zip-enrichment/`, `flux/holidays/`, `flux/air-quality/` scaffolded (PR #48, closes #46)
|
- **API service manifests:** `flux/zip-enrichment/`, `flux/holidays/`, `flux/air-quality/` scaffolded (PR #48, closes #46)
|
||||||
- **Image automation:** `flux/image-automation/` — ImageRepository + ImagePolicy + ImageUpdateAutomation for all three APIs
|
- **Image automation:** `flux/image-automation/` — ImageRepository + ImagePolicy + ImageUpdateAutomation for all three APIs
|
||||||
|
|
||||||
## Completed This Cycle (2026-05-28 — Cycle-2 Triage)
|
## Completed This Cycle (2026-05-30 — Sprint Cycle)
|
||||||
|
- **#108** — STATUS.md updated with 2026-05-30 sprint plan and critical path.
|
||||||
|
- **#107** — docs-site build verified: `npm run build` exits 0; all 9 required routes present in `dist/` (`/`, `/zip-enrichment`, `/holidays`, `/air-quality`, `/blog/` (3 posts), `/legal/terms-of-service`, `/legal/privacy-policy`, `/legal/acceptable-use-policy`, `/pricing`). Unblocks #30.
|
||||||
|
- **PR #105 MERGED** — `scripts/publish-openapi.js` + `.gitea/workflows/publish-openapi.yaml` (closes #100).
|
||||||
|
- **PR #103 MERGED** — `docs-site/src/pages/pricing.astro` + nav link (closes #101).
|
||||||
|
- **PR #104 MERGED** — `research/RESEARCH_LOG.md` first session: VIN Decoder next candidate (closes #102).
|
||||||
|
- **New issues triaged** — #100–#108 (sprint planning cycle issues reviewed).
|
||||||
|
- **`0xWheatyz/api-company`** — still does not exist; fork sync skipped (blocker #47).
|
||||||
|
- **`kustomize build flux/` = PASS** — no regressions.
|
||||||
|
|
||||||
|
## Critical Path (operator must complete in order)
|
||||||
|
1. **Create `0xWheatyz/api-company` repo** (#47) — **highest priority, unblocks everything**
|
||||||
|
2. **Add api-company Flux source to Talos** (#90) — needs Talos PR, reference manifests at `flux/api-company-source/`
|
||||||
|
3. **Create `gitea-leeworks-agents-token` secret** in `flux-system` (#76)
|
||||||
|
4. **Create `gitea-runner-token` secret** in `gitea-runner` (#77)
|
||||||
|
5. **Enable Gitea packages + DNS for `registry.leeworks.dev`** (#4)
|
||||||
|
6. **All other secrets** (#70, #73, #74, #79, #80, #81, #83) follow in order
|
||||||
|
|
||||||
|
## New Issues This Sprint (2026-05-29, #100–#108)
|
||||||
|
- **#100** — `scripts/publish-openapi.js` + CI workflow — **DONE (PR #105 merged)**
|
||||||
|
- **#101** — Pricing page at `docs.leeworks.dev/pricing/` — **DONE (PR #103 merged)**
|
||||||
|
- **#102** — Seed `research/RESEARCH_LOG.md` — **DONE (PR #104 merged)**
|
||||||
|
- **#106** — Add `grafana.leeworks.dev` as 7th subdomain to DNS task (#33) — tracker only; docs already updated (#67)
|
||||||
|
- **#107** — docs-site build verification — **DONE (build passes, all 9 routes confirmed)**
|
||||||
|
- **#108** — STATUS.md update — **DONE (this update)**
|
||||||
|
|
||||||
|
## Backlog
|
||||||
|
- **28 open issues total**; all remaining blocked on operator Phase 0 actions above.
|
||||||
|
- **Agent work that can proceed without operator:** all completed this cycle.
|
||||||
|
|
||||||
|
## Completed Previous Cycle (2026-05-28 — Cycle-2 Triage)
|
||||||
- **#83** — Triaged: `GITEA_TOKEN` Actions secrets needed in `zip-enrichment`, `holidays`, `air-quality` repos. Manual operator task; reuse token from #74 if it has `write:package` scope. Step-by-step instructions posted.
|
- **#83** — Triaged: `GITEA_TOKEN` Actions secrets needed in `zip-enrichment`, `holidays`, `air-quality` repos. Manual operator task; reuse token from #74 if it has `write:package` scope. Step-by-step instructions posted.
|
||||||
- **No open PRs** — queue empty.
|
- **No open PRs** — queue empty.
|
||||||
- **`kustomize build flux/` = PASS** — no regressions.
|
- **`kustomize build flux/` = PASS** — no regressions.
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const { title, description = "leeworks.dev API documentation" } = Astro.props;
|
|||||||
<a href="/zip-enrichment">ZIP Enrichment</a>
|
<a href="/zip-enrichment">ZIP Enrichment</a>
|
||||||
<a href="/holidays">Holidays</a>
|
<a href="/holidays">Holidays</a>
|
||||||
<a href="/air-quality">Air Quality</a>
|
<a href="/air-quality">Air Quality</a>
|
||||||
|
<a href="/pricing">Pricing</a>
|
||||||
<a href="/blog">Blog</a>
|
<a href="/blog">Blog</a>
|
||||||
<a href="https://rapidapi.com/leeworks" target="_blank" rel="noopener">RapidAPI</a>
|
<a href="https://rapidapi.com/leeworks" target="_blank" rel="noopener">RapidAPI</a>
|
||||||
</nav>
|
</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>
|
||||||
@@ -4,3 +4,4 @@ resources:
|
|||||||
- namespace.yaml
|
- namespace.yaml
|
||||||
- externalsecret.yaml
|
- externalsecret.yaml
|
||||||
- helmrelease.yaml
|
- helmrelease.yaml
|
||||||
|
- servicemonitor.yaml
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: monitoring.coreos.com/v1
|
||||||
|
kind: ServiceMonitor
|
||||||
|
metadata:
|
||||||
|
name: air-quality
|
||||||
|
namespace: air-quality
|
||||||
|
labels:
|
||||||
|
release: kube-prometheus-stack
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: air-quality
|
||||||
|
endpoints:
|
||||||
|
- port: http
|
||||||
|
path: /metrics
|
||||||
|
interval: 30s
|
||||||
@@ -4,3 +4,4 @@ resources:
|
|||||||
- namespace.yaml
|
- namespace.yaml
|
||||||
- externalsecret.yaml
|
- externalsecret.yaml
|
||||||
- helmrelease.yaml
|
- helmrelease.yaml
|
||||||
|
- servicemonitor.yaml
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: monitoring.coreos.com/v1
|
||||||
|
kind: ServiceMonitor
|
||||||
|
metadata:
|
||||||
|
name: holidays
|
||||||
|
namespace: holidays
|
||||||
|
labels:
|
||||||
|
release: kube-prometheus-stack
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: holidays
|
||||||
|
endpoints:
|
||||||
|
- port: http
|
||||||
|
path: /metrics
|
||||||
|
interval: 30s
|
||||||
@@ -4,3 +4,4 @@ resources:
|
|||||||
- namespace.yaml
|
- namespace.yaml
|
||||||
- externalsecret.yaml
|
- externalsecret.yaml
|
||||||
- helmrelease.yaml
|
- helmrelease.yaml
|
||||||
|
- servicemonitor.yaml
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: monitoring.coreos.com/v1
|
||||||
|
kind: ServiceMonitor
|
||||||
|
metadata:
|
||||||
|
name: zip-enrichment
|
||||||
|
namespace: zip-enrichment
|
||||||
|
labels:
|
||||||
|
release: kube-prometheus-stack
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: zip-enrichment
|
||||||
|
endpoints:
|
||||||
|
- port: http
|
||||||
|
path: /metrics
|
||||||
|
interval: 30s
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* publish-openapi.js
|
||||||
|
*
|
||||||
|
* Uploads each API's openapi.yaml to the RapidAPI Platform API.
|
||||||
|
* Environment variables required per API (skips silently if not set):
|
||||||
|
* RAPIDAPI_KEY — RapidAPI Platform API bearer token
|
||||||
|
* RAPIDAPI_ZIP_API_ID — API ID for ZIP Enrichment on RapidAPI
|
||||||
|
* RAPIDAPI_ZIP_VERSION_ID — Version ID for ZIP Enrichment
|
||||||
|
* RAPIDAPI_HOLIDAYS_API_ID
|
||||||
|
* RAPIDAPI_HOLIDAYS_VERSION_ID
|
||||||
|
* RAPIDAPI_AQI_API_ID
|
||||||
|
* RAPIDAPI_AQI_VERSION_ID
|
||||||
|
*
|
||||||
|
* Usage: node scripts/publish-openapi.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const https = require('https');
|
||||||
|
|
||||||
|
const RAPIDAPI_KEY = process.env.RAPIDAPI_KEY || process.env.RAPIDAPI_PLATFORM_KEY;
|
||||||
|
|
||||||
|
if (!RAPIDAPI_KEY) {
|
||||||
|
console.log('⚠️ RAPIDAPI_KEY / RAPIDAPI_PLATFORM_KEY not set — skipping all spec uploads.');
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const APIS = [
|
||||||
|
{
|
||||||
|
name: 'zip-enrichment',
|
||||||
|
specPath: path.join(__dirname, '..', 'apis', 'zip-enrichment', 'openapi.yaml'),
|
||||||
|
apiId: process.env.RAPIDAPI_ZIP_API_ID,
|
||||||
|
versionId: process.env.RAPIDAPI_ZIP_VERSION_ID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'holidays',
|
||||||
|
specPath: path.join(__dirname, '..', 'apis', 'holidays', 'openapi.yaml'),
|
||||||
|
apiId: process.env.RAPIDAPI_HOLIDAYS_API_ID,
|
||||||
|
versionId: process.env.RAPIDAPI_HOLIDAYS_VERSION_ID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'air-quality',
|
||||||
|
specPath: path.join(__dirname, '..', 'apis', 'air-quality', 'openapi.yaml'),
|
||||||
|
apiId: process.env.RAPIDAPI_AQI_API_ID,
|
||||||
|
versionId: process.env.RAPIDAPI_AQI_VERSION_ID,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a multipart/form-data body from a file buffer.
|
||||||
|
* Returns { body: Buffer, boundary: string }
|
||||||
|
*/
|
||||||
|
function buildMultipart(fieldName, filename, fileBuffer, contentType = 'application/yaml') {
|
||||||
|
const boundary = '----FormBoundary' + Math.random().toString(36).slice(2);
|
||||||
|
const CRLF = '\r\n';
|
||||||
|
const parts = [
|
||||||
|
Buffer.from(
|
||||||
|
`--${boundary}${CRLF}` +
|
||||||
|
`Content-Disposition: form-data; name="${fieldName}"; filename="${filename}"${CRLF}` +
|
||||||
|
`Content-Type: ${contentType}${CRLF}${CRLF}`
|
||||||
|
),
|
||||||
|
fileBuffer,
|
||||||
|
Buffer.from(`${CRLF}--${boundary}--${CRLF}`),
|
||||||
|
];
|
||||||
|
return { body: Buffer.concat(parts), boundary };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload a spec file to RapidAPI Platform API.
|
||||||
|
* Returns a promise that resolves with the response status code.
|
||||||
|
*/
|
||||||
|
function uploadSpec(api) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!api.apiId || !api.versionId) {
|
||||||
|
console.log(`⏭️ Skipping ${api.name}: API ID or Version ID not configured.`);
|
||||||
|
return resolve(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(api.specPath)) {
|
||||||
|
console.log(`⏭️ Skipping ${api.name}: spec file not found at ${api.specPath}`);
|
||||||
|
return resolve(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileBuffer = fs.readFileSync(api.specPath);
|
||||||
|
const { body, boundary } = buildMultipart('spec', 'openapi.yaml', fileBuffer);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
hostname: 'platformapi1.p.rapidapi.com',
|
||||||
|
path: `/v1/apis/${api.apiId}/versions/${api.versionId}`,
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${RAPIDAPI_KEY}`,
|
||||||
|
'X-RapidAPI-Key': RAPIDAPI_KEY,
|
||||||
|
'Content-Type': `multipart/form-data; boundary=${boundary}`,
|
||||||
|
'Content-Length': body.length,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const req = https.request(options, (res) => {
|
||||||
|
let data = '';
|
||||||
|
res.on('data', (chunk) => { data += chunk; });
|
||||||
|
res.on('end', () => {
|
||||||
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||||
|
console.log(`✓ Published ${api.name} spec to RapidAPI (HTTP ${res.statusCode})`);
|
||||||
|
resolve(res.statusCode);
|
||||||
|
} else {
|
||||||
|
reject(new Error(`Failed to publish ${api.name}: HTTP ${res.statusCode} — ${data}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('error', reject);
|
||||||
|
req.write(body);
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
let hasError = false;
|
||||||
|
for (const api of APIS) {
|
||||||
|
try {
|
||||||
|
await uploadSpec(api);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`✗ ${err.message}`);
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasError) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
Reference in New Issue
Block a user