diff --git a/front/App.tsx b/front/App.tsx index 9f87763..5920d3b 100644 --- a/front/App.tsx +++ b/front/App.tsx @@ -1,9 +1,14 @@ -import React from 'react' +import React, {useEffect} from 'react' import './assets/global.scss' import {AppRouter} from "./pages/Router.tsx"; +import {useUserinfoStore} from "./store/userinfoStore.ts"; export function App() { + const {init} = useUserinfoStore(); + useEffect(()=>{ + init(); // 初始化用户信息 + }, []) return (
) diff --git a/front/components/AppList/index.tsx b/front/components/AppList/index.tsx new file mode 100644 index 0000000..ec8c52f --- /dev/null +++ b/front/components/AppList/index.tsx @@ -0,0 +1,24 @@ +import {List, Space} from "@douyinfe/semi-ui" +import {Link} from "react-router-dom"; +import {formatDate} from "../../../utils/date.ts"; +import {AppModel} from "../../../model"; + +export const AppList = (props: { + list: AppModel[]; + onItemClick?: (app: AppModel) => void; +}) => { + return (
+ {props.list.map(it => ( +
{ + props.onItemClick?.(it) + }}> + + {it.id} + {it.title} + {formatDate(it.create_time)} + + 进入 +
+ ))} +
) +} diff --git a/front/components/Result.tsx b/front/components/Result.tsx deleted file mode 100644 index cc069bd..0000000 --- a/front/components/Result.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from "react"; - -export type ResultProps = { - extra: React.ReactNode; - title: React.ReactNode; - status: number | string; -}; -export const Result: React.FC = (props) => { - return
-
{props.title}
-
{props.extra}
-
-} diff --git a/front/components/Result/index.tsx b/front/components/Result/index.tsx new file mode 100644 index 0000000..f7e2aa0 --- /dev/null +++ b/front/components/Result/index.tsx @@ -0,0 +1,38 @@ +import React from "react"; +import './result.scss' +import {CheckCircleFilled} from "../icons/CheckCircleFilled.tsx"; +import {NotFound} from "../icons/NotFound.tsx"; + +type Status = 'success' | 'error' | 'info' | 'warning' | '404' | '403' | '500'; +export type ResultProps = { + extra?: React.ReactNode; + title?: React.ReactNode; + subTitle?: React.ReactNode; + icon?: React.ReactNode; + status?: Status //number | string; +}; +export const IconMap: { + [key in Status]: React.ReactNode +} = { + success: , + error: , + info: , + warning: , +// error: CloseCircleFilled, +// info: ExclamationCircleFilled, +// warning: WarningFilled, + '404': , + '500': , + '403': , +// '500': serverError, +// '403': unauthorized, +}; +export const Index: React.FC = (props) => { + const icon = props.icon || (props.status ? IconMap[props.status] : <>) + return
+
{icon}
+
{props.title}
+ {props.subTitle &&
{props.subTitle}
} +
{props.extra}
+
+} diff --git a/front/components/Result/result.scss b/front/components/Result/result.scss new file mode 100644 index 0000000..387d5a9 --- /dev/null +++ b/front/components/Result/result.scss @@ -0,0 +1,26 @@ +.result-wrapper { + text-align: center; + padding: 48px 32px; + + .icon { + margin-bottom: 24px; + color:#52c41a; + font-size: 72px; + } + + .title { + color: rgba(0, 0, 0, 0.88); + font-size: 24px; + line-height: 1.3333333333333333; + margin-block: 8px; + } + .sub-title{ + color: rgba(0, 0, 0, 0.45); + font-size: 14px; + line-height: 1.5714285714285714; + } + + .extra { + margin: 24px 0 0 0; + } +} diff --git a/front/components/icons/CheckCircleFilled.tsx b/front/components/icons/CheckCircleFilled.tsx new file mode 100644 index 0000000..0ec54bf --- /dev/null +++ b/front/components/icons/CheckCircleFilled.tsx @@ -0,0 +1,16 @@ +import React from "react"; + +export type SvgProps = {} & React.SVGProps; +export const CheckCircleFilled: React.FC = (props) => { + return () +} diff --git a/front/components/icons/NotFound.tsx b/front/components/icons/NotFound.tsx new file mode 100644 index 0000000..ac36d7a --- /dev/null +++ b/front/components/icons/NotFound.tsx @@ -0,0 +1,157 @@ +import React from "react"; + +export const NotFound: React.FC = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/front/components/panel/panel.module.scss b/front/components/panel/panel.module.scss index a3c1b10..e729bed 100644 --- a/front/components/panel/panel.module.scss +++ b/front/components/panel/panel.module.scss @@ -9,6 +9,7 @@ } .panelTitle{ font-size: 14px; + font-weight: bold; } .panelExtra{ @@ -18,4 +19,4 @@ padding:10px; border-radius: 4px;; } -.noPadding{padding:0;} \ No newline at end of file +.noPadding{padding:0;} diff --git a/front/main.tsx b/front/main.tsx index 9226034..3aff0a1 100644 --- a/front/main.tsx +++ b/front/main.tsx @@ -4,7 +4,7 @@ import ReactDOM from 'react-dom/client'; import {App} from './App' ReactDOM.createRoot(document.querySelector('#root')!).render( - + // - + // ) diff --git a/front/pages/Router.tsx b/front/pages/Router.tsx index 8088a39..cc8f103 100644 --- a/front/pages/Router.tsx +++ b/front/pages/Router.tsx @@ -3,7 +3,8 @@ import {BrowserRouter, HashRouter, Navigate, Route, Routes, useNavigate} from "r import {APP_CONFIG} from "../config.ts"; import {Button} from "@douyinfe/semi-ui"; import DefaultPage from "./index"; -import {Result} from "../components/Result.tsx"; +import {Index} from "../components/Result"; +import {DashboardIndex} from "./dashboard"; const routerMode: 'browser' | 'hash' | string = APP_CONFIG.ROUTER_MODE; @@ -15,7 +16,7 @@ const WebRouter: React.FC<{ const NotFound: React.FC = () => { const navigate = useNavigate(); - return (
}/> + }/> }/>
diff --git a/front/pages/dashboard/index.tsx b/front/pages/dashboard/index.tsx new file mode 100644 index 0000000..cf7c617 --- /dev/null +++ b/front/pages/dashboard/index.tsx @@ -0,0 +1,130 @@ +import React from "react"; +import {Panel} from "../../components/panel"; +import {AppList} from "../../components/AppList"; +import {AppModel, EventDataModel, EventModel} from "../../../model"; +import {appList, appEventList, appEventDataList} from "../../service/api.app.ts"; +import './style.scss' +import {Space, Table} from "@douyinfe/semi-ui"; +import {formatDate} from "../../../utils/date.ts"; +import {useInterval} from "ahooks"; + +export const DashboardIndex: React.FC = () => { + // 数据 + const [apps, setApps] = React.useState([]); + const [currentAppID, setCurrentAppID] = React.useState(0); + const [events, setEvents] = React.useState([]); + const [eventDataList, setEventDataList] = React.useState([]); + const [page, setPage] = React.useState(1); + const [dataTotal, setDataTotal] = React.useState(0); + + const eventDataColumns = [ + { + title: '编号', + dataIndex: 'id', + width: 60 + }, + { + title: '事件', + dataIndex: 'title', + width: 80 + }, + { + title: '路径', + dataIndex: 'path', + width: 120 + }, + { + title: '访问者浏览器', + ellipsis: true, + dataIndex: 'browser' + }, + { + title: '来源', + dataIndex: 'ip', + width: 80 + }, + { + title: '分辨率', + dataIndex: 'resolution', + width: 120 + }, + { + title: '日期', + dataIndex: 'create_time', + render: (text: string) => formatDate(text), + width: 180 + }, + ] + // 加载应用所有的事件 + const getAppEvents = () => { + appEventList(currentAppID).then(list => setEvents(list)) + } + + // 加载引用所有事件上报的数据 + const loadAppEventsAndDataList = (page = 1) => { + appEventDataList({ + appId: currentAppID, + pageSize: 10, + page + }).then(list => { + setEventDataList(list) + setPage(page) + }) + } + + const getAppEventAndData = ()=>{ + if (currentAppID > 0) { + getAppEvents() + loadAppEventsAndDataList() + } + } + // 10s 自动刷新 + useInterval(loadAppEventsAndDataList, 10000) + + React.useEffect(() => { + appList().then(list => setApps(list)) + }, []) + React.useEffect(getAppEventAndData, [currentAppID]) + + + return ( +
+
+

Dashboard

+
+ + + setCurrentAppID(it.id)} + /> + + {currentAppID > 0 && <> + +
+ {events.map(it => ( +
+ + {it.id} + {it.title} + + 查看 +
+ ))} +
+
+ +
+ + + + } + + ); +}; diff --git a/front/pages/dashboard/style.scss b/front/pages/dashboard/style.scss new file mode 100644 index 0000000..886addc --- /dev/null +++ b/front/pages/dashboard/style.scss @@ -0,0 +1,9 @@ +.app-list-wrapper{ + .list-link-item{ + padding:10px; + border-bottom: solid 1px #eee; + &:last-child{ + border-bottom: none; + } + } +} diff --git a/front/pages/index/index.tsx b/front/pages/index/index.tsx index da9952c..496716c 100644 --- a/front/pages/index/index.tsx +++ b/front/pages/index/index.tsx @@ -1,4 +1,4 @@ -import React, {useRef, useState} from "react"; +import React, {useEffect, useRef, useState} from "react"; import {IconGithubLogo, IconWeibo} from "@douyinfe/semi-icons"; import css from './index.module.scss' import {LoginComponent} from "../../components/LoginComponent.tsx"; @@ -11,13 +11,12 @@ const DefaultPage: React.FC = () => { const [loading, setLoading] = useState(false); const navigate = useNavigate(); - const {login} = useUserinfoStore() + const {login, userinfo} = useUserinfoStore() const onFinish = async (values: any) => { setLoading(true); try { const user = await login(values) - setVisible(true); navigate('/dashboard') } catch (err) { // 登录失败 @@ -30,8 +29,12 @@ const DefaultPage: React.FC = () => { setLoading(false); }; - const [visible, setVisible] = useState(false); - const hideModal = () => setVisible(false) + + useEffect(() => { + if (userinfo && userinfo.id > 0) { + navigate('/dashboard') + } + }, [userinfo]) return (
{/**/} diff --git a/front/service/api.app.ts b/front/service/api.app.ts new file mode 100644 index 0000000..d506048 --- /dev/null +++ b/front/service/api.app.ts @@ -0,0 +1,22 @@ +import {get, post} from "./request.ts"; +import {AppModel, EventDataModel, EventModel, UserModel} from "../../model"; + +// 获取用户归属应用列表 +export function appList() { + return get('/api/app/list') +} + +// 根据应用id获取应用所有的事件列表 +export function appEventList(appId: number) { + return get('/api/app/event', {appId}) +} + +// 根据应用id获取应用所有的事件数据列表 +export function appEventDataList(params: { + appId: number; + eventId?: number; + page?: number; + pageSize?: number; +}) { + return get('/api/app/event-data', params) +} diff --git a/front/service/request.ts b/front/service/request.ts index 9e3eaa5..d5a52df 100644 --- a/front/service/request.ts +++ b/front/service/request.ts @@ -59,8 +59,22 @@ Axios.interceptors.response.use(res => { return Promise.reject(err) }) +// 将data对象转化为url参数 + +function dataToQueryString(data: any) { + if (!data) return ''; + return Object.keys(data).map(key => { + return encodeURIComponent(key) + '=' + encodeURIComponent(data[key]) + }).join('&') +} + + export function request(url: string, method: RequestMethod, data: any = null, getOriginResult = false) { return new Promise((resolve, reject) => { + if (method == 'get' && data) { + url += `?${dataToQueryString(data)}` + data = null + } Axios.request>({ url, method, @@ -103,10 +117,10 @@ export function uploadFile(url: string, file: File, data: any = {}, returnOri return request(url, 'post', formData, returnOrigin) } -export function post(url: string, data: any = {}) { +export function post(url: string, data: any = null) { return request(url, 'post', data) } -export function get(url: string, data: any = {}) { +export function get(url: string, data: any = null) { return request(url, 'get', data) } diff --git a/front/store/userinfoStore.ts b/front/store/userinfoStore.ts index 8495d64..54a85e1 100644 --- a/front/store/userinfoStore.ts +++ b/front/store/userinfoStore.ts @@ -33,7 +33,7 @@ export const useUserinfoStore = create<{ /** * 初始化用户数据 */ - init: () => Promise; + init: () => void; }>((set, _, _state) => { return { userinfo: { @@ -43,20 +43,28 @@ export const useUserinfoStore = create<{ init: () => { const state = _state.getState() if (state.token) { - return Promise.resolve() + return; } const token = Storage.get(LOGIN_TOKEN_KEY); - return new Promise((resolve) => { - if (token) { - getInfo().then((info) => { - set({ - userinfo: info, - token - }) + if (token) { + getInfo().then((info) => { + set({ + userinfo: info, + token }) - } - resolve(); - }); + }) + } + // return new Promise((resolve) => { + // if (token) { + // getInfo().then((info) => { + // set({ + // userinfo: info, + // token + // }) + // }) + // } + // resolve(); + // }); }, logout: () => { return new Promise((resolve) => { diff --git a/model/index.ts b/model/index.ts index 4882223..7e400ab 100644 --- a/model/index.ts +++ b/model/index.ts @@ -17,6 +17,8 @@ export type EventDataModel = { uid: number; uuid: number; type: 'pv' | 'uv' | string; + title:string; + description:string; location: string; resolution: string; browser: string; diff --git a/package.json b/package.json index 4f45682..5d30b1f 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@douyinfe/semi-ui": "^2.41.3", "@emotion/css": "^11.11.2", "@types/express": "^4.17.17", + "@types/md5": "^2.3.2", "@types/mocha": "^10.0.1", "@types/mysql": "^2.15.21", "@types/node": "^20.4.9", @@ -24,6 +25,7 @@ "@types/react-dom": "^18.2.7", "@types/supertest": "^2.0.12", "@vitejs/plugin-react": "^4.0.4", + "ahooks": "^3.7.8", "axios": "^1.4.0", "dayjs": "^1.11.9", "mocha": "^10.2.0", @@ -39,7 +41,9 @@ }, "dependencies": { "express": "^4.18.2", + "md5": "^2.3.0", "mysql": "^2.18.1", + "redis": "^4.6.7", "ts-node": "^10.9.1" } } diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..7e866ac Binary files /dev/null and b/public/favicon.ico differ diff --git a/service/core/server.ts b/service/core/server.ts index 908628b..4ac7b7c 100644 --- a/service/core/server.ts +++ b/service/core/server.ts @@ -1,4 +1,4 @@ -import { InitServerOption } from "./types"; +import {InitServerOption} from "./types"; // import { createServer as createServerOrigin } from "http"; // import express = require("express"); import * as express from "express"; @@ -8,9 +8,23 @@ export function createServer(options: Partial, callback?: () = const app = express(); // 将请求体转换为JSON格式 app.use(express.json()) - app.use(express.urlencoded({ extended: true })) + app.use(express.urlencoded({extended: true})) // 监听端口 app.listen(options.port, callback); + // 添加允许跨域中间件 + app.use(function (req, res, next) { + const method = req.method.toLowerCase(); + res.header("Access-Control-Allow-Origin", "*"); + res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS"); + // res.header("Access-Control-Allow-Credentials", "true"); + res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization"); + if (method === 'options') { + res.status(200).end('OK'); + return; + } + next(); + }); + app.get('/ping', (_req, res) => { res.appendHeader('app-ping', 'pong') res.send('pong') diff --git a/service/core/types.ts b/service/core/types.ts index 4bb4de4..f3b24e1 100644 --- a/service/core/types.ts +++ b/service/core/types.ts @@ -1,4 +1,7 @@ -import { Response, Request } from 'express' +import {Response, Request} from 'express' +import {IncomingHttpHeaders} from "http"; +import {UserModel} from "../../model"; + export type InitServerOption = { port: number; host: string; @@ -11,8 +14,9 @@ export type RouteHandleFunctionParam = { query: Record; body: Record; method: HttpMethod; - headers: Record; + user?: UserModel | null; + headers: IncomingHttpHeaders; res: Response> req: Request } -export type RouteHandleFunction = (params: RouteHandleFunctionParam) => void | Promise; \ No newline at end of file +export type RouteHandleFunction = (params: RouteHandleFunctionParam) => void | Promise; diff --git a/service/routes/index.ts b/service/routes/index.ts index 34b2561..3bb2a0c 100644 --- a/service/routes/index.ts +++ b/service/routes/index.ts @@ -2,21 +2,42 @@ import {Application, Request, Response} from "express"; import {RouteHandleFunction, RouteHandleFunctionParam} from "../core/types"; import {home} from "./home"; import {appList, reportToServer, appEvent, eventData} from "./reportor"; -import {getUserInfo, loginHandler} from "./user.ts"; +import {decodeUserToken, getUserInfo, loginHandler} from "./user.ts"; + +const excludes = [ + '/home', + '/api/report', +] // function createRoute(handler: RouteHandleFunction) { return (req: Request, res: Response>) => { - // console.log('params', req.params, req.query, req.body) - handler({ + // console.log('params', req.headers) + const params = { path: req.path, param: req.params, query: req.query, body: req.body, method: req.method, - headers: {}, + headers: req.headers, res, req + }; + const path = req.path; + console.log('request path:', path) + if (excludes.includes(path)) { + return handler(params); + } + const token = req.headers.authorization + if (!token) { + res.send({code: 403, message: '请先登录'}) + return; + } + decodeUserToken(token).then(user => { + handler({ + ...params, + user: typeof (user) == "string" ? JSON.parse(user) : user + }) }) } } diff --git a/service/routes/reportor.ts b/service/routes/reportor.ts index bffca01..3c8343f 100644 --- a/service/routes/reportor.ts +++ b/service/routes/reportor.ts @@ -1,37 +1,46 @@ -import { RouteHandleFunction } from "../core/types"; -import { listAppByUID } from "../service/app"; -import { reportEvent } from "../service/report-service"; +import {RouteHandleFunction} from "../core/types"; +import {listAppByUID, listAppEvent, listAppEventData} from "../service/app"; +import {reportEvent} from "../service/report-service"; -export const reportToServer: RouteHandleFunction = ({ - param, res -}) => { - reportEvent(param).then(() => { - res.send('got and saved!') - }).catch((e: Error) => { - console.log(e) - res.send('got but not saved!') - }); -} +export const reportToServer: RouteHandleFunction = + ({ + body, res + }) => { + reportEvent(body).then(() => { + res.send('got and saved!') + }).catch((e: Error) => { + console.log(e) + res.send('got but not saved!') + }); + } -export const appList: RouteHandleFunction = async ({ - param, res -}) => { - const apps = await listAppByUID(1); - res.send({ code: 0, data: apps }) -} +export const appList: RouteHandleFunction = + async ({ + param, res + }) => { + const apps = await listAppByUID(1); + res.send({code: 0, data: apps}) + } -export const appEvent: RouteHandleFunction = async ({ - query, res, param, body -}) => { +export const appEvent: RouteHandleFunction = + async ({ + query, res, param, body + }) => { + const events = await listAppEvent(query.appId) + res.send({ + code: 0, + data: events + }) + } - res.send({ - query, param, body - }) -} - -export const eventData: RouteHandleFunction = ({ - param, res -}) => { - res.send(JSON.stringify(param)) -} +export const eventData: RouteHandleFunction = + async ({ + query, res + }) => { + const data = await listAppEventData(Number(query.appId), Number(query.page), Number(query.pageSize)) + res.send({ + code: 0, + data + }) + } diff --git a/service/routes/user.ts b/service/routes/user.ts index a699611..240fdc6 100644 --- a/service/routes/user.ts +++ b/service/routes/user.ts @@ -1,17 +1,22 @@ import {RouteHandleFunction} from "../core/types.ts"; import {login} from "../service/app.ts"; import {UserModel} from "../../model"; +import {getFromRedis, setToRedis} from "../service/redis.ts"; export function encodeUserToken(user: UserModel) { if (user == null) throw new Error('user is null') - const token = JSON.stringify(user) - return btoa(encodeURIComponent(token)); + // const token = JSON.stringify(user) + const token = btoa(user.id + ':' + Date.now()) + setToRedis('app-report:user:' + token, JSON.stringify(user)).then(() => console.log('setToRedis success')); + return token; } export function decodeUserToken(token: string) { // 将token转回UserModel对象数据 - const user = decodeURIComponent(atob(token)); - return JSON.parse(user) as UserModel + // const user = decodeURIComponent(atob(token)); + console.log('decodeUserToken token==>app-report:user:', token) + return getFromRedis('app-report:user:' + token) + // return JSON.parse(user) as UserModel } @@ -38,8 +43,14 @@ export const loginHandler: RouteHandleFunction } export const getUserInfo: RouteHandleFunction = - ({headers, res}) => { - const token = headers.Authorization - const user = decodeUserToken(token) - res.send({code: 0, message: '登录成功', data: user}) + async ({headers,user, res}) => { + const token = headers.authorization + if (!token) { + res.send({code: 403, message: '请先登录'}) + return; + } + if (!user) { + res.send({code: 403, message: '请先登录'}) + } + res.send({code: 0, message: 'success', data: user}) } diff --git a/service/service/app.ts b/service/service/app.ts index fc701cc..e821195 100644 --- a/service/service/app.ts +++ b/service/service/app.ts @@ -17,7 +17,7 @@ export function listAppEvent(id: number) { // 查询应用所有的事件数据 export function listAppEventData(id: number, page = 1, pageSize = 10) { - return selectArray('select * from events_data where app_id=? limit ?,?', + return selectArray('select d.*,e.title,e.type,e.description from events_data d,events e where d.event_id = e.id and d.app_id=? limit ?,?', [id, (page - 1) * pageSize, pageSize]) } diff --git a/service/service/mysql.ts b/service/service/mysql.ts index 32ed3f8..99f7449 100644 --- a/service/service/mysql.ts +++ b/service/service/mysql.ts @@ -42,7 +42,7 @@ export async function selectOne(sql: string, params: any = null) { // 统计数据 export async function queryCount(sql: string, params: any = null) { const obj = await selectOne<{ [key: string]: number }>(sql, params); - if(!obj) return 0; + if (!obj) return 0; const keys = Object.keys(obj); return obj[keys[0]]; } @@ -68,13 +68,14 @@ export async function isExist(sql: string, params: any) { function executeSQL(sql: string, params: any) { return new Promise((resolve, reject) => { - pool.query(sql, params, (err, ret) => { + const q = pool.query(sql, params, (err, ret) => { if (err) { reject(err) } else { resolve(ret) } }) + console.log('[SQL]: ' + q.sql,params) }) } diff --git a/service/service/redis.ts b/service/service/redis.ts new file mode 100644 index 0000000..6051b3b --- /dev/null +++ b/service/service/redis.ts @@ -0,0 +1,32 @@ +import {createClient} from 'redis'; + +const client = createClient(); + +client.on('error', err => console.log('Redis Client', err)); + +client.connect().then(() => { + console.log('connected to redis success') +}) + +export async function setToRedis(key: string, value: any, expire?: number) { + if (!value) throw new Error('value is required'); + await client.set(key, JSON.stringify(value)) + if (expire) await client.expire(key, expire); + return; +} + +// export async function getString(key: string) { +// const data = await client.get(key); +// if (!data) return null; +// return data; +// } + +export async function getFromRedis(key: string) { + const data = await client.get(key); + if (!data) return null; + console.log('getFromRedis=>',typeof(data),data) + return JSON.parse(data) as T; +} + +// 导出客户端) +export default client; diff --git a/service/service/report-service.ts b/service/service/report-service.ts index f732e62..67532b4 100644 --- a/service/service/report-service.ts +++ b/service/service/report-service.ts @@ -1,19 +1,16 @@ import { PvUvModel } from "../../model"; import { isExist, insertAndGetInsertId } from './mysql' -const table = { - pv_uv: 'pv_uv' -} export async function reportEvent(data: Partial) { //pv:用户每次打开一个页面便记录1次PV,多次打开同一页面则浏览量累计。 //uv:1天内同一访客的多次访问只记录为一个访客。通过IP和cookie是判断UV值的两种方式。 const { type } = data; if (type == 'uv') { // 判断今日的数据是否已经存在 - const existsToday = await isExist('select count(*) _ from pv_uv where DATE(created_at) = CURDATE() and uuid=?', [data.uuid]) + const existsToday = await isExist('select count(*) _ from events_data where DATE(created_at) = CURDATE() and uuid=?', [data.uuid]) if (existsToday) { return; } } - await insertAndGetInsertId('pv_uv', data) + await insertAndGetInsertId('events_data', data) } diff --git a/yarn.lock b/yarn.lock index 488c4e6..0d6c2b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -564,6 +564,40 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@redis/bloom@1.2.0": + version "1.2.0" + resolved "https://registry.npmmirror.com/@redis/bloom/-/bloom-1.2.0.tgz#d3fd6d3c0af3ef92f26767b56414a370c7b63b71" + integrity sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg== + +"@redis/client@1.5.8": + version "1.5.8" + resolved "https://registry.npmmirror.com/@redis/client/-/client-1.5.8.tgz#a375ba7861825bd0d2dc512282b8bff7b98dbcb1" + integrity sha512-xzElwHIO6rBAqzPeVnCzgvrnBEcFL1P0w8P65VNLRkdVW8rOE58f52hdj0BDgmsdOm4f1EoXPZtH4Fh7M/qUpw== + dependencies: + cluster-key-slot "1.1.2" + generic-pool "3.9.0" + yallist "4.0.0" + +"@redis/graph@1.1.0": + version "1.1.0" + resolved "https://registry.npmmirror.com/@redis/graph/-/graph-1.1.0.tgz#cc2b82e5141a29ada2cce7d267a6b74baa6dd519" + integrity sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg== + +"@redis/json@1.0.4": + version "1.0.4" + resolved "https://registry.npmmirror.com/@redis/json/-/json-1.0.4.tgz#f372b5f93324e6ffb7f16aadcbcb4e5c3d39bda1" + integrity sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw== + +"@redis/search@1.1.3": + version "1.1.3" + resolved "https://registry.npmmirror.com/@redis/search/-/search-1.1.3.tgz#b5a6837522ce9028267fe6f50762a8bcfd2e998b" + integrity sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng== + +"@redis/time-series@1.0.4": + version "1.0.4" + resolved "https://registry.npmmirror.com/@redis/time-series/-/time-series-1.0.4.tgz#af85eb080f6934580e4d3b58046026b6c2b18717" + integrity sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng== + "@remix-run/router@1.8.0": version "1.8.0" resolved "https://registry.npmmirror.com/@remix-run/router/-/router-1.8.0.tgz#e848d2f669f601544df15ce2a313955e4bf0bafc" @@ -634,6 +668,16 @@ resolved "https://registry.npmmirror.com/@types/http-errors/-/http-errors-2.0.1.tgz#20172f9578b225f6c7da63446f56d4ce108d5a65" integrity sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ== +"@types/js-cookie@^2.x.x": + version "2.2.7" + resolved "https://registry.npmmirror.com/@types/js-cookie/-/js-cookie-2.2.7.tgz#226a9e31680835a6188e887f3988e60c04d3f6a3" + integrity sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA== + +"@types/md5@^2.3.2": + version "2.3.2" + resolved "https://registry.npmmirror.com/@types/md5/-/md5-2.3.2.tgz#529bb3f8a7e9e9f621094eb76a443f585d882528" + integrity sha512-v+JFDu96+UYJ3/UWzB0mEglIS//MZXgRaJ4ubUPwOM0gvLc/kcQ3TWNYwENEK7/EcXGQVrW8h/XqednSjBd/Og== + "@types/mime@*": version "3.0.1" resolved "https://registry.npmmirror.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" @@ -767,6 +811,27 @@ acorn@^8.4.1: resolved "https://registry.npmmirror.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== +ahooks-v3-count@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/ahooks-v3-count/-/ahooks-v3-count-1.0.0.tgz#ddeb392e009ad6e748905b3cbf63a9fd8262ca80" + integrity sha512-V7uUvAwnimu6eh/PED4mCDjE7tokeZQLKlxg9lCTMPhN+NjsSbtdacByVlR1oluXQzD3MOw55wylDmQo4+S9ZQ== + +ahooks@^3.7.8: + version "3.7.8" + resolved "https://registry.npmmirror.com/ahooks/-/ahooks-3.7.8.tgz#3fa3c491cd153e884a32b0c4192fc72cf84c4332" + integrity sha512-e/NMlQWoCjaUtncNFIZk3FG1ImSkV/JhScQSkTqnftakRwdfZWSw6zzoWSG9OMYqPNs2MguDYBUFFC6THelWXA== + dependencies: + "@babel/runtime" "^7.21.0" + "@types/js-cookie" "^2.x.x" + ahooks-v3-count "^1.0.0" + dayjs "^1.9.1" + intersection-observer "^0.12.0" + js-cookie "^2.x.x" + lodash "^4.17.21" + resize-observer-polyfill "^1.5.1" + screenfull "^5.0.0" + tslib "^2.4.1" + ansi-colors@4.1.1: version "4.1.1" resolved "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" @@ -967,6 +1032,11 @@ chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +charenc@0.0.2: + version "0.0.2" + resolved "https://registry.npmmirror.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== + chokidar@3.5.3, "chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.2: version "3.5.3" resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" @@ -1001,6 +1071,11 @@ clsx@^1.1.1: resolved "https://registry.npmmirror.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== +cluster-key-slot@1.1.2: + version "1.1.2" + resolved "https://registry.npmmirror.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" + integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -1105,6 +1180,11 @@ create-require@^1.1.0: resolved "https://registry.npmmirror.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== +crypt@0.0.2: + version "0.0.2" + resolved "https://registry.npmmirror.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== + csstype@^3.0.2: version "3.1.2" resolved "https://registry.npmmirror.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" @@ -1122,7 +1202,7 @@ date-fns@^2.29.3: dependencies: "@babel/runtime" "^7.21.0" -dayjs@^1.11.9: +dayjs@^1.11.9, dayjs@^1.9.1: version "1.11.9" resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a" integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA== @@ -1395,6 +1475,11 @@ function-bind@^1.1.1: resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +generic-pool@3.9.0: + version "3.9.0" + resolved "https://registry.npmmirror.com/generic-pool/-/generic-pool-3.9.0.tgz#36f4a678e963f4fdb8707eab050823abc4e8f5e4" + integrity sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g== + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -1537,6 +1622,11 @@ inherits@2, inherits@2.0.4, inherits@~2.0.3: resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +intersection-observer@^0.12.0: + version "0.12.2" + resolved "https://registry.npmmirror.com/intersection-observer/-/intersection-observer-0.12.2.tgz#4a45349cc0cd91916682b1f44c28d7ec737dc375" + integrity sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg== + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -1554,6 +1644,11 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-buffer@~1.1.6: + version "1.1.6" + resolved "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + is-core-module@^2.13.0: version "2.13.0" resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" @@ -1598,6 +1693,11 @@ isarray@~1.0.0: resolved "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== +js-cookie@^2.x.x: + version "2.2.1" + resolved "https://registry.npmmirror.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" + integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -1676,6 +1776,15 @@ make-error@^1.1.1: resolved "https://registry.npmmirror.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +md5@^2.3.0: + version "2.3.0" + resolved "https://registry.npmmirror.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" + integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== + dependencies: + charenc "0.0.2" + crypt "0.0.2" + is-buffer "~1.1.6" + media-typer@0.3.0: version "0.3.0" resolved "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -2088,6 +2197,18 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +redis@^4.6.7: + version "4.6.7" + resolved "https://registry.npmmirror.com/redis/-/redis-4.6.7.tgz#c73123ad0b572776223f172ec78185adb72a6b57" + integrity sha512-KrkuNJNpCwRm5vFJh0tteMxW8SaUzkm5fBH7eL5hd/D0fAkzvapxbfGPP/r+4JAXdQuX7nebsBkBqA2RHB7Usw== + dependencies: + "@redis/bloom" "1.2.0" + "@redis/client" "1.5.8" + "@redis/graph" "1.1.0" + "@redis/json" "1.0.4" + "@redis/search" "1.1.3" + "@redis/time-series" "1.0.4" + regenerator-runtime@^0.14.0: version "0.14.0" resolved "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" @@ -2155,6 +2276,11 @@ scheduler@^0.23.0: dependencies: loose-envify "^1.1.0" +screenfull@^5.0.0: + version "5.2.0" + resolved "https://registry.npmmirror.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba" + integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA== + scroll-into-view-if-needed@^2.2.24: version "2.2.31" resolved "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz#d3c482959dc483e37962d1521254e3295d0d1587" @@ -2377,7 +2503,7 @@ ts-node@^10.9.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tslib@^2.0.0: +tslib@^2.0.0, tslib@^2.4.1: version "2.6.2" resolved "https://registry.npmmirror.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== @@ -2478,16 +2604,16 @@ y18n@^5.0.5: resolved "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@4.0.0, yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + yallist@^3.0.2: version "3.1.1" resolved "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - yaml@^1.10.0: version "1.10.2" resolved "https://registry.npmmirror.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"