91 lines
2.3 KiB
TypeScript
91 lines
2.3 KiB
TypeScript
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,
|
|
};
|
|
}
|