Expand domainr

This commit is contained in:
2023-09-18 21:58:51 -06:00
parent 3e3b1c46fc
commit 6078010607
2 changed files with 207 additions and 2 deletions

View File

@@ -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);
} }

View File

@@ -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: {