Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Omni Connector Hub

One hub for AI providers, email, hosting panels, VPS clouds, chat, GitHub, and
server operations — 21 providers, 278 operations, behind one type-safe
server operations — 24 providers, 301 operations, behind one type-safe
interface with a hash-chained audit ledger.

## Providers
Expand All @@ -18,6 +18,9 @@ interface with a hash-chained audit ledger.
| `github` | 25 | 2 | `GITHUB_TOKEN` | hand-written |
| `gmail` | 79 | 15 | `GOOGLE_CLIENT_ID/SECRET` + refresh tokens | Google Discovery |
| `hetzner` | 72 | 11 | `HETZNER_API_TOKEN` | OpenAPI 3.0.3 |
| `iherb_apify` | 7 | 0 | `IHERB_APIFY_TOKEN` | hand-written |
| `iherb_impact` | 8 | 0 | `IHERB_IMPACT_ACCOUNT_SID` + `IHERB_IMPACT_AUTH_TOKEN` | hand-written |
| `iherb_partnerize` | 8 | 0 | `IHERB_PARTNERIZE_APP_KEY` + `IHERB_PARTNERIZE_USER_KEY` | hand-written |
| `kimi` | 2 | 0 | `MOONSHOT_API_KEY` | hand-written |
| `linode` | 9 | 1 | `LINODE_API_TOKEN` | hand-written |
| `oneprovider` | 6 | 0 | `ONEPROVIDER_API_KEY` | hand-written |
Expand All @@ -32,7 +35,7 @@ interface with a hash-chained audit ledger.
| `whm` | 9 | 1 | `WHM_HOST` + root token | hand-written |
| `whmcs` | 9 | 0 | `WHMCS_URL` + API identifier/secret | hand-written |

**Total: 278 operations (32 destructive)**
**Total: 301 operations (32 destructive)**

## Quick start

Expand Down
42 changes: 42 additions & 0 deletions crates/hub-auth/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,48 @@ impl AuthStore {
});
}

// iHerb Partnerize (affiliate network)
if let (Ok(app_key), Ok(user_key)) = (
std::env::var("IHERB_PARTNERIZE_APP_KEY"),
std::env::var("IHERB_PARTNERIZE_USER_KEY"),
) {
store.add(Credential {
provider: "iherb_partnerize".into(),
account_id: "default".into(),
base_url: Some("https://api.partnerize.com/v3".into()),
auth: AuthMethod::Basic {
username: app_key,
password: user_key,
},
});
}

// iHerb Impact (affiliate network)
if let (Ok(account_sid), Ok(auth_token)) = (
std::env::var("IHERB_IMPACT_ACCOUNT_SID"),
std::env::var("IHERB_IMPACT_AUTH_TOKEN"),
) {
store.add(Credential {
provider: "iherb_impact".into(),
account_id: "default".into(),
base_url: Some("https://api.impact.com".into()),
auth: AuthMethod::Basic {
username: account_sid,
password: auth_token,
},
});
}

// iHerb Apify scraper
if let Ok(token) = std::env::var("IHERB_APIFY_TOKEN") {
store.add(Credential {
provider: "iherb_apify".into(),
account_id: "default".into(),
base_url: Some("https://api.apify.com/v2".into()),
auth: AuthMethod::Bearer { token },
});
}

// SMTP email accounts: EMAIL_SMTP_HOST, EMAIL_SMTP_PORT, EMAIL_SMTP_USER,
// EMAIL_SMTP_PASS, EMAIL_SMTP_FROM, EMAIL_SMTP_TLS for default account.
// EMAIL_SMTP_HOST_<LABEL> etc. for named accounts.
Expand Down
242 changes: 242 additions & 0 deletions specs/iherb_apify.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
{
"openapi": "3.0.3",
"info": {
"title": "iHerb Apify Scraper API",
"version": "1.0.0",
"description": "Apify actor API for the iHerb scraper. Provides product search, product details, reviews, and price monitoring via the Apify platform."
},
"servers": [
{ "url": "https://api.apify.com/v2" }
],
"security": [
{ "BearerAuth": [] }
],
"components": {
"securitySchemes": {
"BearerAuth": {
"type": "http",
"scheme": "bearer",
"description": "Apify API token as Bearer token"
}
}
},
"paths": {
"/acts/vaunted~iherb-scraper/runs?action=search": {
"post": {
"operationId": "run_search",
"summary": "Search iHerb products",
"description": "Starts an Apify actor run to search iHerb products by keyword. Returns a run object with an ID to poll for results.",
"tags": ["scraper"],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["search"],
"properties": {
"search": {
"type": "string",
"description": "Search keyword or phrase (e.g. 'vitamin D', 'omega 3')"
},
"maxItems": {
"type": "integer",
"default": 50,
"description": "Maximum number of products to return"
},
"sort": {
"type": "string",
"enum": ["relevance", "best_selling", "price_asc", "price_desc", "rating", "newest"],
"description": "Sort order for search results"
},
"country": {
"type": "string",
"default": "US",
"description": "Country code for localized pricing and availability"
}
}
}
}
}
}
}
},
"/acts/vaunted~iherb-scraper/runs?action=details": {
"post": {
"operationId": "run_product_details",
"summary": "Get iHerb product details",
"description": "Starts an Apify actor run to scrape full product information from iHerb product URLs.",
"tags": ["scraper"],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["urls"],
"properties": {
"urls": {
"type": "array",
"items": { "type": "string" },
"description": "List of iHerb product URLs to scrape"
},
"country": {
"type": "string",
"default": "US",
"description": "Country code for localized pricing"
}
}
}
}
}
}
}
},
"/acts/vaunted~iherb-scraper/runs?action=reviews": {
"post": {
"operationId": "run_reviews",
"summary": "Scrape iHerb product reviews",
"description": "Starts an Apify actor run to collect product reviews from iHerb, including ratings, text, and reviewer info.",
"tags": ["scraper"],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["productUrl"],
"properties": {
"productUrl": {
"type": "string",
"description": "iHerb product URL to scrape reviews from"
},
"maxReviews": {
"type": "integer",
"default": 100,
"description": "Maximum number of reviews to collect"
},
"sort": {
"type": "string",
"enum": ["newest", "oldest", "highest_rating", "lowest_rating", "most_helpful"],
"description": "Sort order for reviews"
}
}
}
}
}
}
}
},
"/acts/vaunted~iherb-scraper/runs?action=price_monitor": {
"post": {
"operationId": "run_price_monitor",
"summary": "Monitor iHerb product prices",
"description": "Starts an Apify actor run to track and record current prices for specified iHerb products.",
"tags": ["scraper"],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["urls"],
"properties": {
"urls": {
"type": "array",
"items": { "type": "string" },
"description": "List of iHerb product URLs to monitor prices for"
},
"country": {
"type": "string",
"default": "US",
"description": "Country code for localized pricing"
}
}
}
}
}
}
}
},
"/acts/vaunted~iherb-scraper/runs/{run_id}": {
"get": {
"operationId": "get_run",
"summary": "Get scraper run status",
"description": "Returns the status and metadata of a specific actor run, including whether it has finished and the default dataset ID for results.",
"tags": ["runs"],
"parameters": [
{
"name": "run_id",
"in": "path",
"required": true,
"schema": { "type": "string" },
"description": "Actor run ID"
}
]
}
},
"/datasets/{dataset_id}/items": {
"get": {
"operationId": "get_dataset_items",
"summary": "Get scraper run results",
"description": "Returns the scraped data items from a completed actor run's dataset. Use the dataset ID from the run status response.",
"tags": ["runs"],
"parameters": [
{
"name": "dataset_id",
"in": "path",
"required": true,
"schema": { "type": "string" },
"description": "Dataset ID from the actor run"
},
{
"name": "format",
"in": "query",
"schema": { "type": "string", "enum": ["json", "csv", "xlsx"], "default": "json" },
"description": "Output format for the dataset items"
},
{
"name": "limit",
"in": "query",
"schema": { "type": "integer" },
"description": "Maximum number of items to return"
},
{
"name": "offset",
"in": "query",
"schema": { "type": "integer", "default": 0 },
"description": "Number of items to skip"
}
]
}
},
"/acts/vaunted~iherb-scraper/runs": {
"get": {
"operationId": "list_runs",
"summary": "List past scraper runs",
"description": "Returns a list of past actor runs with their statuses, start times, and dataset IDs.",
"tags": ["runs"],
"parameters": [
{
"name": "limit",
"in": "query",
"schema": { "type": "integer", "default": 25 },
"description": "Maximum number of runs to return"
},
{
"name": "offset",
"in": "query",
"schema": { "type": "integer", "default": 0 },
"description": "Number of runs to skip"
},
{
"name": "status",
"in": "query",
"schema": { "type": "string", "enum": ["READY", "RUNNING", "SUCCEEDED", "FAILED", "TIMED-OUT", "ABORTED"] },
"description": "Filter runs by status"
}
]
}
}
}
}
Loading
Loading