mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 20:35:24 +00:00
Fix repo images in authed instance case and add manifest json (#332)
* wip fix repo images * fix config imports * add manifest json * more logos for manifest * add properly padded icon * support old gitlab token case, simplify getImage action, feedback * add changelog entry * fix build error
This commit is contained in:
parent
397262ecf7
commit
8dc41a22b9
17 changed files with 216 additions and 50 deletions
|
|
@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Added seperate page for signup. [#311](https://github.com/sourcebot-dev/sourcebot/pull/331)
|
- Added seperate page for signup. [#311](https://github.com/sourcebot-dev/sourcebot/pull/331)
|
||||||
|
- Fix repo images in authed instance case and add manifest json. [#332](https://github.com/sourcebot-dev/sourcebot/pull/332)
|
||||||
- Added encryption logic for license keys. [#335](https://github.com/sourcebot-dev/sourcebot/pull/335)
|
- Added encryption logic for license keys. [#335](https://github.com/sourcebot-dev/sourcebot/pull/335)
|
||||||
|
|
||||||
## [4.1.1] - 2025-06-03
|
## [4.1.1] - 2025-06-03
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@ import { Logger } from "winston";
|
||||||
import { AppContext } from "./types.js";
|
import { AppContext } from "./types.js";
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { PrismaClient, Repo } from "@sourcebot/db";
|
import { PrismaClient, Repo } from "@sourcebot/db";
|
||||||
import { decrypt } from "@sourcebot/crypto";
|
import { getTokenFromConfig as getTokenFromConfigBase } from "@sourcebot/crypto";
|
||||||
import { Token } from "@sourcebot/schemas/v3/shared.type";
|
|
||||||
import { BackendException, BackendError } from "@sourcebot/error";
|
import { BackendException, BackendError } from "@sourcebot/error";
|
||||||
import * as Sentry from "@sentry/node";
|
import * as Sentry from "@sentry/node";
|
||||||
|
|
||||||
|
|
@ -25,44 +24,21 @@ export const isRemotePath = (path: string) => {
|
||||||
return path.startsWith('https://') || path.startsWith('http://');
|
return path.startsWith('https://') || path.startsWith('http://');
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTokenFromConfig = async (token: Token, orgId: number, db: PrismaClient, logger?: Logger) => {
|
export const getTokenFromConfig = async (token: any, orgId: number, db: PrismaClient, logger?: Logger) => {
|
||||||
if ('secret' in token) {
|
try {
|
||||||
const secretKey = token.secret;
|
return await getTokenFromConfigBase(token, orgId, db);
|
||||||
const secret = await db.secret.findUnique({
|
} catch (error: unknown) {
|
||||||
where: {
|
if (error instanceof Error) {
|
||||||
orgId_key: {
|
|
||||||
key: secretKey,
|
|
||||||
orgId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!secret) {
|
|
||||||
const e = new BackendException(BackendError.CONNECTION_SYNC_SECRET_DNE, {
|
const e = new BackendException(BackendError.CONNECTION_SYNC_SECRET_DNE, {
|
||||||
message: `Secret with key ${secretKey} not found for org ${orgId}`,
|
message: error.message,
|
||||||
});
|
});
|
||||||
Sentry.captureException(e);
|
Sentry.captureException(e);
|
||||||
logger?.error(e.metadata.message);
|
logger?.error(error.message);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
throw error;
|
||||||
const decryptedToken = decrypt(secret.iv, secret.encryptedValue);
|
|
||||||
return decryptedToken;
|
|
||||||
} else {
|
|
||||||
const envToken = process.env[token.env];
|
|
||||||
if (!envToken) {
|
|
||||||
const e = new BackendException(BackendError.CONNECTION_SYNC_SECRET_DNE, {
|
|
||||||
message: `Environment variable ${token.env} not found.`,
|
|
||||||
});
|
|
||||||
Sentry.captureException(e);
|
|
||||||
logger?.error(e.metadata.message);
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
return envToken;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const resolvePathRelativeToConfig = (localPath: string, configPath: string) => {
|
export const resolvePathRelativeToConfig = (localPath: string, configPath: string) => {
|
||||||
let absolutePath = localPath;
|
let absolutePath = localPath;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
"postinstall": "yarn build"
|
"postinstall": "yarn build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@sourcebot/db": "*",
|
||||||
|
"@sourcebot/schemas": "*",
|
||||||
"dotenv": "^16.4.5"
|
"dotenv": "^16.4.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
||||||
|
|
@ -91,3 +91,5 @@ export function verifySignature(data: string, signature: string, publicKeyPath:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { getTokenFromConfig } from './tokenUtils.js';
|
||||||
33
packages/crypto/src/tokenUtils.ts
Normal file
33
packages/crypto/src/tokenUtils.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { PrismaClient } from "@sourcebot/db";
|
||||||
|
import { Token } from "@sourcebot/schemas/v3/shared.type";
|
||||||
|
import { decrypt } from "./index.js";
|
||||||
|
|
||||||
|
export const getTokenFromConfig = async (token: Token, orgId: number, db: PrismaClient) => {
|
||||||
|
if ('secret' in token) {
|
||||||
|
const secretKey = token.secret;
|
||||||
|
const secret = await db.secret.findUnique({
|
||||||
|
where: {
|
||||||
|
orgId_key: {
|
||||||
|
key: secretKey,
|
||||||
|
orgId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!secret) {
|
||||||
|
throw new Error(`Secret with key ${secretKey} not found for org ${orgId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const decryptedToken = decrypt(secret.iv, secret.encryptedValue);
|
||||||
|
return decryptedToken;
|
||||||
|
} else if ('env' in token) {
|
||||||
|
const envToken = process.env[token.env];
|
||||||
|
if (!envToken) {
|
||||||
|
throw new Error(`Environment variable ${token.env} not found.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return envToken;
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid token configuration');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES6",
|
"target": "ES2022",
|
||||||
"module": "CommonJS",
|
"module": "Node16",
|
||||||
"lib": ["ES6"],
|
"lib": ["ES2023"],
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
|
|
@ -11,11 +11,12 @@
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "Node16",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"isolatedModules": true
|
"isolatedModules": true,
|
||||||
|
"resolveJsonModule": true
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"include": ["src/**/*"],
|
||||||
"exclude": ["node_modules", "dist"]
|
"exclude": ["node_modules", "dist"]
|
||||||
|
|
|
||||||
BIN
packages/web/public/logo_512.png
Normal file
BIN
packages/web/public/logo_512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
14
packages/web/public/manifest.json
Normal file
14
packages/web/public/manifest.json
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"name": "Sourcebot",
|
||||||
|
"short_name": "Sourcebot",
|
||||||
|
"display": "standalone",
|
||||||
|
"start_url": "/",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/logo_512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -7,13 +7,16 @@ import { CodeHostType, isServiceError } from "@/lib/utils";
|
||||||
import { prisma } from "@/prisma";
|
import { prisma } from "@/prisma";
|
||||||
import { render } from "@react-email/components";
|
import { render } from "@react-email/components";
|
||||||
import * as Sentry from '@sentry/nextjs';
|
import * as Sentry from '@sentry/nextjs';
|
||||||
import { decrypt, encrypt, generateApiKey, hashSecret } from "@sourcebot/crypto";
|
import { decrypt, encrypt, generateApiKey, hashSecret, getTokenFromConfig } from "@sourcebot/crypto";
|
||||||
import { ConnectionSyncStatus, OrgRole, Prisma, RepoIndexingStatus, StripeSubscriptionStatus, Org, ApiKey } from "@sourcebot/db";
|
import { ConnectionSyncStatus, OrgRole, Prisma, RepoIndexingStatus, StripeSubscriptionStatus, Org, ApiKey } from "@sourcebot/db";
|
||||||
import { ConnectionConfig } from "@sourcebot/schemas/v3/connection.type";
|
import { ConnectionConfig } from "@sourcebot/schemas/v3/connection.type";
|
||||||
import { gerritSchema } from "@sourcebot/schemas/v3/gerrit.schema";
|
import { gerritSchema } from "@sourcebot/schemas/v3/gerrit.schema";
|
||||||
import { giteaSchema } from "@sourcebot/schemas/v3/gitea.schema";
|
import { giteaSchema } from "@sourcebot/schemas/v3/gitea.schema";
|
||||||
import { githubSchema } from "@sourcebot/schemas/v3/github.schema";
|
import { githubSchema } from "@sourcebot/schemas/v3/github.schema";
|
||||||
import { gitlabSchema } from "@sourcebot/schemas/v3/gitlab.schema";
|
import { gitlabSchema } from "@sourcebot/schemas/v3/gitlab.schema";
|
||||||
|
import { GithubConnectionConfig } from "@sourcebot/schemas/v3/github.type";
|
||||||
|
import { GitlabConnectionConfig } from "@sourcebot/schemas/v3/gitlab.type";
|
||||||
|
import { GiteaConnectionConfig } from "@sourcebot/schemas/v3/gitea.type";
|
||||||
import Ajv from "ajv";
|
import Ajv from "ajv";
|
||||||
import { StatusCodes } from "http-status-codes";
|
import { StatusCodes } from "http-status-codes";
|
||||||
import { cookies, headers } from "next/headers";
|
import { cookies, headers } from "next/headers";
|
||||||
|
|
@ -1712,6 +1715,76 @@ export const getSearchContexts = async (domain: string) => sew(() =>
|
||||||
}, /* minRequiredRole = */ OrgRole.GUEST), /* allowSingleTenantUnauthedAccess = */ true
|
}, /* minRequiredRole = */ OrgRole.GUEST), /* allowSingleTenantUnauthedAccess = */ true
|
||||||
));
|
));
|
||||||
|
|
||||||
|
export const getRepoImage = async (repoId: number, domain: string): Promise<ArrayBuffer | ServiceError> => sew(async () => {
|
||||||
|
return await withAuth(async (userId) => {
|
||||||
|
return await withOrgMembership(userId, domain, async ({ org }) => {
|
||||||
|
const repo = await prisma.repo.findUnique({
|
||||||
|
where: {
|
||||||
|
id: repoId,
|
||||||
|
orgId: org.id,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
connections: {
|
||||||
|
include: {
|
||||||
|
connection: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!repo || !repo.imageUrl) {
|
||||||
|
return notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const authHeaders: Record<string, string> = {};
|
||||||
|
for (const { connection } of repo.connections) {
|
||||||
|
try {
|
||||||
|
if (connection.connectionType === 'github') {
|
||||||
|
const config = connection.config as unknown as GithubConnectionConfig;
|
||||||
|
if (config.token) {
|
||||||
|
const token = await getTokenFromConfig(config.token, connection.orgId, prisma);
|
||||||
|
authHeaders['Authorization'] = `token ${token}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (connection.connectionType === 'gitlab') {
|
||||||
|
const config = connection.config as unknown as GitlabConnectionConfig;
|
||||||
|
if (config.token) {
|
||||||
|
const token = await getTokenFromConfig(config.token, connection.orgId, prisma);
|
||||||
|
authHeaders['PRIVATE-TOKEN'] = token;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (connection.connectionType === 'gitea') {
|
||||||
|
const config = connection.config as unknown as GiteaConnectionConfig;
|
||||||
|
if (config.token) {
|
||||||
|
const token = await getTokenFromConfig(config.token, connection.orgId, prisma);
|
||||||
|
authHeaders['Authorization'] = `token ${token}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn(`Failed to get token for connection ${connection.id}:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(repo.imageUrl, {
|
||||||
|
headers: authHeaders,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
logger.warn(`Failed to fetch image from ${repo.imageUrl}: ${response.status}`);
|
||||||
|
return notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageBuffer = await response.arrayBuffer();
|
||||||
|
return imageBuffer;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Error proxying image for repo ${repoId}:`, error);
|
||||||
|
return notFound();
|
||||||
|
}
|
||||||
|
}, /* minRequiredRole = */ OrgRole.GUEST);
|
||||||
|
}, /* allowSingleTenantUnauthedAccess = */ true);
|
||||||
|
});
|
||||||
|
|
||||||
////// Helpers ///////
|
////// Helpers ///////
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { getDisplayTime } from "@/lib/utils";
|
import { getDisplayTime, getRepoImageSrc } from "@/lib/utils";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { StatusIcon } from "../../components/statusIcon";
|
import { StatusIcon } from "../../components/statusIcon";
|
||||||
import { RepoIndexingStatus } from "@sourcebot/db";
|
import { RepoIndexingStatus } from "@sourcebot/db";
|
||||||
|
|
@ -46,14 +46,16 @@ export const RepoListItem = ({
|
||||||
}
|
}
|
||||||
}, [status]);
|
}, [status]);
|
||||||
|
|
||||||
|
const imageSrc = getRepoImageSrc(imageUrl, repoId, domain);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="flex flex-row items-center p-4 border rounded-lg bg-background justify-between"
|
className="flex flex-row items-center p-4 border rounded-lg bg-background justify-between"
|
||||||
>
|
>
|
||||||
<div className="flex flex-row items-center gap-2">
|
<div className="flex flex-row items-center gap-2">
|
||||||
{imageUrl ? (
|
{imageSrc ? (
|
||||||
<Image
|
<Image
|
||||||
src={imageUrl}
|
src={imageSrc}
|
||||||
alt={name}
|
alt={name}
|
||||||
width={32}
|
width={32}
|
||||||
height={32}
|
height={32}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import { ArrowUpDown, ExternalLink, Clock, Loader2, CheckCircle2, XCircle, Trash
|
||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import { Badge } from "@/components/ui/badge"
|
import { Badge } from "@/components/ui/badge"
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn, getRepoImageSrc } from "@/lib/utils"
|
||||||
import { RepoIndexingStatus } from "@sourcebot/db";
|
import { RepoIndexingStatus } from "@sourcebot/db";
|
||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
|
||||||
import { AddRepoButton } from "./addRepoButton"
|
import { AddRepoButton } from "./addRepoButton"
|
||||||
|
|
||||||
export type RepositoryColumnInfo = {
|
export type RepositoryColumnInfo = {
|
||||||
|
repoId: number
|
||||||
name: string
|
name: string
|
||||||
imageUrl?: string
|
imageUrl?: string
|
||||||
connections: {
|
connections: {
|
||||||
|
|
@ -112,7 +113,7 @@ export const columns = (domain: string): ColumnDef<RepositoryColumnInfo>[] => [
|
||||||
<div className="relative h-8 w-8 overflow-hidden rounded-md border bg-muted">
|
<div className="relative h-8 w-8 overflow-hidden rounded-md border bg-muted">
|
||||||
{repo.imageUrl ? (
|
{repo.imageUrl ? (
|
||||||
<Image
|
<Image
|
||||||
src={repo.imageUrl || "/placeholder.svg"}
|
src={getRepoImageSrc(repo.imageUrl, repo.repoId, domain) || "/placeholder.svg"}
|
||||||
alt={`${repo.name} logo`}
|
alt={`${repo.name} logo`}
|
||||||
width={32}
|
width={32}
|
||||||
height={32}
|
height={32}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ export const RepositoryTable = () => {
|
||||||
|
|
||||||
const tableRepos = useMemo(() => {
|
const tableRepos = useMemo(() => {
|
||||||
if (reposLoading) return Array(4).fill(null).map(() => ({
|
if (reposLoading) return Array(4).fill(null).map(() => ({
|
||||||
|
repoId: 0,
|
||||||
name: "",
|
name: "",
|
||||||
connections: [],
|
connections: [],
|
||||||
repoIndexingStatus: RepoIndexingStatus.NEW,
|
repoIndexingStatus: RepoIndexingStatus.NEW,
|
||||||
|
|
@ -35,6 +36,7 @@ export const RepositoryTable = () => {
|
||||||
|
|
||||||
if (!repos) return [];
|
if (!repos) return [];
|
||||||
return repos.map((repo): RepositoryColumnInfo => ({
|
return repos.map((repo): RepositoryColumnInfo => ({
|
||||||
|
repoId: repo.repoId,
|
||||||
name: repo.repoDisplayName ?? repo.repoName,
|
name: repo.repoDisplayName ?? repo.repoName,
|
||||||
imageUrl: repo.imageUrl,
|
imageUrl: repo.imageUrl,
|
||||||
connections: repo.linkedConnections,
|
connections: repo.linkedConnections,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { getRepoImage } from "@/actions";
|
||||||
|
import { isServiceError } from "@/lib/utils";
|
||||||
|
import { NextRequest } from "next/server";
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
request: NextRequest,
|
||||||
|
{ params }: { params: { domain: string; repoId: string } }
|
||||||
|
) {
|
||||||
|
const { domain, repoId } = params;
|
||||||
|
const repoIdNum = parseInt(repoId);
|
||||||
|
|
||||||
|
if (isNaN(repoIdNum)) {
|
||||||
|
return new Response("Invalid repo ID", { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await getRepoImage(repoIdNum, domain);
|
||||||
|
if (isServiceError(result)) {
|
||||||
|
return new Response(result.message, { status: result.statusCode });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(result, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'image/png',
|
||||||
|
'Cache-Control': 'public, max-age=3600',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ import { getEntitlements } from "@/features/entitlements/server";
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Sourcebot",
|
title: "Sourcebot",
|
||||||
description: "Sourcebot",
|
description: "Sourcebot",
|
||||||
|
manifest: "/manifest.json",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
|
|
|
||||||
|
|
@ -409,3 +409,32 @@ export const requiredQueryParamGuard = (request: NextRequest, param: string): Se
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getRepoImageSrc = (imageUrl: string | undefined, repoId: number, domain: string): string | undefined => {
|
||||||
|
if (!imageUrl) return undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = new URL(imageUrl);
|
||||||
|
|
||||||
|
// List of known public instances that don't require authentication
|
||||||
|
const publicHostnames = [
|
||||||
|
'github.com',
|
||||||
|
'gitlab.com',
|
||||||
|
'avatars.githubusercontent.com',
|
||||||
|
'gitea.com',
|
||||||
|
'bitbucket.org',
|
||||||
|
];
|
||||||
|
|
||||||
|
const isPublicInstance = publicHostnames.includes(url.hostname);
|
||||||
|
|
||||||
|
if (isPublicInstance) {
|
||||||
|
return imageUrl;
|
||||||
|
} else {
|
||||||
|
// Use the proxied route for self-hosted instances
|
||||||
|
return `/api/${domain}/repos/${repoId}/image`;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// If URL parsing fails, use the original URL
|
||||||
|
return imageUrl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -33,6 +33,6 @@ export async function middleware(request: NextRequest) {
|
||||||
export const config = {
|
export const config = {
|
||||||
// https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
|
// https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
|
||||||
matcher: [
|
matcher: [
|
||||||
'/((?!api|_next/static|ingest|_next/image|favicon.ico|sitemap.xml|robots.txt|sb_logo_light_large.png|arrow.png|placeholder_avatar.png).*)',
|
'/((?!api|_next/static|ingest|_next/image|favicon.ico|sitemap.xml|robots.txt|manifest.json|logo_192.png|logo_512.png|sb_logo_light_large.png|arrow.png|placeholder_avatar.png).*)',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5803,13 +5803,15 @@ __metadata:
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@sourcebot/crypto@workspace:packages/crypto"
|
resolution: "@sourcebot/crypto@workspace:packages/crypto"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@sourcebot/db": "npm:*"
|
||||||
|
"@sourcebot/schemas": "npm:*"
|
||||||
"@types/node": "npm:^22.7.5"
|
"@types/node": "npm:^22.7.5"
|
||||||
dotenv: "npm:^16.4.5"
|
dotenv: "npm:^16.4.5"
|
||||||
typescript: "npm:^5.7.3"
|
typescript: "npm:^5.7.3"
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@sourcebot/db@workspace:*, @sourcebot/db@workspace:packages/db":
|
"@sourcebot/db@npm:*, @sourcebot/db@workspace:*, @sourcebot/db@workspace:packages/db":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@sourcebot/db@workspace:packages/db"
|
resolution: "@sourcebot/db@workspace:packages/db"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -5869,7 +5871,7 @@ __metadata:
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@sourcebot/schemas@workspace:*, @sourcebot/schemas@workspace:packages/schemas":
|
"@sourcebot/schemas@npm:*, @sourcebot/schemas@workspace:*, @sourcebot/schemas@workspace:packages/schemas":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@sourcebot/schemas@workspace:packages/schemas"
|
resolution: "@sourcebot/schemas@workspace:packages/schemas"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue