feat: add basic webui

This commit is contained in:
2024-12-30 06:39:09 +08:00
parent 608a7fb9c6
commit a4c549e7c3
462 changed files with 35900 additions and 2491 deletions

View File

@@ -0,0 +1,31 @@
import 'server-only';
import { env } from '@konobangu/env';
import { Liveblocks as LiveblocksNode } from '@liveblocks/node';
type AuthenticateOptions = {
userId: string;
orgId: string;
userInfo: Liveblocks['UserMeta']['info'];
};
export const authenticate = async ({
userId,
orgId,
userInfo,
}: AuthenticateOptions) => {
const liveblocks = new LiveblocksNode({
secret: env.LIVEBLOCKS_SECRET,
});
// Start an auth session inside your endpoint
const session = liveblocks.prepareSession(userId, { userInfo });
// Use a naming pattern to allow access to rooms with wildcards
// Giving the user write access on their organization
session.allow(`${orgId}:*`, session.FULL_ACCESS);
// Authorize the user and return the result
const { status, body } = await session.authorize();
return new Response(body, { status });
};

View File

@@ -0,0 +1,49 @@
// Define Liveblocks types for your application
// https://liveblocks.io/docs/api-reference/liveblocks-react#Typing-your-data
declare global {
interface Liveblocks {
// Each user's Presence, for useMyPresence, useOthers, etc.
Presence: {
// Example, real-time cursor coordinates
cursor: { x: number; y: number } | null;
};
// The Storage tree for the room, for useMutation, useStorage, etc.
Storage: {
// Example, a conflict-free list
// animals: LiveList<string>;
};
// Custom user info set when authenticating with a secret key
UserMeta: {
id: string;
info: {
name?: string;
avatar?: string;
color: string;
};
};
// Custom events, for useBroadcastEvent, useEventListener
RoomEvent: {};
// Example has two events, using a union
// | { type: "PLAY" }
// | { type: "REACTION"; emoji: "🔥" };
// Custom metadata set on threads, for useThreads, useCreateThread, etc.
ThreadMetadata: {
// Example, attaching coordinates to a thread
// x: number;
// y: number;
};
// Custom room info set with resolveRoomsInfo, for useRoomInfo
RoomInfo: {
// Example, rooms with a title and url
// title: string;
// url: string;
};
}
}
export {};

View File

@@ -0,0 +1 @@
export * from '@liveblocks/react/suspense';

View File

@@ -0,0 +1,23 @@
{
"name": "@konobangu/collaboration",
"version": "0.0.0",
"private": true,
"scripts": {
"clean": "git clean -xdf .cache .turbo dist node_modules",
"typecheck": "tsc --noEmit --emitDeclarationOnly false"
},
"dependencies": {
"@konobangu/env": "workspace:*",
"@liveblocks/client": "^2.14.0",
"@liveblocks/node": "^2.14.0",
"@liveblocks/react": "^2.14.0",
"react": "^19.0.0",
"server-only": "^0.0.1"
},
"devDependencies": {
"@konobangu/typescript-config": "workspace:*",
"@types/node": "22.10.1",
"@types/react": "19.0.1",
"@types/react-dom": "^19.0.2"
}
}

View File

@@ -0,0 +1,37 @@
'use client';
import type { ResolveMentionSuggestionsArgs } from '@liveblocks/client';
import type { ResolveUsersArgs } from '@liveblocks/node';
import {
ClientSideSuspense,
LiveblocksProvider,
RoomProvider,
} from '@liveblocks/react/suspense';
import type { ComponentProps, ReactNode } from 'react';
type RoomProps = ComponentProps<typeof LiveblocksProvider> & {
id: string;
children: ReactNode;
authEndpoint: string;
fallback: ReactNode;
resolveUsers?: (
args: ResolveUsersArgs
) => Promise<Liveblocks['UserMeta']['info'][]>;
resolveMentionSuggestions?: (
args: ResolveMentionSuggestionsArgs
) => Promise<string[]>;
};
export const Room = ({
id,
children,
authEndpoint,
fallback,
...props
}: RoomProps) => (
<LiveblocksProvider authEndpoint={authEndpoint} {...props}>
<RoomProvider id={id} initialPresence={{ cursor: null }}>
<ClientSideSuspense fallback={fallback}>{children}</ClientSideSuspense>
</RoomProvider>
</LiveblocksProvider>
);

View File

@@ -0,0 +1,8 @@
{
"extends": "@konobangu/typescript-config/react-library.json",
"compilerOptions": {
"baseUrl": "."
},
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}