From ae6d191b914ccefcf941a15a8831b8d119a48598 Mon Sep 17 00:00:00 2001
From: Chris Wanstrath <2+defunkt@users.noreply.github.com>
Date: Mon, 28 Jul 2025 11:21:48 -0700
Subject: [PATCH] project urls
---
packages/cubby/src/components/Project.tsx | 13 +++++++--
packages/cubby/src/server.tsx | 34 +++++++++++++++++++++--
2 files changed, 42 insertions(+), 5 deletions(-)
diff --git a/packages/cubby/src/components/Project.tsx b/packages/cubby/src/components/Project.tsx
index dc93d2b..a983c67 100644
--- a/packages/cubby/src/components/Project.tsx
+++ b/packages/cubby/src/components/Project.tsx
@@ -1,12 +1,14 @@
import { marked } from "marked"
+import { type ProjectMetadata } from "../server"
interface ProjectProps {
- name: string
+ project: ProjectMetadata
readme: string
files: string[]
}
-export function Project({ name, readme, files }: ProjectProps) {
+export function Project({ project, readme, files }: ProjectProps) {
+ const name = project.name
return (
<>
+
Links
+
{files.length > 0 && (
<>
Files
diff --git a/packages/cubby/src/server.tsx b/packages/cubby/src/server.tsx
index f930239..378940b 100644
--- a/packages/cubby/src/server.tsx
+++ b/packages/cubby/src/server.tsx
@@ -9,10 +9,19 @@ import { Layout } from "./components/Layout"
import { Project } from "./components/Project"
import { Projects } from "./components/Projects"
-const PROJECTS_DIR = ".."
-const WORKSHOP_REPO = "https://github.com/probablycorey/the-workshop"
+export const REPO_URL = "https://github.com/probablycorey/the-workshop"
+export const PROJECTS_DIR = ".."
+const WORKSHOP_APP_DOMAIN = "theworkshop.cc"
const CUBBY_DIR = "./cubby"
+export type ProjectMetadata = {
+ name: string
+ mtime: Date
+ status: "recent" | "active" | "inactive",
+ github: string,
+ url: string | null,
+}
+
// ----------------------------------------------------------------------------
// Setup
// ----------------------------------------------------------------------------
@@ -51,6 +60,9 @@ app.get("/", async (c) => {
app.get("/p/:name", async (c) => {
const name = c.req.param("name").replace("..", "")
+ const project = await projectsWithMetadata().then(projects => projects.find(p => p.name === name))
+ if (!project) return c.json({ error: 'Project not found' }, 404)
+
const readme = await Bun.file(`${PROJECTS_DIR}/${name}/README.md`).text()
const cubbyPath = join(CUBBY_DIR, `project_${name}`)
let files: string[] = []
@@ -63,7 +75,7 @@ app.get("/p/:name", async (c) => {
}
}
- return c.html(tsx(
))
+ return c.html(tsx(
))
})
app.get('/p/:id/:filename', async c => {
@@ -133,6 +145,22 @@ async function projects(): Promise
{
return subdirs
}
+async function projectsWithMetadata(): Promise {
+ const projects = await projectsWithDates()
+ return Promise.all(projects.map(async project => ({
+ name: project.name,
+ mtime: project.mtime,
+ status: project.status,
+ github: `${REPO_URL}/tree/main/packages/${project.name}`,
+ url: await isWorkshopApp(project.name) ? `https://${project.name}.${WORKSHOP_APP_DOMAIN}` : null,
+ })))
+}
+
+async function isWorkshopApp(projectName: string): Promise {
+ const json = await Bun.file(`${PROJECTS_DIR}/${projectName}/package.json`).json()
+ return json.scripts && json.scripts["subdomain:start"] !== undefined
+}
+
async function projectsWithDates(): Promise<{ name: string, mtime: Date, status: "recent" | "active" | "inactive" }[]> {
const names = await projects()
const projectsWithDates = await Promise.all(names.map(async name => {