diff --git a/.dockerignore b/.dockerignore index 60da41dd8..3c3629e64 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,8 +1 @@ -# local env files -.env*.local - -# docker-compose env files -.env - -*.key -*.key.pub \ No newline at end of file +node_modules diff --git a/.github/workflows/dockerToHub-dev.yml b/.github/workflows/dockerToHub-dev.yml index 2a60b719d..42afb90ed 100644 --- a/.github/workflows/dockerToHub-dev.yml +++ b/.github/workflows/dockerToHub-dev.yml @@ -56,7 +56,7 @@ jobs: with: host: tx.xiaosi.cc #服务器地址 username: root #用户名 - password: ${{ secrets.SERVER_PASSWORD }} #私钥 安全问题一定都以变量的方式传递!!! + key: ${{ secrets.SSH_PRIVATE_KEY }} #私钥 安全问题一定都以变量的方式传递!!! envs: SERVER_WORKDIR,ALY_DOCKER_PASSWORD,ALY_DOCKER_USERNAME,DOCKER_ENV #使用工作目录变量 script: | cd $SERVER_WORKDIR #进入到工作目录 diff --git a/Dockerfile b/Dockerfile index 929faa12c..ae37461a0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,7 +28,6 @@ ENV CODE="" WORKDIR /app COPY . . -RUN rm -rf ./node_modules COPY --from=deps /app/node_modules ./node_modules RUN yarn build diff --git a/app/api/logs/[...path]/route.ts b/app/api/logs/[...path]/route.ts index 47b6951da..9fccab3d7 100644 --- a/app/api/logs/[...path]/route.ts +++ b/app/api/logs/[...path]/route.ts @@ -2,6 +2,17 @@ import { NextRequest, NextResponse } from "next/server"; import prisma from "@/lib/prisma"; import { insertUser } from "@/lib/auth"; +// function cleanObjectKeys(input: { [key: string]: string }): { +// [key: string]: string; +// } { +// const cleanedObj: { [key: string]: string } = {}; +// Object.keys(input).forEach((key) => { +// cleanedObj[key] = input[key].trim(); +// }); +// console.log('========', input, cleanedObj) +// return cleanedObj; +// } + async function handle( req: NextRequest, { params }: { params: { path: string[] } }, diff --git a/app/app/(admin)/admin/page.tsx b/app/app/(admin)/admin/page.tsx new file mode 100644 index 000000000..c32d89fa9 --- /dev/null +++ b/app/app/(admin)/admin/page.tsx @@ -0,0 +1,19 @@ +// "use client"; +import { Grid, Col, Card, Text, AreaChart, Metric } from "@tremor/react"; +import UsageAnalysis from "./usage-analysis"; + +export default function AdminPage() { + return ( + + + + + + + Title + KPI 2 + + + + ); +} diff --git a/app/app/(admin)/admin/usage-analysis.tsx b/app/app/(admin)/admin/usage-analysis.tsx new file mode 100644 index 000000000..345d49652 --- /dev/null +++ b/app/app/(admin)/admin/usage-analysis.tsx @@ -0,0 +1,171 @@ +// "use client"; +import { BarList, Bold, Card, Flex, Text, Title } from "@tremor/react"; +import prisma from "@/lib/prisma"; +import { estimateTokenLength } from "@/app/utils/token"; + +function HandleLogData(todayLog: [{ userName: string; logEntry: string }]) { + const data1 = todayLog.map((log) => { + return { + name: log.userName ?? "unknown", + value: estimateTokenLength(log.logEntry ?? ""), + }; + }); + + type Accumulator = { + [key: string]: number; + }; + const data2 = data1.reduce((acc, item) => { + if (acc[item.name]) { + acc[item.name] += item.value; + } else { + acc[item.name] = item.value; + } + return acc; + }, {}); + const data3 = Object.entries(data2) + .map(([name, value]) => ({ + name, + value, + })) + .sort((a, b) => { + return b.value - a.value; + }); + return data3; +} + +export default async function UsageAnalysis() { + // 今天日期的开始和结束 + const startDate = new Date(); + startDate.setHours(0, 0, 0, 0); + + const endDate = new Date(); + endDate.setHours(23, 59, 59, 999); + const todayLog = await prisma.logEntry.findMany({ + where: { + createdAt: { + gte: startDate, // gte 表示 '大于等于' + lte: endDate, // lte 表示 '小于等于' + }, + }, + include: { + user: true, + }, + }); + + // @ts-ignore + const log_data = HandleLogData(todayLog); + // console.log("======", log_data); + + // const data = [ + // { + // name: "Twitter", + // value: 456, + // href: "https://twitter.com/tremorlabs", + // icon: function TwitterIcon() { + // return ( + // + // + // + // + // ); + // }, + // }, + // { + // name: "Google", + // value: 351, + // href: "https://google.com", + // icon: function GoogleIcon() { + // return ( + // + // + // + // + // ); + // }, + // }, + // { + // name: "GitHub", + // value: 271, + // href: "https://github.com/tremorlabs/tremor", + // icon: function GitHubIcon() { + // return ( + // + // + // + // + // ); + // }, + // }, + // { + // name: "Reddit", + // value: 191, + // href: "https://reddit.com", + // icon: function RedditIcon() { + // return ( + // + // + // + // + // ); + // }, + // }, + // { + // name: "Youtube", + // value: 91, + // href: "https://www.youtube.com/@tremorlabs3079", + // icon: function YouTubeIcon() { + // return ( + // + // + // + // + // ); + // }, + // }, + // ]; + // @ts-ignore + return ( + + Website Analytics + + + Source + + + Visits + + + + + ); +} diff --git a/app/app/(admin)/layout.tsx b/app/app/(admin)/layout.tsx new file mode 100644 index 000000000..8b7376bc6 --- /dev/null +++ b/app/app/(admin)/layout.tsx @@ -0,0 +1,24 @@ +import "@/app/app/login.scss"; +import { Metadata } from "next"; +import { ReactNode } from "react"; + +export const metadata: Metadata = { + title: "Admin | 管理页面", +}; + +export default async function AdminLayout({ + children, +}: { + children: ReactNode; +}) { + return ( +
+
+

+ Admin Page +

+ {children} +
+
+ ); +} diff --git a/app/app/(auth)/layout.tsx b/app/app/(auth)/layout.tsx index 57458206e..cc4df20a9 100644 --- a/app/app/(auth)/layout.tsx +++ b/app/app/(auth)/layout.tsx @@ -1,4 +1,4 @@ -import "@/app/styles/login.scss"; +import "@/app/app/login.scss"; import { Metadata } from "next"; import { ReactNode } from "react"; // import { useEffect } from "react"; diff --git a/app/styles/login.scss b/app/app/login.scss similarity index 100% rename from app/styles/login.scss rename to app/app/login.scss diff --git a/app/constant.ts b/app/constant.ts index 083474014..ae3dae370 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -92,7 +92,7 @@ Latex inline: $x^2$ Latex block: $$e=mc^2$$ `; -export const SUMMARIZE_MODEL = "gpt-3.5-turbo-16k"; +export const SUMMARIZE_MODEL = "gpt-3.5-turbo-1106"; export const KnowledgeCutOffDate: Record = { default: "2021-09", @@ -133,7 +133,7 @@ export const DEFAULT_MODELS = [ { name: "gpt-4-all", describe: "GPT-4全能版,联网绘图多模态,又慢又贵", - available: true, + available: false, }, // { // name: "gpt-4v", diff --git a/lib/auth.ts b/lib/auth.ts index 234dfa75c..de93680dc 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -251,6 +251,12 @@ function isPinYin(input: string): boolean { export function isName(input: string): boolean { + const denyList = [ + "suibian", + ] + if (denyList.includes(input)) { + return false; + } return isEmail(input) || isHanZi(input) || isPinYin(input); } diff --git a/middleware.ts b/middleware.ts index 5eb43c8ae..4c7990da2 100644 --- a/middleware.ts +++ b/middleware.ts @@ -22,6 +22,14 @@ export default async function middleware(req: NextRequest) { return NextResponse.redirect(new URL("/login", req.url)); } else if (session) { if (path.startsWith("/login") || path.startsWith('/app/login')) return NextResponse.redirect(new URL("/", req.url)); + // admin 认证 + const admin_user = ["sijinhui", "司金辉"] + // @ts-ignore + if ((path.startsWith("/admin") || path.startsWith("/app/admin")) && !admin_user.includes(session?.user?.name)) { + return NextResponse.redirect(new URL("/", req.url)); + } else { + console.log('[admin]', session?.user) + } } if (path == '/login') { @@ -29,6 +37,11 @@ export default async function middleware(req: NextRequest) { new URL(`/app${path}`, req.url), ); } + if (path == "/admin") { + return NextResponse.rewrite( + new URL(`/app${path}`, req.url), + ); + } if (req.method == 'POST' && (path.startsWith("/api/openai/") || path.startsWith("/api/midjourney"))) { // 重写header,添加用户名 diff --git a/package.json b/package.json index 997f0269b..e03b839e4 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "private": false, "license": "mit", "scripts": { - "dev": "npx prisma generate && npx prisma db push && next dev", + "dev": "npx prisma generate && npx prisma db push && next dev", "build": "npx next telemetry disable && npx prisma generate && cross-env BUILD_MODE=standalone next build", "start": "next start", "lint": "next lint", @@ -17,10 +17,11 @@ }, "dependencies": { "@fortaine/fetch-event-source": "^3.0.6", - "@hello-pangea/dnd": "^16.3.0", "@next-auth/prisma-adapter": "^1.0.7", "@prisma/client": "^5.7.0", + "@hello-pangea/dnd": "^16.5.0", "@svgr/webpack": "^8.1.0", + "@tremor/react": "^3.12.1", "@vercel/analytics": "^1.1.1", "emoji-picker-react": "^4.5.15", "fuse.js": "^7.0.0", @@ -50,7 +51,8 @@ "devDependencies": { "@tailwindcss/forms": "^0.5.7", "@tailwindcss/typography": "^0.5.10", - "@tauri-apps/cli": "^1.4.0", + "@tauri-apps/cli": "^1.5.8", + "@types/node": "^20.9.0", "@types/react": "^18.2.14", "@types/react-dom": "^18.2.7", "@types/react-katex": "^3.0.0", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 6e388a800..8778db45b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -65,10 +65,10 @@ model Session { model LogEntry { id Int @id @default(autoincrement()) - ip String? @db.Char(25) + ip String? @db.VarChar(25) path String? @db.Text - model String? @db.Char(25) - userName String? @db.Char(50) + model String? @db.VarChar(25) + userName String? @db.VarChar(50) createdAt DateTime @default(now()) logEntry String? @db.Text user User? @relation(fields: [userName], references: [name], onDelete: NoAction) diff --git a/tailwind.config.js b/tailwind.config.js index f8c4fd898..6fc00d3d8 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -5,6 +5,7 @@ module.exports = { content: [ // "./app/**/*.{js,ts,jsx,tsx,mdx}", "./app/app/**/*.{js,ts,jsx,tsx,mdx}", + "./node_modules/@tremor/**/*.{js,ts,jsx,tsx}", // Tremor module ], theme: { extend: {