Skip to content

Commit e86c054

Browse files
chore: remove classes
1 parent 64a9047 commit e86c054

7 files changed

Lines changed: 205 additions & 206 deletions

File tree

apps/faucet/src/faucet.route.ts

Lines changed: 47 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,75 @@
11
import { isAddress } from "@happy.tech/common"
2-
import type { Hono } from "hono"
2+
import type { Context } from "hono"
33
import { describeRoute } from "hono-openapi"
44
import { resolver } from "hono-openapi/zod"
55
import { validator } from "hono-openapi/zod"
6+
import type { BlankEnv } from "hono/types"
67
import { err } from "neverthrow"
8+
import type { Address } from "viem"
79
import { z } from "zod"
810
import { env } from "./env"
9-
import type { CloudflareService } from "./services/cloudflare"
10-
import type { FaucetService } from "./services/faucet"
11+
import { cloudflareService } from "./services/cloudflare"
12+
import { faucetService } from "./services/faucet"
1113
import { makeResponse } from "./utils"
1214

15+
type Request = {
16+
json: {
17+
address: Address
18+
cfToken: string
19+
}
20+
}
21+
1322
export const inputSchema = z.object({
1423
address: z.string().refine(isAddress),
1524
cfToken: z.string(),
1625
})
1726

1827
export const outputSchema = z.object({
1928
success: z.boolean(),
20-
message: z.string(),
29+
message: z.string().optional(),
2130
})
2231

2332
export type FaucetInput = z.infer<typeof inputSchema>
2433
export type FaucetOutput = z.infer<typeof outputSchema>
2534

26-
export const setupFaucetRoutes = (app: Hono, faucetService: FaucetService, cloudflareService: CloudflareService) => {
27-
const validation = validator("json", inputSchema)
28-
const description = describeRoute({
29-
description: "Faucet endpoint with Cloudflare Turnstile validation",
30-
responses: {
31-
200: {
32-
description: "Tokens sent",
33-
content: { "application/json": { schema: resolver(outputSchema) } },
34-
},
35-
403: {
36-
description: "Turnstile verification failed",
37-
content: { "application/json": { schema: resolver(outputSchema) } },
38-
},
39-
429: {
40-
description: "Rate limit exceeded",
41-
content: { "application/json": { schema: resolver(outputSchema) } },
42-
},
43-
},
44-
validateResponse: env.NODE_ENV !== "production",
45-
})
46-
47-
app.post("/faucet", validation, description, async (c) => {
48-
try {
49-
const { address, cfToken } = c.req.valid("json")
35+
export const validation = validator("json", inputSchema)
5036

51-
const captchaResult = await cloudflareService.verifyTurnstile(cfToken)
37+
export const description = describeRoute({
38+
description: "Faucet endpoint with Cloudflare Turnstile validation",
39+
responses: {
40+
200: {
41+
description: "Tokens sent",
42+
content: { "application/json": { schema: resolver(outputSchema) } },
43+
},
44+
403: {
45+
description: "Turnstile verification failed",
46+
content: { "application/json": { schema: resolver(outputSchema) } },
47+
},
48+
429: {
49+
description: "Rate limit exceeded",
50+
content: { "application/json": { schema: resolver(outputSchema) } },
51+
},
52+
},
53+
validateResponse: env.NODE_ENV !== "production",
54+
})
5255

53-
if (captchaResult.isErr()) {
54-
const response = makeResponse(captchaResult)
55-
return c.json(response[0], response[1])
56-
}
56+
export const faucetHandler = async (c: Context<BlankEnv, "/faucet", { in: Request; out: Request }>) => {
57+
try {
58+
const { address, cfToken } = c.req.valid("json")
5759

58-
const result = await faucetService.sendTokens(address)
60+
const captchaResult = await cloudflareService.verifyTurnstile(cfToken)
5961

60-
const response = makeResponse(result)
61-
return c.json(response[0], response[1])
62-
} catch (error) {
63-
const response = makeResponse(err(error))
62+
if (captchaResult.isErr()) {
63+
const response = makeResponse(captchaResult)
6464
return c.json(response[0], response[1])
6565
}
66-
})
66+
67+
const result = await faucetService.sendTokens(address)
68+
69+
const response = makeResponse(result)
70+
return c.json(response[0], response[1])
71+
} catch (error) {
72+
const response = makeResponse(err(error))
73+
return c.json(response[0], response[1])
74+
}
6775
}

apps/faucet/src/index.ts

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,75 @@
1-
import { Server } from "./server"
1+
import { serve } from "@hono/node-server"
2+
import { apiReference } from "@scalar/hono-api-reference"
3+
import { Hono } from "hono"
4+
import { openAPISpecs } from "hono-openapi"
5+
import { cors } from "hono/cors"
6+
import { logger as loggerMiddleware } from "hono/logger"
7+
import { prettyJSON as prettyJSONMiddleware } from "hono/pretty-json"
8+
import { requestId as requestIdMiddleware } from "hono/request-id"
9+
import { timeout as timeoutMiddleware } from "hono/timeout"
10+
import { timing as timingMiddleware } from "hono/timing"
11+
import pkg from "../package.json" assert { type: "json" }
12+
import { env } from "./env"
13+
import { description, faucetHandler, validation } from "./faucet.route"
14+
import { faucetService } from "./services/faucet"
215

3-
const server = new Server()
4-
server.start()
16+
const app = new Hono()
17+
18+
// Middleware setup
19+
app.use(
20+
"*",
21+
cors({
22+
origin: "*",
23+
}),
24+
)
25+
app.use("*", timingMiddleware())
26+
app.use("*", loggerMiddleware())
27+
app.use("*", prettyJSONMiddleware())
28+
app.use("*", timeoutMiddleware(30_000))
29+
app.use("*", requestIdMiddleware())
30+
31+
// Routes setup
32+
app.get("/", (c) => c.text("Welcome to the Faucet Service!"))
33+
34+
// Faucet route
35+
app.post("/faucet", validation, description, faucetHandler)
36+
37+
// OpenAPI documentation
38+
app.get(
39+
"/docs/openapi.json",
40+
openAPISpecs(app, {
41+
documentation: {
42+
info: { title: "Faucet", version: pkg.version, description: "Faucet API" },
43+
servers: [
44+
...(env.NODE_ENV === "development"
45+
? [
46+
{
47+
url: `http://localhost:${env.APP_PORT}`,
48+
description: "Local",
49+
},
50+
]
51+
: []),
52+
{ url: "https://faucet.testnet.happy.tech", description: "Testnet" },
53+
],
54+
},
55+
}),
56+
)
57+
58+
// API Reference UI
59+
app.get(
60+
"/docs",
61+
apiReference({
62+
pageTitle: "Faucet API Reference - HappyChain",
63+
theme: "kepler",
64+
spec: { url: "/docs/openapi.json" },
65+
showSidebar: true,
66+
hideSearch: false,
67+
}),
68+
)
69+
70+
export async function startServer() {
71+
await faucetService.start()
72+
serve({ fetch: app.fetch, port: env.APP_PORT })
73+
}
74+
75+
startServer()

apps/faucet/src/server.ts

Lines changed: 0 additions & 85 deletions
This file was deleted.

apps/faucet/src/services/cloudflare.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import { type Result, ResultAsync, err, ok } from "neverthrow"
22
import { z } from "zod"
3+
import { env } from "../env"
34
import { FaucetCaptchaError, type FaucetFetchError } from "../errors"
45
import { mapFetchError } from "../utils"
56

67
export const siteVerifyResponseSchema = z.object({
7-
success: z.boolean(),
8+
success: z.literal(true),
89
})
910

1011
export class CloudflareService {
1112
private readonly secret: string
1213

13-
constructor(secret: string) {
14-
this.secret = secret
14+
constructor() {
15+
this.secret = env.TURNSTILE_SECRET
1516
}
1617

1718
async verifyTurnstile(token: string): Promise<Result<undefined, FaucetCaptchaError | FaucetFetchError>> {
@@ -38,3 +39,5 @@ export class CloudflareService {
3839
return ok(undefined)
3940
}
4041
}
42+
43+
export const cloudflareService = new CloudflareService()

apps/faucet/src/services/faucet.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ import type { Address } from "viem"
44
import { env } from "../env"
55
import { FaucetRateLimitError } from "../errors"
66
import { FaucetUsage } from "../faucet-usage.entity"
7-
import type { FaucetUsageRepository } from "../faucet-usage.repository"
7+
import { FaucetUsageRepository } from "../faucet-usage.repository"
88

99
export class FaucetService {
1010
private txm: TransactionManager
1111
private faucetUsageRepository: FaucetUsageRepository
1212

13-
constructor(faucetUsageRepository: FaucetUsageRepository) {
13+
constructor() {
1414
this.txm = new TransactionManager({
1515
rpc: { url: env.RPC_URL },
1616
chainId: env.CHAIN_ID,
1717
blockTime: env.BLOCK_TIME,
1818
privateKey: env.PRIVATE_KEY,
1919
})
20-
this.faucetUsageRepository = faucetUsageRepository
20+
this.faucetUsageRepository = new FaucetUsageRepository()
2121
}
2222

2323
async start() {
@@ -47,3 +47,5 @@ export class FaucetService {
4747
return ok(undefined)
4848
}
4949
}
50+
51+
export const faucetService = new FaucetService()

0 commit comments

Comments
 (0)