synor/sdk/js/src/hosting/client.ts
Gulshan Yadav 74b82d2bb2 Add Synor Storage and Wallet SDKs for Swift
- Implement SynorStorage class for decentralized storage operations including upload, download, pinning, and CAR file management.
- Create supporting types and models for storage operations such as UploadOptions, Pin, and StorageConfig.
- Implement SynorWallet class for wallet operations including wallet creation, address generation, transaction signing, and balance queries.
- Create supporting types and models for wallet operations such as Wallet, Address, and Transaction.
- Introduce error handling for both storage and wallet operations.
2026-01-27 01:56:45 +05:30

396 lines
10 KiB
TypeScript

/**
* Synor Hosting SDK Client
* Decentralized web hosting with domain management, DNS, and deployments.
*/
import type {
HostingConfig,
Domain,
DomainRecord,
RegisterDomainOptions,
DomainAvailability,
DnsRecord,
DnsZone,
Deployment,
DeployOptions,
DeploymentStats,
Certificate,
ProvisionSslOptions,
SiteConfig,
AnalyticsData,
AnalyticsOptions,
} from './types';
import { HostingError } from './types';
const DEFAULT_CONFIG = {
endpoint: 'https://hosting.synor.io/v1',
network: 'mainnet' as const,
timeout: 60000,
retries: 3,
debug: false,
};
/**
* Synor Hosting SDK Client
*
* Provides domain registration, DNS management, site deployment, and SSL provisioning.
*
* @example
* ```typescript
* const hosting = new SynorHosting({ apiKey: 'your-api-key' });
*
* // Register a domain
* const domain = await hosting.registerDomain('mysite.synor');
*
* // Deploy a site from IPFS CID
* const deployment = await hosting.deploy('Qm...', { domain: 'mysite.synor' });
*
* // Provision SSL
* const cert = await hosting.provisionSsl('mysite.synor');
* ```
*/
export class SynorHosting {
private config: Required<HostingConfig>;
private closed = false;
constructor(config: HostingConfig) {
this.config = {
...DEFAULT_CONFIG,
...config,
};
if (!this.config.apiKey) {
throw new HostingError('API key is required');
}
}
// ==================== Domain Operations ====================
/**
* Check domain availability
*/
async checkAvailability(name: string): Promise<DomainAvailability> {
return this.request<DomainAvailability>(`/domains/check/${encodeURIComponent(name)}`);
}
/**
* Register a new domain
*/
async registerDomain(name: string, options?: RegisterDomainOptions): Promise<Domain> {
return this.request<Domain>('/domains', {
method: 'POST',
body: { name, ...options },
});
}
/**
* Get domain information
*/
async getDomain(name: string): Promise<Domain> {
return this.request<Domain>(`/domains/${encodeURIComponent(name)}`);
}
/**
* List all domains
*/
async listDomains(): Promise<Domain[]> {
const response = await this.request<{ domains: Domain[] }>('/domains');
return response.domains;
}
/**
* Update domain record (for IPFS/IPNS resolution)
*/
async updateDomainRecord(name: string, record: DomainRecord): Promise<Domain> {
return this.request<Domain>(`/domains/${encodeURIComponent(name)}/record`, {
method: 'PUT',
body: record,
});
}
/**
* Resolve a domain to its record
*/
async resolveDomain(name: string): Promise<DomainRecord> {
return this.request<DomainRecord>(`/domains/${encodeURIComponent(name)}/resolve`);
}
/**
* Renew a domain
*/
async renewDomain(name: string, years: number = 1): Promise<Domain> {
return this.request<Domain>(`/domains/${encodeURIComponent(name)}/renew`, {
method: 'POST',
body: { years },
});
}
/**
* Transfer domain ownership
*/
async transferDomain(name: string, newOwner: string): Promise<Domain> {
return this.request<Domain>(`/domains/${encodeURIComponent(name)}/transfer`, {
method: 'POST',
body: { new_owner: newOwner },
});
}
// ==================== DNS Operations ====================
/**
* Get DNS zone for a domain
*/
async getDnsZone(domain: string): Promise<DnsZone> {
return this.request<DnsZone>(`/dns/${encodeURIComponent(domain)}`);
}
/**
* Set DNS records for a domain
*/
async setDnsRecords(domain: string, records: DnsRecord[]): Promise<DnsZone> {
return this.request<DnsZone>(`/dns/${encodeURIComponent(domain)}`, {
method: 'PUT',
body: { records },
});
}
/**
* Add a DNS record
*/
async addDnsRecord(domain: string, record: DnsRecord): Promise<DnsZone> {
return this.request<DnsZone>(`/dns/${encodeURIComponent(domain)}/records`, {
method: 'POST',
body: record,
});
}
/**
* Delete a DNS record
*/
async deleteDnsRecord(domain: string, recordType: string, name: string): Promise<DnsZone> {
return this.request<DnsZone>(
`/dns/${encodeURIComponent(domain)}/records/${recordType}/${encodeURIComponent(name)}`,
{ method: 'DELETE' }
);
}
// ==================== Deployment Operations ====================
/**
* Deploy a site from CID
*/
async deploy(cid: string, options?: DeployOptions): Promise<Deployment> {
return this.request<Deployment>('/deployments', {
method: 'POST',
body: { cid, ...options },
});
}
/**
* Get deployment by ID
*/
async getDeployment(id: string): Promise<Deployment> {
return this.request<Deployment>(`/deployments/${encodeURIComponent(id)}`);
}
/**
* List deployments
*/
async listDeployments(domain?: string): Promise<Deployment[]> {
const query = domain ? `?domain=${encodeURIComponent(domain)}` : '';
const response = await this.request<{ deployments: Deployment[] }>(`/deployments${query}`);
return response.deployments;
}
/**
* Rollback to a previous deployment
*/
async rollback(domain: string, deploymentId: string): Promise<Deployment> {
return this.request<Deployment>(`/deployments/${encodeURIComponent(deploymentId)}/rollback`, {
method: 'POST',
body: { domain },
});
}
/**
* Delete a deployment
*/
async deleteDeployment(id: string): Promise<void> {
await this.request(`/deployments/${encodeURIComponent(id)}`, { method: 'DELETE' });
}
/**
* Get deployment stats
*/
async getDeploymentStats(id: string, period: string = '24h'): Promise<DeploymentStats> {
return this.request<DeploymentStats>(
`/deployments/${encodeURIComponent(id)}/stats?period=${period}`
);
}
// ==================== SSL Operations ====================
/**
* Provision SSL certificate
*/
async provisionSsl(domain: string, options?: ProvisionSslOptions): Promise<Certificate> {
return this.request<Certificate>(`/ssl/${encodeURIComponent(domain)}`, {
method: 'POST',
body: options || {},
});
}
/**
* Get certificate status
*/
async getCertificate(domain: string): Promise<Certificate> {
return this.request<Certificate>(`/ssl/${encodeURIComponent(domain)}`);
}
/**
* Renew SSL certificate
*/
async renewCertificate(domain: string): Promise<Certificate> {
return this.request<Certificate>(`/ssl/${encodeURIComponent(domain)}/renew`, {
method: 'POST',
});
}
/**
* Delete/revoke SSL certificate
*/
async deleteCertificate(domain: string): Promise<void> {
await this.request(`/ssl/${encodeURIComponent(domain)}`, { method: 'DELETE' });
}
// ==================== Site Configuration ====================
/**
* Get site configuration
*/
async getSiteConfig(domain: string): Promise<SiteConfig> {
return this.request<SiteConfig>(`/sites/${encodeURIComponent(domain)}/config`);
}
/**
* Update site configuration
*/
async updateSiteConfig(domain: string, config: Partial<SiteConfig>): Promise<SiteConfig> {
return this.request<SiteConfig>(`/sites/${encodeURIComponent(domain)}/config`, {
method: 'PATCH',
body: config,
});
}
/**
* Purge CDN cache
*/
async purgeCache(domain: string, paths?: string[]): Promise<{ purged: number }> {
return this.request<{ purged: number }>(`/sites/${encodeURIComponent(domain)}/cache`, {
method: 'DELETE',
body: paths ? { paths } : undefined,
});
}
// ==================== Analytics ====================
/**
* Get site analytics
*/
async getAnalytics(domain: string, options?: AnalyticsOptions): Promise<AnalyticsData> {
const params = new URLSearchParams();
if (options?.period) params.set('period', options.period);
if (options?.startDate) params.set('start', options.startDate);
if (options?.endDate) params.set('end', options.endDate);
const query = params.toString() ? `?${params}` : '';
return this.request<AnalyticsData>(`/sites/${encodeURIComponent(domain)}/analytics${query}`);
}
// ==================== Lifecycle ====================
/**
* Close the client
*/
close(): void {
this.closed = true;
}
/**
* Check if the client is closed
*/
isClosed(): boolean {
return this.closed;
}
/**
* Health check
*/
async healthCheck(): Promise<boolean> {
try {
const response = await this.request<{ status: string }>('/health');
return response.status === 'healthy';
} catch {
return false;
}
}
/**
* Internal request method
*/
private async request<T>(
path: string,
options: { method?: string; body?: unknown } = {}
): Promise<T> {
if (this.closed) {
throw new HostingError('Client has been closed');
}
const { method = 'GET', body } = options;
let lastError: Error | null = null;
for (let attempt = 0; attempt < this.config.retries; attempt++) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
const response = await fetch(`${this.config.endpoint}${path}`, {
method,
headers: {
'Authorization': `Bearer ${this.config.apiKey}`,
'Content-Type': 'application/json',
'X-SDK-Version': 'js/0.1.0',
},
body: body ? JSON.stringify(body) : undefined,
signal: controller.signal,
});
clearTimeout(timeoutId);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new HostingError(
errorData.message || errorData.error || `HTTP ${response.status}`,
errorData.code,
response.status
);
}
const text = await response.text();
return text ? JSON.parse(text) : ({} as T);
} catch (error) {
lastError = error instanceof Error ? error : new Error(String(error));
if (this.config.debug) {
console.error(`Attempt ${attempt + 1} failed:`, lastError.message);
}
if (attempt < this.config.retries - 1) {
await new Promise(resolve => setTimeout(resolve, (attempt + 1) * 1000));
}
}
}
throw lastError || new HostingError('Unknown error after retries');
}
}