onboarding completed
This commit is contained in:
committed by
Carl-Gerhard Lindesvärd
parent
97627583ec
commit
7d22d2ddad
@@ -9,6 +9,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ramda": "^0.29.1",
|
||||
"superjson": "^1.13.3",
|
||||
"unique-names-generator": "^4.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { anyPass, assocPath, isEmpty, isNil, reject } from 'ramda';
|
||||
import superjson from 'superjson';
|
||||
|
||||
export function toDots(
|
||||
obj: Record<string, unknown>,
|
||||
@@ -38,3 +39,16 @@ export function getSafeJson<T>(str: string): T | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function getSuperJson<T>(str: string): T | null {
|
||||
const json = getSafeJson<T>(str);
|
||||
if (
|
||||
typeof json === 'object' &&
|
||||
json !== null &&
|
||||
'json' in json &&
|
||||
'meta' in json
|
||||
) {
|
||||
return superjson.parse<T>(str);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,12 @@ import { isSameDay, isSameMonth } from 'date-fns';
|
||||
|
||||
export const NOT_SET_VALUE = '(not set)';
|
||||
|
||||
export const ProjectTypeNames = {
|
||||
website: 'Website',
|
||||
app: 'App',
|
||||
backend: 'Backend',
|
||||
} as const;
|
||||
|
||||
export const operators = {
|
||||
is: 'Is',
|
||||
isNot: 'Is not',
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"@prisma/client": "^5.1.1",
|
||||
"ramda": "^0.29.1",
|
||||
"sqlstring": "^2.3.3",
|
||||
"superjson": "^1.13.3",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ProjectType" AS ENUM ('website', 'app', 'backend');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "projects" ADD COLUMN "types" "ProjectType"[] DEFAULT ARRAY[]::"ProjectType"[];
|
||||
@@ -10,11 +10,18 @@ datasource db {
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
enum ProjectType {
|
||||
website
|
||||
app
|
||||
backend
|
||||
}
|
||||
|
||||
model Project {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()"))
|
||||
id String @id @default(dbgenerated("gen_random_uuid()"))
|
||||
name String
|
||||
organizationSlug String
|
||||
eventsCount Int @default(0)
|
||||
eventsCount Int @default(0)
|
||||
types ProjectType[] @default([])
|
||||
|
||||
events Event[]
|
||||
profiles Profile[]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { omit, uniq } from 'ramda';
|
||||
import { escape } from 'sqlstring';
|
||||
import superjson from 'superjson';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { randomSplitName, toDots } from '@openpanel/common';
|
||||
@@ -113,11 +114,51 @@ export interface IServiceCreateEventPayload {
|
||||
meta: EventMeta | undefined;
|
||||
}
|
||||
|
||||
export interface IServiceEventMinimal {
|
||||
id: string;
|
||||
name: string;
|
||||
projectId: string;
|
||||
sessionId: string;
|
||||
createdAt: Date;
|
||||
country?: string | undefined;
|
||||
os?: string | undefined;
|
||||
browser?: string | undefined;
|
||||
device?: string | undefined;
|
||||
brand?: string | undefined;
|
||||
duration: number;
|
||||
path: string;
|
||||
referrer: string | undefined;
|
||||
meta: EventMeta | undefined;
|
||||
minimal: boolean;
|
||||
}
|
||||
|
||||
interface GetEventsOptions {
|
||||
profile?: boolean | Prisma.ProfileSelect;
|
||||
meta?: boolean | Prisma.EventMetaSelect;
|
||||
}
|
||||
|
||||
export function transformMinimalEvent(
|
||||
event: IServiceCreateEventPayload
|
||||
): IServiceEventMinimal {
|
||||
return {
|
||||
id: event.id,
|
||||
name: event.name,
|
||||
projectId: event.projectId,
|
||||
sessionId: event.sessionId,
|
||||
createdAt: event.createdAt,
|
||||
country: event.country,
|
||||
os: event.os,
|
||||
browser: event.browser,
|
||||
device: event.device,
|
||||
brand: event.brand,
|
||||
duration: event.duration,
|
||||
path: event.path,
|
||||
referrer: event.referrer,
|
||||
meta: event.meta,
|
||||
minimal: true,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getLiveVisitors(projectId: string) {
|
||||
const keys = await redis.keys(`live:event:${projectId}:*`);
|
||||
return keys.length;
|
||||
@@ -227,7 +268,7 @@ export async function createEvent(
|
||||
},
|
||||
});
|
||||
|
||||
redisPub.publish('event', JSON.stringify(transformEvent(event)));
|
||||
redisPub.publish('event', superjson.stringify(transformEvent(event)));
|
||||
redis.set(
|
||||
`live:event:${event.project_id}:${event.profile_id}`,
|
||||
'',
|
||||
|
||||
@@ -23,8 +23,9 @@ export function transformOrganization(org: Organization) {
|
||||
|
||||
export async function getCurrentOrganizations() {
|
||||
const session = auth();
|
||||
if (!session.userId) return [];
|
||||
const organizations = await clerkClient.users.getOrganizationMembershipList({
|
||||
userId: session.userId!,
|
||||
userId: session.userId,
|
||||
});
|
||||
return organizations.map((item) => transformOrganization(item.organization));
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import { auth } from '@clerk/nextjs';
|
||||
|
||||
import type { Project } from '../prisma-client';
|
||||
import type { Prisma, Project } from '../prisma-client';
|
||||
import { db } from '../prisma-client';
|
||||
|
||||
export type IServiceProject = Project;
|
||||
export type IServiceProjectWithClients = Prisma.ProjectGetPayload<{
|
||||
include: {
|
||||
clients: true;
|
||||
};
|
||||
}>;
|
||||
|
||||
export async function getProjectById(id: string) {
|
||||
const res = await db.project.findUnique({
|
||||
@@ -19,6 +24,23 @@ export async function getProjectById(id: string) {
|
||||
return res;
|
||||
}
|
||||
|
||||
export async function getProjectWithClients(id: string) {
|
||||
const res = await db.project.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
include: {
|
||||
clients: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!res) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
export async function getProjectsByOrganizationSlug(organizationSlug: string) {
|
||||
return db.project.findMany({
|
||||
where: {
|
||||
|
||||
67
packages/sdks/_info/frameworks.tsx
Normal file
67
packages/sdks/_info/frameworks.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
const api = {
|
||||
logo: 'https://cdn-icons-png.flaticon.com/512/10169/10169724.png',
|
||||
name: 'Rest API',
|
||||
href: 'https://docs.openpanel.dev/docs/api',
|
||||
} as const;
|
||||
|
||||
export const frameworks = {
|
||||
website: [
|
||||
{
|
||||
logo: 'https://upload.wikimedia.org/wikipedia/commons/thumb/6/61/HTML5_logo_and_wordmark.svg/240px-HTML5_logo_and_wordmark.svg.png',
|
||||
name: 'HTML / Script',
|
||||
href: 'https://docs.openpanel.dev/docs/script',
|
||||
},
|
||||
{
|
||||
logo: 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/2300px-React-icon.svg.png',
|
||||
name: 'React',
|
||||
href: 'https://docs.openpanel.dev/docs/react',
|
||||
},
|
||||
{
|
||||
logo: 'https://static-00.iconduck.com/assets.00/nextjs-icon-512x512-y563b8iq.png',
|
||||
name: 'Next.js',
|
||||
href: 'https://docs.openpanel.dev/docs/nextjs',
|
||||
},
|
||||
{
|
||||
logo: 'https://www.datocms-assets.com/205/1642515307-square-logo.svg',
|
||||
name: 'Remix',
|
||||
href: 'https://docs.openpanel.dev/docs/remix',
|
||||
},
|
||||
{
|
||||
logo: 'https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Vue.js_Logo_2.svg/1024px-Vue.js_Logo_2.svg.png',
|
||||
name: 'Vue',
|
||||
href: 'https://docs.openpanel.dev/docs/vue',
|
||||
},
|
||||
{
|
||||
logo: 'https://astro.build/assets/press/astro-icon-dark.png',
|
||||
name: 'Astro',
|
||||
href: 'https://docs.openpanel.dev/docs/astro',
|
||||
},
|
||||
api,
|
||||
],
|
||||
app: [
|
||||
{
|
||||
logo: 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/2300px-React-icon.svg.png',
|
||||
name: 'React-Native',
|
||||
href: 'https://docs.openpanel.dev/docs/react-native',
|
||||
},
|
||||
api,
|
||||
],
|
||||
backend: [
|
||||
{
|
||||
logo: 'https://static-00.iconduck.com/assets.00/node-js-icon-454x512-nztofx17.png',
|
||||
name: 'Node',
|
||||
href: 'https://docs.openpanel.dev/docs/node',
|
||||
},
|
||||
{
|
||||
logo: 'https://expressjs.com/images/favicon.png',
|
||||
name: 'Express',
|
||||
href: 'https://docs.openpanel.dev/docs/express',
|
||||
},
|
||||
{
|
||||
logo: 'https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/Laravel.svg/1969px-Laravel.svg.png',
|
||||
name: 'Laravel',
|
||||
href: 'https://github.com/tbleckert/openpanel-laravel/tree/main',
|
||||
},
|
||||
api,
|
||||
],
|
||||
} as const;
|
||||
1
packages/sdks/_info/index.ts
Normal file
1
packages/sdks/_info/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './frameworks';
|
||||
27
packages/sdks/_info/package.json
Normal file
27
packages/sdks/_info/package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "@openpanel/sdk-info",
|
||||
"version": "0.0.1",
|
||||
"main": "index.ts",
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"format": "prettier --check \"**/*.{mjs,ts,md,json}\"",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"with-env": "dotenv -e ../../.env -c --"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openpanel/eslint-config": "workspace:*",
|
||||
"@openpanel/prettier-config": "workspace:*",
|
||||
"@openpanel/tsconfig": "workspace:*",
|
||||
"eslint": "^8.48.0",
|
||||
"prettier": "^3.0.3",
|
||||
"prisma": "^5.1.1",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"extends": [
|
||||
"@openpanel/eslint-config/base"
|
||||
]
|
||||
},
|
||||
"prettier": "@openpanel/prettier-config"
|
||||
}
|
||||
12
packages/sdks/_info/tsconfig.json
Normal file
12
packages/sdks/_info/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "@openpanel/tsconfig/base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
|
||||
},
|
||||
"include": ["."],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -97,3 +97,36 @@ export const zCreateReference = z.object({
|
||||
projectId: z.string(),
|
||||
datetime: z.string(),
|
||||
});
|
||||
|
||||
export const zOnboardingProject = z
|
||||
.object({
|
||||
organization: z.string().min(3),
|
||||
project: z.string().min(3),
|
||||
domain: z.string().url().or(z.literal('').or(z.null())),
|
||||
website: z.boolean(),
|
||||
app: z.boolean(),
|
||||
backend: z.boolean(),
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
if (data.website && !data.domain) {
|
||||
ctx.addIssue({
|
||||
code: 'custom',
|
||||
message: 'Domain is required for website tracking',
|
||||
path: ['domain'],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
data.website === false &&
|
||||
data.app === false &&
|
||||
data.backend === false
|
||||
) {
|
||||
['app', 'backend', 'website'].forEach((key) => {
|
||||
ctx.addIssue({
|
||||
code: 'custom',
|
||||
message: 'At least one type must be selected',
|
||||
path: [key],
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user