mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 12:25:22 +00:00
Authentication (#164)
This commit is contained in:
parent
7029aa70c1
commit
6cf10b4988
25 changed files with 634 additions and 132 deletions
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"bradlc.vscode-tailwindcss"
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"prisma.prisma"
|
||||
]
|
||||
}
|
||||
26
Dockerfile
26
Dockerfile
|
|
@ -10,6 +10,14 @@ RUN go mod download
|
|||
COPY vendor/zoekt ./
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -o /cmd/ ./cmd/...
|
||||
|
||||
# ------ Build Database ------
|
||||
FROM node-alpine AS database-builder
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json yarn.lock* ./
|
||||
COPY ./packages/db ./packages/db
|
||||
RUN yarn workspace @sourcebot/db install --frozen-lockfile
|
||||
|
||||
# ------ Build Web ------
|
||||
FROM node-alpine AS web-builder
|
||||
RUN apk add --no-cache libc6-compat
|
||||
|
|
@ -17,6 +25,8 @@ WORKDIR /app
|
|||
|
||||
COPY package.json yarn.lock* ./
|
||||
COPY ./packages/web ./packages/web
|
||||
COPY --from=database-builder /app/node_modules ./node_modules
|
||||
COPY --from=database-builder /app/packages/db ./packages/db
|
||||
|
||||
# Fixes arm64 timeouts
|
||||
RUN yarn config set registry https://registry.npmjs.org/
|
||||
|
|
@ -27,18 +37,16 @@ ENV NEXT_TELEMETRY_DISABLED=1
|
|||
ARG NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED=BAKED_NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED
|
||||
ARG NEXT_PUBLIC_SOURCEBOT_VERSION=BAKED_NEXT_PUBLIC_SOURCEBOT_VERSION
|
||||
ENV NEXT_PUBLIC_POSTHOG_PAPIK=BAKED_NEXT_PUBLIC_POSTHOG_PAPIK
|
||||
|
||||
# @nocheckin: This was interfering with the the `matcher` regex in middleware.ts,
|
||||
# causing regular expressions parsing errors when making a request. It's unclear
|
||||
# why exactly this was happening, but it's likely due to a bad replacement happening
|
||||
# in the `sed` command.
|
||||
# @note: leading "/" is required for the basePath property. @see: https://nextjs.org/docs/app/api-reference/next-config-js/basePath
|
||||
ARG NEXT_PUBLIC_DOMAIN_SUB_PATH=/BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH
|
||||
# ARG NEXT_PUBLIC_DOMAIN_SUB_PATH=/BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH
|
||||
|
||||
RUN yarn workspace @sourcebot/web build
|
||||
|
||||
# ------ Build Database ------
|
||||
FROM node-alpine AS database-builder
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json yarn.lock* ./
|
||||
COPY ./packages/db ./packages/db
|
||||
RUN yarn workspace @sourcebot/db install --frozen-lockfile
|
||||
|
||||
|
||||
# ------ Build Backend ------
|
||||
FROM node-alpine AS backend-builder
|
||||
|
|
|
|||
|
|
@ -107,46 +107,50 @@ echo -e "\e[34m[Info] Using config file at: '$CONFIG_PATH'.\e[0m"
|
|||
done
|
||||
}
|
||||
|
||||
|
||||
# Update specifically NEXT_PUBLIC_DOMAIN_SUB_PATH w/o requiring a rebuild.
|
||||
# Ultimately, the DOMAIN_SUB_PATH sets the `basePath` param in the next.config.mjs.
|
||||
# Similar to above, we pass in a `BAKED_` sentinal value into next.config.mjs at build
|
||||
# time. Unlike above, the `basePath` configuration is set in files other than just javascript
|
||||
# code (e.g., manifest files, css files, etc.), so this section has subtle differences.
|
||||
# @nocheckin: This was interfering with the the `matcher` regex in middleware.ts,
|
||||
# causing regular expressions parsing errors when making a request. It's unclear
|
||||
# why exactly this was happening, but it's likely due to a bad replacement happening
|
||||
# in the `sed` command.
|
||||
#
|
||||
# @see: https://nextjs.org/docs/app/api-reference/next-config-js/basePath
|
||||
# @see: https://phase.dev/blog/nextjs-public-runtime-variables/
|
||||
{
|
||||
if [ ! -z "$DOMAIN_SUB_PATH" ]; then
|
||||
# If the sub-path is "/", this creates problems with certain replacements. For example:
|
||||
# /BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH/_next/image -> //_next/image (notice the double slash...)
|
||||
# To get around this, we default to an empty sub-path, which is the default when no sub-path is defined.
|
||||
if [ "$DOMAIN_SUB_PATH" = "/" ]; then
|
||||
DOMAIN_SUB_PATH=""
|
||||
# # Update specifically NEXT_PUBLIC_DOMAIN_SUB_PATH w/o requiring a rebuild.
|
||||
# # Ultimately, the DOMAIN_SUB_PATH sets the `basePath` param in the next.config.mjs.
|
||||
# # Similar to above, we pass in a `BAKED_` sentinal value into next.config.mjs at build
|
||||
# # time. Unlike above, the `basePath` configuration is set in files other than just javascript
|
||||
# # code (e.g., manifest files, css files, etc.), so this section has subtle differences.
|
||||
# #
|
||||
# # @see: https://nextjs.org/docs/app/api-reference/next-config-js/basePath
|
||||
# # @see: https://phase.dev/blog/nextjs-public-runtime-variables/
|
||||
# {
|
||||
# if [ ! -z "$DOMAIN_SUB_PATH" ]; then
|
||||
# # If the sub-path is "/", this creates problems with certain replacements. For example:
|
||||
# # /BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH/_next/image -> //_next/image (notice the double slash...)
|
||||
# # To get around this, we default to an empty sub-path, which is the default when no sub-path is defined.
|
||||
# if [ "$DOMAIN_SUB_PATH" = "/" ]; then
|
||||
# DOMAIN_SUB_PATH=""
|
||||
|
||||
# Otherwise, we need to ensure that the sub-path starts with a slash, since this is a requirement
|
||||
# for the basePath property. For example, assume DOMAIN_SUB_PATH=/bot, then:
|
||||
# /BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH/_next/image -> /bot/_next/image
|
||||
elif [[ ! "$DOMAIN_SUB_PATH" =~ ^/ ]]; then
|
||||
DOMAIN_SUB_PATH="/$DOMAIN_SUB_PATH"
|
||||
fi
|
||||
fi
|
||||
# # Otherwise, we need to ensure that the sub-path starts with a slash, since this is a requirement
|
||||
# # for the basePath property. For example, assume DOMAIN_SUB_PATH=/bot, then:
|
||||
# # /BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH/_next/image -> /bot/_next/image
|
||||
# elif [[ ! "$DOMAIN_SUB_PATH" =~ ^/ ]]; then
|
||||
# DOMAIN_SUB_PATH="/$DOMAIN_SUB_PATH"
|
||||
# fi
|
||||
# fi
|
||||
|
||||
if [ ! -z "$DOMAIN_SUB_PATH" ]; then
|
||||
echo -e "\e[34m[Info] DOMAIN_SUB_PATH was set to "$DOMAIN_SUB_PATH". Overriding default path.\e[0m"
|
||||
fi
|
||||
# if [ ! -z "$DOMAIN_SUB_PATH" ]; then
|
||||
# echo -e "\e[34m[Info] DOMAIN_SUB_PATH was set to "$DOMAIN_SUB_PATH". Overriding default path.\e[0m"
|
||||
# fi
|
||||
|
||||
# Always set NEXT_PUBLIC_DOMAIN_SUB_PATH to DOMAIN_SUB_PATH (even if it is empty!!)
|
||||
export NEXT_PUBLIC_DOMAIN_SUB_PATH="$DOMAIN_SUB_PATH"
|
||||
# # Always set NEXT_PUBLIC_DOMAIN_SUB_PATH to DOMAIN_SUB_PATH (even if it is empty!!)
|
||||
# export NEXT_PUBLIC_DOMAIN_SUB_PATH="$DOMAIN_SUB_PATH"
|
||||
|
||||
# Iterate over _all_ files in the web directory, making substitutions for the `BAKED_` sentinal values
|
||||
# with their actual desired runtime value.
|
||||
find /app/packages/web -type f |
|
||||
while read file; do
|
||||
# @note: the leading "/" is required here as it is included at build time. See Dockerfile.
|
||||
sed -i "s|/BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH|${NEXT_PUBLIC_DOMAIN_SUB_PATH}|g" "$file"
|
||||
done
|
||||
}
|
||||
# # Iterate over _all_ files in the web directory, making substitutions for the `BAKED_` sentinal values
|
||||
# # with their actual desired runtime value.
|
||||
# find /app/packages/web -type f |
|
||||
# while read file; do
|
||||
# # @note: the leading "/" is required here as it is included at build time. See Dockerfile.
|
||||
# sed -i "s|/BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH|${NEXT_PUBLIC_DOMAIN_SUB_PATH}|g" "$file"
|
||||
# done
|
||||
# }
|
||||
|
||||
|
||||
# Run supervisord
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
"scripts": {
|
||||
"build": "yarn workspaces run build",
|
||||
"test": "yarn workspaces run test",
|
||||
"dev": "npm-run-all --print-label --parallel dev:zoekt dev:backend dev:web dev:redis",
|
||||
"dev:mt": "npm-run-all --print-label --parallel dev:zoekt:mt dev:backend dev:web dev:redis",
|
||||
"dev": "yarn workspace @sourcebot/db prisma:migrate:dev && npm-run-all --print-label --parallel dev:zoekt dev:backend dev:web dev:redis",
|
||||
"dev:mt": "yarn workspace @sourcebot/db prisma:migrate:dev && npm-run-all --print-label --parallel dev:zoekt:mt dev:backend dev:web dev:redis",
|
||||
"dev:zoekt": "export PATH=\"$PWD/bin:$PATH\" && export SRC_TENANT_ENFORCEMENT_MODE=none && zoekt-webserver -index .sourcebot/index -rpc",
|
||||
"dev:zoekt:mt": "export PATH=\"$PWD/bin:$PATH\" && export SRC_TENANT_ENFORCEMENT_MODE=strict && zoekt-webserver -index .sourcebot/index -rpc",
|
||||
"dev:backend": "yarn workspace @sourcebot/backend dev:watch",
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
"lowdb": "^7.0.1",
|
||||
"micromatch": "^4.0.8",
|
||||
"posthog-node": "^4.2.1",
|
||||
"@sourcebot/db": "^0.1.0",
|
||||
"simple-git": "^3.27.0",
|
||||
"strip-json-comments": "^5.0.1",
|
||||
"winston": "^3.15.0",
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ export const syncConfig = async (configPath: string, db: PrismaClient, signal: A
|
|||
name: repoName,
|
||||
tenantId: 0, // TODO: add support for tenantId in GitLab config
|
||||
isFork,
|
||||
isArchived: project.archived,
|
||||
isArchived: !!project.archived,
|
||||
metadata: {
|
||||
'zoekt.web-url-type': 'gitlab',
|
||||
'zoekt.web-url': project.web_url,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import micromatch from "micromatch";
|
|||
import { createLogger } from "./logger.js";
|
||||
import { GitLabConfig } from "./schemas/v2.js";
|
||||
import { AppContext } from "./types.js";
|
||||
import { getTokenFromConfig, marshalBool, measure } from "./utils.js";
|
||||
import { getTokenFromConfig, measure } from "./utils.js";
|
||||
|
||||
const logger = createLogger("GitLab");
|
||||
export const GITLAB_CLOUD_HOSTNAME = "gitlab.com";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"name" TEXT,
|
||||
"email" TEXT,
|
||||
"emailVerified" DATETIME,
|
||||
"image" TEXT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Account" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"userId" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"provider" TEXT NOT NULL,
|
||||
"providerAccountId" TEXT NOT NULL,
|
||||
"refresh_token" TEXT,
|
||||
"access_token" TEXT,
|
||||
"expires_at" INTEGER,
|
||||
"token_type" TEXT,
|
||||
"scope" TEXT,
|
||||
"id_token" TEXT,
|
||||
"session_state" TEXT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "VerificationToken" (
|
||||
"identifier" TEXT NOT NULL,
|
||||
"token" TEXT NOT NULL,
|
||||
"expires" DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token");
|
||||
|
|
@ -41,3 +41,48 @@ model Repo {
|
|||
|
||||
@@unique([external_id, external_codeHostUrl])
|
||||
}
|
||||
|
||||
// @see : https://authjs.dev/concepts/database-models#user
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
name String?
|
||||
email String? @unique
|
||||
emailVerified DateTime?
|
||||
image String?
|
||||
accounts Account[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
// @see : https://authjs.dev/concepts/database-models#account
|
||||
model Account {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
type String
|
||||
provider String
|
||||
providerAccountId String
|
||||
refresh_token String?
|
||||
access_token String?
|
||||
expires_at Int?
|
||||
token_type String?
|
||||
scope String?
|
||||
id_token String?
|
||||
session_state String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([provider, providerAccountId])
|
||||
}
|
||||
|
||||
// @see : https://authjs.dev/concepts/database-models#verificationtoken
|
||||
model VerificationToken {
|
||||
identifier String
|
||||
token String
|
||||
expires DateTime
|
||||
|
||||
@@unique([identifier, token])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,14 @@ const nextConfig = {
|
|||
// This is required to support PostHog trailing slash API requests
|
||||
skipTrailingSlashRedirect: true,
|
||||
|
||||
// @nocheckin: This was interfering with the the `matcher` regex in middleware.ts,
|
||||
// causing regular expressions parsing errors when making a request. It's unclear
|
||||
// why exactly this was happening, but it's likely due to a bad replacement happening
|
||||
// in the `sed` command.
|
||||
// @note: this is evaluated at build time.
|
||||
...(process.env.NEXT_PUBLIC_DOMAIN_SUB_PATH ? {
|
||||
basePath: process.env.NEXT_PUBLIC_DOMAIN_SUB_PATH,
|
||||
} : {})
|
||||
// ...(process.env.NEXT_PUBLIC_DOMAIN_SUB_PATH ? {
|
||||
// basePath: process.env.NEXT_PUBLIC_DOMAIN_SUB_PATH,
|
||||
// } : {})
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
"test": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/prisma-adapter": "^2.7.4",
|
||||
"@codemirror/commands": "^6.6.0",
|
||||
"@codemirror/lang-cpp": "^6.0.2",
|
||||
"@codemirror/lang-css": "^6.3.0",
|
||||
|
|
@ -39,6 +40,7 @@
|
|||
"@hookform/resolvers": "^3.9.0",
|
||||
"@iconify/react": "^5.1.0",
|
||||
"@iizukak/codemirror-lang-wgsl": "^0.3.0",
|
||||
"@radix-ui/react-avatar": "^1.1.2",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
|
|
@ -89,6 +91,7 @@
|
|||
"http-status-codes": "^2.3.0",
|
||||
"lucide-react": "^0.435.0",
|
||||
"next": "14.2.21",
|
||||
"next-auth": "^5.0.0-beta.25",
|
||||
"next-themes": "^0.3.0",
|
||||
"posthog-js": "^1.161.5",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
|
|
@ -119,6 +122,7 @@
|
|||
"jsdom": "^25.0.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8",
|
||||
"@sourcebot/db": "^0.1.0",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5",
|
||||
"vite-tsconfig-paths": "^5.1.3",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
import { handlers } from "@/auth";
|
||||
export const { GET, POST } = handlers;
|
||||
|
|
@ -8,13 +8,15 @@ import { NextRequest } from "next/server";
|
|||
|
||||
export const POST = async (request: NextRequest) => {
|
||||
const body = await request.json();
|
||||
const tenantId = await request.headers.get("X-Tenant-ID");
|
||||
const tenantId = request.headers.get("X-Tenant-ID");
|
||||
|
||||
console.log(`Search request received. Tenant ID: ${tenantId}`);
|
||||
|
||||
const parsed = await searchRequestSchema.safeParseAsync({
|
||||
...body,
|
||||
...(tenantId && { tenantId: parseInt(tenantId) }),
|
||||
...(tenantId ? {
|
||||
tenantId: parseInt(tenantId)
|
||||
} : {}),
|
||||
});
|
||||
if (!parsed.success) {
|
||||
return serviceErrorResponse(
|
||||
|
|
|
|||
|
|
@ -1,90 +1,112 @@
|
|||
'use client';
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { NavigationMenu as NavigationMenuBase, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, navigationMenuTriggerStyle } from "@/components/ui/navigation-menu";
|
||||
import Link from "next/link";
|
||||
import { GitHubLogoIcon, DiscordLogoIcon } from "@radix-ui/react-icons";
|
||||
import { SettingsDropdown } from "./settingsDropdown";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import Image from "next/image";
|
||||
import logoDark from "../../../public/sb_logo_dark_small.png";
|
||||
import logoLight from "../../../public/sb_logo_light_small.png";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { ProfilePicture } from "./profilePicture";
|
||||
import { signOut } from "@/auth";
|
||||
import { SettingsDropdown } from "./settingsDropdown";
|
||||
import { GitHubLogoIcon, DiscordLogoIcon } from "@radix-ui/react-icons";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
const SOURCEBOT_DISCORD_URL = "https://discord.gg/6Fhp27x7Pb";
|
||||
const SOURCEBOT_GITHUB_URL = "https://github.com/sourcebot-dev/sourcebot";
|
||||
|
||||
export const NavigationMenu = () => {
|
||||
const router = useRouter();
|
||||
export const NavigationMenu = async () => {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-screen h-fit">
|
||||
<div className="flex flex-row justify-between items-center py-1.5 px-3">
|
||||
<div className="flex flex-row items-center">
|
||||
<div
|
||||
className="mr-3 cursor-pointer"
|
||||
onClick={() => {
|
||||
router.push("/");
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src={logoDark}
|
||||
className="h-11 w-auto hidden dark:block"
|
||||
alt={"Sourcebot logo"}
|
||||
priority={true}
|
||||
/>
|
||||
<Image
|
||||
src={logoLight}
|
||||
className="h-11 w-auto block dark:hidden"
|
||||
alt={"Sourcebot logo"}
|
||||
priority={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row justify-between items-center py-1.5 px-3">
|
||||
<div className="flex flex-row items-center">
|
||||
<Link
|
||||
href="/"
|
||||
className="mr-3 cursor-pointer"
|
||||
>
|
||||
<Image
|
||||
src={logoDark}
|
||||
className="h-11 w-auto hidden dark:block"
|
||||
alt={"Sourcebot logo"}
|
||||
priority={true}
|
||||
/>
|
||||
<Image
|
||||
src={logoLight}
|
||||
className="h-11 w-auto block dark:hidden"
|
||||
alt={"Sourcebot logo"}
|
||||
priority={true}
|
||||
/>
|
||||
</Link>
|
||||
|
||||
<NavigationMenuBase>
|
||||
<NavigationMenuList>
|
||||
<NavigationMenuItem>
|
||||
<Link href="/" legacyBehavior passHref>
|
||||
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
||||
Search
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<Link href="/repos" legacyBehavior passHref>
|
||||
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
||||
Repositories
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
</NavigationMenuList>
|
||||
</NavigationMenuBase>
|
||||
</div>
|
||||
<NavigationMenuBase>
|
||||
<NavigationMenuList>
|
||||
<NavigationMenuItem>
|
||||
<Link href="/" legacyBehavior passHref>
|
||||
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
||||
Search
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<Link href="/repos" legacyBehavior passHref>
|
||||
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
||||
Repositories
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
</NavigationMenuList>
|
||||
</NavigationMenuBase>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<form
|
||||
action={async () => {
|
||||
"use server";
|
||||
redirect(SOURCEBOT_DISCORD_URL);
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => {
|
||||
window.open(SOURCEBOT_DISCORD_URL, "_blank");
|
||||
}}
|
||||
type="submit"
|
||||
>
|
||||
<DiscordLogoIcon className="w-4 h-4" />
|
||||
</Button>
|
||||
</form>
|
||||
<form
|
||||
action={async () => {
|
||||
"use server";
|
||||
redirect(SOURCEBOT_GITHUB_URL);
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => {
|
||||
window.open(SOURCEBOT_GITHUB_URL, "_blank");
|
||||
}}
|
||||
type="submit"
|
||||
>
|
||||
<GitHubLogoIcon className="w-4 h-4" />
|
||||
</Button>
|
||||
<SettingsDropdown />
|
||||
</div>
|
||||
</form>
|
||||
<SettingsDropdown />
|
||||
<form
|
||||
action={async () => {
|
||||
"use server";
|
||||
await signOut();
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="outline"
|
||||
size="default"
|
||||
>
|
||||
Logout
|
||||
</Button>
|
||||
</form>
|
||||
<ProfilePicture />
|
||||
</div>
|
||||
<Separator />
|
||||
</div>
|
||||
<Separator />
|
||||
</div>
|
||||
|
||||
|
||||
)
|
||||
|
|
|
|||
20
packages/web/src/app/components/profilePicture.tsx
Normal file
20
packages/web/src/app/components/profilePicture.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { auth } from "@/auth"
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
AvatarImage,
|
||||
} from "@/components/ui/avatar"
|
||||
|
||||
export const ProfilePicture = async () => {
|
||||
const session = await auth()
|
||||
|
||||
return (
|
||||
<Avatar>
|
||||
<AvatarImage
|
||||
src={session?.user?.image ?? ""}
|
||||
alt="@shadcn"
|
||||
/>
|
||||
<AvatarFallback>U</AvatarFallback>
|
||||
</Avatar>
|
||||
)
|
||||
}
|
||||
87
packages/web/src/app/login/page.tsx
Normal file
87
packages/web/src/app/login/page.tsx
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import { providerMap, signIn } from "@/auth"
|
||||
import { AuthError } from "next-auth"
|
||||
import { redirect } from "next/navigation"
|
||||
import logoDark from "@/public/sb_logo_dark_large.png";
|
||||
import logoLight from "@/public/sb_logo_light_large.png";
|
||||
import githubLogo from "@/public/github.svg";
|
||||
import Image from "next/image";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
const SIGNIN_ERROR_URL = "/login";
|
||||
|
||||
export default async function Login(props: {
|
||||
searchParams: { callbackUrl: string | undefined }
|
||||
}) {
|
||||
return (
|
||||
<div className="flex flex-col justify-center items-center h-screen">
|
||||
<div className="flex flex-col items-center border p-16 rounded-lg gap-6">
|
||||
<div>
|
||||
<Image
|
||||
src={logoDark}
|
||||
className="h-16 w-auto hidden dark:block"
|
||||
alt={"Sourcebot logo"}
|
||||
priority={true}
|
||||
/>
|
||||
<Image
|
||||
src={logoLight}
|
||||
className="h-16 w-auto block dark:hidden"
|
||||
alt={"Sourcebot logo"}
|
||||
priority={true}
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
Object.values(providerMap)
|
||||
.map((provider) => {
|
||||
if (provider.id === "github") {
|
||||
return {
|
||||
provider,
|
||||
logo: githubLogo,
|
||||
}
|
||||
}
|
||||
|
||||
return { provider }
|
||||
})
|
||||
.map(({ provider, logo }) => (
|
||||
<form
|
||||
key={provider.id}
|
||||
action={async () => {
|
||||
"use server"
|
||||
try {
|
||||
await signIn(provider.id, {
|
||||
redirectTo: props.searchParams?.callbackUrl ?? "",
|
||||
})
|
||||
} catch (error) {
|
||||
// Signin can fail for a number of reasons, such as the user
|
||||
// not existing, or the user not having the correct role.
|
||||
// In some cases, you may want to redirect to a custom error
|
||||
if (error instanceof AuthError) {
|
||||
return redirect(`${SIGNIN_ERROR_URL}?error=${error.type}`)
|
||||
}
|
||||
|
||||
// Otherwise if a redirects happens Next.js can handle it
|
||||
// so you can just re-thrown the error and let Next.js handle it.
|
||||
// Docs:
|
||||
// https://nextjs.org/docs/app/api-reference/functions/redirect#server-component
|
||||
throw error
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
type="submit"
|
||||
>
|
||||
{logo && (
|
||||
<Image
|
||||
src={logo}
|
||||
alt={provider.name}
|
||||
className="w-5 h-5 invert dark:invert-0 mr-2"
|
||||
/>
|
||||
)}
|
||||
Sign in with {provider.name}
|
||||
</Button>
|
||||
</form>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -2,8 +2,8 @@ import { listRepositories } from "@/lib/server/searchService";
|
|||
import { isServiceError } from "@/lib/utils";
|
||||
import Image from "next/image";
|
||||
import { Suspense } from "react";
|
||||
import logoDark from "../../public/sb_logo_dark_large.png";
|
||||
import logoLight from "../../public/sb_logo_light_large.png";
|
||||
import logoDark from "@/public/sb_logo_dark_large.png";
|
||||
import logoLight from "@/public/sb_logo_light_large.png";
|
||||
import { NavigationMenu } from "./components/navigationMenu";
|
||||
import { RepositoryCarousel } from "./components/repositoryCarousel";
|
||||
import { SearchBar } from "./components/searchBar";
|
||||
|
|
|
|||
38
packages/web/src/auth.ts
Normal file
38
packages/web/src/auth.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import NextAuth from "next-auth"
|
||||
import GitHub from "next-auth/providers/github"
|
||||
import { PrismaAdapter } from "@auth/prisma-adapter"
|
||||
import { prisma } from "@/prisma";
|
||||
import type { Provider } from "next-auth/providers"
|
||||
import { AUTH_GITHUB_CLIENT_ID, AUTH_GITHUB_CLIENT_SECRET, AUTH_SECRET } from "./lib/environment";
|
||||
|
||||
const providers: Provider[] = [
|
||||
GitHub({
|
||||
clientId: AUTH_GITHUB_CLIENT_ID,
|
||||
clientSecret: AUTH_GITHUB_CLIENT_SECRET,
|
||||
}),
|
||||
];
|
||||
|
||||
// @see: https://authjs.dev/guides/pages/signin
|
||||
export const providerMap = providers
|
||||
.map((provider) => {
|
||||
if (typeof provider === "function") {
|
||||
const providerData = provider()
|
||||
return { id: providerData.id, name: providerData.name }
|
||||
} else {
|
||||
return { id: provider.id, name: provider.name }
|
||||
}
|
||||
})
|
||||
.filter((provider) => provider.id !== "credentials");
|
||||
|
||||
|
||||
export const { handlers, signIn, signOut, auth } = NextAuth({
|
||||
secret: AUTH_SECRET,
|
||||
adapter: PrismaAdapter(prisma),
|
||||
session: {
|
||||
strategy: "jwt",
|
||||
},
|
||||
providers: providers,
|
||||
pages: {
|
||||
signIn: "/login"
|
||||
}
|
||||
})
|
||||
50
packages/web/src/components/ui/avatar.tsx
Normal file
50
packages/web/src/components/ui/avatar.tsx
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Avatar = React.forwardRef<
|
||||
React.ElementRef<typeof AvatarPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AvatarPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Avatar.displayName = AvatarPrimitive.Root.displayName
|
||||
|
||||
const AvatarImage = React.forwardRef<
|
||||
React.ElementRef<typeof AvatarPrimitive.Image>,
|
||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AvatarPrimitive.Image
|
||||
ref={ref}
|
||||
className={cn("aspect-square h-full w-full", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AvatarImage.displayName = AvatarPrimitive.Image.displayName
|
||||
|
||||
const AvatarFallback = React.forwardRef<
|
||||
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AvatarPrimitive.Fallback
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-full w-full items-center justify-center rounded-full bg-muted",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
|
||||
|
||||
export { Avatar, AvatarImage, AvatarFallback }
|
||||
|
|
@ -6,3 +6,7 @@ export const ZOEKT_WEBSERVER_URL = getEnv(process.env.ZOEKT_WEBSERVER_URL, "http
|
|||
export const SHARD_MAX_MATCH_COUNT = getEnvNumber(process.env.SHARD_MAX_MATCH_COUNT, 10000);
|
||||
export const TOTAL_MAX_MATCH_COUNT = getEnvNumber(process.env.TOTAL_MAX_MATCH_COUNT, 100000);
|
||||
export const NODE_ENV = process.env.NODE_ENV;
|
||||
|
||||
export const AUTH_SECRET = getEnv(process.env.AUTH_SECRET); // Generate using `npx auth secret`
|
||||
export const AUTH_GITHUB_CLIENT_ID = getEnv(process.env.AUTH_GITHUB_CLIENT_ID);
|
||||
export const AUTH_GITHUB_CLIENT_SECRET = getEnv(process.env.AUTH_GITHUB_CLIENT_SECRET);
|
||||
|
|
@ -5,4 +5,5 @@ export enum ErrorCode {
|
|||
REPOSITORY_NOT_FOUND = 'REPOSITORY_NOT_FOUND',
|
||||
FILE_NOT_FOUND = 'FILE_NOT_FOUND',
|
||||
INVALID_REQUEST_BODY = 'INVALID_REQUEST_BODY',
|
||||
NOT_AUTHENTICATED = 'NOT_AUTHENTICATED',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,3 +68,11 @@ export const unexpectedError = (message: string): ServiceError => {
|
|||
message: `Unexpected error: ${message}`,
|
||||
};
|
||||
}
|
||||
|
||||
export const notAuthenticated = (): ServiceError => {
|
||||
return {
|
||||
statusCode: StatusCodes.UNAUTHORIZED,
|
||||
errorCode: ErrorCode.NOT_AUTHENTICATED,
|
||||
message: "Not authenticated",
|
||||
}
|
||||
}
|
||||
46
packages/web/src/middleware.ts
Normal file
46
packages/web/src/middleware.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
|
||||
import { auth } from "@/auth";
|
||||
import { Session } from "next-auth";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { notAuthenticated, serviceErrorResponse } from "./lib/serviceError";
|
||||
|
||||
interface NextAuthRequest extends NextRequest {
|
||||
auth: Session | null;
|
||||
}
|
||||
|
||||
const apiMiddleware = (req: NextAuthRequest) => {
|
||||
if (req.nextUrl.pathname.startsWith("/api/auth")) {
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
if (!req.auth) {
|
||||
return serviceErrorResponse(
|
||||
notAuthenticated(),
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
const defaultMiddleware = (req: NextAuthRequest) => {
|
||||
if (!req.auth && req.nextUrl.pathname !== "/login") {
|
||||
const newUrl = new URL("/login", req.nextUrl.origin);
|
||||
return NextResponse.redirect(newUrl);
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
export default auth(async (req) => {
|
||||
if (req.nextUrl.pathname.startsWith("/api")) {
|
||||
return apiMiddleware(req);
|
||||
}
|
||||
|
||||
return defaultMiddleware(req);
|
||||
})
|
||||
|
||||
|
||||
export const config = {
|
||||
// https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
|
||||
matcher: ['/((?!_next/static|ingest|_next/image|favicon.ico|sitemap.xml|robots.txt).*)'],
|
||||
}
|
||||
6
packages/web/src/prisma.ts
Normal file
6
packages/web/src/prisma.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import { PrismaClient } from "@sourcebot/db";
|
||||
|
||||
// @see: https://authjs.dev/getting-started/adapters/prisma
|
||||
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient }
|
||||
export const prisma = globalForPrisma.prisma || new PrismaClient()
|
||||
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma
|
||||
140
yarn.lock
140
yarn.lock
|
|
@ -16,6 +16,37 @@
|
|||
"@types/json-schema" "^7.0.15"
|
||||
js-yaml "^4.1.0"
|
||||
|
||||
"@auth/core@0.37.2":
|
||||
version "0.37.2"
|
||||
resolved "https://registry.yarnpkg.com/@auth/core/-/core-0.37.2.tgz#0db8a94a076846bd88eb7f9273618513e2285cb2"
|
||||
integrity sha512-kUvzyvkcd6h1vpeMAojK2y7+PAV5H+0Cc9+ZlKYDFhDY31AlvsB+GW5vNO4qE3Y07KeQgvNO9U0QUx/fN62kBw==
|
||||
dependencies:
|
||||
"@panva/hkdf" "^1.2.1"
|
||||
"@types/cookie" "0.6.0"
|
||||
cookie "0.7.1"
|
||||
jose "^5.9.3"
|
||||
oauth4webapi "^3.0.0"
|
||||
preact "10.11.3"
|
||||
preact-render-to-string "5.2.3"
|
||||
|
||||
"@auth/core@0.37.4":
|
||||
version "0.37.4"
|
||||
resolved "https://registry.yarnpkg.com/@auth/core/-/core-0.37.4.tgz#c51410aa7d0997fa22a07a196d2c21c8b1bca71b"
|
||||
integrity sha512-HOXJwXWXQRhbBDHlMU0K/6FT1v+wjtzdKhsNg0ZN7/gne6XPsIrjZ4daMcFnbq0Z/vsAbYBinQhhua0d77v7qw==
|
||||
dependencies:
|
||||
"@panva/hkdf" "^1.2.1"
|
||||
jose "^5.9.6"
|
||||
oauth4webapi "^3.1.1"
|
||||
preact "10.24.3"
|
||||
preact-render-to-string "6.5.11"
|
||||
|
||||
"@auth/prisma-adapter@^2.7.4":
|
||||
version "2.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@auth/prisma-adapter/-/prisma-adapter-2.7.4.tgz#4890be47a9f227f449832302d955c565c02879ee"
|
||||
integrity sha512-3T/X94R9J1sxOLQtsD3ijIZ0JGHPXlZQxRr/8NpnZBJ3KGxun/mNsZ1MwMRhTxy0mmn9JWXk7u9+xCcVn0pu3A==
|
||||
dependencies:
|
||||
"@auth/core" "0.37.4"
|
||||
|
||||
"@babel/runtime@^7.18.6":
|
||||
version "7.25.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.7.tgz#7ffb53c37a8f247c8c4d335e89cdf16a2e0d0fb6"
|
||||
|
|
@ -1270,6 +1301,11 @@
|
|||
dependencies:
|
||||
"@octokit/openapi-types" "^22.2.0"
|
||||
|
||||
"@panva/hkdf@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@panva/hkdf/-/hkdf-1.2.1.tgz#cb0d111ef700136f4580349ff0226bf25c853f23"
|
||||
integrity sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==
|
||||
|
||||
"@pkgjs/parseargs@^0.11.0":
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
||||
|
|
@ -1333,6 +1369,16 @@
|
|||
dependencies:
|
||||
"@radix-ui/react-primitive" "2.0.0"
|
||||
|
||||
"@radix-ui/react-avatar@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-avatar/-/react-avatar-1.1.2.tgz#24af4c66bb5271460a4a6b74c4f4f9d4789d3d90"
|
||||
integrity sha512-GaC7bXQZ5VgZvVvsJ5mu/AEbjYLnhhkoidOboC50Z6FFlLA03wG2ianUoH+zgDQ31/9gCF59bE4+2bBgTyMiig==
|
||||
dependencies:
|
||||
"@radix-ui/react-context" "1.1.1"
|
||||
"@radix-ui/react-primitive" "2.0.1"
|
||||
"@radix-ui/react-use-callback-ref" "1.1.0"
|
||||
"@radix-ui/react-use-layout-effect" "1.1.0"
|
||||
|
||||
"@radix-ui/react-collection@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.1.0.tgz#f18af78e46454a2360d103c2251773028b7724ed"
|
||||
|
|
@ -1348,6 +1394,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz#656432461fc8283d7b591dcf0d79152fae9ecc74"
|
||||
integrity sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==
|
||||
|
||||
"@radix-ui/react-compose-refs@1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz#6f766faa975f8738269ebb8a23bad4f5a8d2faec"
|
||||
integrity sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==
|
||||
|
||||
"@radix-ui/react-context@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.0.tgz#6df8d983546cfd1999c8512f3a8ad85a6e7fcee8"
|
||||
|
|
@ -1503,6 +1554,13 @@
|
|||
dependencies:
|
||||
"@radix-ui/react-slot" "1.1.0"
|
||||
|
||||
"@radix-ui/react-primitive@2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz#6d9efc550f7520135366f333d1e820cf225fad9e"
|
||||
integrity sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==
|
||||
dependencies:
|
||||
"@radix-ui/react-slot" "1.1.1"
|
||||
|
||||
"@radix-ui/react-roving-focus@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz#b30c59daf7e714c748805bfe11c76f96caaac35e"
|
||||
|
|
@ -1547,6 +1605,13 @@
|
|||
dependencies:
|
||||
"@radix-ui/react-compose-refs" "1.1.0"
|
||||
|
||||
"@radix-ui/react-slot@1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.1.tgz#ab9a0ffae4027db7dc2af503c223c978706affc3"
|
||||
integrity sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==
|
||||
dependencies:
|
||||
"@radix-ui/react-compose-refs" "1.1.1"
|
||||
|
||||
"@radix-ui/react-toast@^1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-toast/-/react-toast-1.2.2.tgz#fdd8ed0b80f47d6631dfd90278fee6debc06bf33"
|
||||
|
|
@ -1850,6 +1915,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/braces/-/braces-3.0.4.tgz#403488dc1c8d0db288270d3bbf0ce5f9c45678b4"
|
||||
integrity sha512-0WR3b8eaISjEW7RpZnclONaLFDf7buaowRHdqLp4vLj54AsSAYWfh3DRbfiYJY9XDxMgx1B4sE1Afw2PGpuHOA==
|
||||
|
||||
"@types/cookie@0.6.0":
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5"
|
||||
integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==
|
||||
|
||||
"@types/estree@1.0.6", "@types/estree@^1.0.0":
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
|
||||
|
|
@ -2851,6 +2921,11 @@ concat-map@0.0.1:
|
|||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
|
||||
|
||||
cookie@0.7.1:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9"
|
||||
integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==
|
||||
|
||||
crelt@^1.0.5:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.6.tgz#7cc898ea74e190fb6ef9dae57f8f81cf7302df72"
|
||||
|
|
@ -4338,6 +4413,11 @@ jiti@^1.21.0:
|
|||
resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268"
|
||||
integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==
|
||||
|
||||
jose@^5.9.3, jose@^5.9.6:
|
||||
version "5.9.6"
|
||||
resolved "https://registry.yarnpkg.com/jose/-/jose-5.9.6.tgz#77f1f901d88ebdc405e57cce08d2a91f47521883"
|
||||
integrity sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
|
|
@ -4710,6 +4790,13 @@ natural-compare@^1.4.0:
|
|||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||
|
||||
next-auth@^5.0.0-beta.25:
|
||||
version "5.0.0-beta.25"
|
||||
resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-5.0.0-beta.25.tgz#3a9f9734e1d8fa5ced545360f1afc24862cb92d5"
|
||||
integrity sha512-2dJJw1sHQl2qxCrRk+KTQbeH+izFbGFPuJj5eGgBZFYyiYYtvlrBeUw1E/OJJxTRjuxbSYGnCTkUIRsIIW0bog==
|
||||
dependencies:
|
||||
"@auth/core" "0.37.2"
|
||||
|
||||
next-themes@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.3.0.tgz#b4d2a866137a67d42564b07f3a3e720e2ff3871a"
|
||||
|
|
@ -4807,6 +4894,11 @@ nwsapi@^2.2.12:
|
|||
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.13.tgz#e56b4e98960e7a040e5474536587e599c4ff4655"
|
||||
integrity sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==
|
||||
|
||||
oauth4webapi@^3.0.0, oauth4webapi@^3.1.1:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/oauth4webapi/-/oauth4webapi-3.1.4.tgz#50695385cea8e7a43f3e2e23bc33ea27faece4a7"
|
||||
integrity sha512-eVfN3nZNbok2s/ROifO0UAc5G8nRoLSbrcKJ09OqmucgnhXEfdIQOR4gq1eJH1rN3gV7rNw62bDEgftsgFtBEg==
|
||||
|
||||
object-assign@^4.0.1, object-assign@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
|
|
@ -5143,6 +5235,28 @@ posthog-node@^4.2.1:
|
|||
axios "^1.7.4"
|
||||
rusha "^0.8.14"
|
||||
|
||||
preact-render-to-string@5.2.3:
|
||||
version "5.2.3"
|
||||
resolved "https://registry.yarnpkg.com/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz#23d17376182af720b1060d5a4099843c7fe92fe4"
|
||||
integrity sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==
|
||||
dependencies:
|
||||
pretty-format "^3.8.0"
|
||||
|
||||
preact-render-to-string@6.5.11:
|
||||
version "6.5.11"
|
||||
resolved "https://registry.yarnpkg.com/preact-render-to-string/-/preact-render-to-string-6.5.11.tgz#467e69908a453497bb93d4d1fc35fb749a78e027"
|
||||
integrity sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==
|
||||
|
||||
preact@10.11.3:
|
||||
version "10.11.3"
|
||||
resolved "https://registry.yarnpkg.com/preact/-/preact-10.11.3.tgz#8a7e4ba19d3992c488b0785afcc0f8aa13c78d19"
|
||||
integrity sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==
|
||||
|
||||
preact@10.24.3:
|
||||
version "10.24.3"
|
||||
resolved "https://registry.yarnpkg.com/preact/-/preact-10.24.3.tgz#086386bd47071e3b45410ef20844c21e23828f64"
|
||||
integrity sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==
|
||||
|
||||
preact@^10.19.3:
|
||||
version "10.24.2"
|
||||
resolved "https://registry.yarnpkg.com/preact/-/preact-10.24.2.tgz#42179771d3b06e7adb884e3f8127ddd3d99b78f6"
|
||||
|
|
@ -5163,6 +5277,11 @@ pretty-bytes@^6.1.1:
|
|||
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-6.1.1.tgz#38cd6bb46f47afbf667c202cfc754bffd2016a3b"
|
||||
integrity sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==
|
||||
|
||||
pretty-format@^3.8.0:
|
||||
version "3.8.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-3.8.0.tgz#bfbed56d5e9a776645f4b1ff7aa1a3ac4fa3c385"
|
||||
integrity sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==
|
||||
|
||||
prisma@^6.2.1:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/prisma/-/prisma-6.2.1.tgz#457b210326d66d0e6f583cc6f9cd2819b984408f"
|
||||
|
|
@ -5731,16 +5850,8 @@ string-argv@^0.3.1:
|
|||
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6"
|
||||
integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
string-width@^4.1.0:
|
||||
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
|
||||
name string-width-cjs
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
|
|
@ -5837,14 +5948,7 @@ string_decoder@^1.1.1, string_decoder@^1.3.0:
|
|||
dependencies:
|
||||
safe-buffer "~5.2.0"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
|
|
|
|||
Loading…
Reference in a new issue