--- /dev/null
+.env
+.DS_Store
--- /dev/null
+/** DEPENDENCIES */
+import "jsr:@std/dotenv/load";
+import app from "./router.ts";
+
+/** CONSTANTS */
+const port = parseInt(Deno.env.get("PORT") ?? "8000");
+const publicDir = "./public";
+
+/** UTILS */
+function getContentType(filename: string): string {
+ const ext = filename.split(".").pop();
+ switch (ext) {
+ case "html":
+ return "text/html";
+ case "css":
+ return "text/css";
+ case "js":
+ return "text/javascript";
+ case "json":
+ return "application/json";
+ case "png":
+ return "image/png";
+ case "jpeg":
+ return "image/jpeg";
+ case "gif":
+ return "image/gif";
+ case "svg":
+ return "image/svg+xml";
+ case "ico":
+ return "image/x-icon";
+ default:
+ return "application/octet-stream";
+ }
+}
+async function serveStaticFile(filename: string): Promise<Response> {
+ try {
+ const filePath = `${publicDir}/${filename}`;
+ const file = await Deno.readFile(filePath);
+ const contentType = getContentType(filename);
+ return new Response(file, {
+ headers: { "Content-Type": contentType },
+ });
+ } catch (_err) {
+ return new Response("Not found", { status: 404 });
+ }
+}
+
+/** LOGIC */
+for await (const file of Deno.readDir(publicDir)) {
+ app.get(`/${file.name}`, async () => {
+ return await serveStaticFile(file.name);
+ });
+}
+app.get("/", async () => {
+ return await serveStaticFile("index.html");
+});
+
+/** SERVICE */
+Deno.serve({ port }, (req, info) => app.handler(req, info));
--- /dev/null
+* {
+ margin: 0;
+ padding: 0;
+ font-family: Helvetica, sans-serif;
+ transition: all ease-in-out 0.2s;
+}
+body {
+ background-color: whitesmoke;
+ width: 50%;
+ margin: 20px auto;
+ display: flex;
+ flex-direction: column;
+ gap: 30px;
+}
+body h1, h2 {
+ margin-bottom: 5px;
+}
+body header {
+ display: flex;
+ align-items: center;
+ gap: 20px;
+}
+body header img {
+ width: 30%;
+ height: auto;
+ object-fit: contain;
+}
+body nav {
+ text-align: center;
+ padding: 15px 0;
+ border-top: 1px solid black;
+ border-bottom: 1px solid black;
+}
+body blockquote {
+ margin: 10px 5px 30px;
+ padding-left: 10px;
+ border-left: 2px solid black;
+}
+
+/* Medium */
+@media (max-width: 768px) {
+ body {
+ width: 75%;
+ }
+}
+
+/* Small */
+@media (max-width: 480px) {
+ body {
+ width: 95%;
+ }
+ body header {
+ flex-direction: column;
+ }
+ body header img {
+ width: 50%;
+ }
+}
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+ <link rel="stylesheet" href="/index.css" />
+ <title>Roberto Morado</title>
+ </head>
+ <body>
+ <header>
+ <img src="/christ.png" alt="Jesus Christ Representation" />
+ <section>
+ <h1>JESUS CHRIST IS LORD</h1>
+ <p>The Alpha and the Omega.</p>
+ <p>The Lamb who was slain.</p>
+ <p>The King who reigns forever.</p>
+ <blockquote>
+ “For from Him and through Him and to Him are all things. To
+ Him be the glory forever. Amen.” —Romans 11:36
+ </blockquote>
+ </section>
+ </header>
+ <!--nav><a href="/">Home</a></nav-->
+ <main>
+ <h2>THE TESTIMONY OF GOD</h2>
+ <p>God is holy.</p>
+ <p>Man is sinful.</p>
+ <p>The soul that sins will die.</p>
+ <p>But God, being rich in mercy, sent His Son.</p>
+ <p>
+ Jesus Christ, the Righteous One, bore our sins in His body on
+ the cross.
+ </p>
+ <p>He was pierced for our transgressions.</p>
+ <p>
+ He died, was buried, and rose again on the third day, just as
+ the Scriptures said.
+ </p>
+ <p>
+ He is seated at the right hand of the Father—reigning until all
+ His enemies are under His feet.
+ </p>
+ <blockquote>
+ “This is the witness: that God gave us eternal life, and this
+ life is in His Son.” —1 John 5:11
+ </blockquote>
+
+ <h2>THE CALL OF GOD</h2>
+
+ <p>Repent, for the kingdom of heaven is at hand.</p>
+ <p>Believe in the Lord Jesus Christ and you will be saved.</p>
+ <p>
+ For there is no other name under heaven given among men by which
+ we must be saved.
+ </p>
+
+ <p>Everyone who calls on the name of the Lord will be saved.</p>
+ <p>
+ Everyone who does not obey the Son will not see life, but the
+ wrath of God remains on him.
+ </p>
+
+ <blockquote>
+ “I am the way, and the truth, and the life; no one comes to the
+ Father but through Me.” —John 14:6
+ </blockquote>
+
+ <h2>THE GLORY OF GOD</h2>
+
+ <p>
+ To Him who loves us and released us from our sins by His blood—
+ to Him be the glory, the dominion, and the praise forever.
+ </p>
+
+ <p>Let every knee bow.</p>
+ <p>Let every tongue confess.</p>
+ <p>Let every breath give praise.</p>
+
+ <p>Christ is all.</p>
+
+ <blockquote>
+ “Worthy is the Lamb who was slain—to receive power and riches
+ and wisdom and might and honor and glory and blessing.”
+ —Revelation 5:12
+ </blockquote>
+ </main>
+ <!--footer></footer-->
+ </body>
+</html>
--- /dev/null
+class Router {
+ private routes: Map<string, Map<string, Deno.ServeHandler>> = new Map();
+
+ private register(
+ method: string,
+ pathname: string,
+ handler: Deno.ServeHandler,
+ ): void {
+ if (!this.routes.has(method)) {
+ this.routes.set(method, new Map());
+ }
+ this.routes.get(method)!.set(pathname, handler);
+ }
+ private match(
+ method: string,
+ url: URL,
+ ): { handler: Deno.ServeHandler | null } {
+ const handlers = this.routes.get(method) || new Map();
+ for (const [pathname, handler] of handlers) {
+ const uri = new URLPattern({ pathname });
+ const match = uri.exec(url);
+ if (match) {
+ return { handler };
+ }
+ }
+
+ return { handler: null };
+ }
+ get(pathname: string, handler: Deno.ServeHandler) {
+ this.register("GET", pathname, handler);
+ }
+ post(pathname: string, handler: Deno.ServeHandler) {
+ this.register("POST", pathname, handler);
+ }
+ handler(
+ req: Request,
+ info: Deno.ServeHandlerInfo,
+ ): Response | Promise<Response> {
+ const { handler } = this.match(req.method, new URL(req.url));
+
+ if (!handler) {
+ return new Response("Not found", { status: 404 });
+ }
+
+ return handler(req, info);
+ }
+}
+
+export default new Router();