Expand domainr
This commit is contained in:
@@ -1,19 +1,30 @@
|
||||
import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common';
|
||||
import { DomainrproxyService } from './domainrproxy.service';
|
||||
import { AuthGuard } from 'src/auth/auth.guard';
|
||||
import {
|
||||
ApiBearerAuth,
|
||||
ApiOkResponse,
|
||||
ApiResponse,
|
||||
ApiTags,
|
||||
} from '@nestjs/swagger';
|
||||
import { DomainrParsedStatusResult } from './domainrproxy.service';
|
||||
|
||||
@ApiTags('Domainr')
|
||||
@Controller('domainrproxy')
|
||||
export class DomainrproxyController {
|
||||
constructor(private readonly proxyService: DomainrproxyService) {}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Get(':domain')
|
||||
@ApiOkResponse({ type: DomainrParsedStatusResult })
|
||||
@ApiBearerAuth()
|
||||
queryDomain(@Param('domain') domain: string) {
|
||||
return this.proxyService.queryForDomain(domain);
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Post('search')
|
||||
@ApiBearerAuth()
|
||||
search(@Body() body: { query: string }) {
|
||||
return this.proxyService.search(body.query);
|
||||
}
|
||||
|
@@ -1,12 +1,205 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import axios from 'axios';
|
||||
import { any } from 'ramda';
|
||||
|
||||
export interface DomainrSearchResult {
|
||||
domain: string;
|
||||
host: string;
|
||||
subdomain: string;
|
||||
path: string;
|
||||
registerURL: string;
|
||||
}
|
||||
|
||||
const statusStrings = [
|
||||
/**
|
||||
* Unknown status, usually resulting from an error or
|
||||
* misconfiguration.
|
||||
*
|
||||
* Example: foobar.llp
|
||||
*/
|
||||
'unknown',
|
||||
/**
|
||||
* The domain is not present in DNS.
|
||||
*
|
||||
* Example: hello.edu
|
||||
*/
|
||||
'undelgated',
|
||||
/**
|
||||
* Available for new registration.
|
||||
*
|
||||
* Example: nonexistent.xyz
|
||||
*/
|
||||
'inactive',
|
||||
/**
|
||||
* TLD not yet in the root zone file
|
||||
*
|
||||
* Example: heartb.eat
|
||||
*/
|
||||
'pending',
|
||||
/**
|
||||
* Disallowed by the registry, ICANN, or other (wrong
|
||||
* script, etc.).
|
||||
*
|
||||
* Example: a.mobi
|
||||
*/
|
||||
'disallowed',
|
||||
/**
|
||||
* Claimed or reserved by some party (not available for new
|
||||
* registration).
|
||||
*
|
||||
* Example: fedex.name
|
||||
*/
|
||||
'claimed',
|
||||
/**
|
||||
* Explicitly reserved by ICANN, the registry, or another
|
||||
* party.
|
||||
*
|
||||
* Example: s.work
|
||||
*/
|
||||
'reserved',
|
||||
/**
|
||||
* Domains Protected Marks List, reserved for trademark
|
||||
* holders.
|
||||
*
|
||||
* Example: google.bargains
|
||||
*/
|
||||
'dpml',
|
||||
/**
|
||||
* Technically invalid, e.g. too long or too short.
|
||||
* Example: a domain longer than 64 characters
|
||||
*/
|
||||
'invalid',
|
||||
/**
|
||||
* Registered, but possibly available via the aftermarket.
|
||||
*
|
||||
* Example: vertical.coffee
|
||||
*/
|
||||
'active',
|
||||
/**
|
||||
* Active and parked, possibly available via the aftermarket.
|
||||
*
|
||||
* Example: wi.io
|
||||
*/
|
||||
'parked',
|
||||
/**
|
||||
* Explicitly marketed as for sale via the aftermarket.
|
||||
*
|
||||
* Example: wi.io
|
||||
*/
|
||||
'marketed',
|
||||
/**
|
||||
* e.g. in the Redemption Grace Period, and possibly available
|
||||
* via a backorder service. Not guaranteed to be present for
|
||||
* all expiring domains.
|
||||
*
|
||||
* Example: An expiring domain.
|
||||
*/
|
||||
'expiring',
|
||||
/**
|
||||
* e.g. in the Pending Delete phase, and possibly available
|
||||
* via a backorder service. Not guaranteed to be present for
|
||||
* all deleting domains.
|
||||
*
|
||||
* Example: A expired domain pending removal
|
||||
* from the registry.
|
||||
*/
|
||||
'deleting',
|
||||
/**
|
||||
* e.g. via the BuyDomains service.
|
||||
*
|
||||
* Example: An aftermarket domain with an explicit price.
|
||||
*/
|
||||
'priced',
|
||||
/**
|
||||
* e.g. in the Afternic inventory.
|
||||
*
|
||||
* Example: An aftermarket domain available for
|
||||
* fast-transfer.
|
||||
*/
|
||||
'transferable',
|
||||
/**
|
||||
* Premium domain name for sale by the registry.
|
||||
*
|
||||
* Example: ace.pizza
|
||||
*/
|
||||
'premium',
|
||||
/**
|
||||
* A public suffix according to publicsuffix.org.
|
||||
*
|
||||
* Example: blogspot.com
|
||||
*/
|
||||
'suffix',
|
||||
/**
|
||||
* A zone (domain extension) in the Domainr database.
|
||||
*
|
||||
* Example: .co.uk
|
||||
*/
|
||||
'zone',
|
||||
/**
|
||||
* A top-level domain.
|
||||
*
|
||||
* Example: .com
|
||||
*/
|
||||
'tld',
|
||||
] as const;
|
||||
|
||||
export type Status = (typeof statusStrings)[number];
|
||||
|
||||
export class DomainrStatusResult {
|
||||
@ApiProperty()
|
||||
public readonly domain: string;
|
||||
@ApiProperty()
|
||||
public readonly zone: string;
|
||||
@ApiProperty()
|
||||
public readonly status: string;
|
||||
/**
|
||||
* Deprecated
|
||||
* @deprecated
|
||||
*/
|
||||
@ApiProperty()
|
||||
public readonly summary: string;
|
||||
}
|
||||
|
||||
export class DomainrParsedStatusResult extends DomainrStatusResult {
|
||||
@ApiProperty()
|
||||
public readonly statuses: Status[];
|
||||
}
|
||||
|
||||
export class DomainrStatusResponse {
|
||||
status: DomainrStatusResult[];
|
||||
}
|
||||
@Injectable()
|
||||
export class DomainrproxyService {
|
||||
private readonly logger = new Logger(DomainrproxyService.name);
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
|
||||
canRegister = (status: Status) => (['inactive'] as Status[]).includes(status);
|
||||
|
||||
canPurchase = (status: Status) =>
|
||||
(
|
||||
[
|
||||
'inactive',
|
||||
'parked',
|
||||
'marketed',
|
||||
'priced',
|
||||
'transferable',
|
||||
'premium',
|
||||
] as Status[]
|
||||
).includes(status);
|
||||
|
||||
parseStatusResult = (incoming: DomainrStatusResult) => {
|
||||
const statuses: Status[] = incoming.status.split(' ') as Status[];
|
||||
|
||||
return {
|
||||
...incoming,
|
||||
statuses,
|
||||
canRegister: any(this.canRegister, statuses),
|
||||
canPurchase: any(this.canPurchase, statuses),
|
||||
};
|
||||
};
|
||||
|
||||
queryForDomain = async (domain: string) => {
|
||||
this.logger.verbose(`Handling domainr query for domain ${domain}`);
|
||||
const options = {
|
||||
@@ -22,8 +215,8 @@ export class DomainrproxyService {
|
||||
},
|
||||
};
|
||||
|
||||
const result = await axios.request(options);
|
||||
return result.data;
|
||||
const result = await axios.request<DomainrStatusResponse>(options);
|
||||
return result.data.status.map(this.parseStatusResult);
|
||||
};
|
||||
|
||||
search = async (query: string) => {
|
||||
@@ -34,6 +227,7 @@ export class DomainrproxyService {
|
||||
params: {
|
||||
query,
|
||||
defaults: 'com,net,org,io,sh,wtf',
|
||||
registrar: 'namecheap.com',
|
||||
'mashape-key': this.configService.get<string>('rapidApiKey'),
|
||||
},
|
||||
headers: {
|
||||
|
Reference in New Issue
Block a user