import { OAuthResolverError } from "@atproto/oauth-client-node";
import { serve } from "@hono/node-server";
import { Hono } from "hono";
import { generateSignedCookie, getSignedCookie } from "hono/cookie";
import { client } from "./atproto/client";
import { resolveMiniDoc } from "./utils";
import "dotenv/config";
import { jetstream } from "./ingester";
import { Agent } from "@atproto/api";
import { TID } from "@atproto/common";
import { AppFooodzReview } from "./lexicons";
import { is } from "@atcute/lexicons";
import { Layout } from "./components";
import { db } from "./drizzle/db";
import { users } from "./drizzle/schema";
import { eq } from "drizzle-orm";
const app = new Hono();
app.get("/client-metadata.json", (c) => {
return c.json(client.clientMetadata);
});
app.get("/oauth/callback", async (c) => {
const params = new URLSearchParams(c.req.url.split("?")[1]);
try {
const { session } = await client.callback(params);
// check if user is already added to db
const user = await db.query.users.findFirst({
where: eq(users.did, session.did),
});
if (!user) {
console.log("user not found in db, inserting");
await db.insert(users).values({
did: session.did,
});
}
const cookie = await generateSignedCookie(
"__foooodz_session",
session.did,
process.env.COOKIE_SECRET as string,
{
path: "/",
httpOnly: true,
sameSite: "lax",
maxAge: 60 * 60 * 24 * 7,
secure: process.env.NODE_ENV === "production",
},
);
return new Response(null, {
status: 302,
headers: {
"Set-Cookie": cookie,
Location: "/",
},
});
} catch (error) {
if (error instanceof Error) {
throw error;
}
}
});
app.post("/login", async (c) => {
try {
const formData = await c.req.formData();
const handle = formData.get("handle");
if (!handle || typeof handle !== "string") {
return c.json(
{
ok: false,
message: "Invalid handle",
},
400,
);
}
const { did } = await resolveMiniDoc(handle);
const ac = new AbortController();
const redirectUrl = await client.authorize(did, {
signal: ac.signal,
});
return c.redirect(redirectUrl.toString());
} catch (error) {
if (error instanceof OAuthResolverError) {
return c.json({ ok: false, message: error.message }, 500);
}
console.log(error);
return c.json({ ok: false, message: "something went wrong" }, 500);
}
});
app.get("/", async (c) => {
return c.html(
,
);
});
app.get("/review", async (c) => {
const reviews = await db.query.reviews.findMany({
limit: 10,
});
return c.html(
{reviews.map((review) => (
-
{review.place}
{review.review}
{review.rating}
))}
,
);
});
app.post("/review", async (c) => {
const cookie = await getSignedCookie(c, process.env.COOKIE_SECRET as string);
if (cookie.__foooodz_session) {
try {
const oauthSession = await client.restore(cookie.__foooodz_session);
const agent = new Agent(oauthSession);
const formData = await c.req.formData();
const place = formData.get("place") as string;
const review = formData.get("review") as string;
const rating = formData.get("rating") as string;
const rkey = TID.nextStr();
const record: AppFooodzReview.Main = {
$type: "app.fooodz.review",
place,
rating: +rating,
review,
createdAt: new Date().toISOString(),
};
if (!is(AppFooodzReview.mainSchema, record)) {
return c.json({ ok: false, message: "invalid review record" }, 400);
}
await agent.com.atproto.repo.putRecord({
repo: agent.assertDid,
rkey,
record,
collection: "app.fooodz.review",
validate: false,
});
return c.redirect("/review");
} catch (error) {
return c.json({ ok: false, message: "failed to write record" }, 500);
}
}
});
serve(
{
fetch: app.fetch,
port: 3000,
},
(info) => {
console.log(`Server is running @ http://localhost:${info.port}`);
jetstream.start();
},
);