This commit is contained in:
2026-03-05 15:28:55 +02:00
parent aad888ff2c
commit 1df8a3f452
13 changed files with 3059 additions and 10822 deletions

2830
bun.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,9 @@ const { openInPopup } = useUserSession();
<UCard class="max-w-sm grid gap-6 justify-center items-center"> <UCard class="max-w-sm grid gap-6 justify-center items-center">
<div class="flex flex-col gap-10 items-center justify-center"> <div class="flex flex-col gap-10 items-center justify-center">
<div class="flex gap-2 items-center"> <div class="flex gap-2 items-center">
<div class="font-bold text-xl tracking-tight font-fira-code"> <div
class="font-bold text-xl tracking-tight font-fira-code"
>
Ghostwriter Ghostwriter
</div> </div>
<NuxtImg src="/ghostwriter-logo.png" class="size-9" /> <NuxtImg src="/ghostwriter-logo.png" class="size-9" />
@@ -23,8 +25,8 @@ const { openInPopup } = useUserSession();
Sign in to your account. Sign in to your account.
</div> </div>
<div class="text-center"> <div class="text-center">
Connect with Strava to automatically add personalized titles and Connect with Strava to automatically add personalized
descriptions to your activities. titles and descriptions to your activities.
</div> </div>
</div> </div>
<div <div

View File

@@ -14,11 +14,6 @@ export default defineNuxtConfig({
hookdeckKey: "", hookdeckKey: "",
openaiApiKey: "", openaiApiKey: "",
databaseUrl: "", databaseUrl: "",
public: {
posthogPublicKey: "",
posthogHost: "",
posthogDefaults: "2025-05-24",
},
}, },
future: { compatibilityVersion: 4 }, future: { compatibilityVersion: 4 },
compatibilityDate: "2025-03-01", compatibilityDate: "2025-03-01",

View File

@@ -22,8 +22,6 @@
"nuxt": "^4.0.3", "nuxt": "^4.0.3",
"nuxt-auth-utils": "0.5.23", "nuxt-auth-utils": "0.5.23",
"openai": "^5.12.2", "openai": "^5.12.2",
"posthog-js": "^1.259.0",
"posthog-node": "^5.6.0",
"radash": "^12.1.1", "radash": "^12.1.1",
"ts-pattern": "^5.8.0", "ts-pattern": "^5.8.0",
"url": "^0.11.4", "url": "^0.11.4",

View File

@@ -54,14 +54,14 @@ const saveOp = watchPausable(
<div class="font-bold text-lg">Welcome to Ghostwriter!</div> <div class="font-bold text-lg">Welcome to Ghostwriter!</div>
<div> <div>
Let's generate fun and engaging titles and descriptions for your Strava Let's generate fun and engaging titles and descriptions for your
activities automatically, right when they are created. Customize your Strava activities automatically, right when they are created.
preferences below. Customize your preferences below.
</div> </div>
<div> <div>
Add a touch of creativity to your Strava workouts. Simply enable it and Add a touch of creativity to your Strava workouts. Simply enable it
choose your language, and we'll do the rest! and choose your language, and we'll do the rest!
</div> </div>
</UContainer> </UContainer>
@@ -69,12 +69,15 @@ const saveOp = watchPausable(
<div class="font-bold text-lg"> Support</div> <div class="font-bold text-lg"> Support</div>
<UCard class=""> <UCard class="">
<div class="flex flex-col gap-8"> <div class="flex flex-col gap-8">
Ghostwriter 👻 is free to use, but it takes time and resources to keep Ghostwriter 👻 is free to use, but it takes time and resources
it running smoothly. If you enjoy it, consider supporting the app and to keep it running smoothly. If you enjoy it, consider
its creator - every bit helps! supporting the app and its creator - every bit helps!
</div> </div>
<template #footer> <template #footer>
<ULink href="https://buymeacoffee.com/mariosant" target="_blank"> <ULink
href="https://buymeacoffee.com/mariosant"
target="_blank"
>
<NuxtImg <NuxtImg
src="images/bmac-orange-button.png" src="images/bmac-orange-button.png"
height="32px" height="32px"
@@ -190,11 +193,15 @@ const saveOp = watchPausable(
<CardField> <CardField>
<template #title> Athlete ID </template> <template #title> Athlete ID </template>
<template #description> <template #description>
Your Athlete ID. Click it to view your profile on Strava.</template Your Athlete ID. Click it to view your profile on
Strava.</template
> >
<template #value> <template #value>
<ULink :href="stravaLink" class="underline flex items-center gap-2"> <ULink
:href="stravaLink"
class="underline flex items-center gap-2"
>
{{ user.id }} {{ user.id }}
<UIcon <UIcon
name="heroicons:arrow-top-right-on-square" name="heroicons:arrow-top-right-on-square"

View File

@@ -1,20 +0,0 @@
import posthog from "posthog-js";
export default defineNuxtPlugin(() => {
const runtimeConfig = useRuntimeConfig();
const posthogClient = posthog.init(runtimeConfig.public.posthogPublicKey, {
api_host: runtimeConfig.public.posthogHost,
//@ts-expect-error typing is more explicit than what it should
defaults: runtimeConfig.public.posthogDefaults,
loaded: (posthog) => {
if (import.meta.env.MODE === "development") posthog.debug();
},
});
return {
provide: {
posthog: () => posthogClient,
},
};
});

10491
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +0,0 @@
onlyBuiltDependencies:
- '@parcel/watcher'
- '@tailwindcss/oxide'
- core-js
- esbuild
- sharp
- vue-demi

View File

@@ -20,22 +20,9 @@ export default defineEventHandler(async (event) => {
}, },
}); });
posthog.identifyImmediate({
distinctId: String(user!.id),
properties: {
name: user!.name,
country: user!.country,
},
});
await db await db
.delete(tables.users) .delete(tables.users)
.where(eq(tables.users.id, get(body, "object_id"))); .where(eq(tables.users.id, get(body, "object_id")));
posthog.captureImmediate({
distinctId: get(body, "object_id"),
event: "user deleted",
});
sendNoContent(event); sendNoContent(event);
}); });

View File

@@ -1,29 +0,0 @@
import { PostHog } from "posthog-node";
import { waitUntil } from "@vercel/functions";
export default defineNitroPlugin((nitroApp) => {
const runtimeConfig = useRuntimeConfig();
const posthog = new PostHog(runtimeConfig.public.posthogPublicKey, {
host: runtimeConfig.public.posthogHost,
flushAt: 1,
flushInterval: 0,
});
nitroApp.hooks.hook("request", (event) => {
event.context.posthog = posthog;
});
nitroApp.hooks.hook("beforeResponse", () => {
waitUntil(posthog.shutdown());
});
nitroApp.hooks.hook("close", () => {
waitUntil(posthog.shutdown());
});
});
declare module "h3" {
interface H3EventContext {
posthog: PostHog;
}
}

View File

@@ -24,8 +24,6 @@ export default defineOAuthStravaEventHandler({
}); });
} }
const posthog = event.context.posthog;
const userPayload = { const userPayload = {
id: auth.user.id, id: auth.user.id,
name: `${auth.user.firstname} ${auth.user.lastname}`, name: `${auth.user.firstname} ${auth.user.lastname}`,
@@ -82,19 +80,6 @@ export default defineOAuthStravaEventHandler({
user: userPayload, user: userPayload,
}); });
posthog.identifyImmediate({
distinctId: String(user!.id),
properties: {
name: user!.name,
country: user!.country,
},
});
posthog.captureImmediate({
distinctId: String(user!.id),
event: "user logged in",
});
sendRedirect(event, "/"); sendRedirect(event, "/");
}, },
}); });

View File

@@ -1,20 +0,0 @@
import { PostHog } from "posthog-node";
let client: PostHog;
export const usePosthog = () => {
const runtimeConfig = useRuntimeConfig();
client =
client ??
new PostHog(runtimeConfig.public.posthogPublicKey, {
host: runtimeConfig.public.posthogHost,
defaults: runtimeConfig.public.posthogDefaults,
});
if (process.dev) {
client.debug();
}
return client;
};