Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dist-ssr
src-tauri/target
src-tauri/target/release
*.local

.pnpm-store
# Editor directories and files
.vscode/*
!.vscode/extensions.json
Expand All @@ -31,3 +31,5 @@ src-tauri/target/release
.env
.env.*
!.env.example
.turbo
.wrangler
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
1. 必须使用pnpm作为包管理器
2. 禁止调用Agent打开Dev
190 changes: 0 additions & 190 deletions TEMP_segment.txt

This file was deleted.

File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions apps/hrttracker/logic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from '@hrt-tracker/core';
38 changes: 38 additions & 0 deletions apps/hrttracker/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "hrttracker",
"private": true,
"version": "1.3.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"tauri:dev": "tauri dev",
"tauri:build": "tauri build",
"tauri": "tauri"
},
"dependencies": {
"@hrt-tracker/core": "workspace:*",
"@types/uuid": "^10.0.0",
"clsx": "^2.1.1",
"jsqr": "1.4.0",
"lucide-react": "^0.344.0",
"prop-types": "15.8.1",
"qrcode.react": "^4.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^7.11.0",
"recharts": "^2.12.7",
"tailwind-merge": "^3.4.0",
"uuid": "^9.0.1"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20251205.0",
"@tauri-apps/cli": "^1.6.3",
"@types/node": "^22.14.0",
"@vitejs/plugin-react": "^5.0.0",
"typescript": "~5.8.2",
"vite": "^6.2.0",
"vite-plugin-pwa": "^1.2.0"
}
}
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
replayPersonalModel, computeSimulationWithCI, initPersonalModel,
ekfUpdatePersonalModel,
} from '../../logic';
import { computeDataHash } from '../utils/dataHash';
import { computeDataHash } from '../../logic';

const PERSONAL_MODEL_KEY = 'hrt-personal-model';
const APPLY_E2_LEARNING_TO_CPA_KEY = 'hrt-apply-e2-learning-to-cpa';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import React, { createContext, useContext, useState, useCallback, useEffect, use
import apiClient from '../api/client';
import { useAuth } from './AuthContext';
import { useSecurityPassword } from './SecurityPasswordContext';
import { computeDataHash } from '../utils/dataHash';
import { isLogoutInProgress } from '../utils/authSessionState';
import { computeDataHash, isLogoutInProgress } from '../../logic';

interface CloudSyncContextType {
isSyncing: boolean;
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ import {
} from 'lucide-react';
import { v4 as uuidv4 } from 'uuid';

import { DoseEvent, LabResult, encryptData, decryptData } from '../../logic';
import { computeDataHash } from '../utils/dataHash';
import { DoseEvent, LabResult, encryptData, decryptData, computeDataHash } from '../../logic';
import { APP_VERSION } from '../constants';
import CustomSelect from '../components/CustomSelect';
import ImportModal from '../components/ImportModal';
Expand All @@ -30,13 +29,13 @@ import ModelInfoModal from '../components/ModelInfoModal';
import DisclaimerModal from '../components/DisclaimerModal';
import StatisticsModal from '../components/StatisticsModal';
import type { Lang } from '../i18n/translations';
import flagCN from '../flag_svg/🇨🇳.svg';
import flagTW from '../flag_svg/🇹🇼.svg';
import flagHK from '../flag_svg/🇭🇰.svg';
import flagUS from '../flag_svg/🇺🇸.svg';
import flagJP from '../flag_svg/🇯🇵.svg';
import flagRU from '../flag_svg/🇷🇺.svg';
import flagUA from '../flag_svg/🇺🇦.svg';
import flagCN from '../assets/flags/cn.svg';
import flagTW from '../assets/flags/tw.svg';
import flagHK from '../assets/flags/hk.svg';
import flagUS from '../assets/flags/us.svg';
import flagJP from '../assets/flags/jp.svg';
import flagRU from '../assets/flags/ru.svg';
import flagUA from '../assets/flags/ua.svg';

type JsonRecord = Record<string, unknown>;

Expand Down
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions apps/hrttracker/src/utils/authSessionState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { isLogoutInProgress, setLogoutInProgress } from '../../logic';

export { isLogoutInProgress, setLogoutInProgress };
File renamed without changes.
53 changes: 53 additions & 0 deletions apps/hrttracker/src/utils/crypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// 安全密码加密/解密工具
// 使用 AES-GCM 对称加密

import { deleteCookie, getCookie, setCookie } from './cookies';
import { encryptString, decryptString } from '../../logic';

const SECURITY_PASSWORD_COOKIE = 'hrt-security-pwd';
const SECURITY_PASSWORD_COOKIE_DAYS = 3650;

/**
* 保存安全密码到 Cookie(加密,长期有效)
* @returns true if saved successfully, false otherwise
*/
export async function saveSecurityPassword(password: string, username: string): Promise<boolean> {
try {
const encrypted = await encryptString(password, username);
const saved = setCookie(SECURITY_PASSWORD_COOKIE, encrypted, SECURITY_PASSWORD_COOKIE_DAYS);

if (saved) {
console.log('Security password saved to cookie successfully');
} else {
console.error('Failed to save security password to cookie');
}

return saved;
} catch (error) {
console.error('Failed to save security password:', error);
return false;
}
}

/**
* 从 Cookie 获取安全密码(解密)
*/
export async function getSecurityPassword(username: string): Promise<string | null> {
try {
const encrypted = getCookie(SECURITY_PASSWORD_COOKIE);
if (!encrypted) return null;

return await decryptString(encrypted, username);
} catch (error) {
console.error('Failed to get security password:', error);
return null;
}
}

/**
* 清除安全密码 Cookie
* @returns true if deleted successfully, false otherwise
*/
export async function clearSecurityPassword(): Promise<boolean> {
return deleteCookie(SECURITY_PASSWORD_COOKIE);
}
15 changes: 2 additions & 13 deletions src/utils/helpers.tsx → apps/hrttracker/src/utils/helpers.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { Syringe, Pill, Droplet, Sticker, X } from 'lucide-react';
import { Route, DoseEvent, Ester, getBioavailabilityMultiplier, getToE2Factor, ExtraKey } from '../../logic';
import { Route, getRouteIcon as getRouteIconCore, getBioDoseMG, getRawDoseMG } from '../../logic';
import { Lang } from '../i18n/translations';

export const formatDate = (date: Date, lang: Lang) => {
Expand All @@ -23,15 +23,4 @@ export const getRouteIcon = (route: Route) => {
}
};

export const getBioDoseMG = (event: DoseEvent) => {
const multiplier = getBioavailabilityMultiplier(event.route, event.ester, event.extras || {});
return multiplier * event.doseMG;
};

export const getRawDoseMG = (event: DoseEvent) => {
if (event.route === Route.patchRemove) return null;
if (event.extras[ExtraKey.releaseRateUGPerDay]) return null;
const factor = getToE2Factor(event.ester);
if (!factor) return event.doseMG;
return event.doseMG / factor;
};
export { getBioDoseMG, getRawDoseMG };
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading