diff --git a/migrations/.gitkeep b/migrations/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/migrations/0000_hesitant_kylun.sql b/migrations/0000_hesitant_kylun.sql deleted file mode 100644 index 2c76dcc0fd..0000000000 --- a/migrations/0000_hesitant_kylun.sql +++ /dev/null @@ -1,8 +0,0 @@ -CREATE TABLE "events" ( - "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "events_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), - "type" text NOT NULL, - "timestamp" bigint NOT NULL, - "properties" json NOT NULL, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); diff --git a/migrations/meta/0000_snapshot.json b/migrations/meta/0000_snapshot.json deleted file mode 100644 index 4ebacc9d10..0000000000 --- a/migrations/meta/0000_snapshot.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "id": "5ab333f5-a980-4564-a919-3d15608505d0", - "prevId": "00000000-0000-0000-0000-000000000000", - "version": "7", - "dialect": "postgresql", - "tables": { - "public.events": { - "name": "events", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "identity": { - "type": "always", - "name": "events_id_seq", - "schema": "public", - "increment": "1", - "startWith": "1", - "minValue": "1", - "maxValue": "2147483647", - "cache": "1", - "cycle": false - } - }, - "type": { - "name": "type", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "timestamp": { - "name": "timestamp", - "type": "bigint", - "primaryKey": false, - "notNull": true - }, - "properties": { - "name": "properties", - "type": "json", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - } - }, - "enums": {}, - "schemas": {}, - "sequences": {}, - "roles": {}, - "policies": {}, - "views": {}, - "_meta": { - "columns": {}, - "schemas": {}, - "tables": {} - } -} diff --git a/migrations/meta/_journal.json b/migrations/meta/_journal.json deleted file mode 100644 index 11751f2659..0000000000 --- a/migrations/meta/_journal.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": "7", - "dialect": "postgresql", - "entries": [ - { - "idx": 0, - "version": "7", - "when": 1747204609794, - "tag": "0000_hesitant_kylun", - "breakpoints": true - } - ] -} diff --git a/src/actions/analytics.ts b/src/actions/analytics.ts new file mode 100644 index 0000000000..b776372ab6 --- /dev/null +++ b/src/actions/analytics.ts @@ -0,0 +1,31 @@ +import { z } from 'zod'; + +import { client } from '@/lib/server/analytics'; + +const usageSchema = z.array( + z.object({ + userId: z.string(), + count: z.coerce.number(), + }), +); + +export type Usage = z.infer; + +export const getUsage = async (orgId?: string | null): Promise => { + if (!orgId) { + return []; + } + + const resultSet = await client.query({ + query: ` + SELECT userId, COUNT(1) as count + FROM "events" + WHERE orgId = {orgId: String} + GROUP BY 1; + `, + format: 'JSONEachRow', + query_params: { orgId }, + }); + + return usageSchema.parse(await resultSet.json()); +}; diff --git a/src/app/(authenticated)/dashboard/Dashboard.tsx b/src/app/(authenticated)/dashboard/Dashboard.tsx new file mode 100644 index 0000000000..50ad26ae83 --- /dev/null +++ b/src/app/(authenticated)/dashboard/Dashboard.tsx @@ -0,0 +1,28 @@ +'use client'; + +import { useAuth } from '@clerk/nextjs'; +import { useTranslations } from 'next-intl'; + +import type { Usage } from '@/actions/analytics'; + +import { + TitleBar, + UsageAnalyticsCard, + AuditLogCard, +} from '@/components/dashboard'; + +export const Dashboard = ({ usage }: { usage: Usage }) => { + const { userId, orgId } = useAuth(); + const t = useTranslations('DashboardIndex'); + + return ( + <> + +
Org: {orgId}
+
User: {userId}
+
Usage: {JSON.stringify(usage, null, 2)}
+ + + + ); +}; diff --git a/src/app/(authenticated)/dashboard/analytics/page.tsx b/src/app/(authenticated)/dashboard/analytics/page.tsx index 03566afd01..530e36abc5 100644 --- a/src/app/(authenticated)/dashboard/analytics/page.tsx +++ b/src/app/(authenticated)/dashboard/analytics/page.tsx @@ -14,7 +14,6 @@ const AnalyticsPageContainer = () => { title={t('title_bar')} description={t('title_bar_description')} /> - ); diff --git a/src/app/(authenticated)/dashboard/audit-logs/page.tsx b/src/app/(authenticated)/dashboard/audit-logs/page.tsx index ea7b49b55f..fa710ee14d 100644 --- a/src/app/(authenticated)/dashboard/audit-logs/page.tsx +++ b/src/app/(authenticated)/dashboard/audit-logs/page.tsx @@ -17,7 +17,6 @@ const AuditLogsPage = () => { const [selectedLog, setSelectedLog] = useState(null); const [isDrawerOpen, setIsDrawerOpen] = useState(false); - // Get filtered logs based on selected time period const logs = getFilteredLogs(Number(timePeriod)); const handleLogClick = (log: AuditLog) => { @@ -35,7 +34,6 @@ const AuditLogsPage = () => { title="Audit Logs" description="View all organization activity and changes" /> -

All Activity

@@ -43,8 +41,6 @@ const AuditLogsPage = () => { Showing all audit logs for your organization

- - {/* Time period toggle */}
- - {/* Log entries */}
{logs.length > 0 ? ( logs.map((log: AuditLog) => ( @@ -82,8 +76,6 @@ const AuditLogsPage = () => { )}
- - {/* Drawer for log details */} - - - - - ); +export default async function Page() { + const { orgId } = await auth(); + const usage = await getUsage(orgId); + return ; } diff --git a/src/app/(authenticated)/layout.tsx b/src/app/(authenticated)/layout.tsx index 6008686c30..85c50637b9 100644 --- a/src/app/(authenticated)/layout.tsx +++ b/src/app/(authenticated)/layout.tsx @@ -1,15 +1,15 @@ +import { currentUser } from '@clerk/nextjs/server'; import { redirect } from 'next/navigation'; -import { auth as clerkAuth } from '@clerk/nextjs/server'; export default async function AuthenticatedLayout({ children, }: { children: React.ReactNode; }) { - const auth = await clerkAuth(); + const user = await currentUser(); - if (!auth.userId) { - redirect('/'); + if (!user) { + return redirect('/sign-in'); } return children; diff --git a/src/app/page.tsx b/src/app/page.tsx index 89030f37e9..efebc99071 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,7 +1,7 @@ import { redirect } from 'next/navigation'; -import { auth as clerkAuth } from '@clerk/nextjs/server'; +import { currentUser } from '@clerk/nextjs/server'; export default async function Page() { - const { userId } = await clerkAuth(); - redirect(userId ? '/dashboard' : '/sign-in'); + const user = await currentUser(); + redirect(user ? '/dashboard' : '/sign-in'); } diff --git a/src/components/dashboard/index.ts b/src/components/dashboard/index.ts new file mode 100644 index 0000000000..1e688bbd58 --- /dev/null +++ b/src/components/dashboard/index.ts @@ -0,0 +1,3 @@ +export { TitleBar } from './TitleBar'; +export { UsageAnalyticsCard } from './UsageAnalyticsCard'; +export { AuditLogCard } from './AuditLogCard'; diff --git a/src/components/layout/Navbar.tsx b/src/components/layout/Navbar.tsx deleted file mode 100644 index 702ffedd50..0000000000 --- a/src/components/layout/Navbar.tsx +++ /dev/null @@ -1,41 +0,0 @@ -'use client'; - -import Link from 'next/link'; -import { useTranslations } from 'next-intl'; - -import { Button } from '@/components/ui'; -import { Section, LocaleSwitcher, ThemeSwitcher } from '@/components/layout'; - -import { Logo } from './Logo'; - -export const Navbar = () => { - const t = useTranslations('Navbar'); - - return ( -
-
- - - -
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
-
-
- ); -}; diff --git a/src/components/layout/index.ts b/src/components/layout/index.ts index e62750e6d6..26164551e4 100644 --- a/src/components/layout/index.ts +++ b/src/components/layout/index.ts @@ -1,4 +1,3 @@ -export { Navbar } from './Navbar'; export { Footer } from './Footer'; export { Section } from './Section'; export { LocaleSwitcher } from './LocaleSwitcher'; diff --git a/src/db/clickhouse.ts b/src/db/clickhouse.ts deleted file mode 100644 index b4fa7f679c..0000000000 --- a/src/db/clickhouse.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - -CREATE TABLE default.events -( - -- Shared - `id` UUID, - `orgId` String, - `userId` String, - `timestamp` Int32, - `type` String, - - -- App - `appVersion` String, - `vscodeVersion` String, - `platform` String, - `editorName` String, - `language` String, - `mode` String, - - -- Task - `taskId` Nullable(String), - `apiProvider` Nullable(String), - `modelId` Nullable(String), - `diffStrategy` Nullable(String), - `isSubtask` Nullable(Bool), - - -- Completion - `inputTokens` Nullable(Int32), - `outputTokens` Nullable(Int32), - `cacheReadTokens` Nullable(Int32), - `cacheWriteTokens` Nullable(Int32), - `cost` Nullable(Float32) -) -ENGINE = SharedMergeTree('/clickhouse/tables/{uuid}/{shard}', '{replica}') -ORDER BY (id, type, timestamp) -SETTINGS index_granularity = 8192; - -*/ diff --git a/src/db/schema.ts b/src/db/schema.ts index 49dec5aea2..70b786d12e 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -1,11 +1 @@ -import { pgTable, text, timestamp, integer } from 'drizzle-orm/pg-core'; - -export const usersTable = pgTable('users', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - authenticationId: text('authentication_id').notNull(), - createdAt: timestamp('created_at', { mode: 'date' }).defaultNow().notNull(), - updatedAt: timestamp('updated_at', { mode: 'date' }) - .defaultNow() - .$onUpdate(() => new Date()) - .notNull(), -}); +// TODO diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 59bda25deb..9bc4c09ad2 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -3,10 +3,6 @@ "meta_title": "Roo Code Cloud", "meta_description": "Cloud management for Roo Code." }, - "Navbar": { - "sign_in": "Sign In", - "sign_up": "Sign Up" - }, "Footer": { "terms_of_service": "Terms Of Service", "privacy_policy": "Privacy Policy" @@ -21,11 +17,6 @@ }, "DashboardIndex": { "title_bar": "Dashboard", - "title_bar_description": "Welcome to your dashboard", - "message_state_title": "Let's get started", - "message_state_description": "You can customize this page by editing the file at dashboard/page.tsx", - "message_state_button": "Star on GitHub", - "message_state_alternative": "Want more features using the same stack? Try .", "analytics_title": "Usage Analytics", "analytics_description": "Organization usage metrics and statistics", "analytics_period_7_days": "Last 7 days", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index a1a70f51ce..de490c95f3 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -3,10 +3,6 @@ "meta_title": "Roo Code Cloud", "meta_description": "Gestion cloud pour Roo Code." }, - "Navbar": { - "sign_in": "Se connecter", - "sign_up": "S'inscrire" - }, "Footer": { "terms_of_service": "Conditions d'utilisation", "privacy_policy": "Politique de confidentialité" @@ -21,11 +17,6 @@ }, "DashboardIndex": { "title_bar": "Tableau de bord", - "title_bar_description": "Bienvenue sur votre tableau de bord", - "message_state_title": "C'est parti", - "message_state_description": "Vous pouvez personnaliser cette page en modifiant le fichier dans dashboard/page.tsx", - "message_state_button": "Mettez une étoile sur GitHub", - "message_state_alternative": "Vous voulez plus de fonctionnalités en utilisant la même stack ? Essayez .", "analytics_title": "Analytiques d'utilisation", "analytics_description": "Métriques et statistiques d'utilisation de l'organisation", "analytics_period_7_days": "7 derniers jours", diff --git a/src/lib/server/analytics.ts b/src/lib/server/analytics.ts index 2c4ac825ee..ace477b45c 100644 --- a/src/lib/server/analytics.ts +++ b/src/lib/server/analytics.ts @@ -3,7 +3,7 @@ import { createClient } from '@clickhouse/client'; import { CloudEvent } from '@/schemas'; import { Env } from './env'; -const client = createClient({ +export const client = createClient({ url: Env.CLICKHOUSE_URL, username: Env.CLICKHOUSE_USERNAME, password: Env.CLICKHOUSE_PASSWORD,