-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.js
201 lines (182 loc) · 4.79 KB
/
server.js
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
import Fastify from "fastify";
import fastifyMultiPart from "@fastify/multipart";
import fastifySwagger from "@fastify/swagger";
import fastifyEnv from "@fastify/env";
import fastifySwaggerUi from "@fastify/swagger-ui";
import fastifyHttpProxy from "@fastify/http-proxy";
import createError from "@fastify/error";
import { irys } from "./bundlr.js";
import Metrics from "./metrics.js";
const { FileUploadTime } = Metrics.initialize();
const UploadError = createError(
"UPLOAD_ERROR",
"The upload was not successful"
);
const FundError = createError("FUND_ERROR", "The funding was not successful");
// Initialize Fastify with logging enabled
const fastify = Fastify({
logger: true,
});
// Register environment variables
await fastify.register(fastifyEnv, {
dotenv: true,
schema: {
type: "object",
required: ["ARWEAVE_PRIVATE_KEY", "IRYS_URL", "IRYS_GATEWAY"],
properties: {
ARWEAVE_PRIVATE_KEY: {
type: "string",
},
IRYS_URL: {
type: "string",
},
IRYS_GATEWAY: {
type: "string",
},
PORT: {
type: "number",
default: 3000,
},
},
},
});
// add proxy to the prometheus metrics endpoint
await fastify.register(fastifyHttpProxy, {
upstream: "http://localhost:9464/metrics",
prefix: "/metrics",
});
// Register multipart plugin with limits
await fastify.register(fastifyMultiPart, {
limits: {
fields: 0, // Max number of non-file fields
files: 1, // Max number of file fields
fileSize: 1024 * 1024 * 250, // Max file size in bytes
},
});
await fastify.register(fastifySwagger, {
exposeRoute: true,
swagger: {
info: {
title: "HUB Upload API",
description: "Upload API for HUB",
version: "0.1.0",
},
externalDocs: {
url: "https://docs.holaplex.com",
description: "HUB documentation",
},
host: "localhost:3000",
schemes: ["http"],
consumes: ["application/json"],
produces: ["application/json"],
tags: [],
securityDefinitions: {
apiKey: {
type: "apiKey",
name: "Authorization",
in: "header",
},
},
},
});
await fastify.register(fastifySwaggerUi, {
routePrefix: "/documentation",
initOAuth: {},
uiConfig: {
docExpansion: "full",
deepLinking: false,
},
uiHooks: {
onRequest: function (request, reply, next) {
next();
},
preHandler: function (request, reply, next) {
next();
},
},
staticCSP: true,
transformStaticCSP: (header) => header,
});
// Initialize uploader with environment variables
const uploader = irys(
fastify.config.IRYS_GATEWAY,
fastify.config.IRYS_URL,
fastify.config.ARWEAVE_PRIVATE_KEY
);
// Health check endpoint
fastify.get("/health", async function handler(request, reply) {
reply.send({ status: "ok" });
});
// Upload endpoint
fastify.post(
"/uploads",
{
schema: {
description: "Upload a file or JSON",
tags: ["file"],
consumes: ["multipart/form-data", "application/json"],
response: {
200: {
description: "File uploaded successfully",
type: "object",
properties: {
uri: { type: "string" },
cid: { type: "string" },
},
},
},
},
},
async function handler(request, reply) {
if (request.validationError) {
reply.status(400).send(request.validationError);
return;
}
let upload_status = "FAILED";
const start = Date.now();
let contentType;
let buffer;
try {
// Check if request is multipart
if (request.isMultipart()) {
// Get file from request and convert to buffer
const data = await request.file();
buffer = await data.toBuffer();
contentType = data.mimetype;
} else {
buffer = Buffer.from(JSON.stringify(request.body));
contentType = "application/json";
}
const results = await uploader.upload(buffer, contentType);
reply.send(results);
upload_status = "COMPLETED";
} catch (err) {
fastify.log.error(err);
// Send upload error as response if any error occurs
reply.send(new UploadError());
} finally {
FileUploadTime.record(Date.now() - start, { upload_status });
}
}
);
fastify.post("/fund/:bytes", {}, async function handler(request, reply) {
const { bytes } = request.params;
try {
const price = await uploader.fund(bytes);
reply.send({ price });
} catch (err) {
fastify.log.error(err);
// Send upload error as response if any error occurs
reply.send(new FundError());
}
});
// Start the server
try {
await fastify.ready();
fastify.swagger();
await fastify.listen({ port: fastify.config.PORT, host: "0.0.0.0" });
} catch (err) {
// Log error and exit process if server fails to start
fastify.log.error(err);
process.exit(1);
}