{"sha":"698bdaa1fa6ab7821cb743dc1dec67aa07c3665f","node_id":"C_kwDORD3Y59oAKDY5OGJkYWExZmE2YWI3ODIxY2I3NDNkYzFkZWM2N2FhMDdjMzY2NWY","commit":{"author":{"name":"gizzy","email":"me@gizzy.pro","date":"2026-02-02T18:52:25Z"},"committer":{"name":"gizzy","email":"me@gizzy.pro","date":"2026-02-02T18:52:25Z"},"message":"feat: ratelimit handler + fix for it not moving stuff for new devlogs i forgor about that case","tree":{"sha":"dba76a247f57a33b9b8b00ae71e187d83694b741","url":"https://api.github.com/repos/GizzyUwU/govgiz/git/trees/dba76a247f57a33b9b8b00ae71e187d83694b741"},"url":"https://api.github.com/repos/GizzyUwU/govgiz/git/commits/698bdaa1fa6ab7821cb743dc1dec67aa07c3665f","comment_count":0,"verification":{"verified":false,"reason":"unsigned","signature":null,"payload":null,"verified_at":null}},"url":"https://api.github.com/repos/GizzyUwU/govgiz/commits/698bdaa1fa6ab7821cb743dc1dec67aa07c3665f","html_url":"https://github.com/GizzyUwU/govgiz/commit/698bdaa1fa6ab7821cb743dc1dec67aa07c3665f","comments_url":"https://api.github.com/repos/GizzyUwU/govgiz/commits/698bdaa1fa6ab7821cb743dc1dec67aa07c3665f/comments","author":{"login":"GizzyUwU","id":61115942,"node_id":"MDQ6VXNlcjYxMTE1OTQy","avatar_url":"https://avatars.githubusercontent.com/u/61115942?v=4","gravatar_id":"","url":"https://api.github.com/users/GizzyUwU","html_url":"https://github.com/GizzyUwU","followers_url":"https://api.github.com/users/GizzyUwU/followers","following_url":"https://api.github.com/users/GizzyUwU/following{/other_user}","gists_url":"https://api.github.com/users/GizzyUwU/gists{/gist_id}","starred_url":"https://api.github.com/users/GizzyUwU/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GizzyUwU/subscriptions","organizations_url":"https://api.github.com/users/GizzyUwU/orgs","repos_url":"https://api.github.com/users/GizzyUwU/repos","events_url":"https://api.github.com/users/GizzyUwU/events{/privacy}","received_events_url":"https://api.github.com/users/GizzyUwU/received_events","type":"User","user_view_type":"public","site_admin":false},"committer":{"login":"GizzyUwU","id":61115942,"node_id":"MDQ6VXNlcjYxMTE1OTQy","avatar_url":"https://avatars.githubusercontent.com/u/61115942?v=4","gravatar_id":"","url":"https://api.github.com/users/GizzyUwU","html_url":"https://github.com/GizzyUwU","followers_url":"https://api.github.com/users/GizzyUwU/followers","following_url":"https://api.github.com/users/GizzyUwU/following{/other_user}","gists_url":"https://api.github.com/users/GizzyUwU/gists{/gist_id}","starred_url":"https://api.github.com/users/GizzyUwU/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GizzyUwU/subscriptions","organizations_url":"https://api.github.com/users/GizzyUwU/orgs","repos_url":"https://api.github.com/users/GizzyUwU/repos","events_url":"https://api.github.com/users/GizzyUwU/events{/privacy}","received_events_url":"https://api.github.com/users/GizzyUwU/received_events","type":"User","user_view_type":"public","site_admin":false},"parents":[{"sha":"07b3487f54bc06b62bcceb717473469060c4a358","url":"https://api.github.com/repos/GizzyUwU/govgiz/commits/07b3487f54bc06b62bcceb717473469060c4a358","html_url":"https://github.com/GizzyUwU/govgiz/commit/07b3487f54bc06b62bcceb717473469060c4a358"}],"stats":{"total":184,"additions":150,"deletions":34},"files":[{"sha":"cc025bf9645621f8a4f870c0fe656e03ae0ed211","filename":"src/automation/ftToMDX.ts","status":"modified","additions":39,"deletions":34,"changes":73,"blob_url":"https://github.com/GizzyUwU/govgiz/blob/698bdaa1fa6ab7821cb743dc1dec67aa07c3665f/src%2Fautomation%2FftToMDX.ts","raw_url":"https://github.com/GizzyUwU/govgiz/raw/698bdaa1fa6ab7821cb743dc1dec67aa07c3665f/src%2Fautomation%2FftToMDX.ts","contents_url":"https://api.github.com/repos/GizzyUwU/govgiz/contents/src%2Fautomation%2FftToMDX.ts?ref=698bdaa1fa6ab7821cb743dc1dec67aa07c3665f","patch":"@@ -19,6 +19,23 @@ if (!fs.existsSync(POSTS_JSON)) {\n   fs.writeFileSync(POSTS_JSON, \"[]\", \"utf-8\");\n }\n \n+const withRateLimit = async <T>(\n+  fn: () => Promise<T | null>,\n+  label?: string,\n+): Promise<T | null> => {\n+  while (true) {\n+    const res = await fn();\n+\n+    if (ft.lastCode === 429) {\n+      console.log(`Rate limit hit${label ? ` (${label})` : \"\"}, waiting…`);\n+      await sleep(10 * 1000);\n+      continue;\n+    }\n+\n+    return res;\n+  }\n+};\n+\n const posts: Post[] = JSON.parse(fs.readFileSync(POSTS_JSON, \"utf-8\"));\n \n const slugify = (text: string) =>\n@@ -29,25 +46,20 @@ const slugify = (text: string) =>\n \n const ft = new FT(process.env.FT_API_KEY);\n \n-const me = await ft.user({ id: \"me\" });\n+const me = await withRateLimit(() => ft.user({ id: \"me\" }), \"user:me\");\n if (!me?.project_ids?.length) process.exit(0);\n \n const projects: Project[] = [];\n \n const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n \n for (const id of me.project_ids) {\n-  while (true) {\n-    const res = await ft.project({ id });\n-\n-    if (ft.lastCode === 429) {\n-      await sleep(10 * 1000);\n-      continue;\n-    }\n+  const project = await withRateLimit(\n+    () => ft.project({ id }),\n+    `project:${id}`,\n+  );\n \n-    if (res) projects.push(res);\n-    break;\n-  }\n+  if (project) projects.push(project);\n }\n \n for (const project of projects) {\n@@ -71,15 +83,14 @@ for (const project of projects) {\n \n   const devlogs: Devlog[] = [];\n   for (const id of project.devlog_ids) {\n-    const devlog = await ft.devlog({ projectId: project.id, devlogId: id });\n+    const devlog = await withRateLimit(\n+      () => ft.devlog({ projectId: project.id, devlogId: id }),\n+      `devlog:${project.id}/${id}`,\n+    );\n+\n     if (devlog) devlogs.push(devlog);\n   }\n \n-  devlogs.sort(\n-    (a, b) =>\n-      new Date(b.created_at).getTime() - new Date(a.created_at).getTime(),\n-  );\n-\n   const filePath = path.join(POSTS_DIR, `${slug}.md`);\n   let existingContent = \"\";\n   if (fs.existsSync(filePath)) {\n@@ -90,23 +101,19 @@ for (const project of projects) {\n   let body = existingContent;\n   if (fmMatch) body = existingContent.slice(fmMatch[0].length);\n \n-  const existingDevlogIds = Array.from(body.matchAll(/^## Devlog (\\d+)/gm)).map(\n-    (m) => m[1],\n+  devlogs.sort(\n+    (a, b) =>\n+      new Date(b.created_at).getTime() - new Date(a.created_at).getTime(),\n   );\n \n-  const newDevlogs = devlogs\n-    .filter((d) => !existingDevlogIds.includes(d.id.toString()))\n-    .map(\n-      (d) =>\n-        `## Devlog ${d.id} • ${new Date(d.created_at).toISOString().slice(0, 10)}\\n\\n` +\n-        `${d.body}\\n\\n${d.likes_count} likes • ${Math.round(d.duration_seconds / 60)} min\\n\\n`,\n-    );\n-\n-  if (newDevlogs.length > 0) console.log(\"Woah new devlogs for\", project.id);\n-\n   const preDevlogContent = body.split(/^\\s*## Devlog/m)[0];\n \n-  const existingDevlogContent = body.slice(preDevlogContent.length);\n+  const rebuiltDevlogs = devlogs.map(\n+    (d) =>\n+      `## Devlog ${d.id} • ${new Date(d.created_at).toISOString().slice(0, 10)}\\n\\n` +\n+      `${d.body}\\n\\n` +\n+      `${d.likes_count} likes • ${Math.round(d.duration_seconds / 60)} min\\n\\n`,\n+  );\n \n   const frontmatterContent =\n     `---\\n` +\n@@ -117,10 +124,8 @@ for (const project of projects) {\n     `---\\n`;\n \n   const finalContent =\n-    frontmatterContent +\n-    preDevlogContent +\n-    existingDevlogContent +\n-    newDevlogs.join(\"\");\n+    frontmatterContent + preDevlogContent + rebuiltDevlogs.join(\"\");\n+\n   fs.writeFileSync(filePath, finalContent, \"utf8\");\n }\n "},{"sha":"0289f66aee7aa4d7a4c5c6417807092058540b69","filename":"src/routes/blog/posts/govgiz.md","status":"modified","additions":29,"deletions":0,"changes":29,"blob_url":"https://github.com/GizzyUwU/govgiz/blob/698bdaa1fa6ab7821cb743dc1dec67aa07c3665f/src%2Froutes%2Fblog%2Fposts%2Fgovgiz.md","raw_url":"https://github.com/GizzyUwU/govgiz/raw/698bdaa1fa6ab7821cb743dc1dec67aa07c3665f/src%2Froutes%2Fblog%2Fposts%2Fgovgiz.md","contents_url":"https://api.github.com/repos/GizzyUwU/govgiz/contents/src%2Froutes%2Fblog%2Fposts%2Fgovgiz.md?ref=698bdaa1fa6ab7821cb743dc1dec67aa07c3665f","patch":"@@ -98,3 +98,32 @@ Wow an hour just spent on this? ![bleh](https://images.weserv.nl/?url=https%3A%2\n \n 0 likes • 86 min\n \n+## Devlog 18019 • 2026-01-31\n+\n+BLEHHHHHH 2 HOURS ALREADY DAMN BLEHHH\r\n+ANYWAY ![3c](https://images.weserv.nl/?url=https%3A%2F%2Femoji.slack-edge.com%2FT09V59WQY1E%2F3c%2F8c317faf11962206.png&w=30&h=30&fit=contain&n=-1) ![3cnuke](https://images.weserv.nl/?url=https%3A%2F%2Femoji.slack-edge.com%2FT09V59WQY1E%2F3cnuke%2Fe35ee9bec8dfa875.png&w=30&h=30&fit=contain&n=-1)\r\n+UPDATEEEEEEEEEEEEEE AAAAAAAAAAAAAAAAAA\r\n+- I did sum code theft ![3c](https://images.weserv.nl/?url=https%3A%2F%2Femoji.slack-edge.com%2FT09V59WQY1E%2F3c%2F8c317faf11962206.png&w=30&h=30&fit=contain&n=-1) someone made a blog in solidjs with mdx before me so i stole sum code from it [there blog on it is here](https://andi.dev/blog/how-solid-start-blog/) so i made use of that code for mine but modifying it so it fits uk gov design bleh\r\n+- I did other stuff but forgor\r\n+### Changelog\r\n+- [feat: basic ahh blog i ripped off from someones site but trying to make fit this design](https://api.github.com/repos/GizzyUwU/govgiz/commits/4b2613c90b8a156fd557a6569138a5416a1876cd)\r\n+\n+\n+0 likes • 131 min\n+\n+## Devlog 17146 • 2026-01-29\n+\n+Im eepy its 1 am but gotta devlog so yipeeeeeeeeeeeeeeeeee\r\n+- Uses hackatime now to get my hours coded today\r\n+- Shows my local time which also live updates using typedjs to update it\r\n+- Small desc about me uses typedjs to change values bleh ![yay](https://images.weserv.nl/?url=https%3A%2F%2Femoji.slack-edge.com%2FT09V59WQY1E%2Fyay%2F47296c029c8ee253.gif&w=30&h=30&fit=contain&n=-1) \n+\n+0 likes • 47 min\n+\n+## Devlog 17136 • 2026-01-29\n+\n+Blehhhh ![3c](https://images.weserv.nl/?url=https%3A%2F%2Femoji.slack-edge.com%2FT09V59WQY1E%2F3c%2F8c317faf11962206.png&w=30&h=30&fit=contain&n=-1) so new project first devlog i spent most of time fighitng to get it to work with solidjs but it works AND LOOKS SEXY i also wiped up custom logo in figma balls\r\n+\n+\n+0 likes • 87 min\n+"},{"sha":"be047232dc27a4dd0843a3a8ac2eca0c418ce71b","filename":"src/routes/blog/posts/logpheus.md","status":"modified","additions":82,"deletions":0,"changes":82,"blob_url":"https://github.com/GizzyUwU/govgiz/blob/698bdaa1fa6ab7821cb743dc1dec67aa07c3665f/src%2Froutes%2Fblog%2Fposts%2Flogpheus.md","raw_url":"https://github.com/GizzyUwU/govgiz/raw/698bdaa1fa6ab7821cb743dc1dec67aa07c3665f/src%2Froutes%2Fblog%2Fposts%2Flogpheus.md","contents_url":"https://api.github.com/repos/GizzyUwU/govgiz/contents/src%2Froutes%2Fblog%2Fposts%2Flogpheus.md?ref=698bdaa1fa6ab7821cb743dc1dec67aa07c3665f","patch":"@@ -79,6 +79,88 @@ Mreowwwww! I'm literally just improving logpheus handling of issues to use bugsi\n \n 1 likes • 105 min\n \n+## Devlog 13586 • 2026-01-19\n+\n+Bleh not devlogged this in a while but i got time so might aswell\r\n+Fixed bugs!\r\n+- You can now add multiple projects to one api key as fixed bug preventing this\r\n+- you can no longer be a stinky and try add the same project twice under a api key\n+\n+0 likes • 46 min\n+\n+## Devlog 4960 • 2026-01-01\n+\n+Nvm i hate code! 16m of more bug fixing to get it to stop spamming qwp\n+\n+0 likes • 16 min\n+\n+## Devlog 4952 • 2026-01-01\n+\n+I'm logging this cuz free time! Logpheus felt like going on a spamming spree because of a bug in my code. Every minute a check ran and my dumbass made it update db to the value of the check so every 2 minutes it would change value back to one that causes shipped message to post causing spam hopefully this should be fixed tho\n+\n+0 likes • 29 min\n+\n+## Devlog 4943 • 2025-12-31\n+\n+WOOOOOOO just took down prod for 10 minutes because im dumb! I spent this time fixing my dumb mistakes, adding support to use full postgres in prod so I only need pglite in dev making my life easier in both cases and adding validation of api keys when added!\n+\n+0 likes • 124 min\n+\n+## Devlog 4899 • 2025-12-31\n+\n+This whole devlog time was spent on another migration because I found out about pglite (postgres in wasm) and drizzle supported it so I migrated over to that instead because drizzle makes my life way more convienent especially working on logpheus it just took so long to setup as it was my first time using drizzle and i had to also create migration from sqlite and json to pglite to ensure no data loss.\n+\n+0 likes • 290 min\n+\n+## Devlog 4394 • 2025-12-30\n+\n+# Migration!\r\n+Using json was really annoying especially for every project using their own cache json file for devlogs so instead i swapped to SQL which is using bun:sqlite for storing api keys, projects etc taking advantage of buns implementation which is pretty fast but also its less resource usage slightly because I'm not using json for everything I need parsing it stringifying it pushing it to file a lot. All previous json files will be migrated on start to the new db.\n+\n+0 likes • 113 min\n+\n+## Devlog 4167 • 2025-12-29\n+\n+OKAY REAL DEVLOG THIS TIME, markdown support is now fully added i think yes\n+\n+0 likes • 28 min\n+\n+## Devlog 4157 • 2025-12-29\n+\n+Trying to implement support for markdown in logpheus\r\n+<u>meow</u>\r\n+\r\n+i like <u>potatos</u>\n+\n+0 likes • 34 min\n+\n+## Devlog 4147 • 2025-12-29\n+\n+# Haiiiiiiiiiiii\r\n+> I NEED MARKDOWN TO TEST LOGPHEUS THIS ISNT REALLY A DEVLOG JUST TESTING SOMETHING DEVLOG SOON THO\r\n+\r\n+~~REA~~\r\n+*DK*\r\n+**YOUR BALD**\r\n+- QUACK\r\n+- A\r\n+\r\n+1. A\r\n+2. a\r\n+- [x] quacked at people today\r\n+- [ ] meowed at people today\r\n+`sniffs you`\r\n+```\r\n+arf arf\r\n+```\r\n+[quacker](https://example.com)\r\n+\r\n+\r\n+---\r\n+sadgagagdag\n+\n+0 likes • 19 min\n+\n ## Devlog 4012 • 2025-12-29\n \n UH MORE HOURS FOR ME YIPEEEEEE so basically i in this time explode prod a couple times because of the new dockerfile but fixed it added improved error handling so that it doesnt spam my logs when something like a 401 happens i added ability to change api key in a channel using /logpheus-config, i converted timestamps to utc and now use the good format not the us format for dates so DD/MM/YYYY and it is now clickable timestamp so users can click it and convert it to their timezone if they want to and i moved the ext header which is useless to axios.create instead of spammed in every func call"}]}