api: add protected CRUD routers and DNS check logic
This commit is contained in:
parent
c7edd90a7e
commit
5264cccb6c
11 changed files with 709 additions and 8 deletions
91
packages/api/src/lib/domain-check.ts
Normal file
91
packages/api/src/lib/domain-check.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import { createDb, domain } from "@minmon/db";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { resolve4 } from "node:dns/promises";
|
||||
import { Effect } from "effect";
|
||||
|
||||
export type DomainCheckStatus = "MATCH" | "MISMATCH" | "UNRESOLVED";
|
||||
|
||||
export type DomainCheckResult = {
|
||||
status: DomainCheckStatus;
|
||||
resolvedIps: string[];
|
||||
checkedAt: Date;
|
||||
message: string | null;
|
||||
};
|
||||
|
||||
type DomainCheckTarget = {
|
||||
name: string;
|
||||
expectedServerIp: string;
|
||||
};
|
||||
|
||||
export function resolveIpv4ARecords(hostname: string) {
|
||||
return Effect.tryPromise({
|
||||
try: async () => [...new Set(await resolve4(hostname))],
|
||||
catch: (error: unknown) =>
|
||||
error instanceof Error ? error : new Error("Unable to resolve IPv4 A records"),
|
||||
});
|
||||
}
|
||||
|
||||
export function checkDomainDns(target: DomainCheckTarget) {
|
||||
return Effect.match(resolveIpv4ARecords(target.name), {
|
||||
onSuccess: (resolvedIps: string[]): DomainCheckResult => ({
|
||||
status: resolvedIps.includes(target.expectedServerIp) ? "MATCH" : "MISMATCH",
|
||||
resolvedIps,
|
||||
checkedAt: new Date(),
|
||||
message: null,
|
||||
}),
|
||||
onFailure: (error: Error): DomainCheckResult => ({
|
||||
status: "UNRESOLVED",
|
||||
resolvedIps: [],
|
||||
checkedAt: new Date(),
|
||||
message: error.message,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
export async function persistDomainCheck(
|
||||
database: ReturnType<typeof createDb>,
|
||||
input: {
|
||||
domainId: string;
|
||||
result: DomainCheckResult;
|
||||
},
|
||||
) {
|
||||
const [updatedDomain] = await database
|
||||
.update(domain)
|
||||
.set({
|
||||
resolutionStatus: input.result.status,
|
||||
lastResolvedIps: input.result.resolvedIps,
|
||||
lastResolvedIp: input.result.resolvedIps[0] ?? null,
|
||||
lastCheckedAt: input.result.checkedAt,
|
||||
lastCheckMessage: input.result.message,
|
||||
})
|
||||
.where(eq(domain.id, input.domainId))
|
||||
.returning();
|
||||
|
||||
return updatedDomain ?? null;
|
||||
}
|
||||
|
||||
export async function runAndPersistDomainCheck(
|
||||
database: ReturnType<typeof createDb>,
|
||||
input: {
|
||||
domainId: string;
|
||||
name: string;
|
||||
expectedServerIp: string;
|
||||
},
|
||||
) {
|
||||
const result = await Effect.runPromise(
|
||||
checkDomainDns({
|
||||
name: input.name,
|
||||
expectedServerIp: input.expectedServerIp,
|
||||
}),
|
||||
);
|
||||
|
||||
const updatedDomain = await persistDomainCheck(database, {
|
||||
domainId: input.domainId,
|
||||
result,
|
||||
});
|
||||
|
||||
return {
|
||||
result,
|
||||
domain: updatedDomain,
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue