diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 06e5437..549a50c 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -3,7 +3,7 @@ name: Build and Push Docker Image on: push: branches: - - '**' # Run on all branches + - 'main' tags: - 'v*.*.*' pull_request: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..5f90ebe --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,66 @@ +# Contributing to OpenBookLM + +First off, thank you for considering contributing to OpenBookLM! It's people like you that make OpenBookLM such a great tool for democratizing learning with AI. + +We welcome all types of contributions: bug reports, bug fixes, documentation improvements, new features, and design tweaks. This document provides a simple guide to help you get started. + +## How to Contribute + +### 1. Fork the Repository +Start by forking the [OpenBookLM repository](https://github.com/open-biz/OpenBookLM) to your own GitHub account. You can do this by clicking the **Fork** button at the top right of the repository page. + +### 2. Clone Your Fork +Clone the forked repository to your local machine: + +```bash +git clone https://github.com/YOUR_USERNAME/OpenBookLM.git +cd OpenBookLM +``` + +### 3. Create a Feature Branch +Create a new branch for your feature or bugfix. Use a descriptive name for your branch: + +```bash +git checkout -b feature/your-amazing-feature +# or for a bugfix: +git checkout -b fix/your-bugfix-name +``` + +### 4. Make Your Changes +Make your changes to the codebase. Remember to: +- Follow the existing code style and conventions. +- Keep your changes focused on a single feature or bug. +- Add or update tests if applicable. + +### 5. Commit Your Changes +Write clear, concise commit messages. We prefer the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format: + +```bash +git commit -m "feat(ui): add new audio generation settings panel" +``` + +### 6. Push to Your Fork +Push your changes to your fork on GitHub: + +```bash +git push origin feature/your-amazing-feature +``` + +### 7. Submit a Pull Request +Go back to the original OpenBookLM repository and you will see a prompt to **Compare & pull request**. +- Click it, and fill out the pull request template. +- Clearly describe what changes you've made and why. +- Link any relevant issues (e.g., "Closes #42"). + +## PR Format Guidelines +- **Title:** Use a clear, descriptive title (e.g., `feat: integrate WebAssembly for local token counting`). +- **Description:** Provide a brief summary of the changes, the motivation behind them, and how to test them. +- **Screenshots:** If your PR includes UI changes, please attach before/after screenshots or GIFs. + +## Code of Conduct +Please be respectful and considerate of others when communicating in issues and pull requests. We aim to foster a welcoming and inclusive community. + +## Need Help? +If you're stuck or have questions, feel free to open a draft PR or create an issue asking for guidance. We are happy to help! + +Thank you for contributing! ๐ diff --git a/Dockerfile b/Dockerfile index daff5c8..9bb46e4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Use the official Bun image -FROM oven/bun:1.1 AS base +FROM oven/bun:latest AS base WORKDIR /app # No Clerk environment variables @@ -34,10 +34,6 @@ WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 -# Create system user -RUN addgroup --system --gid 1001 nodejs -RUN adduser --system --uid 1001 nextjs - # Copy built application COPY --from=builder /app/package.json ./package.json COPY --from=builder /app/bun.lock ./bun.lock @@ -48,9 +44,10 @@ COPY --from=builder /app/.next/static ./.next/static COPY --from=builder /app/prisma ./prisma # Set permissions -RUN chown -R nextjs:nodejs . +# Since we are using the official bun image, we just use the built-in bun user +RUN chown -R bun:bun . -USER nextjs +USER bun EXPOSE 3000 diff --git a/README.md b/README.md index 0693c91..dadb5b7 100644 --- a/README.md +++ b/README.md @@ -227,11 +227,7 @@ To get a local copy up and running, follow these steps. Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. -1. Fork the Project -2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) -3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) -4. Push to the Branch (`git push origin feature/AmazingFeature`) -5. Open a Pull Request +Please see our [CONTRIBUTING.md](CONTRIBUTING.md) file for detailed guidelines on how to fork the repository, structure your branches, format your pull requests, and start building!
diff --git a/src/app/api/chat/history/route.ts b/src/app/api/chat/history/route.ts index d3582df..a4dd324 100644 --- a/src/app/api/chat/history/route.ts +++ b/src/app/api/chat/history/route.ts @@ -9,13 +9,13 @@ export async function GET(req: Request) { const user = await getCurrentUser(); const userId = user?.id; if (!userId) { - return new NextResponse("Unauthorized", { status: 401 }); + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const url = new URL(req.url); const notebookId = url.searchParams.get("notebookId"); if (!notebookId) { - return new NextResponse("NotebookId is required", { status: 400 }); + return NextResponse.json({ error: "NotebookId is required" }, { status: 400 }); } const key = `chat:${userId}:${notebookId}`; @@ -67,7 +67,7 @@ export async function GET(req: Request) { return NextResponse.json(messages); } catch (error) { console.error("[CHAT_HISTORY_GET]", error); - return new NextResponse("Internal Error", { status: 500 }); + return NextResponse.json({ error: "Internal Error" }, { status: 500 }); } } @@ -76,12 +76,12 @@ export async function POST(req: Request) { const user = await getCurrentUser(); const userId = user?.id; if (!userId) { - return new NextResponse("Unauthorized", { status: 401 }); + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const { messages, notebookId } = await req.json(); if (!notebookId || !Array.isArray(messages)) { - return new NextResponse("Invalid request data", { status: 400 }); + return NextResponse.json({ error: "Invalid request data" }, { status: 400 }); } // Save to database first @@ -107,6 +107,6 @@ export async function POST(req: Request) { return NextResponse.json({ success: true }); } catch (error) { console.error("[CHAT_HISTORY_POST]", error); - return new NextResponse("Internal Error", { status: 500 }); + return NextResponse.json({ error: "Internal Error" }, { status: 500 }); } } diff --git a/src/app/api/chat/route.ts b/src/app/api/chat/route.ts index 2d9ce2e..73f43e6 100644 --- a/src/app/api/chat/route.ts +++ b/src/app/api/chat/route.ts @@ -32,7 +32,7 @@ export async function POST(req: Request) { try { const user = await getOrCreateUser(); if (!user) { - return new NextResponse("Unauthorized", { status: 401 }); + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const body = await req.json(); @@ -167,6 +167,6 @@ const finalMessages = [ { status: 400 } ); } - return new NextResponse("Internal Error", { status: 500 }); + return NextResponse.json({ error: "Internal Error" }, { status: 500 }); } } diff --git a/src/app/api/notebooks/[id]/route.ts b/src/app/api/notebooks/[id]/route.ts index 07196da..84a7e9b 100644 --- a/src/app/api/notebooks/[id]/route.ts +++ b/src/app/api/notebooks/[id]/route.ts @@ -28,7 +28,7 @@ export async function GET( ) { const params = await props.params; try { - const user = await getOrCreateUser(); + const user = await getOrCreateUser(true); if (!user) { return NextResponse.json( { error: "Please sign in to access notebooks" }, @@ -83,15 +83,15 @@ export async function PUT( ) { const params = await props.params; try { - const user = await getCurrentUser(); + const user = await getOrCreateUser(true); const userId = user?.id; if (!userId) { - return new NextResponse("Unauthorized", { status: 401 }); + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const redis = getRedisClient(); if (!redis) { - return new NextResponse("Redis connection failed", { status: 500 }); + return NextResponse.json({ error: "Redis connection failed" }, { status: 500 }); } const notebook = await req.json(); @@ -102,7 +102,7 @@ export async function PUT( return NextResponse.json({ success: true }); } catch (error) { console.error("[NOTEBOOK_PUT]", error); - return new NextResponse("Internal Error", { status: 500 }); + return NextResponse.json({ error: "Internal Error" }, { status: 500 }); } } @@ -112,7 +112,7 @@ export async function PATCH( ) { const params = await props.params; try { - const user = await getOrCreateUser(); + const user = await getOrCreateUser(true); if (!user) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } diff --git a/src/app/api/notebooks/route.ts b/src/app/api/notebooks/route.ts index b6b25aa..76ebf09 100644 --- a/src/app/api/notebooks/route.ts +++ b/src/app/api/notebooks/route.ts @@ -9,9 +9,9 @@ export async function GET() { const currentUser = await getCurrentUser(); const userId = currentUser?.id; if (!userId) { - return new NextResponse("Unauthorized", { status: 401 }); + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } - const user = await getOrCreateUser(); + const user = await getOrCreateUser(true); const notebooks = await prisma.notebook.findMany({ where: { userId: user?.id, @@ -28,22 +28,22 @@ export async function GET() { return NextResponse.json(notebooks); } catch (error) { console.error("[NOTEBOOKS_GET]", error); - return new NextResponse("Internal Error", { status: 500 }); + return NextResponse.json({ error: "Internal Error" }, { status: 500 }); } } export async function POST(req: Request) { try { - const user = await getOrCreateUser(); + const user = await getOrCreateUser(true); if (!user) { - return new NextResponse("Unauthorized", { status: 401 }); + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const body = await req.json(); const { title, provider } = body; if (!title) { - return new NextResponse("Title is required", { status: 400 }); + return NextResponse.json({ error: "Title is required" }, { status: 400 }); } const notebook = await prisma.notebook.create({ @@ -58,6 +58,6 @@ export async function POST(req: Request) { return NextResponse.json(notebook); } catch (error) { console.error("[NOTEBOOKS_POST]", error); - return new NextResponse("Internal Error", { status: 500 }); + return NextResponse.json({ error: "Internal Error" }, { status: 500 }); } } diff --git a/src/app/home-page.tsx b/src/app/home-page.tsx index a1b2e70..7a6dcba 100644 --- a/src/app/home-page.tsx +++ b/src/app/home-page.tsx @@ -10,7 +10,15 @@ import { PanelLeftClose, PanelRightClose, Trash2, + Settings, + LayoutGrid } from "lucide-react"; +import Image from "next/image"; +import { useSession, signOut } from "@/lib/auth-client"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { DropdownMenuSeparator } from "@/components/ui/dropdown-menu"; +import { LogOut } from "lucide-react"; +import { LoginModal } from "@/components/login-modal"; import { CreateNotebookDialog } from "@/components/create-notebook-dialog"; import { Card } from "@/components/ui/card"; import { ShareDialog } from "@/components/share-dialog"; @@ -24,6 +32,7 @@ import { import { toast } from "sonner"; import { NotImplementedDialog } from "@/components/not-implemented-dialog"; +import { NotebookCard } from "@/components/notebook-card"; interface Notebook { id: string; @@ -44,6 +53,8 @@ export default function HomePage({ const [viewMode, setViewMode] = useState<"grid" | "list">("grid"); const [deletingNotebookId, setDeletingNotebookId] = useState{session.user.name}
} + {session.user.email && ( ++ {session.user.email} +
+ )} +Be the first to know when we launch!
-{children}
, - h1: ({ children }) =>
- {children}
-
- ),
- pre: ({ children }) => {children},
- blockquote: ({ children }) => (
- - {children} -- ), - a: ({ children, href }) => ( - - {children} - - ), -}; diff --git a/src/components/model-selector.tsx b/src/components/model-selector.tsx deleted file mode 100644 index b5b67ab..0000000 --- a/src/components/model-selector.tsx +++ /dev/null @@ -1,97 +0,0 @@ -"use client" - -import { useState } from 'react' -import { ChevronRight } from 'lucide-react' -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog" -import { Button } from "@/components/ui/button" - -const models = [ - { id: 'llama', name: 'LLama 70B' }, - { id: 'claude', name: 'Claude 3.5 Sonnet' }, - { id: 'openai', name: 'OpenAI o1' }, - { id: 'custom', name: 'Bring Your Own' }, -] - -export function ModelSelector() { - const [selectedModel, setSelectedModel] = useState(models[0]) - const [showCustomModal, setShowCustomModal] = useState(false) - - const handleModelSelect = (model: typeof models[0]) => { - if (model.id === 'custom') { - setShowCustomModal(true) - } else { - setSelectedModel(model) - } - } - - return ( - <> -
{session.user.name}
} + {session.user.email && ( ++ {session.user.email} +
+ )} +