diff --git a/admin/user.go b/admin/user.go index d201b2f..9b03657 100644 --- a/admin/user.go +++ b/admin/user.go @@ -118,10 +118,20 @@ func passwordMigration(db *sql.DB, cache *redis.Client, id int64, password strin if len(password) < 6 || len(password) > 36 { return fmt.Errorf("password length must be between 6 and 36") } + var username string + hash_passwd := utils.Sha2Encrypt(password) + err_u := db.QueryRow("SELECT username FROM auth WHERE id = ?", id).Scan(&username) + if err_u != nil { + return fmt.Errorf("failed to fetch username: %v", err_u) + } + cacheKey := fmt.Sprintf("nio:user:%s", username) + if err_u := cache.Del(context.Background(), cacheKey).Err(); err_u != nil { + return fmt.Errorf("failed to delete cache: %v", err_u) + } _, err := globals.ExecDb(db, ` UPDATE auth SET password = ? WHERE id = ? - `, utils.Sha2Encrypt(password), id) + `, hash_passwd, id) cache.Del(context.Background(), fmt.Sprint("nio:user:root")) diff --git a/app/package.json b/app/package.json index 5850720..b00980c 100644 --- a/app/package.json +++ b/app/package.json @@ -46,7 +46,7 @@ "html-to-image": "^1.11.11", "i18next": "^23.4.6", "localforage": "^1.10.0", - "lucide-react": "^0.309.0", + "lucide-react": "^0.320.0", "match-sorter": "^6.3.1", "mermaid": "^10.9.0", "next-themes": "^0.2.1", diff --git a/app/src/components/ExportAllMsgButton.tsx b/app/src/components/ExportAllMsgButton.tsx new file mode 100644 index 0000000..6bc0ccd --- /dev/null +++ b/app/src/components/ExportAllMsgButton.tsx @@ -0,0 +1,178 @@ +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "./ui/dialog.tsx"; +import { Image, MenuSquare, PanelRight, ClipboardPaste } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import "@/assets/common/editor.less"; +import MarkdownExport from "./Markdown.tsx"; +import React, { useMemo, useState } from "react"; +import { Toggle } from "./ui/toggle.tsx"; +import { mobile } from "@/utils/device.ts"; +import { ChatAction } from "@/components/home/assemblies/ChatAction.tsx"; +import { cn } from "@/components/ui/lib/utils.ts"; +import { Message } from "@/api/types.tsx"; +import { + useMessages, +} from "@/store/chat.ts"; + +type ExportAllMsgButtonProps = { + maxLength?: number; + + formatter?: (value: string) => string; + title?: string; + + open?: boolean; + setOpen?: (open: boolean) => void; + children?: React.ReactNode; + + submittable?: boolean; + onSubmit?: (value: string) => void; + closeOnSubmit?: boolean; +}; + +function ExportAllMsgButtonCall({ + formatter, +}: ExportAllMsgButtonProps) { + const { t } = useTranslation(); + const [openPreview, setOpenPreview] = useState(!mobile); + const [openInput, setOpenInput] = useState(true); + const messages: Message[] = useMessages(); + const jsonArray = messages.map(message => ({ + role: message.role, + content: message.content + })); + const jsonString = `\`\`\`json\n${JSON.stringify(jsonArray, null, 4)}\n\`\`\``; + + function convertToMarkdown(jsonArray: { role: any; content: any; }[]) { + return jsonArray.map(({ role, content }) => { + const roleText = role === 'user' ? 'û˵' : 'AI˵'; + return `## ${roleText}\n\n${content}`; + }).join('\n\n'); + } + + const markdownString = convertToMarkdown(jsonArray); + const markdownValue = useMemo(() => { + return formatter ? formatter(markdownString) : markdownString; + }, [markdownString, formatter]); + + + + + return ( +
+
+
+ { + setOpenPreview(false); + setOpenInput(true); + }} + > + + + + { + setOpenPreview(true); + setOpenInput(true); + }} + > + + + + { + setOpenPreview(true); + setOpenInput(false); + }} + > + + +
+
+
+ {openInput && ( + + + + )} + {openPreview && ( + + )} +
+
+ +
+ ); +} + +function ExportAllMsgButton(props: ExportAllMsgButtonProps) { + const { t } = useTranslation(); + + return ( + <> + + {!props.setOpen && ( + + {props.children ?? ( + + + + )} + + )} + + + {props.title ?? t("export.user_used")} + + + + + + + + + ); +} + +export default ExportAllMsgButton; + +export function JSONTransMarkdownProvider({ ...props }: ExportAllMsgButtonProps) { + return ( + `\`\`\`markdown\n${value}\n\`\`\``} + + /> + ); +} \ No newline at end of file diff --git a/app/src/components/Markdown.tsx b/app/src/components/Markdown.tsx index d3f0ee8..2da5523 100644 --- a/app/src/components/Markdown.tsx +++ b/app/src/components/Markdown.tsx @@ -96,6 +96,52 @@ function Markdown({ [processedContent, acceptHtml, codeStyle, className, loading], ); } + +type MarkdownExportProps = { + children: string; + className?: string; + acceptHtml?: boolean; + codeStyle?: string; + loading?: boolean; +}; +function MarkdownExport({ + children, + acceptHtml, + codeStyle, + className, + loading, +}: MarkdownExportProps) { + const processedContent = useMemo(() => { + let content = children; + + // Inline math: replace \(...\) with $ ... $ + content = content.replace(/\\\((.*?)\\\)/g, (_, equation) => `$ ${equation.trim()} $`); + + // Block math: replace \[...\] with $$...$$ on separate lines + content = content.replace( + /\s*\\\[\s*([\s\S]*?)\s*\\\]\s*/g, + (_, equation) => `\n$$\n${equation.trim()}\n$$\n` + ); + + return content; + }, [children]); + + return useMemo( + () => ( + + ), + [processedContent, acceptHtml, codeStyle, className, loading], + ); +} +export { MarkdownExport }; + + type CodeMarkdownProps = MarkdownProps & { filename: string; }; diff --git a/app/src/components/home/ChatWrapper.tsx b/app/src/components/home/ChatWrapper.tsx index 16ff105..c9232bd 100644 --- a/app/src/components/home/ChatWrapper.tsx +++ b/app/src/components/home/ChatWrapper.tsx @@ -35,6 +35,7 @@ import ScrollAction from "@/components/home/assemblies/ScrollAction.tsx"; import { cn } from "@/components/ui/lib/utils.ts"; import { goAuth } from "@/utils/app.ts"; import { getModelFromId } from "@/conf/model.ts"; +import { JSONTransMarkdownProvider } from "@/components/ExportAllMsgButton.tsx"; type InterfaceProps = { scrollable: boolean; @@ -180,6 +181,7 @@ function ChatWrapper() { +
diff --git a/app/src/components/home/assemblies/ChatAction.tsx b/app/src/components/home/assemblies/ChatAction.tsx index 4d80037..80f7742 100644 --- a/app/src/components/home/assemblies/ChatAction.tsx +++ b/app/src/components/home/assemblies/ChatAction.tsx @@ -108,6 +108,19 @@ export function SettingsAction() { ); } +export function ExportButtonAction() { + const { t } = useTranslation(); + const dispatch = useDispatch(); + + return ( + dispatch(openDialog())} + > + + + ); +} export function MarketAction() { const { t } = useTranslation(); diff --git a/app/src/resources/i18n/cn.json b/app/src/resources/i18n/cn.json index 8dfd01b..4b82ec0 100644 --- a/app/src/resources/i18n/cn.json +++ b/app/src/resources/i18n/cn.json @@ -328,6 +328,11 @@ "update-success": "更新成功", "update-success-prompt": "您已更新至最新版本。" }, + "export":{ + "text": "导出文本", + "user_used": "用户自己导出", + "pdf": "导出成pdf" + }, "share": { "title": "分享", "share-conversation": "分享对话",