diff --git a/server/src/database/dedicated-database/dedicated-database-task.service.ts b/server/src/database/dedicated-database/dedicated-database-task.service.ts index 771665e..3561546 100644 --- a/server/src/database/dedicated-database/dedicated-database-task.service.ts +++ b/server/src/database/dedicated-database/dedicated-database-task.service.ts @@ -188,7 +188,28 @@ export class DedicatedDatabaseTaskService { const manifest = await this.dbService.getDeployManifest(region, user, appid) if (!manifest) { - throw new Error(`stop dedicated database ${appid} manifest not found`) + await this.db + .collection('DedicatedDatabase') + .updateOne( + { + appid: data.appid, + phase: DedicatedDatabasePhase.Stopping, + }, + + { + $set: { + phase: DedicatedDatabasePhase.Stopped, + lockedAt: TASK_LOCK_INIT_TIME, + updatedAt: new Date(), + }, + }, + ) + + this.logger.debug( + `update dedicated database ${appid} state to stopped,note: ddb manifest not found`, + ) + + return } const stopped = diff --git a/server/src/database/dedicated-database/dedicated-database.service.ts b/server/src/database/dedicated-database/dedicated-database.service.ts index de3e68e..87cbc67 100644 --- a/server/src/database/dedicated-database/dedicated-database.service.ts +++ b/server/src/database/dedicated-database/dedicated-database.service.ts @@ -146,9 +146,11 @@ export class DedicatedDatabaseService { const requestMemory = limitMemory * (region.bundleConf?.memoryRequestLimitRatio || 0.5) + const label = appid const template = region.deployManifest.database const tmpl = _.template(template) const manifest = tmpl({ + label, name, limitCPU, limitMemory, @@ -167,6 +169,12 @@ export class DedicatedDatabaseService { appid: string, ): Promise { const ddbDeployManifest = await this.getDeployManifest(region, user, appid) + + if (!ddbDeployManifest) { + this.logger.debug(`restart ddb, deploy manifest not found for ${appid}`) + return true + } + const replicas = Number(ddbDeployManifest.spec.componentSpecs[0].replicas) const limitCPU = extractNumber( diff --git a/server/src/initializer/deploy-manifest/database.yaml b/server/src/initializer/deploy-manifest/database.yaml index 83eea69..1c84734 100644 --- a/server/src/initializer/deploy-manifest/database.yaml +++ b/server/src/initializer/deploy-manifest/database.yaml @@ -7,6 +7,7 @@ metadata: clusterdefinition.kubeblocks.io/name: mongodb clusterversion.kubeblocks.io/name: mongodb-5.0 sealos-db-provider-cr: <%- name %> + sealaf-app: <%- label %> annotations: {} name: <%- name %> spec: diff --git a/server/src/instance/instance.service.ts b/server/src/instance/instance.service.ts index 586e750..12d46e5 100644 --- a/server/src/instance/instance.service.ts +++ b/server/src/instance/instance.service.ts @@ -107,7 +107,7 @@ export class InstanceService { deployment.spec = await this.makeDeploymentSpec( app, deployment.spec.template.metadata.labels, - this.getRuntimeLabel(appid), + deployment.spec.template.metadata.labels, ) const appsV1Api = this.cluster.makeAppsV1Api() const deploymentResult = await appsV1Api.replaceNamespacedDeployment( @@ -242,11 +242,19 @@ export class InstanceService { // db connection uri let dbConnectionUri: string const dedicatedDatabase = await this.dedicatedDatabaseService.findOne(appid) + if (dedicatedDatabase) { - dbConnectionUri = await this.dedicatedDatabaseService.getConnectionUri( - user, - dedicatedDatabase, - ) + try { + dbConnectionUri = await this.dedicatedDatabaseService.getConnectionUri( + user, + dedicatedDatabase, + ) + } catch (e) { + dbConnectionUri = '' + this.logger.debug( + `get db connection uri for ${appid} failed: ${e}, maybe ddb cluster manifest have been deleted`, + ) + } } const NODE_MODULES_PUSH_URL = @@ -582,10 +590,12 @@ export class InstanceService { private getRuntimeLabel(appid: string) { const SEALOS = 'cloud.sealos.io/app-deploy-manager' + const SEALAF_APP = 'sealaf-app' const labels: Record = { [LABEL_KEY_APP_ID]: appid, [SEALOS]: this.getAppDeployName(appid), app: this.getAppDeployName(appid), + [SEALAF_APP]: appid, } return labels diff --git a/web/src/pages/app/functions/mods/AIChatPanel/index.tsx b/web/src/pages/app/functions/mods/AIChatPanel/index.tsx index 47a34e3..ca5b555 100644 --- a/web/src/pages/app/functions/mods/AIChatPanel/index.tsx +++ b/web/src/pages/app/functions/mods/AIChatPanel/index.tsx @@ -1,9 +1,10 @@ -import { useRef, useState } from "react"; +import { KeyboardEvent, useCallback, useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { FaRegStopCircle } from "react-icons/fa"; -import { Avatar, Button, HStack, Input } from "@chakra-ui/react"; +import { Avatar, Box, Button, Textarea, VStack } from "@chakra-ui/react"; import { useMutation } from "@tanstack/react-query"; import axios from "axios"; +import { debounce } from "lodash"; import { v4 as uuidv4 } from "uuid"; import { LafAILogoIcon } from "@/components/CommonIcon"; @@ -19,7 +20,7 @@ export default function AIChatPanel() { let source = CancelToken.source(); const contentDomRef = useRef(null); - const inputRef = useRef(null); + const textareaRef = useRef(null); const [prompt, setPrompt] = useState(""); @@ -31,6 +32,9 @@ export default function AIChatPanel() { }, ]); + const [contentHeight, setContentHeight] = useState("calc(100% - 120px)"); + const [isComposing, setIsComposing] = useState(false); + async function handleSubmit() { if (generateCode.isLoading) { generateCode.reset(); @@ -69,6 +73,7 @@ export default function AIChatPanel() { }); }, 100); setPrompt(""); + resetHeight(); await generateCode.mutateAsync({ value: prompt, }); @@ -76,7 +81,7 @@ export default function AIChatPanel() { } const { data: generateCodeRes, ...generateCode } = useMutation((params: any) => { - inputRef.current?.focus(); + textareaRef.current?.focus(); return axios({ url: siteSettings.ai_pilot_url?.value, method: "POST", @@ -104,64 +109,118 @@ export default function AIChatPanel() { }); }); + const adjustTextareaHeight = useCallback( + debounce(() => { + const textarea = textareaRef.current; + if (textarea) { + textarea.style.height = "auto"; + const scrollHeight = textarea.scrollHeight; + const newHeight = scrollHeight <= 120 ? 120 : Math.min(scrollHeight, 280); + textarea.style.height = `${newHeight}px`; + setContentHeight(`calc(100% - ${newHeight}px)`); + } + }, 100), + [], + ); + + useEffect(() => { + return () => { + adjustTextareaHeight.cancel(); + }; + }, [adjustTextareaHeight]); + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "Enter" && !e.shiftKey && !isComposing) { + e.preventDefault(); + handleSubmit(); + } + }; + + const resetHeight = useCallback(() => { + const textarea = textareaRef.current; + if (textarea) { + textarea.style.height = "120px"; + setContentHeight("calc(100% - 120px)"); + } + }, []); + + const handleChange = (e: React.ChangeEvent) => { + adjustTextareaHeight(); + setPrompt(e.target.value); + }; + return ( -
-
+ +
    - {aiChatHistory.map((item, index) => { - return ( -
  • -
    -
    - {item.isAi ? ( - - ) : ( - - )} -
    - + {aiChatHistory.map((item, index) => ( +
  • +
    +
    {item.isAi ? ( -
    - -
    + ) : ( -
    {item.text}
    + )}
    -
  • - ); - })} +
    + {item.isAi ? ( + + ) : ( +
    {item.text}
    + )} +
    +
+ + ))} -
- - - + +