Expand domainr
This commit is contained in:
@@ -1,19 +1,30 @@
|
|||||||
import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common';
|
import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common';
|
||||||
import { DomainrproxyService } from './domainrproxy.service';
|
import { DomainrproxyService } from './domainrproxy.service';
|
||||||
import { AuthGuard } from 'src/auth/auth.guard';
|
import { AuthGuard } from 'src/auth/auth.guard';
|
||||||
|
import {
|
||||||
|
ApiBearerAuth,
|
||||||
|
ApiOkResponse,
|
||||||
|
ApiResponse,
|
||||||
|
ApiTags,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import { DomainrParsedStatusResult } from './domainrproxy.service';
|
||||||
|
|
||||||
|
@ApiTags('Domainr')
|
||||||
@Controller('domainrproxy')
|
@Controller('domainrproxy')
|
||||||
export class DomainrproxyController {
|
export class DomainrproxyController {
|
||||||
constructor(private readonly proxyService: DomainrproxyService) {}
|
constructor(private readonly proxyService: DomainrproxyService) {}
|
||||||
|
|
||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@Get(':domain')
|
@Get(':domain')
|
||||||
|
@ApiOkResponse({ type: DomainrParsedStatusResult })
|
||||||
|
@ApiBearerAuth()
|
||||||
queryDomain(@Param('domain') domain: string) {
|
queryDomain(@Param('domain') domain: string) {
|
||||||
return this.proxyService.queryForDomain(domain);
|
return this.proxyService.queryForDomain(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@Post('search')
|
@Post('search')
|
||||||
|
@ApiBearerAuth()
|
||||||
search(@Body() body: { query: string }) {
|
search(@Body() body: { query: string }) {
|
||||||
return this.proxyService.search(body.query);
|
return this.proxyService.search(body.query);
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,205 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import axios from 'axios';
|
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()
|
@Injectable()
|
||||||
export class DomainrproxyService {
|
export class DomainrproxyService {
|
||||||
private readonly logger = new Logger(DomainrproxyService.name);
|
private readonly logger = new Logger(DomainrproxyService.name);
|
||||||
constructor(private readonly configService: ConfigService) {}
|
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) => {
|
queryForDomain = async (domain: string) => {
|
||||||
this.logger.verbose(`Handling domainr query for domain ${domain}`);
|
this.logger.verbose(`Handling domainr query for domain ${domain}`);
|
||||||
const options = {
|
const options = {
|
||||||
@@ -22,8 +215,8 @@ export class DomainrproxyService {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await axios.request(options);
|
const result = await axios.request<DomainrStatusResponse>(options);
|
||||||
return result.data;
|
return result.data.status.map(this.parseStatusResult);
|
||||||
};
|
};
|
||||||
|
|
||||||
search = async (query: string) => {
|
search = async (query: string) => {
|
||||||
@@ -34,6 +227,7 @@ export class DomainrproxyService {
|
|||||||
params: {
|
params: {
|
||||||
query,
|
query,
|
||||||
defaults: 'com,net,org,io,sh,wtf',
|
defaults: 'com,net,org,io,sh,wtf',
|
||||||
|
registrar: 'namecheap.com',
|
||||||
'mashape-key': this.configService.get<string>('rapidApiKey'),
|
'mashape-key': this.configService.get<string>('rapidApiKey'),
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
|
Reference in New Issue
Block a user