-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathconfig.schema.ts
More file actions
132 lines (120 loc) · 4.68 KB
/
config.schema.ts
File metadata and controls
132 lines (120 loc) · 4.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import pRetry from "p-retry";
import { prettifyError, ZodError, z } from "zod/v4";
import type { EnsApiPublicConfig } from "@ensnode/ensnode-sdk";
import {
buildRpcConfigsFromEnv,
canFallbackToTheGraph,
ENSNamespaceSchema,
invariant_rpcConfigsSpecifiedForRootChain,
makeENSIndexerPublicConfigSchema,
OptionalPortNumberSchema,
RpcConfigsSchema,
TheGraphApiKeySchema,
} from "@ensnode/ensnode-sdk/internal";
import { ENSApi_DEFAULT_PORT } from "@/config/defaults";
import { EnsDbConfigSchema } from "@/config/ensdb-config.schema";
import type { EnsApiEnvironment } from "@/config/environment";
import { invariant_ensIndexerPublicConfigVersionInfo } from "@/config/validations";
import { ensDbClient } from "@/lib/ensdb/singleton";
import logger from "@/lib/logger";
import { ensApiVersionInfo } from "@/lib/version-info";
/**
* Schema for validating custom referral program edition config set URL.
*/
const CustomReferralProgramEditionConfigSetUrlSchema = z
.string()
.transform((val, ctx) => {
try {
return new URL(val);
} catch {
ctx.addIssue({
code: "custom",
message: `CUSTOM_REFERRAL_PROGRAM_EDITIONS is not a valid URL: ${val}`,
});
return z.NEVER;
}
})
.optional();
const EnsApiConfigSchema = z
.object({
port: OptionalPortNumberSchema.default(ENSApi_DEFAULT_PORT),
theGraphApiKey: TheGraphApiKeySchema,
namespace: ENSNamespaceSchema,
rpcConfigs: RpcConfigsSchema,
ensIndexerPublicConfig: makeENSIndexerPublicConfigSchema("ensIndexerPublicConfig"),
customReferralProgramEditionConfigSetUrl: CustomReferralProgramEditionConfigSetUrlSchema,
})
.extend(EnsDbConfigSchema.shape)
.check(invariant_rpcConfigsSpecifiedForRootChain)
.check(invariant_ensIndexerPublicConfigVersionInfo);
export type EnsApiConfig = z.infer<typeof EnsApiConfigSchema>;
/**
* Builds the EnsApiConfig from an EnsApiEnvironment object, fetching the EnsIndexerPublicConfig.
*
* @returns A validated EnsApiConfig object
* @throws Error with formatted validation messages if environment parsing fails
*/
export async function buildConfigFromEnvironment(env: EnsApiEnvironment): Promise<EnsApiConfig> {
try {
// TODO: transfer the responsibility of fetching
// the ENSIndexer Public Config to a middleware layer, as per:
// https://github.com/namehash/ensnode/issues/1806
const ensIndexerPublicConfig = await pRetry(
async () => {
const config = await ensDbClient.getEnsIndexerPublicConfig();
if (!config) {
throw new Error("ENSIndexer Public Config not yet available in ENSDb.");
}
return config;
},
{
retries: 13, // This allows for a total of over 1 hour of retries with the exponential backoff strategy
onFailedAttempt: ({ error, attemptNumber, retriesLeft }) => {
logger.info(
`ENSIndexer Public Config fetch attempt ${attemptNumber} failed (${error.message}). ${retriesLeft} retries left.`,
);
},
},
);
const rpcConfigs = buildRpcConfigsFromEnv(env, ensIndexerPublicConfig.namespace);
return EnsApiConfigSchema.parse({
port: env.PORT,
ensDbUrl: env.ENSDB_URL,
theGraphApiKey: env.THEGRAPH_API_KEY,
ensIndexerPublicConfig,
namespace: ensIndexerPublicConfig.namespace,
ensIndexerSchemaName: ensIndexerPublicConfig.ensIndexerSchemaName,
rpcConfigs,
customReferralProgramEditionConfigSetUrl: env.CUSTOM_REFERRAL_PROGRAM_EDITIONS,
});
} catch (error) {
if (error instanceof ZodError) {
logger.error(`Failed to parse environment configuration: \n${prettifyError(error)}\n`);
} else if (error instanceof Error) {
logger.error(error, `Failed to build EnsApiConfig`);
} else {
logger.error(`Unknown Error`);
}
process.exit(1);
}
}
/**
* Builds the ENSApi public configuration from an EnsApiConfig object.
*
* @param config - The validated EnsApiConfig object
* @returns A complete ENSApiPublicConfig object
*/
export function buildEnsApiPublicConfig(config: EnsApiConfig): EnsApiPublicConfig {
return {
versionInfo: ensApiVersionInfo,
theGraphFallback: canFallbackToTheGraph({
namespace: config.namespace,
// NOTE: very important here that we replace the actual server-side api key with a placeholder
// so that it's not sent to clients as part of the `theGraphFallback.url`. The placeholder must
// pass validation, of course, but the only validation necessary is that it is a string.
theGraphApiKey: config.theGraphApiKey ? "<API_KEY>" : undefined,
isSubgraphCompatible: config.ensIndexerPublicConfig.isSubgraphCompatible,
}),
ensIndexerPublicConfig: config.ensIndexerPublicConfig,
};
}