committed by
GitHub
parent
f0b7526847
commit
df05e2dab3
@@ -1,86 +1,95 @@
|
||||
# Dockerfile that builds the web app only
|
||||
ARG NODE_VERSION=20.15.1
|
||||
FROM --platform=linux/amd64 node:${NODE_VERSION}-slim AS base
|
||||
|
||||
FROM node:${NODE_VERSION}-slim AS base
|
||||
|
||||
RUN corepack enable && apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
openssl \
|
||||
libssl3 \
|
||||
&& apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ARG DATABASE_URL
|
||||
ENV DATABASE_URL=$DATABASE_URL
|
||||
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
|
||||
RUN corepack enable
|
||||
|
||||
RUN apt update \
|
||||
&& apt install -y curl python3 make g++ \
|
||||
&& curl -L https://raw.githubusercontent.com/tj/n/master/bin/n -o n \
|
||||
&& bash n $NODE_VERSION \
|
||||
&& rm n \
|
||||
&& npm install -g n \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json package.json
|
||||
COPY pnpm-lock.yaml pnpm-lock.yaml
|
||||
COPY pnpm-workspace.yaml pnpm-workspace.yaml
|
||||
COPY apps/api/package.json apps/api/package.json
|
||||
COPY packages/db/package.json packages/db/package.json
|
||||
COPY packages/redis/package.json packages/redis/package.json
|
||||
COPY packages/trpc/package.json packages/trpc/package.json
|
||||
COPY packages/queue/package.json packages/queue/package.json
|
||||
COPY packages/common/package.json packages/common/package.json
|
||||
COPY packages/constants/package.json packages/constants/package.json
|
||||
COPY packages/validation/package.json packages/validation/package.json
|
||||
COPY packages/sdks/sdk/package.json packages/sdks/sdk/package.json
|
||||
# Workspace
|
||||
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
# Apps
|
||||
COPY apps/api/package.json ./apps/api/
|
||||
# Packages
|
||||
COPY packages/db/package.json packages/db/
|
||||
COPY packages/trpc/package.json packages/trpc/
|
||||
COPY packages/queue/package.json packages/queue/
|
||||
COPY packages/redis/package.json packages/redis/
|
||||
COPY packages/common/package.json packages/common/
|
||||
COPY packages/sdks/sdk/package.json packages/sdks/sdk/
|
||||
COPY packages/constants/package.json packages/constants/
|
||||
COPY packages/validation/package.json packages/validation/
|
||||
COPY packages/sdks/sdk/package.json packages/sdks/sdk/
|
||||
|
||||
# Patches
|
||||
COPY patches patches
|
||||
|
||||
# BUILD
|
||||
FROM base AS build
|
||||
|
||||
WORKDIR /app/apps/api
|
||||
RUN pnpm install --frozen-lockfile
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
python3 \
|
||||
make \
|
||||
g++
|
||||
|
||||
WORKDIR /app
|
||||
COPY apps/api apps/api
|
||||
COPY packages packages
|
||||
COPY tooling tooling
|
||||
RUN pnpm db:codegen
|
||||
WORKDIR /app
|
||||
RUN pnpm install --frozen-lockfile && \
|
||||
pnpm store prune
|
||||
|
||||
WORKDIR /app/apps/api
|
||||
RUN pnpm run build
|
||||
COPY apps/api ./apps/api
|
||||
COPY packages ./packages
|
||||
COPY tooling ./tooling
|
||||
|
||||
RUN pnpm db:codegen && \
|
||||
pnpm --filter api run build
|
||||
|
||||
# PROD
|
||||
FROM base AS prod
|
||||
|
||||
WORKDIR /app/apps/api
|
||||
RUN pnpm install --frozen-lockfile --prod
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
python3 \
|
||||
make \
|
||||
g++
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=build /app/package.json ./
|
||||
COPY --from=build /app/pnpm-lock.yaml ./
|
||||
RUN pnpm install --frozen-lockfile --prod && \
|
||||
pnpm store prune
|
||||
|
||||
# FINAL
|
||||
FROM base AS runner
|
||||
|
||||
COPY --from=build /app/package.json /app/package.json
|
||||
COPY --from=prod /app/node_modules /app/node_modules
|
||||
ENV NODE_ENV=production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=build /app/package.json ./
|
||||
COPY --from=prod /app/node_modules ./node_modules
|
||||
|
||||
# Apps
|
||||
COPY --from=build /app/apps/api /app/apps/api
|
||||
|
||||
# Apps node_modules
|
||||
COPY --from=prod /app/apps/api/node_modules /app/apps/api/node_modules
|
||||
COPY --from=build /app/apps/api ./apps/api
|
||||
|
||||
# Packages
|
||||
COPY --from=build /app/packages/db /app/packages/db
|
||||
COPY --from=build /app/packages/redis /app/packages/redis
|
||||
COPY --from=build /app/packages/trpc /app/packages/trpc
|
||||
COPY --from=build /app/packages/queue /app/packages/queue
|
||||
COPY --from=build /app/packages/common /app/packages/common
|
||||
|
||||
# Packages node_modules
|
||||
COPY --from=prod /app/packages/db/node_modules /app/packages/db/node_modules
|
||||
COPY --from=prod /app/packages/redis/node_modules /app/packages/redis/node_modules
|
||||
COPY --from=prod /app/packages/trpc/node_modules /app/packages/trpc/node_modules
|
||||
COPY --from=prod /app/packages/queue/node_modules /app/packages/queue/node_modules
|
||||
COPY --from=prod /app/packages/common/node_modules /app/packages/common/node_modules
|
||||
COPY --from=build /app/packages/db ./packages/db
|
||||
COPY --from=build /app/packages/trpc ./packages/trpc
|
||||
COPY --from=build /app/packages/queue ./packages/queue
|
||||
COPY --from=build /app/packages/redis ./packages/redis
|
||||
COPY --from=build /app/packages/common ./packages/common
|
||||
COPY --from=build /app/packages/sdks/sdk ./packages/sdks/sdk
|
||||
COPY --from=build /app/packages/constants ./packages/constants
|
||||
COPY --from=build /app/packages/validation ./packages/validation
|
||||
|
||||
RUN pnpm db:codegen
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import icoToPng from 'ico-to-png';
|
||||
import sharp from 'sharp';
|
||||
|
||||
import { createHash } from '@openpanel/common';
|
||||
import { ch, TABLE_NAMES } from '@openpanel/db';
|
||||
import { getRedisCache } from '@openpanel/redis';
|
||||
|
||||
interface GetFaviconParams {
|
||||
@@ -110,3 +111,37 @@ export async function clearFavicons(
|
||||
}
|
||||
return reply.status(404).send('OK');
|
||||
}
|
||||
|
||||
export async function ping(
|
||||
request: FastifyRequest<{
|
||||
Body: {
|
||||
domain: string;
|
||||
count: number;
|
||||
};
|
||||
}>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
await ch.insert({
|
||||
table: TABLE_NAMES.self_hosting,
|
||||
values: [
|
||||
{
|
||||
domain: request.body.domain,
|
||||
count: request.body.count,
|
||||
created_at: new Date(),
|
||||
},
|
||||
],
|
||||
format: 'JSONEachRow',
|
||||
});
|
||||
reply.status(200).send({
|
||||
message: `Success`,
|
||||
count: request.body.count,
|
||||
domain: request.body.domain,
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error(e, 'Failed to insert ping');
|
||||
reply.status(500).send({
|
||||
error: 'Failed to insert ping',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ export async function clerkWebhook(
|
||||
|
||||
if (payload.type === 'user.created') {
|
||||
const email = payload.data.email_addresses[0]?.email_address;
|
||||
const emails = payload.data.email_addresses.map((e) => e.email_address);
|
||||
|
||||
if (!email) {
|
||||
return Response.json(
|
||||
@@ -63,14 +64,16 @@ export async function clerkWebhook(
|
||||
|
||||
const memberships = await db.member.findMany({
|
||||
where: {
|
||||
email,
|
||||
email: {
|
||||
in: emails,
|
||||
},
|
||||
userId: null,
|
||||
},
|
||||
});
|
||||
|
||||
for (const membership of memberships) {
|
||||
const access = pathOr<string[]>([], ['meta', 'access'], membership);
|
||||
db.$transaction([
|
||||
await db.$transaction([
|
||||
// Update the member to link it to the user
|
||||
// This will remove the item from invitations
|
||||
db.member.update({
|
||||
@@ -123,7 +126,6 @@ export async function clerkWebhook(
|
||||
deletedAt: new Date(),
|
||||
firstName: null,
|
||||
lastName: null,
|
||||
email: `deleted+${payload.data.id}@openpanel.dev`,
|
||||
},
|
||||
}),
|
||||
db.projectAccess.deleteMany({
|
||||
|
||||
@@ -2,6 +2,12 @@ import * as controller from '@/controllers/misc.controller';
|
||||
import type { FastifyPluginCallback } from 'fastify';
|
||||
|
||||
const miscRouter: FastifyPluginCallback = (fastify, opts, done) => {
|
||||
fastify.route({
|
||||
method: 'POST',
|
||||
url: '/ping',
|
||||
handler: controller.ping,
|
||||
});
|
||||
|
||||
fastify.route({
|
||||
method: 'GET',
|
||||
url: '/favicon',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
ARG NODE_VERSION=20.15.1
|
||||
|
||||
FROM --platform=linux/amd64 node:${NODE_VERSION}-slim AS base
|
||||
FROM node:${NODE_VERSION}-slim AS base
|
||||
|
||||
ENV SKIP_ENV_VALIDATION="1"
|
||||
|
||||
@@ -11,17 +11,15 @@ ARG ENABLE_INSTRUMENTATION_HOOK
|
||||
ENV ENABLE_INSTRUMENTATION_HOOK=$ENABLE_INSTRUMENTATION_HOOK
|
||||
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
|
||||
RUN corepack enable
|
||||
# Install necessary dependencies for prisma
|
||||
RUN apt-get update && apt-get install -y \
|
||||
openssl \
|
||||
libssl3 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN apt update \
|
||||
&& apt install -y curl \
|
||||
&& curl -L https://raw.githubusercontent.com/tj/n/master/bin/n -o n \
|
||||
&& bash n $NODE_VERSION \
|
||||
&& rm n \
|
||||
&& npm install -g n
|
||||
RUN corepack enable
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
@@ -39,14 +37,14 @@ COPY packages/common/package.json packages/common/package.json
|
||||
COPY packages/constants/package.json packages/constants/package.json
|
||||
COPY packages/validation/package.json packages/validation/package.json
|
||||
COPY packages/sdks/sdk/package.json packages/sdks/sdk/package.json
|
||||
COPY patches patches
|
||||
|
||||
# BUILD
|
||||
FROM base AS build
|
||||
|
||||
WORKDIR /app/apps/dashboard
|
||||
WORKDIR /app
|
||||
RUN pnpm install --frozen-lockfile --ignore-scripts
|
||||
|
||||
WORKDIR /app
|
||||
COPY apps/dashboard apps/dashboard
|
||||
COPY packages packages
|
||||
COPY tooling tooling
|
||||
@@ -57,48 +55,44 @@ 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__"
|
||||
# Check entrypoint for this little fellow
|
||||
ENV NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_eW9sby5jb20k"
|
||||
# Does not need to be replaced
|
||||
ENV NEXT_PUBLIC_CLERK_SIGN_IN_URL="/login"
|
||||
ENV NEXT_PUBLIC_CLERK_SIGN_UP_URL="/register"
|
||||
ENV NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL="/"
|
||||
ENV NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL="/"
|
||||
|
||||
RUN pnpm run build
|
||||
|
||||
# PROD
|
||||
FROM base AS prod
|
||||
|
||||
WORKDIR /app/apps/dashboard
|
||||
RUN pnpm install --frozen-lockfile --prod --ignore-scripts
|
||||
|
||||
# FINAL
|
||||
# RUNNER
|
||||
FROM base AS runner
|
||||
|
||||
COPY --from=build /app/package.json /app/package.json
|
||||
COPY --from=prod /app/node_modules /app/node_modules
|
||||
# Apps
|
||||
COPY --from=build /app/apps/dashboard /app/apps/dashboard
|
||||
# Apps node_modules
|
||||
COPY --from=prod /app/apps/dashboard/node_modules /app/apps/dashboard/node_modules
|
||||
# Packages
|
||||
COPY --from=build /app/packages/db /app/packages/db
|
||||
COPY --from=build /app/packages/redis /app/packages/redis
|
||||
COPY --from=build /app/packages/common /app/packages/common
|
||||
COPY --from=build /app/packages/queue /app/packages/queue
|
||||
COPY --from=build /app/packages/constants /app/packages/constants
|
||||
COPY --from=build /app/packages/validation /app/packages/validation
|
||||
COPY --from=build /app/packages/sdks/sdk /app/packages/sdks/sdk
|
||||
# Packages node_modules
|
||||
COPY --from=prod /app/packages/db/node_modules /app/packages/db/node_modules
|
||||
COPY --from=prod /app/packages/redis/node_modules /app/packages/redis/node_modules
|
||||
COPY --from=prod /app/packages/common/node_modules /app/packages/common/node_modules
|
||||
COPY --from=prod /app/packages/validation/node_modules /app/packages/validation/node_modules
|
||||
COPY --from=prod /app/packages/queue/node_modules /app/packages/queue/node_modules
|
||||
WORKDIR /app
|
||||
|
||||
RUN pnpm db:codegen
|
||||
ENV NODE_ENV=production
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
WORKDIR /app/apps/dashboard
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
# Set the correct permission for prerender cache
|
||||
RUN mkdir .next
|
||||
RUN chown nextjs:nodejs .next
|
||||
|
||||
# Set the correct permissions for the entire /app directory
|
||||
COPY --from=build --chown=nextjs:nodejs /app/apps/dashboard/.next/standalone ./
|
||||
COPY --from=build --chown=nextjs:nodejs /app/apps/dashboard/.next/static ./apps/dashboard/.next/static
|
||||
COPY --from=build --chown=nextjs:nodejs /app/apps/dashboard/public ./apps/dashboard/public
|
||||
|
||||
# Copy and set permissions for the entrypoint script
|
||||
COPY --from=build --chown=nextjs:nodejs /app/apps/dashboard/entrypoint.sh ./entrypoint.sh
|
||||
RUN chmod +x ./entrypoint.sh
|
||||
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
# CMD ["pnpm", "start"]
|
||||
COPY --from=build /app/apps/dashboard/entrypoint.sh /usr/bin/
|
||||
RUN chmod +x /usr/bin/entrypoint.sh
|
||||
ENTRYPOINT ["entrypoint.sh", "pnpm", "start"]
|
||||
ENV PORT=3000
|
||||
ENV HOSTNAME=0.0.0.0
|
||||
|
||||
ENTRYPOINT [ "/app/entrypoint.sh", "node", "/app/apps/dashboard/server.js"]
|
||||
@@ -1,32 +1,39 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
echo "> Replace env variable placeholders with runtime values..."
|
||||
# Define an array of environment variables to check
|
||||
variables_to_replace=(
|
||||
"NEXT_PUBLIC_DASHBOARD_URL"
|
||||
"NEXT_PUBLIC_API_URL"
|
||||
"NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY"
|
||||
)
|
||||
|
||||
# Define environment variables to check (space-separated string)
|
||||
variables_to_replace="NEXT_PUBLIC_DASHBOARD_URL NEXT_PUBLIC_API_URL NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY"
|
||||
|
||||
# Replace env variable placeholders with real values
|
||||
for key in "${variables_to_replace[@]}"; do
|
||||
value=$(printenv $key)
|
||||
if [ ! -z "$value" ]; then
|
||||
echo " - Searching for $key with value $value..."
|
||||
# Use a custom placeholder for 'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY' or use the actual key otherwise
|
||||
if [ "$key" = "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY" ]; then
|
||||
placeholder="pk_test_eW9sby5jb20k"
|
||||
for key in $variables_to_replace; do
|
||||
value=$(eval echo \$"$key")
|
||||
if [ -n "$value" ]; then
|
||||
echo " - Searching for $key with value $value..."
|
||||
# Use a custom placeholder for 'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY' or use the actual key otherwise
|
||||
case "$key" in
|
||||
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY)
|
||||
placeholder="pk_test_eW9sby5jb20k"
|
||||
;;
|
||||
*)
|
||||
placeholder="__${key}__"
|
||||
;;
|
||||
esac
|
||||
# Run the replacement
|
||||
find /app -type f \( -name "*.js" -o -name "*.html" \) | while read -r file; do
|
||||
if grep -q "$placeholder" "$file"; then
|
||||
echo " - Replacing in file: $file"
|
||||
sed -i "s|$placeholder|$value|g" "$file"
|
||||
fi
|
||||
done
|
||||
else
|
||||
placeholder="__${key}__"
|
||||
echo " - Skipping $key as it has no value set."
|
||||
fi
|
||||
# Run the replacement
|
||||
find /app/apps/dashboard/.next/ -type f \( -name "*.js" -o -name "*.html" \) -exec sed -i "s|$placeholder|$value|g" {} \;
|
||||
|
||||
else
|
||||
echo " - Skipping $key as it has no value set."
|
||||
fi
|
||||
done
|
||||
|
||||
echo "> Done!"
|
||||
echo "> Running $@"
|
||||
|
||||
# Execute the container's main process (CMD in Dockerfile)
|
||||
exec "$@"
|
||||
@@ -9,6 +9,7 @@ await import('./src/env.mjs');
|
||||
|
||||
/** @type {import("next").NextConfig} */
|
||||
const config = {
|
||||
output: 'standalone',
|
||||
webpack: (config, { isServer }) => {
|
||||
if (isServer) {
|
||||
config.plugins = [...config.plugins, new PrismaPlugin()];
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"scripts": {
|
||||
"dev": "rm -rf .next && pnpm with-env next dev",
|
||||
"testing": "pnpm dev",
|
||||
"build": "next build",
|
||||
"build": "pnpm with-env next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint .",
|
||||
"format": "prettier --check \"**/*.{tsx,mjs,ts,md,json}\"",
|
||||
|
||||
@@ -16,9 +16,8 @@ const SkipOnboarding = () => {
|
||||
res.refetch();
|
||||
}, [pathname]);
|
||||
|
||||
console.log(res.data);
|
||||
|
||||
if (!pathname.startsWith('/onboarding')) return null;
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => {
|
||||
@@ -30,6 +29,7 @@ const SkipOnboarding = () => {
|
||||
text: 'Are you sure you want to skip onboarding? Since you do not have any projects, you will be logged out.',
|
||||
onConfirm() {
|
||||
auth.signOut();
|
||||
router.replace(process.env.NEXT_PUBLIC_CLERK_SIGN_IN_URL);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { MetadataRoute } from 'next';
|
||||
|
||||
export default function manifest(): MetadataRoute.Manifest {
|
||||
return {
|
||||
id: process.env.NEXT_PUBLIC_DASHBOARD_URL,
|
||||
name: 'Openpanel.dev',
|
||||
short_name: 'Openpanel.dev',
|
||||
description: '',
|
||||
@@ -9,12 +10,5 @@ export default function manifest(): MetadataRoute.Manifest {
|
||||
display: 'standalone',
|
||||
background_color: '#fff',
|
||||
theme_color: '#fff',
|
||||
icons: [
|
||||
{
|
||||
src: '/favicon.ico',
|
||||
sizes: 'any',
|
||||
type: 'image/x-icon',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ $openpanel->event(
|
||||
<strong>Usage</strong>
|
||||
<p>Create a custom event called "my_event".</p>
|
||||
<Syntax
|
||||
code={`curl 'https://api.openpanel.dev/track' \\
|
||||
code={`curl '${process.env.NEXT_PUBLIC_API_URL}/track' \\
|
||||
-H 'content-type: application/json' \\
|
||||
-H 'openpanel-client-id: ${clientId}' \\
|
||||
-H 'openpanel-client-secret: ${clientSecret}' \\
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 424 KiB |
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"index": {
|
||||
"title": "Introduction",
|
||||
"title": "Home",
|
||||
"type": "page"
|
||||
},
|
||||
"docs": {
|
||||
"title": "Documentation",
|
||||
"type": "page"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,6 @@
|
||||
{
|
||||
"index": "Get Started",
|
||||
"-- Frameworks": {
|
||||
"type": "separator",
|
||||
"title": "Frameworks"
|
||||
},
|
||||
"script": "Web (Script Tag)",
|
||||
"web": "Web (Module)",
|
||||
"nextjs": "Next.js",
|
||||
"react": "React",
|
||||
"react-native": "React-Native",
|
||||
"remix": "Remix",
|
||||
"vue": "Vue",
|
||||
"astro": "Astro",
|
||||
"node": "Node",
|
||||
"express": "Express (backend)",
|
||||
"-- Others": {
|
||||
"type": "separator",
|
||||
"title": "Others"
|
||||
},
|
||||
"javascript": "JavaScript",
|
||||
"api": "API",
|
||||
"export": "Export",
|
||||
"migration": "Migrations"
|
||||
"sdks": "SDKs",
|
||||
"migration": "Migrations",
|
||||
"self-hosting": "Self-hosting"
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
# Astro
|
||||
|
||||
You can use <Link href="/docs/script">script tag</Link> or <Link href="/docs/web">Web SDK</Link> to track events in Astro.
|
||||
@@ -68,7 +68,7 @@ Clears the current user identifier and ends the session.
|
||||
<BrandLogo src="https://upload.wikimedia.org/wikipedia/commons/thumb/6/61/HTML5_logo_and_wordmark.svg/240px-HTML5_logo_and_wordmark.svg.png" />
|
||||
}
|
||||
title="HTML / Script"
|
||||
href="/docs/script"
|
||||
href="/docs/sdks/script"
|
||||
>
|
||||
{' '}
|
||||
</Card>
|
||||
@@ -77,7 +77,7 @@ Clears the current user identifier and ends the session.
|
||||
<BrandLogo src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/2300px-React-icon.svg.png" />
|
||||
}
|
||||
title="React"
|
||||
href="/docs/react"
|
||||
href="/docs/sdks/react"
|
||||
>
|
||||
{' '}
|
||||
</Card>
|
||||
@@ -86,7 +86,7 @@ Clears the current user identifier and ends the session.
|
||||
<BrandLogo src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/2300px-React-icon.svg.png" />
|
||||
}
|
||||
title="React-Native"
|
||||
href="/docs/react-native"
|
||||
href="/docs/sdks/react-native"
|
||||
>
|
||||
{' '}
|
||||
</Card>
|
||||
@@ -94,11 +94,11 @@ Clears the current user identifier and ends the session.
|
||||
icon={
|
||||
<BrandLogo
|
||||
isDark
|
||||
src="https://static-00.iconduck.com/assets.00/nextjs-icon-512x512-y563b8iq.png"
|
||||
src="https://pbs.twimg.com/profile_images/1565710214019444737/if82cpbS_400x400.jpg"
|
||||
/>
|
||||
}
|
||||
title="Next.js"
|
||||
href="/docs/nextjs"
|
||||
href="/docs/sdks/nextjs"
|
||||
>
|
||||
{' '}
|
||||
</Card>
|
||||
@@ -110,7 +110,7 @@ Clears the current user identifier and ends the session.
|
||||
/>
|
||||
}
|
||||
title="Remix"
|
||||
href="/docs/remix"
|
||||
href="/docs/sdks/remix"
|
||||
>
|
||||
{' '}
|
||||
</Card>
|
||||
@@ -119,7 +119,7 @@ Clears the current user identifier and ends the session.
|
||||
<BrandLogo src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Vue.js_Logo_2.svg/1024px-Vue.js_Logo_2.svg.png" />
|
||||
}
|
||||
title="Vue"
|
||||
href="/docs/vue"
|
||||
href="/docs/sdks/vue"
|
||||
>
|
||||
{' '}
|
||||
</Card>
|
||||
@@ -131,7 +131,7 @@ Clears the current user identifier and ends the session.
|
||||
/>
|
||||
}
|
||||
title="Astro"
|
||||
href="/docs/astro"
|
||||
href="/docs/sdks/astro"
|
||||
>
|
||||
{' '}
|
||||
</Card>
|
||||
@@ -140,3 +140,28 @@ Clears the current user identifier and ends the session.
|
||||
## Unofficial SDKs
|
||||
|
||||
While not officially supported, the following community-contributed SDKs are available.
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
icon={
|
||||
<BrandLogo
|
||||
src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/Laravel.svg/1200px-Laravel.svg.png"
|
||||
/>
|
||||
}
|
||||
title="Laravel"
|
||||
href="https://github.com/tbleckert/openpanel-laravel"
|
||||
>
|
||||
{' '}
|
||||
</Card>
|
||||
<Card
|
||||
icon={
|
||||
<BrandLogo
|
||||
src="https://storage.googleapis.com/cms-storage-bucket/0dbfcc7a59cd1cf16282.png"
|
||||
/>
|
||||
}
|
||||
title="Flutter"
|
||||
href="https://github.com/stevenosse/openpanel_flutter"
|
||||
>
|
||||
{' '}
|
||||
</Card>
|
||||
</Cards>
|
||||
@@ -1,5 +0,0 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
# Node
|
||||
|
||||
Use <Link href="/docs/javascript">Javascript SDK</Link> to track events in Node.
|
||||
@@ -1,5 +0,0 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
# React
|
||||
|
||||
Use <Link href="/docs/script">script tag</Link> or <Link href="/docs/web">Web SDK</Link> for now. We'll add a dedicated react sdk soon.
|
||||
@@ -1,5 +0,0 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
# Remix
|
||||
|
||||
Use <Link href="/docs/script">script tag</Link> or <Link href="/docs/web">Web SDK</Link> for now. We'll add a dedicated remix sdk soon.
|
||||
19
apps/docs/src/pages/docs/sdks/_meta.json
Normal file
19
apps/docs/src/pages/docs/sdks/_meta.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"script": "Web (Script Tag)",
|
||||
"web": "Web (Module)",
|
||||
"nextjs": "Next.js",
|
||||
"react": "React",
|
||||
"react-native": "React-Native",
|
||||
"remix": "Remix",
|
||||
"vue": "Vue",
|
||||
"astro": "Astro",
|
||||
"node": "Node",
|
||||
"express": "Express (backend)",
|
||||
"-- Others": {
|
||||
"type": "separator",
|
||||
"title": "Others"
|
||||
},
|
||||
"javascript": "JavaScript",
|
||||
"api": "API",
|
||||
"export": "Export"
|
||||
}
|
||||
5
apps/docs/src/pages/docs/sdks/astro.mdx
Normal file
5
apps/docs/src/pages/docs/sdks/astro.mdx
Normal file
@@ -0,0 +1,5 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
# Astro
|
||||
|
||||
You can use <Link href="/docs/sdks/script">script tag</Link> or <Link href="/docs/sdks/web">Web SDK</Link> to track events in Astro.
|
||||
@@ -7,7 +7,7 @@ import CommonSdkConfig from 'src/components/common-sdk-config.mdx';
|
||||
|
||||
# Express
|
||||
|
||||
The Express middleware is a basic wrapper around [Javascript SDK](/docs/javascript). It provides a simple way to add the SDK to your Express application.
|
||||
The Express middleware is a basic wrapper around [Javascript SDK](/docs/sdks/javascript). It provides a simple way to add the SDK to your Express application.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -121,7 +121,7 @@ export const op = new Openpanel({
|
||||
op.track('my_event', { foo: 'bar' });
|
||||
```
|
||||
|
||||
Refer to the [Javascript SDK](/docs/javascript#usage) for usage instructions.
|
||||
Refer to the [Javascript SDK](/docs/sdks/javascript#usage) for usage instructions.
|
||||
|
||||
### Tracking Events
|
||||
|
||||
5
apps/docs/src/pages/docs/sdks/node.mdx
Normal file
5
apps/docs/src/pages/docs/sdks/node.mdx
Normal file
@@ -0,0 +1,5 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
# Node
|
||||
|
||||
Use <Link href="/docs/sdks/javascript">Javascript SDK</Link> to track events in Node.
|
||||
@@ -108,4 +108,4 @@ op.track('my_event', { foo: 'bar' });
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
|
||||
For more information on how to use the SDK, check out the [Javascript SDK](/docs/javascript#usage).
|
||||
For more information on how to use the SDK, check out the [Javascript SDK](/docs/sdks/javascript#usage).
|
||||
5
apps/docs/src/pages/docs/sdks/react.mdx
Normal file
5
apps/docs/src/pages/docs/sdks/react.mdx
Normal file
@@ -0,0 +1,5 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
# React
|
||||
|
||||
Use <Link href="/docs/sdks/script">script tag</Link> or <Link href="/docs/sdks/web">Web SDK</Link> for now. We'll add a dedicated react sdk soon.
|
||||
5
apps/docs/src/pages/docs/sdks/remix.mdx
Normal file
5
apps/docs/src/pages/docs/sdks/remix.mdx
Normal file
@@ -0,0 +1,5 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
# Remix
|
||||
|
||||
Use <Link href="/docs/sdks/script">script tag</Link> or <Link href="/docs/sdks/web">Web SDK</Link> for now. We'll add a dedicated remix sdk soon.
|
||||
5
apps/docs/src/pages/docs/sdks/vue.mdx
Normal file
5
apps/docs/src/pages/docs/sdks/vue.mdx
Normal file
@@ -0,0 +1,5 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
# Vue
|
||||
|
||||
Use <Link href="/docs/sdks/script">script tag</Link> or <Link href="/docs/sdks/web">Web SDK</Link> for now. We'll add a dedicated react sdk soon.
|
||||
@@ -45,4 +45,4 @@ op.track('my_event', { foo: 'bar' });
|
||||
|
||||
## Usage
|
||||
|
||||
Refer to the [Javascript SDK](/docs/javascript#usage) for usage instructions.
|
||||
Refer to the [Javascript SDK](/docs/sdks/javascript#usage) for usage instructions.
|
||||
3
apps/docs/src/pages/docs/self-hosting/_meta.json
Normal file
3
apps/docs/src/pages/docs/self-hosting/_meta.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"index": "Get started"
|
||||
}
|
||||
121
apps/docs/src/pages/docs/self-hosting/index.mdx
Normal file
121
apps/docs/src/pages/docs/self-hosting/index.mdx
Normal file
@@ -0,0 +1,121 @@
|
||||
import { Callout, Tabs, Steps } from 'nextra/components';
|
||||
|
||||
# Self-hosting
|
||||
|
||||
<Callout>OpenPanel is not stable yet. If you still want to self-host you can go ahead. Bear in mind that new changes might give a little headache to keep up with.</Callout>
|
||||
|
||||
This is a simple guide how to get started with OpenPanel on your own VPS.
|
||||
|
||||
## Instructions
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- VPS of any kind (only tested on Ubuntu 24.04)
|
||||
- [Clerk.com](https://clerk.com) account (they have a free tier)
|
||||
|
||||
### Quickstart
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Openpanel-dev/openpanel && cd openpanel/self-hosting && ./start
|
||||
# After setup is complete run `./start` to start OpenPanel
|
||||
```
|
||||
|
||||
<Steps>
|
||||
|
||||
### Clone
|
||||
|
||||
Clone the repository to your VPS
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Openpanel-dev/openpanel.git
|
||||
```
|
||||
|
||||
### Run the setup script
|
||||
|
||||
The setup script will do 3 things
|
||||
|
||||
1. Install node (if you accept)
|
||||
2. Install docker (if you accept)
|
||||
3. Execute a node script that will ask some questions about your setup
|
||||
4. After this is done you'll need to point a webhook inside Clerk (https://your-domain.com/api/webhook/clerk)
|
||||
|
||||
> Setup takes 5-10 minutes depending on your VPS. It'll build all the docker images.
|
||||
|
||||
```bash
|
||||
cd openpanel/self-hosting
|
||||
./setup
|
||||
```
|
||||
|
||||
### Start 🚀
|
||||
|
||||
Run the `./start` script located inside the self-hosting folder
|
||||
|
||||
```bash
|
||||
./start
|
||||
```
|
||||
</Steps>
|
||||
|
||||
## Clerk.com
|
||||
|
||||
<Callout>
|
||||
Some might wonder why we use Clerk.com for authentication. The main reason for this is that Clerk have great support for iOS and Android apps. We're in the process of building an iOS app and we want to have a seamless experience for our users.
|
||||
|
||||
**next-auth** is great, but lacks good support for mobile apps.
|
||||
</Callout>
|
||||
|
||||
You'll need to create an account at [Clerk.com](https://clerk.com) and create a new project. You'll need the 3 keys that Clerk provides you with.
|
||||
|
||||
- **Publishable key** `pk_live_xxx`
|
||||
- **Secret key** `sk_live_xxx`
|
||||
- **Signing secret** `"whsec_xxx"`
|
||||
|
||||
### Webhooks
|
||||
|
||||
You'll also need to add a webhook to your domain. We listen on some events from Clerk to keep our database in sync.
|
||||
|
||||
#### URL
|
||||
|
||||
- **Path**: `/api/webhook/clerk`
|
||||
- **Example**: `https://your-domain.com/api/webhook/clerk`
|
||||
|
||||
#### Events we listen to
|
||||
|
||||
- `organizationMembership.created`
|
||||
- `user.created`
|
||||
- `organizationMembership.deleted`
|
||||
- `user.updated`
|
||||
- `user.deleted`
|
||||
|
||||
## Good to know
|
||||
|
||||
### Always use correct api url
|
||||
|
||||
When self-hosting you'll need to provide your api url when initializing the SDK.
|
||||
|
||||
The path should be `/api` and the domain should be your domain.
|
||||
|
||||
```html filename="index.html" {4}
|
||||
<script>
|
||||
window.op = window.op||function(...args){(window.op.q=window.op.q||[]).push(args);};
|
||||
window.op('init', {
|
||||
apiUrl: 'https://your-domain.com/api',
|
||||
clientId: 'YOUR_CLIENT_ID',
|
||||
trackScreenViews: true,
|
||||
trackOutgoingLinks: true,
|
||||
trackAttributes: true,
|
||||
});
|
||||
</script>
|
||||
<script src="https://openpanel.dev/op1.js" defer async></script>
|
||||
```
|
||||
|
||||
```js filename="op.ts" {4}
|
||||
import { OpenPanel } from '@openpanel/sdk';
|
||||
|
||||
const op = new OpenPanel({
|
||||
apiUrl: 'https://your-domain.com/api',
|
||||
clientId: 'YOUR_CLIENT_ID',
|
||||
trackScreenViews: true,
|
||||
trackOutgoingLinks: true,
|
||||
trackAttributes: true,
|
||||
});
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
# Vue
|
||||
|
||||
Use <Link href="/docs/script">script tag</Link> or <Link href="/docs/web">Web SDK</Link> for now. We'll add a dedicated vue sdk soon.
|
||||
@@ -1,3 +1,5 @@
|
||||
<img src="https://openpanel.dev/ogimage.png" />
|
||||
|
||||
# Introduction
|
||||
|
||||
Openpanel is an open-source alternative to Mixpanel. Combining the power of Mixpanel with the ease of Plausible, Openpanel is a privacy-focused analytics tool that gives you the insights you need to make data-driven decisions.
|
||||
|
||||
@@ -19,7 +19,7 @@ export default {
|
||||
height="32"
|
||||
width="32"
|
||||
/>
|
||||
<strong style={{ marginLeft: '8px' }}>openpanel</strong>
|
||||
<strong style={{ marginLeft: '8px' }}>OpenPanel</strong>
|
||||
</>
|
||||
),
|
||||
head: () => {
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 235 KiB After Width: | Height: | Size: 428 KiB |
@@ -1,85 +1,77 @@
|
||||
ARG NODE_VERSION=20.15.1
|
||||
|
||||
FROM --platform=linux/amd64 node:${NODE_VERSION}-slim AS base
|
||||
FROM node:${NODE_VERSION}-slim AS base
|
||||
|
||||
ARG DATABASE_URL
|
||||
ENV DATABASE_URL=$DATABASE_URL
|
||||
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
|
||||
RUN corepack enable
|
||||
|
||||
RUN apt update \
|
||||
&& apt install -y curl \
|
||||
&& curl -L https://raw.githubusercontent.com/tj/n/master/bin/n -o n \
|
||||
&& bash n $NODE_VERSION \
|
||||
&& rm n \
|
||||
&& npm install -g n
|
||||
RUN corepack enable && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
openssl \
|
||||
libssl3 && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json package.json
|
||||
COPY pnpm-lock.yaml pnpm-lock.yaml
|
||||
COPY pnpm-workspace.yaml pnpm-workspace.yaml
|
||||
COPY apps/worker/package.json apps/worker/package.json
|
||||
COPY packages/db/package.json packages/db/package.json
|
||||
COPY packages/redis/package.json packages/redis/package.json
|
||||
COPY packages/logger/package.json packages/logger/package.json
|
||||
COPY packages/queue/package.json packages/queue/package.json
|
||||
COPY packages/common/package.json packages/common/package.json
|
||||
COPY packages/constants/package.json packages/constants/package.json
|
||||
COPY packages/validation/package.json packages/validation/package.json
|
||||
COPY packages/sdks/sdk/package.json packages/sdks/sdk/package.json
|
||||
COPY patches patches
|
||||
# Workspace
|
||||
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
# Apps
|
||||
COPY apps/worker/package.json ./apps/worker/
|
||||
# Packages
|
||||
COPY packages/db/package.json ./packages/db/
|
||||
COPY packages/redis/package.json ./packages/redis/
|
||||
COPY packages/queue/package.json ./packages/queue/
|
||||
COPY packages/logger/package.json ./packages/logger/
|
||||
COPY packages/common/package.json ./packages/common/
|
||||
COPY packages/constants/package.json ./packages/constants/
|
||||
# Patches
|
||||
COPY patches ./patches
|
||||
|
||||
# BUILD
|
||||
FROM base AS build
|
||||
|
||||
WORKDIR /app/apps/worker
|
||||
RUN pnpm install --frozen-lockfile --ignore-scripts
|
||||
WORKDIR /app
|
||||
RUN pnpm install --frozen-lockfile && \
|
||||
pnpm store prune
|
||||
|
||||
WORKDIR /app
|
||||
COPY apps/worker apps/worker
|
||||
COPY packages packages
|
||||
COPY tooling tooling
|
||||
RUN pnpm db:codegen
|
||||
COPY apps/worker ./apps/worker
|
||||
COPY packages ./packages
|
||||
COPY tooling ./tooling
|
||||
|
||||
WORKDIR /app/apps/worker
|
||||
RUN pnpm run build
|
||||
RUN pnpm db:codegen && \
|
||||
pnpm --filter worker run build
|
||||
|
||||
# PROD
|
||||
FROM base AS prod
|
||||
|
||||
WORKDIR /app/apps/worker
|
||||
RUN pnpm install --frozen-lockfile --prod --ignore-scripts
|
||||
WORKDIR /app
|
||||
COPY --from=build /app/package.json ./
|
||||
COPY --from=build /app/pnpm-lock.yaml ./
|
||||
RUN pnpm install --frozen-lockfile --prod && \
|
||||
pnpm store prune
|
||||
|
||||
# FINAL
|
||||
FROM base AS runner
|
||||
|
||||
COPY --from=build /app/package.json /app/package.json
|
||||
COPY --from=prod /app/node_modules /app/node_modules
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=build /app/package.json ./
|
||||
COPY --from=prod /app/node_modules ./node_modules
|
||||
|
||||
# Apps
|
||||
COPY --from=build /app/apps/worker /app/apps/worker
|
||||
|
||||
# Apps node_modules
|
||||
COPY --from=prod /app/apps/worker/node_modules /app/apps/worker/node_modules
|
||||
COPY --from=build /app/apps/worker ./apps/worker
|
||||
|
||||
# Packages
|
||||
COPY --from=build /app/packages/db /app/packages/db
|
||||
COPY --from=build /app/packages/redis /app/packages/redis
|
||||
COPY --from=build /app/packages/logger /app/packages/logger
|
||||
COPY --from=build /app/packages/queue /app/packages/queue
|
||||
COPY --from=build /app/packages/common /app/packages/common
|
||||
|
||||
# Packages node_modules
|
||||
COPY --from=prod /app/packages/db/node_modules /app/packages/db/node_modules
|
||||
COPY --from=prod /app/packages/redis/node_modules /app/packages/redis/node_modules
|
||||
COPY --from=prod /app/packages/logger/node_modules /app/packages/logger/node_modules
|
||||
COPY --from=prod /app/packages/queue/node_modules /app/packages/queue/node_modules
|
||||
COPY --from=prod /app/packages/common/node_modules /app/packages/common/node_modules
|
||||
COPY --from=build /app/packages/db ./packages/db
|
||||
COPY --from=build /app/packages/redis ./packages/redis
|
||||
COPY --from=build /app/packages/logger ./packages/logger
|
||||
COPY --from=build /app/packages/queue ./packages/queue
|
||||
COPY --from=build /app/packages/common ./packages/common
|
||||
|
||||
RUN pnpm db:codegen
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@baselime/pino-transport": "^0.1.5",
|
||||
"@bull-board/api": "^5.21.0",
|
||||
"@bull-board/express": "^5.21.0",
|
||||
"@bull-board/api": "5.21.0",
|
||||
"@bull-board/express": "5.21.0",
|
||||
"@openpanel/common": "workspace:*",
|
||||
"@openpanel/db": "workspace:*",
|
||||
"@openpanel/logger": "workspace:*",
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { WorkerOptions } from 'bullmq';
|
||||
import { Worker } from 'bullmq';
|
||||
import express from 'express';
|
||||
|
||||
import { createInitialSalts } from '@openpanel/db';
|
||||
import { cronQueue, eventsQueue, sessionsQueue } from '@openpanel/queue';
|
||||
import { getRedisQueue } from '@openpanel/redis';
|
||||
|
||||
@@ -15,7 +16,7 @@ import { register } from './metrics';
|
||||
|
||||
const PORT = parseInt(process.env.WORKER_PORT || '3000', 10);
|
||||
const serverAdapter = new ExpressAdapter();
|
||||
serverAdapter.setBasePath(process.env.SELF_HOSTED ? '/worker' : '/');
|
||||
serverAdapter.setBasePath('/');
|
||||
const app = express();
|
||||
|
||||
const workerOptions: WorkerOptions = {
|
||||
@@ -162,10 +163,28 @@ async function start() {
|
||||
}
|
||||
);
|
||||
|
||||
if (process.env.SELF_HOSTED && process.env.NODE_ENV === 'production') {
|
||||
await cronQueue.add(
|
||||
'ping',
|
||||
{
|
||||
type: 'ping',
|
||||
payload: undefined,
|
||||
},
|
||||
{
|
||||
jobId: 'ping',
|
||||
repeat: {
|
||||
pattern: '0 0 * * *',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const repeatableJobs = await cronQueue.getRepeatableJobs();
|
||||
|
||||
console.log('Repeatable jobs:');
|
||||
console.log(repeatableJobs);
|
||||
|
||||
await createInitialSalts();
|
||||
}
|
||||
|
||||
start();
|
||||
|
||||
26
apps/worker/src/jobs/cron.ping.ts
Normal file
26
apps/worker/src/jobs/cron.ping.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { chQuery, TABLE_NAMES } from '@openpanel/db';
|
||||
|
||||
export async function ping() {
|
||||
const [res] = await chQuery<{ count: number }>(
|
||||
`SELECT COUNT(*) as count FROM ${TABLE_NAMES.events}`
|
||||
);
|
||||
|
||||
if (typeof res?.count === 'number') {
|
||||
const response = await fetch('https://api.openpanel.com/misc/ping', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
domain: process.env.NEXT_PUBLIC_DASHBOARD_URL,
|
||||
count: res?.count,
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
throw new Error('Failed to ping the server');
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import type { Job } from 'bullmq';
|
||||
import { eventBuffer, profileBuffer } from '@openpanel/db';
|
||||
import type { CronQueuePayload } from '@openpanel/queue';
|
||||
|
||||
import { ping } from './cron.ping';
|
||||
import { salt } from './cron.salt';
|
||||
|
||||
export async function cronJob(job: Job<CronQueuePayload>) {
|
||||
@@ -16,5 +17,8 @@ export async function cronJob(job: Job<CronQueuePayload>) {
|
||||
case 'flushProfiles': {
|
||||
return await profileBuffer.flush();
|
||||
}
|
||||
case 'ping': {
|
||||
return await ping();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user