Skip to content

Commit 3eaf83e

Browse files
authored
Merge pull request #1 from PriyanshuPz/self-sustained
Self sustained
2 parents b5dfd5b + e1192b5 commit 3eaf83e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1437
-1057
lines changed

.dockerignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ node_modules/*
77
**/generated/*
88
**/.env
99
.env
10+
cloud/*
11+
out/*
12+
.github/*
13+
/self-host/*
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Publish Kbnet Platform Docker image
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
paths:
8+
- "apps/platform/**"
9+
- ".github/workflows/build-platform-image.yml"
10+
11+
jobs:
12+
push_to_registry:
13+
name: Push Docker image to Docker Hub
14+
runs-on: ubuntu-latest
15+
permissions:
16+
packages: write
17+
contents: read
18+
attestations: write
19+
id-token: write
20+
steps:
21+
- name: Check out the repo
22+
uses: actions/checkout@v4
23+
24+
- name: Log in to Docker Hub
25+
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
26+
with:
27+
username: ${{ secrets.DOCKER_USERNAME }}
28+
password: ${{ secrets.DOCKER_PASSWORD }}
29+
30+
- name: Extract metadata (tags, labels) for Docker
31+
id: meta
32+
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
33+
with:
34+
images: p8labs/kbnet-platform
35+
tags: |
36+
type=raw,value=latest
37+
type=sha
38+
39+
- name: Build and push Docker image
40+
id: push
41+
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
42+
with:
43+
context: .
44+
file: ./apps/platform/Dockerfile
45+
push: true
46+
tags: ${{ steps.meta.outputs.tags }}
47+
labels: ${{ steps.meta.outputs.labels }}
48+
49+
- name: Generate artifact attestation
50+
uses: actions/attest-build-provenance@v2
51+
with:
52+
subject-name: index.docker.io/p8labs/kbnet-platform
53+
subject-digest: ${{ steps.push.outputs.digest }}
54+
push-to-registry: true

apps/platform/Dockerfile

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
FROM node:22-alpine AS base
2+
RUN npm install -g bun@latest
3+
RUN apk add --no-cache tree
4+
5+
6+
FROM base AS source
7+
8+
WORKDIR /app
9+
COPY . .
10+
RUN bunx turbo prune @kbnet/platform --docker
11+
12+
13+
14+
FROM base AS builder
15+
WORKDIR /app
16+
17+
COPY --from=source /app/out/full/ .
18+
RUN bun install
19+
RUN bun run build
20+
21+
22+
23+
FROM base AS runner
24+
WORKDIR /app
25+
26+
RUN addgroup --system --gid 1001 kbnet
27+
RUN adduser --system --uid 1001 bunuser
28+
USER bunuser
29+
30+
COPY --from=builder --chown=bunuser:kbnet /app/apps/platform/.next/standalone ./
31+
32+
COPY --from=builder --chown=bunuser:kbnet /app/apps/platform/public ./apps/platform/public
33+
COPY --from=builder --chown=bunuser:kbnet /app/apps/platform/.next/static ./apps/platform/.next/static
34+
35+
36+
EXPOSE 3000
37+
38+
ENV HOSTNAME=0.0.0.0
39+
40+
CMD ["node", "apps/platform/server.js"]

apps/platform/next.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { PrismaPlugin } from "@prisma/nextjs-monorepo-workaround-plugin";
44
const nextConfig: NextConfig = {
55
/* config options here */
66
devIndicators: false,
7-
7+
output: "standalone",
88
webpack: (config, { isServer }) => {
99
config.plugins = [...config.plugins, new PrismaPlugin()];
1010
if (!isServer) {

apps/platform/package.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030
"@radix-ui/react-switch": "^1.2.5",
3131
"@radix-ui/react-toggle": "^1.1.9",
3232
"@radix-ui/react-tooltip": "^1.2.7",
33-
"@react-spring/web": "^10.0.1",
34-
"@use-gesture/react": "^10.3.1",
3533
"better-auth": "^1.2.9",
3634
"class-variance-authority": "^0.7.1",
3735
"clsx": "^2.1.1",
@@ -46,7 +44,6 @@
4644
"react-hook-form": "^7.57.0",
4745
"react-markdown": "^10.1.0",
4846
"react-shiki": "^0.7.1",
49-
"react-spring": "^10.0.1",
5047
"react-swipeable": "^7.0.2",
5148
"remark-gfm": "^4.0.1",
5249
"sonner": "^2.0.5",

apps/platform/src/app/(site)/layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ export default function SiteLayout({
88
}) {
99
return (
1010
<div className="min-h-screen flex flex-col bg-background text-foreground">
11-
<Navbar />
11+
<Navbar transparent={false} />
1212
<main className="flex-1">{children}</main>
13-
<Footer />
13+
<Footer transparent={false} />
1414
</div>
1515
);
1616
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import { auth } from "@/lib/auth";
2+
import { prisma } from "@kbnet/db";
3+
import { headers } from "next/headers";
4+
import { NextResponse } from "next/server";
5+
import { encryptConfig } from "@kbnet/shared/encryptor";
6+
7+
type SetupRequestBody = {
8+
mindsdb: boolean;
9+
googleAI: boolean;
10+
apiKey?: string;
11+
};
12+
13+
async function handleGoogleAIIntegration(
14+
userId: string,
15+
googleAI: boolean,
16+
apiKey?: string,
17+
existingIntegration?: any
18+
) {
19+
// Case 1: Enabling Google AI
20+
if (googleAI) {
21+
// Case 1a: New integration - API key required
22+
if (!existingIntegration && !apiKey) {
23+
throw new Error("API key is required for new Google AI integration");
24+
}
25+
26+
// Case 1b: Existing integration - just enable if no new key
27+
if (existingIntegration && !apiKey) {
28+
return prisma.integration.update({
29+
where: {
30+
userId_type: {
31+
userId,
32+
type: "GOOGLE",
33+
},
34+
},
35+
data: { enabled: true },
36+
});
37+
}
38+
39+
// Case 1c: New key provided (for both new and existing)
40+
const { encrypted, iv, authTag } = encryptConfig(
41+
JSON.stringify({ apiKey })
42+
);
43+
44+
const integrationData = {
45+
config: encrypted,
46+
encryptionIV: iv,
47+
encryptionAuthTag: authTag,
48+
enabled: true,
49+
};
50+
51+
return existingIntegration
52+
? prisma.integration.update({
53+
where: {
54+
userId_type: {
55+
userId,
56+
type: "GOOGLE",
57+
},
58+
},
59+
data: integrationData,
60+
})
61+
: prisma.integration.create({
62+
data: {
63+
...integrationData,
64+
userId,
65+
type: "GOOGLE",
66+
},
67+
});
68+
}
69+
70+
// Case 2: Disabling Google AI
71+
if (existingIntegration) {
72+
return prisma.integration.update({
73+
where: {
74+
userId_type: {
75+
userId,
76+
type: "GOOGLE",
77+
},
78+
},
79+
data: { enabled: false },
80+
});
81+
}
82+
83+
return null;
84+
}
85+
86+
async function updateUserSettings(userId: string, settings: SetupRequestBody) {
87+
return prisma.user.update({
88+
where: { id: userId },
89+
data: {
90+
useMindsDB: settings.mindsdb,
91+
useBYO: settings.googleAI,
92+
},
93+
});
94+
}
95+
96+
export async function POST(request: Request) {
97+
try {
98+
// 1. Authentication
99+
const session = await auth.api.getSession({
100+
headers: await headers(),
101+
});
102+
103+
if (!session?.user?.id) {
104+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
105+
}
106+
107+
// 2. Get request body
108+
const body: SetupRequestBody = await request.json();
109+
110+
// 3. Get existing integration
111+
const existingIntegration = await prisma.integration.findUnique({
112+
where: {
113+
userId_type: {
114+
userId: session.user.id,
115+
type: "GOOGLE",
116+
},
117+
},
118+
});
119+
120+
// 4. Handle Google AI integration
121+
try {
122+
await handleGoogleAIIntegration(
123+
session.user.id,
124+
body.googleAI,
125+
body.apiKey,
126+
existingIntegration
127+
);
128+
} catch (error) {
129+
return NextResponse.json(
130+
{ error: (error as Error).message },
131+
{ status: 400 }
132+
);
133+
}
134+
135+
// 5. Update user settings
136+
await updateUserSettings(session.user.id, body);
137+
138+
return NextResponse.json({ success: true });
139+
} catch (error) {
140+
console.error("Error creating/updating settings:", error);
141+
return NextResponse.json(
142+
{ error: "Failed to create/update settings" },
143+
{ status: 500 }
144+
);
145+
}
146+
}

apps/platform/src/app/auth/logout/page.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
"use client";
22

3-
import { useEffect } from "react";
3+
import { Suspense, useEffect } from "react";
44
import { useSearchParams, useRouter } from "next/navigation";
55
import { Loader2 } from "lucide-react";
66
import { authClient } from "@/lib/auth-client";
77

88
export default function LogoutPage() {
9+
return (
10+
<Suspense>
11+
<LogoutContent />
12+
</Suspense>
13+
);
14+
}
15+
16+
function LogoutContent() {
917
const router = useRouter();
1018
const searchParams = useSearchParams();
1119

@@ -17,7 +25,7 @@ export default function LogoutPage() {
1725

1826
// Sign out the user
1927
await authClient.signOut();
20-
28+
router.refresh();
2129
// Redirect to the specified URL
2230
router.push(redirectTo);
2331
} catch (error) {

0 commit comments

Comments
 (0)