import { ValTown } from "npm:@valtown/sdk";
import { Hono } from "npm:hono";
import { logger } from "npm:hono/logger";
type WebdavOptions = {
token?: string;
};
export function serveVals(
req: Request,
options?: WebdavOptions,
): Response | Promise<Response> {
const app = new Hono();
const vt = new ValTown({
bearerToken: options?.token,
});
app.use(logger());
app.on("PROPFIND", "/", async () => {
try {
const me = await vt.me.profile.retrieve();
const vals: ValTown.BasicVal[] = [];
while (true) {
const { data, links } = await vt.users.vals.list(me.id, {
offset: vals.length,
limit: 100,
});
vals.push(...data);
if (!links.next) {
break;
}
}
const body = `<?xml version="1.0" encoding="utf-8"?>
<multistatus xmlns="DAV:">
${
vals.map((val) => `
<response>
<href>${xmlEscape(val.name + ".tsx")}</href>
<propstat>
<prop>
<displayname>${xmlEscape(val.name + ".tsx")}</displayname>
<resourcetype></resourcetype>
<getcontentlength>${val.code ? getByteLength(val.code) : 0}</getcontentlength>
<getcontenttype>text/plain</getcontenttype>
<getlastmodified>${val.createdAt}</getlastmodified>
</prop>
<status>HTTP/1.1 200 OK</status>
</propstat>
</response>
`).join("")
}
</multistatus>`;
return new Response(body, {
status: 207,
headers: {
"content-type": "application/xml",
},
});
} catch (error) {
return new Response(
`Internal Server Error: ${xmlEscape(error.message)}`,
{
status: 500,
headers: {
"content-type": "text/plain",
},
},
);
}
});
app.on("PROPFIND", "/:val", async (c) => {
const valName = c.req.param("val").slice(0, -4);
const me = await vt.me.profile.retrieve();
if (!me.username) {
return c.text("You must set a username to use this feature", 400);
}
try {
const val = await vt.alias.username.valName.retrieve(
me.username,
valName,
);
const body = `<?xml version="1.0" encoding="utf-8"?>
<multistatus xmlns="DAV:">
<response>
<href>${xmlEscape(val.name + ".tsx")}</href>
<propstat>
<prop>
<displayname>${xmlEscape(val.name + ".tsx")}</displayname>
<resourcetype></resourcetype>
<getcontentlength>${val.code ? getByteLength(val.code) : 0}</getcontentlength>
<getcontenttype>text/plain</getcontenttype>
<getlastmodified>${val.createdAt}</getlastmodified>