improve: prepare for coolify and general self-hosting improvements (#175)

* fix(self-hosting): improve docker compose, add healthchecks, rename env SELF_HOSTED

* improve(db): improve initial migration when no data exists

* fix(db): misstakes were made

* improve(dashboard): better curl preview depending on project type

* fix(db): fix migrations

* fix(onboarding): ensure we publish event correctly

* wip

* fix: curl preview

* add coolify template

* fix(dashboard): page -> route

* fix

* fix env
This commit is contained in:
Carl-Gerhard Lindesvärd
2025-06-23 22:21:11 +02:00
committed by GitHub
parent 4a2dbc5c4d
commit 92d62c3e5c
22 changed files with 382 additions and 60 deletions

View File

@@ -20,6 +20,7 @@ ENV PATH="$PNPM_HOME:$PATH"
RUN apt-get update && apt-get install -y \
openssl \
libssl3 \
curl \
&& rm -rf /var/lib/apt/lists/*
RUN corepack enable
@@ -62,6 +63,7 @@ WORKDIR /app/apps/dashboard
# Will be replaced on runtime
ENV NEXT_PUBLIC_DASHBOARD_URL="__NEXT_PUBLIC_DASHBOARD_URL__"
ENV NEXT_PUBLIC_API_URL="__NEXT_PUBLIC_API_URL__"
ENV NEXT_PUBLIC_SELF_HOSTED="__NEXT_PUBLIC_SELF_HOSTED__"
RUN pnpm run build

View File

@@ -4,7 +4,7 @@ set -e
echo "> Replace env variable placeholders with runtime values..."
# Define environment variables to check (space-separated string)
variables_to_replace="NEXT_PUBLIC_DASHBOARD_URL NEXT_PUBLIC_API_URL"
variables_to_replace="NEXT_PUBLIC_DASHBOARD_URL NEXT_PUBLIC_API_URL NEXT_PUBLIC_SELF_HOSTED"
# Replace env variable placeholders with real values
for key in $variables_to_replace; do

View File

@@ -99,7 +99,7 @@ export default function LayoutMenu({
</div>
</ProjectLink>
)}
{process.env.SELF_HOSTED && (
{process.env.NEXT_PUBLIC_SELF_HOSTED === 'true' && (
<a
className="rounded p-2 row items-center gap-2 hover:bg-def-200"
href="https://openpanel.dev/supporter"
@@ -231,7 +231,7 @@ export default function LayoutMenu({
))}
</div>
</div>
{process.env.SELF_HOSTED && (
{process.env.NEXT_PUBLIC_SELF_HOSTED === 'true' && (
<div className="mt-auto w-full ">
<div className={cn('text-sm w-full text-center')}>
Self-hosted instance

View File

@@ -27,7 +27,7 @@ export default async function Page({
params: { organizationSlug: organizationId },
searchParams,
}: PageProps) {
const isBillingEnabled = !process.env.SELF_HOSTED;
const isBillingEnabled = process.env.NEXT_PUBLIC_SELF_HOSTED !== 'true';
const tab = parseAsStringEnum(['org', 'billing', 'members', 'invites'])
.withDefault('org')
.parseServerSide(searchParams.tab);

View File

@@ -104,22 +104,34 @@ function CurlPreview({ project }: { project: IServiceProjectWithClients }) {
return null;
}
const payload: Record<string, any> = {
type: 'track',
payload: {
name: 'screen_view',
properties: {
__title: `Testing OpenPanel - ${project.name}`,
__path: `${project.domain}`,
__referrer: `${process.env.NEXT_PUBLIC_DASHBOARD_URL}`,
},
},
};
if (project.types.includes('app')) {
payload.payload.properties.__path = '/';
delete payload.payload.properties.__referrer;
}
if (project.types.includes('backend')) {
payload.payload.name = 'test_event';
payload.payload.properties = {};
}
const code = `curl -X POST ${process.env.NEXT_PUBLIC_API_URL}/track \\
-H "Content-Type: application/json" \\
-H "openpanel-client-id: ${client.id}" \\
-H "openpanel-client-secret: ${secret}" \\
-H "User-Agent: ${window.navigator.userAgent}" \\
-d '{
"type": "track",
"payload": {
"name": "screen_view",
"properties": {
"__title": "Testing OpenPanel - ${project.name}",
"__path": "${project.domain}",
"__referrer": "${process.env.NEXT_PUBLIC_DASHBOARD_URL}"
}
}
}'`;
-H "User-Agent: ${typeof window !== 'undefined' ? window.navigator.userAgent : ''}" \\
-d '${JSON.stringify(payload)}'`;
return (
<div className="card">

View File

@@ -27,7 +27,7 @@ const Verify = async ({ params: { projectId } }: Props) => {
const [project, events] = await Promise.all([
await getProjectWithClients(projectId),
getEvents(
`SELECT * FROM ${TABLE_NAMES.events} WHERE project_id = ${escape(projectId)} LIMIT 100`,
`SELECT * FROM ${TABLE_NAMES.events} WHERE project_id = ${escape(projectId)} ORDER BY created_at DESC LIMIT 100`,
),
]);
@@ -35,7 +35,7 @@ const Verify = async ({ params: { projectId } }: Props) => {
return <div>Hmm, something fishy is going on. Please reload the page.</div>;
}
return <OnboardingVerify project={project} events={events} />;
return <OnboardingVerify project={project} events={events.reverse()} />;
};
export default Verify;

View File

@@ -1,7 +0,0 @@
export const runtime = 'edge';
export const dynamic = 'force-dynamic'; // no caching
export async function GET(request: Request) {
const headers = Object.fromEntries(request.headers.entries());
return Response.json({ headers, region: process.env.VERCEL_REGION });
}

View File

@@ -0,0 +1,5 @@
export const dynamic = 'force-dynamic'; // no caching
export async function GET(request: Request) {
return Response.json({ status: 'ok' });
}