mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-04-29 10:26:23 +02:00
Update feature flags + env docs
This commit is contained in:
parent
0df92e80c6
commit
cbac042003
20 changed files with 210 additions and 168 deletions
|
|
@ -294,7 +294,7 @@ export async function getDownloadUrlForFile(
|
|||
}
|
||||
|
||||
const command = new GetObjectCommand({
|
||||
Bucket: process.env.UPLOADS_S3_BUCKET,
|
||||
Bucket: process.env.RAG_UPLOADS_S3_BUCKET,
|
||||
Key: file.data.s3Key,
|
||||
});
|
||||
|
||||
|
|
@ -328,7 +328,7 @@ export async function getUploadUrlsForFilesDataSource(
|
|||
const s3Key = `datasources/files/${projectIdPrefix}/${projectId}/${sourceId}/${fileId}/${file.name}`;
|
||||
// Generate presigned URL
|
||||
const command = new PutObjectCommand({
|
||||
Bucket: process.env.UPLOADS_S3_BUCKET,
|
||||
Bucket: process.env.RAG_UPLOADS_S3_BUCKET,
|
||||
Key: s3Key,
|
||||
ContentType: file.type,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ export async function createProject(formData: FormData) {
|
|||
secret,
|
||||
nextWorkflowNumber: 1,
|
||||
testRunCounter: 0,
|
||||
webhookUrl: 'http://tools_webhook:3005/tool_call',
|
||||
});
|
||||
|
||||
// add first workflow version
|
||||
|
|
|
|||
4
apps/rowboat/app/lib/feature_flags.ts
Normal file
4
apps/rowboat/app/lib/feature_flags.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export const USE_RAG = process.env.USE_RAG === 'true';
|
||||
export const USE_RAG_UPLOADS = process.env.USE_RAG_UPLOADS === 'true';
|
||||
export const USE_RAG_SCRAPING = process.env.USE_RAG_SCRAPING === 'true';
|
||||
export const USE_CHAT_WIDGET = process.env.USE_CHAT_WIDGET === 'true';
|
||||
|
|
@ -569,9 +569,11 @@ export function DeleteProjectSection({
|
|||
|
||||
export default function App({
|
||||
projectId,
|
||||
useChatWidget,
|
||||
chatWidgetHost,
|
||||
}: {
|
||||
projectId: string;
|
||||
useChatWidget: boolean;
|
||||
chatWidgetHost: string;
|
||||
}) {
|
||||
return <div className="flex flex-col h-full">
|
||||
|
|
@ -586,7 +588,7 @@ export default function App({
|
|||
<SecretSection projectId={projectId} />
|
||||
<ApiKeysSection projectId={projectId} />
|
||||
<WebhookUrlSection projectId={projectId} />
|
||||
<ChatWidgetSection projectId={projectId} chatWidgetHost={chatWidgetHost} />
|
||||
{useChatWidget && <ChatWidgetSection projectId={projectId} chatWidgetHost={chatWidgetHost} />}
|
||||
<DeleteProjectSection projectId={projectId} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { Metadata } from "next";
|
||||
import App from "./app";
|
||||
import { USE_CHAT_WIDGET } from "@/app/lib/feature_flags";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Project config",
|
||||
};
|
||||
|
|
@ -13,6 +15,7 @@ export default function Page({
|
|||
}) {
|
||||
return <App
|
||||
projectId={params.projectId}
|
||||
chatWidgetHost={process.env.CHAT_WIDGET_HOST || 'https://chat.rowboatlabs.com'}
|
||||
useChatWidget={USE_CHAT_WIDGET}
|
||||
chatWidgetHost={process.env.CHAT_WIDGET_HOST || ''}
|
||||
/>;
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { Nav } from "./nav";
|
||||
import { USE_RAG } from "@/app/lib/feature_flags";
|
||||
|
||||
export default async function Layout({
|
||||
params,
|
||||
|
|
@ -7,10 +8,10 @@ export default async function Layout({
|
|||
params: { projectId: string }
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const useDataSources = process.env.USE_DATA_SOURCES === 'true';
|
||||
const useRag = USE_RAG;
|
||||
|
||||
return <div className="flex h-full">
|
||||
<Nav projectId={params.projectId} useDataSources={useDataSources} />
|
||||
<Nav projectId={params.projectId} useRag={useRag} />
|
||||
<div className="grow p-2 overflow-auto bg-background dark:bg-background rounded-tl-lg">
|
||||
{children}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -44,11 +44,11 @@ function NavLink({ href, label, icon, collapsed, selected = false }: {
|
|||
export default function Menu({
|
||||
projectId,
|
||||
collapsed,
|
||||
useDataSources,
|
||||
useRag,
|
||||
}: {
|
||||
projectId: string;
|
||||
collapsed: boolean;
|
||||
useDataSources: boolean;
|
||||
useRag: boolean;
|
||||
}) {
|
||||
const pathname = usePathname();
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ export default function Menu({
|
|||
icon={<PlayIcon size={16} />}
|
||||
selected={pathname.startsWith(`/projects/${projectId}/test`)}
|
||||
/>
|
||||
{useDataSources && (
|
||||
{useRag && (
|
||||
<NavLink
|
||||
href={`/projects/${projectId}/sources`}
|
||||
label="Connect"
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@ import { useEffect, useState } from "react";
|
|||
import clsx from "clsx";
|
||||
import Menu from "./menu";
|
||||
import { getProjectConfig } from "../../actions/project_actions";
|
||||
import { ChevronsLeftIcon, ChevronsRightIcon, FolderOpenIcon, PanelLeftCloseIcon, PanelLeftOpenIcon } from "lucide-react";
|
||||
import { FolderOpenIcon, PanelLeftCloseIcon, PanelLeftOpenIcon } from "lucide-react";
|
||||
|
||||
export function Nav({
|
||||
projectId,
|
||||
useDataSources,
|
||||
useRag,
|
||||
}: {
|
||||
projectId: string;
|
||||
useDataSources: boolean;
|
||||
useRag: boolean;
|
||||
}) {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const [projectName, setProjectName] = useState<string | null>(null);
|
||||
|
|
@ -56,6 +56,6 @@ export function Nav({
|
|||
<FolderOpenIcon size={16} className="ml-1" />
|
||||
</Link>
|
||||
</Tooltip>}
|
||||
<Menu projectId={projectId} collapsed={collapsed} useDataSources={useDataSources} />
|
||||
<Menu projectId={projectId} collapsed={collapsed} useRag={useRag} />
|
||||
</div>;
|
||||
}
|
||||
|
|
@ -186,26 +186,28 @@ function ToolCall({
|
|||
sender={sender}
|
||||
/>;
|
||||
}
|
||||
if (matchingWorkflowTool && testProfile && !testProfile.mockTools) {
|
||||
return <ClientToolCall
|
||||
if (!matchingWorkflowTool ||
|
||||
matchingWorkflowTool.mockTool ||
|
||||
(testProfile && testProfile.mockTools)) {
|
||||
return <MockToolCall
|
||||
toolCall={toolCall}
|
||||
result={result}
|
||||
handleResult={handleResult}
|
||||
projectId={projectId}
|
||||
messages={messages}
|
||||
sender={sender}
|
||||
testProfile={testProfile}
|
||||
workflowTool={matchingWorkflowTool}
|
||||
systemMessage={systemMessage}
|
||||
/>;
|
||||
}
|
||||
return <MockToolCall
|
||||
return <ClientToolCall
|
||||
toolCall={toolCall}
|
||||
result={result}
|
||||
handleResult={handleResult}
|
||||
projectId={projectId}
|
||||
messages={messages}
|
||||
sender={sender}
|
||||
testProfile={testProfile}
|
||||
workflowTool={matchingWorkflowTool}
|
||||
systemMessage={systemMessage}
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,27 +8,17 @@ import { PlusIcon } from "lucide-react";
|
|||
import { useRouter } from "next/navigation";
|
||||
|
||||
export function Form({
|
||||
projectId
|
||||
projectId,
|
||||
useRagUploads,
|
||||
useRagScraping,
|
||||
}: {
|
||||
projectId: string;
|
||||
useRagUploads: boolean;
|
||||
useRagScraping: boolean;
|
||||
}) {
|
||||
const [sourceType, setSourceType] = useState("");
|
||||
const router = useRouter();
|
||||
|
||||
// async function createCrawlDataSource(formData: FormData) {
|
||||
// const source = await createDataSource({
|
||||
// projectId,
|
||||
// name: formData.get('name') as string,
|
||||
// data: {
|
||||
// type: 'crawl',
|
||||
// startUrl: formData.get('startUrl') as string,
|
||||
// limit: parseInt(formData.get('limit') as string),
|
||||
// },
|
||||
// status: 'queued',
|
||||
// });
|
||||
// router.push(`/projects/${projectId}/sources/${source._id}`);
|
||||
// }
|
||||
|
||||
async function createUrlsDataSource(formData: FormData) {
|
||||
const source = await createDataSource({
|
||||
projectId,
|
||||
|
|
@ -80,14 +70,11 @@ export function Form({
|
|||
label="Select type"
|
||||
selectedKeys={[sourceType]}
|
||||
onChange={handleSourceTypeChange}
|
||||
disabledKeys={[
|
||||
...(useRagUploads ? [] : ['files']),
|
||||
...(useRagScraping ? [] : ['urls']),
|
||||
]}
|
||||
>
|
||||
{/* <SelectItem
|
||||
key="crawl"
|
||||
value="crawl"
|
||||
startContent={<DataSourceIcon type="crawl" />}
|
||||
>
|
||||
Crawl URLs
|
||||
</SelectItem> */}
|
||||
<SelectItem
|
||||
key="urls"
|
||||
startContent={<DataSourceIcon type="urls" />}
|
||||
|
|
@ -102,62 +89,6 @@ export function Form({
|
|||
</SelectItem>
|
||||
</Select>
|
||||
|
||||
{/* {sourceType === "crawl" && <form
|
||||
action={createCrawlDataSourceWithProjectId}
|
||||
className="flex flex-col gap-4"
|
||||
>
|
||||
<Input
|
||||
required
|
||||
type="text"
|
||||
name="url"
|
||||
label="Specify starting URL to crawl"
|
||||
labelPlacement="outside"
|
||||
placeholder="https://example.com"
|
||||
variant="bordered"
|
||||
/>
|
||||
<div className="self-start w-[200px]">
|
||||
<Input
|
||||
required
|
||||
type="number"
|
||||
min={1}
|
||||
max={5000}
|
||||
name="limit"
|
||||
label="Maximum pages to crawl"
|
||||
labelPlacement="outside"
|
||||
placeholder="100"
|
||||
defaultValue={"100"}
|
||||
variant="bordered"
|
||||
/>
|
||||
</div>
|
||||
<div className="self-start">
|
||||
<Input
|
||||
required
|
||||
type="text"
|
||||
name="name"
|
||||
label="Name this data source"
|
||||
labelPlacement="outside"
|
||||
placeholder="e.g. Help articles"
|
||||
variant="bordered"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm">
|
||||
<p>Note:</p>
|
||||
<ul className="list-disc ml-4">
|
||||
<li>Expect about 5-10 minutes to crawl 100 pages</li>
|
||||
</ul>
|
||||
</div>
|
||||
<FormStatusButton
|
||||
props={{
|
||||
type: "submit",
|
||||
children: "Add data source",
|
||||
className: "self-start",
|
||||
startContent: <svg className="w-[24px] h-[24px]" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
|
||||
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1" d="M5 12h14m-7 7V5" />
|
||||
</svg>,
|
||||
}}
|
||||
/>
|
||||
</form>} */}
|
||||
|
||||
{sourceType === "urls" && <form
|
||||
action={createUrlsDataSource}
|
||||
className="flex flex-col gap-4"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { Metadata } from "next";
|
||||
import { Form } from "./form";
|
||||
import { redirect } from "next/navigation";
|
||||
import { USE_RAG, USE_RAG_UPLOADS, USE_RAG_SCRAPING } from "../../../../lib/feature_flags";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Add data source"
|
||||
|
|
@ -11,9 +12,7 @@ export default async function Page({
|
|||
}: {
|
||||
params: { projectId: string }
|
||||
}) {
|
||||
const useDataSources = process.env.USE_DATA_SOURCES === 'true';
|
||||
|
||||
if (!useDataSources) {
|
||||
if (!USE_RAG) {
|
||||
redirect(`/projects/${params.projectId}`);
|
||||
}
|
||||
|
||||
|
|
@ -23,6 +22,10 @@ export default async function Page({
|
|||
<h1 className="text-lg">Add data source</h1>
|
||||
</div>
|
||||
</div>
|
||||
<Form projectId={params.projectId} />
|
||||
<Form
|
||||
projectId={params.projectId}
|
||||
useRagUploads={USE_RAG_UPLOADS}
|
||||
useRagScraping={USE_RAG_SCRAPING}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
|
|
@ -10,8 +10,7 @@ import { ActionButton, StructuredPanel } from "../../../lib/components/structure
|
|||
import { FormSection } from "../../../lib/components/form-section";
|
||||
import { EditableField } from "../../../lib/components/editable-field";
|
||||
import { Label } from "../../../lib/components/label";
|
||||
import { PlusIcon, SparklesIcon, ChevronRight, ChevronDown } from "lucide-react";
|
||||
import { List } from "./config_list";
|
||||
import { PlusIcon, ChevronRight, ChevronDown } from "lucide-react";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { usePreviewModal } from "./preview-modal";
|
||||
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter } from "@heroui/react";
|
||||
|
|
@ -33,6 +32,7 @@ export function AgentConfig({
|
|||
dataSources,
|
||||
handleUpdate,
|
||||
handleClose,
|
||||
useRag,
|
||||
}: {
|
||||
projectId: string,
|
||||
workflow: z.infer<typeof Workflow>,
|
||||
|
|
@ -44,6 +44,7 @@ export function AgentConfig({
|
|||
dataSources: WithStringId<z.infer<typeof DataSource>>[],
|
||||
handleUpdate: (agent: z.infer<typeof WorkflowAgent>) => void,
|
||||
handleClose: () => void,
|
||||
useRag: boolean,
|
||||
}) {
|
||||
const [isAdvancedConfigOpen, setIsAdvancedConfigOpen] = useState(false);
|
||||
|
||||
|
|
@ -160,7 +161,7 @@ export function AgentConfig({
|
|||
/>
|
||||
</FormSection>
|
||||
|
||||
<FormSection label="RAG (beta)" showDivider>
|
||||
{useRag && <FormSection label="RAG (beta)" showDivider>
|
||||
<div className="flex flex-col gap-3">
|
||||
<Dropdown>
|
||||
<DropdownTrigger>
|
||||
|
|
@ -272,7 +273,7 @@ export function AgentConfig({
|
|||
</>
|
||||
)}
|
||||
</div>
|
||||
</FormSection>
|
||||
</FormSection>}
|
||||
|
||||
<FormSection label="Model" showDivider>
|
||||
<CustomDropdown
|
||||
|
|
|
|||
|
|
@ -9,12 +9,13 @@ import { WorkflowSelector } from "./workflow_selector";
|
|||
import { Spinner } from "@heroui/react";
|
||||
import { cloneWorkflow, createWorkflow, fetchPublishedWorkflowId, fetchWorkflow } from "../../../actions/workflow_actions";
|
||||
import { listDataSources } from "../../../actions/datasource_actions";
|
||||
import { TestProfile } from "@/app/lib/types/testing_types";
|
||||
|
||||
export function App({
|
||||
projectId,
|
||||
useRag,
|
||||
}: {
|
||||
projectId: string;
|
||||
useRag: boolean;
|
||||
}) {
|
||||
const [selectorKey, setSelectorKey] = useState(0);
|
||||
const [workflow, setWorkflow] = useState<WithStringId<z.infer<typeof Workflow>> | null>(null);
|
||||
|
|
@ -106,6 +107,7 @@ export function App({
|
|||
publishedWorkflowId={publishedWorkflowId}
|
||||
handleShowSelector={handleShowSelector}
|
||||
handleCloneVersion={handleCloneVersion}
|
||||
useRag={useRag}
|
||||
/>}
|
||||
</>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { Metadata } from "next";
|
||||
import { App } from "./app";
|
||||
import { USE_RAG } from "@/app/lib/feature_flags";
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Workflow"
|
||||
|
|
@ -12,5 +15,6 @@ export default async function Page({
|
|||
}) {
|
||||
return <App
|
||||
projectId={params.projectId}
|
||||
useRag={USE_RAG}
|
||||
/>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -534,12 +534,14 @@ export function WorkflowEditor({
|
|||
publishedWorkflowId,
|
||||
handleShowSelector,
|
||||
handleCloneVersion,
|
||||
useRag,
|
||||
}: {
|
||||
dataSources: WithStringId<z.infer<typeof DataSource>>[];
|
||||
workflow: WithStringId<z.infer<typeof Workflow>>;
|
||||
publishedWorkflowId: string | null;
|
||||
handleShowSelector: () => void;
|
||||
handleCloneVersion: (workflowId: string) => void;
|
||||
useRag: boolean;
|
||||
}) {
|
||||
const [state, dispatch] = useReducer<Reducer<State, Action>>(reducer, {
|
||||
patches: [],
|
||||
|
|
@ -873,6 +875,7 @@ export function WorkflowEditor({
|
|||
dataSources={dataSources}
|
||||
handleUpdate={handleUpdateAgent.bind(null, state.present.selection.name)}
|
||||
handleClose={handleUnselectAgent}
|
||||
useRag={useRag}
|
||||
/>}
|
||||
{state.present.selection?.type === "tool" && <ToolConfig
|
||||
key={state.present.selection.name}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
import '../lib/loadenv';
|
||||
import FirecrawlApp from '@mendable/firecrawl-js';
|
||||
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
|
||||
import { z } from 'zod';
|
||||
import { dataSourceDocsCollection, dataSourcesCollection } from '../lib/mongodb';
|
||||
import { EmbeddingRecord } from "../lib/types/datasource_types";
|
||||
import { DataSourceDoc } from "../lib/types/datasource_types";
|
||||
import { DataSource } from "../lib/types/datasource_types";
|
||||
import { EmbeddingRecord, DataSourceDoc, DataSource } from "../lib/types/datasource_types";
|
||||
import { WithId } from 'mongodb';
|
||||
import { embedMany } from 'ai';
|
||||
import { embeddingModel } from '../lib/embedding';
|
||||
|
|
@ -14,6 +11,7 @@ import { PrefixLogger } from "../lib/utils";
|
|||
import { GoogleGenerativeAI } from "@google/generative-ai";
|
||||
import { GetObjectCommand } from "@aws-sdk/client-s3";
|
||||
import { uploadsS3Client } from '../lib/uploads_s3_client';
|
||||
import crypto from 'crypto';
|
||||
|
||||
const splitter = new RecursiveCharacterTextSplitter({
|
||||
separators: ['\n\n', '\n', '. ', '.', ''],
|
||||
|
|
@ -31,11 +29,11 @@ const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY || '');
|
|||
|
||||
async function getFileContent(s3Key: string): Promise<Buffer> {
|
||||
const command = new GetObjectCommand({
|
||||
Bucket: process.env.UPLOADS_S3_BUCKET,
|
||||
Bucket: process.env.RAG_UPLOADS_S3_BUCKET,
|
||||
Key: s3Key,
|
||||
});
|
||||
const response = await uploadsS3Client.send(command);
|
||||
const chunks: Buffer[] = [];
|
||||
const chunks: Uint8Array[] = [];
|
||||
for await (const chunk of response.Body as any) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,28 +3,16 @@ import FirecrawlApp from '@mendable/firecrawl-js';
|
|||
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
|
||||
import { z } from 'zod';
|
||||
import { dataSourceDocsCollection, dataSourcesCollection } from '../lib/mongodb';
|
||||
import { EmbeddingRecord } from "../lib/types/datasource_types";
|
||||
import { DataSourceDoc } from "../lib/types/datasource_types";
|
||||
import { DataSource } from "../lib/types/datasource_types";
|
||||
import { EmbeddingRecord, DataSourceDoc, DataSource } from "../lib/types/datasource_types";
|
||||
import { WithId } from 'mongodb';
|
||||
import { embedMany } from 'ai';
|
||||
import { embeddingModel } from '../lib/embedding';
|
||||
import { qdrantClient } from '../lib/qdrant';
|
||||
import { PrefixLogger } from "../lib/utils";
|
||||
import crypto from 'crypto';
|
||||
|
||||
const firecrawl = new FirecrawlApp({ apiKey: process.env.FIRECRAWL_API_KEY });
|
||||
|
||||
const firecrawlHttpAuth = {
|
||||
'Authorization': `Bearer ${process.env.FIRECRAWL_API_KEY}`,
|
||||
}
|
||||
|
||||
type Webpage = {
|
||||
title: string,
|
||||
url: string,
|
||||
markdown: string,
|
||||
html: string,
|
||||
}
|
||||
|
||||
const splitter = new RecursiveCharacterTextSplitter({
|
||||
separators: ['\n\n', '\n', '. ', '.', ''],
|
||||
chunkSize: 1024,
|
||||
|
|
@ -36,18 +24,6 @@ const minute = 60 * second;
|
|||
const hour = 60 * minute;
|
||||
const day = 24 * hour;
|
||||
|
||||
const firecrawlStatusPollInterval = 60 * second;
|
||||
|
||||
/*
|
||||
const source: z.infer<typeof SourceSchema> = {
|
||||
_id: new ObjectId(),
|
||||
url: "https://www.example.com",
|
||||
type: "web",
|
||||
status: 'processing',
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
*/
|
||||
|
||||
async function retryable<T>(fn: () => Promise<T>, maxAttempts: number = 3): Promise<T> {
|
||||
let attempts = 0;
|
||||
while (true) {
|
||||
|
|
|
|||
46
apps/rowboat/scripts.Dockerfile
Normal file
46
apps/rowboat/scripts.Dockerfile
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# syntax=docker.io/docker/dockerfile:1
|
||||
|
||||
FROM node:18-alpine AS base
|
||||
|
||||
# Install dependencies only when needed
|
||||
FROM base AS deps
|
||||
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies based on the preferred package manager
|
||||
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
|
||||
RUN \
|
||||
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
|
||||
elif [ -f package-lock.json ]; then npm ci; \
|
||||
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
|
||||
else echo "Lockfile not found." && exit 1; \
|
||||
fi
|
||||
|
||||
|
||||
# Rebuild the source code only when needed
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
|
||||
# Next.js collects completely anonymous telemetry data about general usage.
|
||||
# Learn more here: https://nextjs.org/telemetry
|
||||
# Uncomment the following line in case you want to disable telemetry during the build.
|
||||
# ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
RUN \
|
||||
if [ -f yarn.lock ]; then yarn run build; \
|
||||
elif [ -f package-lock.json ]; then npm run build; \
|
||||
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
|
||||
else echo "Lockfile not found." && exit 1; \
|
||||
fi
|
||||
|
||||
ENV NODE_ENV=production
|
||||
# Uncomment the following line in case you want to disable telemetry during runtime.
|
||||
# ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
USER nextjs
|
||||
Loading…
Add table
Add a link
Reference in a new issue