mirror of
https://git.haroon.hackclub.app/haroon/Battler-Generator.git
synced 2024-11-09 23:49:38 +00:00
644 lines
18 KiB
TypeScript
644 lines
18 KiB
TypeScript
import { createCanvas, loadImage, GlobalFonts } from "@napi-rs/canvas";
|
|
import {
|
|
GenerateBattlerOptions,
|
|
GenerateBattleImageOptions,
|
|
PlayerActionOptions,
|
|
Opponents,
|
|
CustomActionOptions,
|
|
CommunityOpponents
|
|
} from "./types";
|
|
import {
|
|
generateBattler, drawText, applyText,
|
|
validOpponents, validPlayerActions
|
|
} from "./utils";
|
|
|
|
import { createServer } from "node:http";
|
|
|
|
import GenerateParty from "./party"
|
|
|
|
GlobalFonts
|
|
.registerFromPath(import.meta.dirname + "/LilitaOne.ttf", "Lilita One");
|
|
|
|
const opponents = {
|
|
[Opponents.BattleBeginner]: {
|
|
text: "Battle Beginner",
|
|
colour: ["#007500", "#FFFFFF"]
|
|
},
|
|
[Opponents.BattleBeginner2024]: {
|
|
text: "Battle Beginner",
|
|
colour: ["#007500", "#FFFFFF"]
|
|
},
|
|
[Opponents.BattleCasual]: {
|
|
text: "Battle Casual",
|
|
colour: ["#757500", "#FFFFFF"]
|
|
},
|
|
[Opponents.BattleMaster]: {
|
|
text: "Battle Master",
|
|
colour: ["#750000", "#FFFFFF"]
|
|
},
|
|
[Opponents.BattlePro]: {
|
|
text: "Battle Pro",
|
|
colour: ["#753000", "#FFFFFF"]
|
|
},
|
|
[Opponents.Battler]: {
|
|
text: "Battler",
|
|
colour: ["#FFFFFF", "#000000"]
|
|
},
|
|
[Opponents.BattlerElite]: {
|
|
text: "Battler Elite",
|
|
colour: ["#FFFFFF", "#FF0000"]
|
|
},
|
|
[Opponents.None]: {
|
|
text: "",
|
|
colour: ["#FFFFFF", "#FFFFFF"]
|
|
},
|
|
[Opponents.RapStar]: {
|
|
text: "Rap Star",
|
|
colour: ["#FF0000", "#FFFFFF"]
|
|
},
|
|
[Opponents.ArtMaster]: {
|
|
text: "Art Master",
|
|
colour: ["#000000", "#FFFFFF"]
|
|
},
|
|
[Opponents.TheInterrogator]: {
|
|
text: "The Interrogator",
|
|
colour: ["#FFFFFF", "#000000"]
|
|
},
|
|
[Opponents.CommonOverseer]: {
|
|
text: "Common Overseer",
|
|
colour: ["#000000", "#32CD32"]
|
|
},
|
|
[Opponents.Moonlite]: {
|
|
text: "Moonlite",
|
|
colour: ["#000000", "#FFFFFF"]
|
|
},
|
|
[Opponents.Dicey]: {
|
|
text: "Dicey",
|
|
colour: ["#000000", "#FFFFFF"]
|
|
},
|
|
[Opponents.Kacey]: {
|
|
text: "Kacey",
|
|
colour: ["#000000", "#FFFFFF"]
|
|
},
|
|
[Opponents.Lexa]: {
|
|
text: "Lexa",
|
|
colour: ["#FFFFFF", "#FF00FF"]
|
|
},
|
|
[Opponents.Delta]: {
|
|
text: "Delta",
|
|
colour: ["#000000", "#FFFFFF"]
|
|
},
|
|
[Opponents.DoodlePro]: {
|
|
text: "Doodle Pro",
|
|
colour: ["#000000", "#FFFFFF"]
|
|
},
|
|
[Opponents.GuitarHero]: {
|
|
text: "Guitar Hero",
|
|
colour: ["#FFFFFF", "#FF8000"]
|
|
},
|
|
[Opponents.SmallKeyMaster]: {
|
|
text: "Small KeyMaster",
|
|
colour: ["#FFFFFF", "#32CD32"]
|
|
},
|
|
[Opponents.RareOverseer]: {
|
|
text: "Rare Overseer",
|
|
colour: ["#000000", "#00FFFF"]
|
|
},
|
|
[Opponents.DEMUL]: {
|
|
text: "DEMUL",
|
|
colour: ["#FFFFFF", "#000000"]
|
|
},
|
|
[Opponents.AgentZ]: {
|
|
text: "Agent Z",
|
|
colour: ["#FFFFFF", "#000000"]
|
|
},
|
|
[Opponents.MediumKeyMaster]: {
|
|
text: "Medium KeyMaster",
|
|
colour: ["#000000", "#00FFFF"]
|
|
},
|
|
[Opponents.IncomingSword]: {
|
|
text: "Incoming Sword",
|
|
colour: ["#FF0000", "#FFFFFF"]
|
|
},
|
|
[Opponents.Eda]: {
|
|
text: "Eda",
|
|
colour: ["#9000FF", "#FFFFFF"]
|
|
},
|
|
[Opponents.TheFirewall]: {
|
|
text: "The Firewall",
|
|
colour: ["#FFFFFF", "#FFAA00"]
|
|
},
|
|
[Opponents.HouseannorSupport]: {
|
|
text: "Houseannor Support",
|
|
colour: ["#FF00FF", "#FFFFFF"]
|
|
},
|
|
[Opponents.Dicey2024]: {
|
|
text: "Dicey",
|
|
colour: ["#000000", "#FFFFFF"]
|
|
},
|
|
[Opponents.CakeDay]: {
|
|
text: "Cake Day",
|
|
colour: ["#FA61FF", "#FFFFFF"]
|
|
},
|
|
[Opponents.MasterOG]: {
|
|
text: "Master OG",
|
|
colour: ["#FF0000", "#FFFFFF"]
|
|
}
|
|
}
|
|
|
|
const communityopponents = {
|
|
[CommunityOpponents.Stik]: {
|
|
text: "Stik",
|
|
colour: ["#FF0000", "#000000"]
|
|
}
|
|
}
|
|
|
|
const server = createServer(async (req, res) => {
|
|
if (req.method === "OPTIONS") {
|
|
res.setHeader("access-control-allow-origin", "*")
|
|
return res.end();
|
|
}
|
|
|
|
const url = new URL(req.url!, "https://loc.al");
|
|
|
|
if (req.method == "GET" && url.pathname == "/status") {
|
|
return res.end("OK")
|
|
}
|
|
|
|
if (req.method == "GET" && url.pathname == "/battler.png") {
|
|
console.log(`Generating battler`)
|
|
const opts = Object.fromEntries(
|
|
Array.from(url.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as GenerateBattlerOptions;
|
|
|
|
const battler = await generateBattler(opts);
|
|
|
|
res.setHeader('content-type', 'image/png');
|
|
|
|
return res.end(battler.toBuffer("image/png"), 'binary')
|
|
}
|
|
|
|
if (req.method == "GET" && url.pathname == "/profilebattler.png") {
|
|
const opts = Object.fromEntries(
|
|
Array.from(url.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as GenerateBattlerOptions & { username: string };
|
|
|
|
console.log(`Generating profile battler for ${opts.username}`)
|
|
|
|
const canvas = createCanvas(1280, 1280);
|
|
const ctx = canvas.getContext("2d");
|
|
|
|
const battler = await generateBattler(opts)
|
|
|
|
ctx.drawImage(battler, 0, 0);
|
|
|
|
ctx.textAlign = "center"
|
|
|
|
ctx.font = applyText(canvas, opts.username, canvas.width - 20)
|
|
|
|
drawText([640, 1100], opts.username, opts.username == "homeannor" ? "#AA00FF" : "#000000", { colour: "#FFFFFF", width: 20 }, ctx)
|
|
|
|
res.setHeader('content-type', 'image/png');
|
|
|
|
return res.end(canvas.toBuffer("image/png"), 'binary')
|
|
}
|
|
if (
|
|
req.method == "GET" &&
|
|
(url.pathname == "/battlesquadfight.png" ||
|
|
url.pathname == "/battlestart.png")
|
|
) {
|
|
const opts = Object.fromEntries(
|
|
Array.from(url.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as GenerateBattlerOptions & GenerateBattleImageOptions;
|
|
|
|
if (!opts.opponent) {
|
|
res.writeHead(400, { 'content-type': "application/json" })
|
|
return res.end(
|
|
JSON.stringify({
|
|
error: '"opponent" query parameter not specified',
|
|
})
|
|
);
|
|
}
|
|
|
|
console.log(`Generating battle start for ${opts.username} and ${opts.opponent}`)
|
|
|
|
// Image dimensions
|
|
const canvas = createCanvas(1920, 1080);
|
|
const context = canvas.getContext("2d");
|
|
|
|
if (!validOpponents.includes(opts.opponent)) opts.opponent = Opponents.None
|
|
|
|
const SquadBackground = await loadImage(
|
|
`./assets/battlebgs/${opts.opponent}.png`,
|
|
);
|
|
|
|
context.drawImage(SquadBackground, 0, 0, canvas.width, canvas.height);
|
|
|
|
const battler = await generateBattler(opts);
|
|
|
|
context.drawImage(battler, -140, -100, battler.width, battler.height);
|
|
|
|
context.textAlign = "center"
|
|
|
|
context.font = applyText(canvas, opponents[opts.opponent].text, 875)
|
|
|
|
drawText([1450, 1000], opponents[opts.opponent].text, opponents[opts.opponent].colour[1], { colour: opponents[opts.opponent].colour[0], width: 20 }, context);
|
|
|
|
context.font = applyText(canvas, opts.username, 675)
|
|
|
|
drawText([470, 1000], opts.username, opts.username == "homeannor" ? "#AA00FF" : "#000000", { colour: "#FFFFFF", width: 20 }, context)
|
|
|
|
res.setHeader('content-type', 'image/png');
|
|
|
|
return res.end(canvas.toBuffer("image/png"), 'binary')
|
|
}
|
|
|
|
if (
|
|
req.method == "GET" &&
|
|
url.pathname == "/communityvs.png"
|
|
) {
|
|
const opts = Object.fromEntries(
|
|
Array.from(url.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as GenerateBattlerOptions & GenerateBattleImageOptions;
|
|
|
|
if (!opts.opponent) {
|
|
res.writeHead(400, { 'content-type': "application/json" })
|
|
return res.end(
|
|
JSON.stringify({
|
|
error: '"opponent" query parameter not specified',
|
|
})
|
|
);
|
|
}
|
|
|
|
console.log(`Generating battle start for ${opts.username} and ${opts.opponent}`)
|
|
|
|
// Image dimensions
|
|
const canvas = createCanvas(1920, 1080);
|
|
const context = canvas.getContext("2d");
|
|
|
|
if (!validOpponents.includes(opts.opponent)) opts.opponent = CommunityOpponents.None
|
|
|
|
const SquadBackground = await loadImage(
|
|
`./assets/communityvs/${opts.opponent}.png`,
|
|
);
|
|
|
|
context.drawImage(SquadBackground, 0, 0, canvas.width, canvas.height);
|
|
|
|
const battler = await generateBattler(opts);
|
|
|
|
context.drawImage(battler, -140, -100, battler.width, battler.height);
|
|
|
|
context.textAlign = "center"
|
|
|
|
context.font = applyText(canvas, opponents[opts.opponent].text, 875)
|
|
|
|
drawText([1450, 1000], opponents[opts.opponent].text, opponents[opts.opponent].colour[1], { colour: opponents[opts.opponent].colour[0], width: 20 }, context);
|
|
|
|
context.font = applyText(canvas, opts.username, 675)
|
|
|
|
drawText([470, 1000], opts.username, opts.username == "homeannor" ? "#AA00FF" : "#000000", { colour: "#FFFFFF", width: 20 }, context)
|
|
|
|
res.setHeader('content-type', 'image/png');
|
|
|
|
return res.end(canvas.toBuffer("image/png"), 'binary')
|
|
}
|
|
|
|
if (
|
|
req.method == "GET" &&
|
|
url.pathname == "/playeraction.png"
|
|
) {
|
|
const opts = Object.fromEntries(
|
|
Array.from(url.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as PlayerActionOptions;
|
|
|
|
if (!opts.action) {
|
|
res.writeHead(400, { 'content-type': "application/json" })
|
|
return res.end(
|
|
JSON.stringify({
|
|
error: '"action" query parameter not specified',
|
|
})
|
|
);
|
|
}
|
|
|
|
if (!validPlayerActions.includes(opts.action)) {
|
|
res.writeHead(400, { 'content-type': "application/json" })
|
|
return res.end(
|
|
JSON.stringify({
|
|
error:
|
|
'"action" query parameter not formatted correctly. Got "' +
|
|
opts.action +
|
|
'".',
|
|
})
|
|
);
|
|
}
|
|
|
|
console.log(`Generating ${opts.action} for ${opts.username}`)
|
|
|
|
// Image dimensions
|
|
const canvas = createCanvas(1920, 1080);
|
|
const context = canvas.getContext("2d");
|
|
|
|
const actionBackground = await loadImage(
|
|
`./assets/actionbgs/${opts.action}.png`,
|
|
);
|
|
|
|
context.drawImage(actionBackground, 0, 0, canvas.width, canvas.height);
|
|
|
|
const pUrl = new URL(encodeURI(opts.player))
|
|
|
|
const player = Object.fromEntries(
|
|
Array.from(pUrl.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as GenerateBattlerOptions
|
|
|
|
if (opts.action.endsWith("Left")) {
|
|
const battler = await generateBattler(player);
|
|
|
|
context.drawImage(battler, -50, -100, battler.width, battler.height);
|
|
} else if (opts.action.endsWith("Right")) {
|
|
const battler = await generateBattler({ ...player, direction: "left" });
|
|
|
|
context.drawImage(battler, (canvas.width / 2) - 300, -100, battler.width, battler.height);
|
|
}
|
|
|
|
res.setHeader('content-type', 'image/png');
|
|
|
|
return res.end(canvas.toBuffer("image/png"), 'binary')
|
|
}
|
|
|
|
if (
|
|
req.method == "GET" &&
|
|
url.pathname == "/customaction.png"
|
|
) {
|
|
const opts = Object.fromEntries(
|
|
Array.from(url.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as CustomActionOptions;
|
|
|
|
if (!opts.background) {
|
|
res.writeHead(400, { 'content-type': "application/json" })
|
|
return res.end(
|
|
JSON.stringify({
|
|
error:
|
|
'"background" query parameter not found',
|
|
})
|
|
);
|
|
}
|
|
|
|
console.log(`Generating custom action (${opts.background})`)
|
|
|
|
// Image dimensions
|
|
const canvas = createCanvas(1920, 1080);
|
|
const context = canvas.getContext("2d");
|
|
|
|
const actionBackground = await loadImage(
|
|
opts.background,
|
|
);
|
|
|
|
context.drawImage(actionBackground, 0, 0, canvas.width, canvas.height);
|
|
|
|
const pUrl = new URL(encodeURI(opts.player))
|
|
|
|
const player = Object.fromEntries(
|
|
Array.from(pUrl.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as GenerateBattlerOptions
|
|
|
|
const battler = await generateBattler(player);
|
|
|
|
if (opts.position == "middle") {
|
|
context.drawImage(battler, (1920 / 2) - 640, -100, battler.width, battler.height);
|
|
} else {
|
|
context.drawImage(battler, -50, -100, battler.width, battler.height);
|
|
}
|
|
|
|
res.setHeader('content-type', 'image/png');
|
|
|
|
return res.end(canvas.toBuffer("image/png"), 'binary')
|
|
}
|
|
|
|
if (
|
|
req.method == "GET" &&
|
|
url.pathname == "/playermap.png"
|
|
) {
|
|
const opts = Object.fromEntries(
|
|
Array.from(url.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as { player1: string, player2: string, map: string };
|
|
|
|
if (!opts.player1 || !opts.player2 || !opts.map) {
|
|
res.writeHead(400, { 'content-type': "application/json" })
|
|
return res.end(
|
|
JSON.stringify({
|
|
error:
|
|
'Did not pass all parameters',
|
|
})
|
|
);
|
|
}
|
|
|
|
console.log(`Generating player map (${opts.map})`)
|
|
|
|
const p1URL = new URL(encodeURI(opts.player1))
|
|
const p2URL = new URL(encodeURI(opts.player2))
|
|
|
|
const p1Opts = Object.fromEntries(
|
|
Array.from(p1URL.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as GenerateBattlerOptions
|
|
const p2Opts = Object.fromEntries(
|
|
Array.from(p2URL.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as GenerateBattlerOptions
|
|
|
|
const p1 = await generateBattler(p1Opts);
|
|
const p2 = await generateBattler({ ...p2Opts, direction: "left" })
|
|
|
|
// Image dimensions
|
|
const canvas = createCanvas(1920, 1080);
|
|
const context = canvas.getContext("2d");
|
|
|
|
const Background = await loadImage(
|
|
`./assets/playerlocations/${opts.map}.png`,
|
|
);
|
|
|
|
context.drawImage(Background, 0, 0, canvas.width, canvas.height);
|
|
|
|
context.drawImage(p1, 140, 313, p1.width / 2, p1.height / 2);
|
|
context.drawImage(p2, (canvas.width / 2) + 177, 313, p2.width / 2, p2.height / 2);
|
|
|
|
res.setHeader('content-type', 'image/png');
|
|
|
|
return res.end(canvas.toBuffer("image/png"), 'binary')
|
|
}
|
|
|
|
if (
|
|
req.method == "GET" &&
|
|
url.pathname == "/map.png"
|
|
) {
|
|
const opts = Object.fromEntries(
|
|
Array.from(url.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as { player: string, map: string };
|
|
|
|
if (!opts.player || !opts.map) {
|
|
res.writeHead(400, { 'content-type': "application/json" })
|
|
return res.end(
|
|
JSON.stringify({
|
|
error:
|
|
'Did not pass all parameters',
|
|
})
|
|
);
|
|
}
|
|
|
|
console.log(`Generating map (${opts.map})`)
|
|
|
|
const p1URL = new URL(encodeURI(opts.player))
|
|
|
|
const p1Opts = Object.fromEntries(
|
|
Array.from(p1URL.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as GenerateBattlerOptions
|
|
|
|
const p1 = await generateBattler(p1Opts);
|
|
|
|
// Image dimensions
|
|
const canvas = createCanvas(1920, 1080);
|
|
const context = canvas.getContext("2d");
|
|
|
|
const Background = await loadImage(
|
|
`./assets/locations/${opts.map}.png`,
|
|
);
|
|
|
|
context.drawImage(Background, 0, 0, canvas.width, canvas.height);
|
|
|
|
context.drawImage(p1, 140, 313, p1.width / 2, p1.height / 2);
|
|
|
|
res.setHeader('content-type', 'image/png');
|
|
|
|
return res.end(canvas.toBuffer("image/png"), 'binary')
|
|
}
|
|
|
|
if (
|
|
req.method == "GET" &&
|
|
url.pathname == "/communitymap.png"
|
|
) {
|
|
const opts = Object.fromEntries(
|
|
Array.from(url.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as { player: string, map: string };
|
|
|
|
if (!opts.player || !opts.map) {
|
|
res.writeHead(400, { 'content-type': "application/json" })
|
|
return res.end(
|
|
JSON.stringify({
|
|
error:
|
|
'Did not pass all parameters',
|
|
})
|
|
);
|
|
}
|
|
|
|
console.log(`Generating map (${opts.map})`)
|
|
|
|
const p1URL = new URL(encodeURI(opts.player))
|
|
|
|
const p1Opts = Object.fromEntries(
|
|
Array.from(p1URL.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as GenerateBattlerOptions
|
|
|
|
const p1 = await generateBattler(p1Opts);
|
|
|
|
// Image dimensions
|
|
const canvas = createCanvas(1920, 1080);
|
|
const context = canvas.getContext("2d");
|
|
|
|
const Background = await loadImage(
|
|
`./assets/communitymaps/${opts.map}.png`,
|
|
);
|
|
|
|
context.drawImage(Background, 0, 0, canvas.width, canvas.height);
|
|
|
|
context.drawImage(p1, 140, 313, p1.width / 2, p1.height / 2);
|
|
|
|
res.setHeader('content-type', 'image/png');
|
|
|
|
return res.end(canvas.toBuffer("image/png"), 'binary')
|
|
}
|
|
|
|
if (
|
|
req.method == "GET" &&
|
|
url.pathname == "/pvp.png"
|
|
) {
|
|
const opts = Object.fromEntries(
|
|
Array.from(url.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as { player1: string, player2: string, username1: string, username2: string };
|
|
|
|
if (!opts.player1 || !opts.player2 || !opts.username1 || !opts.username2) {
|
|
res.writeHead(400, { 'content-type': "application/json" })
|
|
return res.end(
|
|
JSON.stringify({
|
|
error:
|
|
'Did not pass all parameters',
|
|
})
|
|
);
|
|
}
|
|
|
|
console.log(`Generating PvP image for ${opts.username1} and ${opts.username2}`)
|
|
|
|
const p1URL = new URL(encodeURI(opts.player1))
|
|
const p2URL = new URL(encodeURI(opts.player2))
|
|
|
|
const p1Opts = Object.fromEntries(
|
|
Array.from(p1URL.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as GenerateBattlerOptions
|
|
const p2Opts = Object.fromEntries(
|
|
Array.from(p2URL.searchParams.entries())
|
|
.map(([key, value]) => [key, value.replaceAll('+', ' ')]),
|
|
) as GenerateBattlerOptions
|
|
|
|
const p1 = await generateBattler(p1Opts);
|
|
const p2 = await generateBattler({ ...p2Opts, direction: "left" })
|
|
|
|
// Image dimensions
|
|
const canvas = createCanvas(1920, 1080);
|
|
const context = canvas.getContext("2d");
|
|
|
|
const Background = await loadImage(
|
|
`./assets/battlebgs/PvP.png`,
|
|
);
|
|
|
|
context.drawImage(Background, 0, 0, canvas.width, canvas.height);
|
|
|
|
context.drawImage(p1, -140, -100, p1.width, p1.height);
|
|
context.drawImage(p2, (canvas.width / 2) - 180, -100, p2.width, p2.height);
|
|
|
|
context.textAlign = "center"
|
|
|
|
context.font = applyText(canvas, opts.username2, 675)
|
|
|
|
drawText([1450, 1000], opts.username2, opts.username2 == "homeannor" ? "#AA00FF" : "#000000", { colour: "#FFFFFF", width: 20 }, context)
|
|
|
|
context.font = applyText(canvas, opts.username1, 675)
|
|
|
|
drawText([470, 1000], opts.username1, opts.username1 == "homeannor" ? "#AA00FF" : "#000000", { colour: "#FFFFFF", width: 20 }, context)
|
|
|
|
res.setHeader('content-type', 'image/png');
|
|
|
|
return res.end(canvas.toBuffer("image/png"), 'binary')
|
|
}
|
|
|
|
console.log(`Failed request (${req.method}, ${url.pathname})`)
|
|
res.writeHead(404)
|
|
return res.end("404 Not Found")
|
|
})
|
|
|
|
server.listen(60125, () => {
|
|
console.log(`Listening on localhost:60125`)
|
|
})
|