Compare commits
95
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
308ace8559 | ||
|
|
6d560583ae | ||
|
|
f2c7dae4a8 | ||
|
|
afbb9bc603 | ||
|
|
82c4ae8a84 | ||
|
|
c5fc328f2b | ||
|
|
77e057c580 | ||
|
|
75cabe8339 | ||
|
|
58879024a2 | ||
|
|
96f4e09bd5 | ||
|
|
fd4079c9c3 | ||
|
|
86335dfaa4 | ||
|
|
a23b10959c | ||
|
|
c460f09821 | ||
|
|
1d800c536e | ||
|
|
0fc45aa6ec | ||
|
|
32f404ac90 | ||
|
|
06e9a3e694 | ||
|
|
317072539d | ||
|
|
08dd766886 | ||
|
|
c9477b562e | ||
|
|
50283269fb | ||
|
|
04bc48b6a7 | ||
|
|
702c8daf6c | ||
|
|
a1c267b0d4 | ||
|
|
836035f4cd | ||
|
|
b29be62747 | ||
|
|
a9dc632986 | ||
|
|
cc2677ce06 | ||
|
|
71dcf7e1b1 | ||
|
|
30bc489b5c | ||
|
|
4ded8e30ed | ||
|
|
be70868d0b | ||
|
|
49583599b3 | ||
|
|
4b19d1d929 | ||
|
|
921f3496ea | ||
|
|
7234b4b40a | ||
|
|
eef296942f | ||
|
|
e689e44923 | ||
|
|
fc23919a4e | ||
|
|
f34c8c3bae | ||
|
|
9f16db4286 | ||
|
|
fd029ad124 | ||
|
|
e963140bf9 | ||
|
|
533a05832e | ||
|
|
086f7aecf9 | ||
|
|
1edf57b75d | ||
|
|
4ce701e99a | ||
|
|
8813766aff | ||
|
|
aca8a05c0f | ||
|
|
9caaa303e5 | ||
|
|
a714013e3e | ||
|
|
1a695920ce | ||
|
|
025fcc3244 | ||
|
|
636e566657 | ||
|
|
040d560aff | ||
|
|
a4af607c41 | ||
|
|
a386660833 | ||
|
|
034c87f955 | ||
|
|
f83deefa47 | ||
|
|
a4ab22588c | ||
|
|
61d3028909 | ||
|
|
368e99a380 | ||
|
|
39282b9633 | ||
|
|
7f213b5c9e | ||
|
|
a4cc4e9871 | ||
|
|
1cd5b85a4b | ||
|
|
8203a7f893 | ||
|
|
0f71323c11 | ||
|
|
93b7c7bb0a | ||
|
|
67be8d8f7b | ||
|
|
06aa48f6ad | ||
|
|
6bd39aeed2 | ||
|
|
fd66c529c2 | ||
|
|
cee21a39d9 | ||
|
|
e3ac00a641 | ||
|
|
74e1e7dd13 | ||
|
|
fd27242e4e | ||
|
|
20cc1ccde9 | ||
|
|
9401bd2b3d | ||
|
|
fb9e2350b3 | ||
|
|
2e6cc986a0 | ||
|
|
d20837f9f1 | ||
|
|
c0b55277a4 | ||
|
|
49037371f8 | ||
|
|
2fa41c774b | ||
|
|
1673cbdb7d | ||
|
|
5066007d9d | ||
|
|
9a8313173d | ||
|
|
83cc218c5e | ||
|
|
215263bf2f | ||
|
|
2725f00f68 | ||
|
|
b1c1b16e66 | ||
|
|
51256335ba | ||
|
|
8cfa3802d5 |
@@ -0,0 +1,12 @@
|
||||
# 大目录/与镜像无关的内容不进 build context
|
||||
# 注意: dist/ 和 ota-package/ 不能忽略 —— CI 预构建产物要通过 context 进入镜像
|
||||
.git
|
||||
.gitea
|
||||
.local
|
||||
.pnpm-store
|
||||
node_modules
|
||||
src-tauri
|
||||
docs
|
||||
*.log
|
||||
readme
|
||||
deploy
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
# Windows 外壳构建模式 (npm run build:shell / vite build --mode shell)
|
||||
# 外壳内前端跑在 tauri://localhost, 没有同源后端, API 必须指向远端 Express。
|
||||
# 与 android 打包脚本 (scripts/wsl/build-apk.sh) 注入的值保持一致;
|
||||
# 进程环境变量优先级高于本文件, android 流程不受影响。
|
||||
#
|
||||
# 0.2.1 起正式域名: https://iboard.honor3.com (NPM 反代 -> baihu:3001, 受信证书)
|
||||
# PowerSync 同步端点默认同域 (NPM 把 /sync 反代到同步服务); 若改用独立子域,
|
||||
# 打包时用 VITE_POWERSYNC_URL=https://sync.example.com 覆盖即可。
|
||||
VITE_API_BASE=https://iboard.honor3.com
|
||||
VITE_POWERSYNC_URL=https://iboard.honor3.com
|
||||
+200
-182
@@ -1,182 +1,200 @@
|
||||
name: Android Build & Publish
|
||||
|
||||
# 仅在 android-* 分支 push 时触发 (如 android/release, android-1.0)
|
||||
# 构建 release APK (keystore 签名) 并上传到 Gitea Generic Package Registry
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'android-*'
|
||||
|
||||
jobs:
|
||||
build-android:
|
||||
runs-on: ubuntu-latest
|
||||
# 需要写权限: 上传 packages
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
env:
|
||||
# 固定工具链版本, 便于 actions/cache 命中
|
||||
# Android 后端地址 (bake 进 APK); 与 scripts/wsl/build-apk.sh 保持一致
|
||||
VITE_API_BASE: http://bh.vps.honor3.com:3001
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://gitea.com/actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: https://gitea.com/actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
|
||||
# 版本号一致性: package.json / tauri.conf.json / Cargo.toml / Cargo.lock 必须完全相等。
|
||||
# tauri.conf.json 的 version 就是 APK 的 versionName, 漏改会打出一个版本号错误的安装包。
|
||||
- name: Check version consistency
|
||||
run: node scripts/ci/check-version.mjs
|
||||
|
||||
- name: Setup JDK 17
|
||||
uses: https://gitea.com/actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: '17'
|
||||
|
||||
# ---------- 缓存: 命中后跳过下面的安装 ----------
|
||||
- name: Cache Android SDK & NDK
|
||||
id: cache-android
|
||||
uses: https://gitea.com/actions/cache@v4
|
||||
with:
|
||||
path: ~/android-sdk
|
||||
key: android-sdk-ndk-27.2-${{ runner.os }}
|
||||
|
||||
- name: Cache Rust toolchain
|
||||
id: cache-rust
|
||||
uses: https://gitea.com/actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo
|
||||
~/.rustup
|
||||
# 工具链升级时 bump 版本号使缓存失效重装 (含 cargo-ndk)
|
||||
key: rust-android-v1-${{ runner.os }}
|
||||
|
||||
- name: Cache Tauri cargo target
|
||||
uses: https://gitea.com/actions/cache@v4
|
||||
with:
|
||||
path: src-tauri/target
|
||||
key: tauri-target-android-${{ runner.os }}
|
||||
|
||||
# ---------- 安装工具链 (仅缓存未命中时) ----------
|
||||
- name: Install Rust + Android targets
|
||||
if: steps.cache-rust.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
|
||||
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
. "$HOME/.cargo/env"
|
||||
rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android i686-linux-android
|
||||
cargo install cargo-ndk --locked
|
||||
|
||||
- name: Install Android SDK & NDK
|
||||
if: steps.cache-android.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
export ANDROID_HOME="$HOME/android-sdk"
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends unzip
|
||||
mkdir -p "$ANDROID_HOME/cmdline-tools"
|
||||
wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O /tmp/cmdline-tools.zip
|
||||
unzip -q /tmp/cmdline-tools.zip -d /tmp/
|
||||
mv /tmp/cmdline-tools "$ANDROID_HOME/cmdline-tools/latest"
|
||||
export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"
|
||||
yes | sdkmanager --licenses >/dev/null || true
|
||||
sdkmanager --install \
|
||||
"platforms;android-36" \
|
||||
"build-tools;36.0.0" \
|
||||
"platform-tools" \
|
||||
"ndk;27.2.12479018" \
|
||||
"cmake;3.22.1"
|
||||
|
||||
- name: Install frontend deps
|
||||
run: npm ci --no-audit --no-fund
|
||||
|
||||
# ---------- 全局 Android 环境 (对后续所有 step 生效, 无论缓存是否命中) ----------
|
||||
- name: Set Android env
|
||||
run: |
|
||||
echo "ANDROID_HOME=$HOME/android-sdk" >> "$GITHUB_ENV"
|
||||
echo "ANDROID_NDK_HOME=$HOME/android-sdk/ndk/27.2.12479018" >> "$GITHUB_ENV"
|
||||
echo "$HOME/android-sdk/cmdline-tools/latest/bin" >> "$GITHUB_PATH"
|
||||
echo "$HOME/android-sdk/platform-tools" >> "$GITHUB_PATH"
|
||||
|
||||
# ---------- 构建 ----------
|
||||
- name: Init Android project
|
||||
run: |
|
||||
# tauri android init 需要 cargo (运行 cargo metadata)。
|
||||
# Rust 工具链缓存命中时 "Install Rust" 步骤被跳过, 不会把 cargo 加进全局 PATH,
|
||||
# 因此这里显式加载 cargo 环境 (与 Build release APK 步骤保持一致)。
|
||||
. "$HOME/.cargo/env"
|
||||
cd src-tauri
|
||||
npx tauri android init --ci
|
||||
|
||||
- name: Inject release signing config
|
||||
run: bash scripts/ci/android-signing.sh
|
||||
env:
|
||||
ANDROID_KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_B64 }}
|
||||
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
|
||||
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
||||
|
||||
- name: Build release APK
|
||||
run: |
|
||||
export ANDROID_HOME="$HOME/android-sdk"
|
||||
export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/27.2.12479018"
|
||||
export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH"
|
||||
export CARGO_HOME="$HOME/.cargo"
|
||||
. "$HOME/.cargo/env"
|
||||
cd src-tauri
|
||||
npx tauri android build --apk
|
||||
# 断言签名配置仍在且 release 块实际引用了它 (防止 tauri 重新生成 gen/android 或正则未命中)
|
||||
if ! grep -q 'create("release")' gen/android/app/build.gradle.kts || ! grep -q 'signingConfig = signingConfigs.getByName("release")' gen/android/app/build.gradle.kts; then
|
||||
echo "ERROR: 构建后签名配置丢失/未生效, APK 未签名" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Locate APK
|
||||
id: apk
|
||||
run: |
|
||||
# 优先 universal 已签名 APK, 找不到再回退到其他已签名 APK
|
||||
APK=""
|
||||
UNIVERSAL=$(find src-tauri/gen/android/app/build/outputs/apk/universal/release -name '*.apk' ! -name '*unsigned*' 2>/dev/null | head -1)
|
||||
if [ -n "$UNIVERSAL" ]; then
|
||||
APK="$UNIVERSAL"
|
||||
else
|
||||
APK=$(find src-tauri/gen/android/app/build/outputs/apk -name '*.apk' ! -name '*unsigned*' | head -1)
|
||||
fi
|
||||
if [ -z "$APK" ]; then
|
||||
echo "ERROR: 未找到已签名 APK" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "path=$APK" >> $GITHUB_OUTPUT
|
||||
ls -lh "$APK"
|
||||
|
||||
# ---------- 上传到 Gitea Generic Package ----------
|
||||
- name: Publish APK to Gitea packages
|
||||
env:
|
||||
# 注意: 不能用 GITEA_TOKEN (内置 job token, 无组织 packages 写权限, 且命名规则禁止 GITEA_ 前缀)
|
||||
# 用一个自定义名 secret 存放有 packages 写权限的 user token
|
||||
PACKAGE_TOKEN: ${{ secrets.PACKAGE_TOKEN }}
|
||||
APK_PATH: ${{ steps.apk.outputs.path }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# 包版本: 用分支名去掉 android- 前缀 + 短 SHA, 保证每次 push 唯一
|
||||
BRANCH="${GITHUB_REF_NAME#android-}"
|
||||
PKG_VERSION="${BRANCH}-${GITHUB_SHA:0:8}"
|
||||
FILE_NAME="iboard-$(basename "$APK_PATH")"
|
||||
OWNER="${GITHUB_REPOSITORY_OWNER}"
|
||||
PKG_NAME="iboard"
|
||||
# Gitea Generic Package Registry 上传端点 (PUT)
|
||||
# 注意: GITHUB_SERVER_URL 是仓库 web 根地址, 例如 http://git.honor3.com
|
||||
URL="${GITHUB_SERVER_URL}/api/packages/${OWNER}/generic/${PKG_NAME}/${PKG_VERSION}/${FILE_NAME}"
|
||||
echo "上传到: ${URL}"
|
||||
curl -sS --fail-with-body \
|
||||
-H "Authorization: token ${PACKAGE_TOKEN}" \
|
||||
--upload-file "$APK_PATH" \
|
||||
"${URL}"
|
||||
echo ""
|
||||
echo "已发布: ${URL}"
|
||||
name: Android Build & Publish
|
||||
|
||||
# 仅在 android-* 分支 push 时触发 (如 android/release, android-1.0)
|
||||
# 构建 release APK (keystore 签名) 并上传到 Gitea Generic Package Registry
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'android-*'
|
||||
|
||||
# 同分支连续 push 时取消旧的构建, 省资源也避免上传过时 APK
|
||||
concurrency:
|
||||
group: android-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-android:
|
||||
runs-on: ubuntu-latest
|
||||
# 需要写权限: 上传 packages
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
env:
|
||||
# 固定工具链版本, 便于 actions/cache 命中
|
||||
# Android 后端地址 (bake 进 APK); 与 scripts/wsl/build-apk.sh 保持一致
|
||||
VITE_API_BASE: https://iboard.honor3.com
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://gitea.com/actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: https://gitea.com/actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
|
||||
# 版本号一致性: package.json / tauri.conf.json / Cargo.toml / Cargo.lock 必须完全相等。
|
||||
# tauri.conf.json 的 version 就是 APK 的 versionName, 漏改会打出一个版本号错误的安装包。
|
||||
- name: Check version consistency
|
||||
run: node scripts/ci/check-version.mjs
|
||||
|
||||
- name: Setup JDK 17
|
||||
uses: https://gitea.com/actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: '17'
|
||||
|
||||
# ---------- 缓存: 命中后跳过下面的安装 ----------
|
||||
- name: Cache Android SDK & NDK
|
||||
id: cache-android
|
||||
uses: https://gitea.com/actions/cache@v4
|
||||
with:
|
||||
path: ~/android-sdk
|
||||
key: android-sdk-ndk-27.2-${{ runner.os }}
|
||||
|
||||
- name: Cache Rust toolchain
|
||||
id: cache-rust
|
||||
uses: https://gitea.com/actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo
|
||||
~/.rustup
|
||||
# 工具链升级时 bump 版本号使缓存失效重装 (含 cargo-ndk)
|
||||
# v2: v1 缓存是从只缓存 toolchains 的旧配置生成的, 缺 cargo-ndk/settings.toml,
|
||||
# 且缓存命中后不会回写, 所以 bump 一次重建完整缓存。
|
||||
# 注意: 必须缓存整个 ~/.rustup。只缓存 ~/.rustup/toolchains 会丢掉
|
||||
# settings.toml (默认 toolchain 记录), 缓存命中时 cargo 无法选择版本。
|
||||
key: rust-android-v2-${{ runner.os }}
|
||||
|
||||
- name: Cache Tauri cargo target
|
||||
uses: https://gitea.com/actions/cache@v4
|
||||
with:
|
||||
path: src-tauri/target
|
||||
# 依赖变化 → 新 key; 命不中时回退最近一次的 target 目录做增量编译
|
||||
key: tauri-target-android-${{ hashFiles('src-tauri/Cargo.lock') }}
|
||||
restore-keys: tauri-target-android-
|
||||
|
||||
# ---------- 安装/修复工具链 (缓存命中时也要执行) ----------
|
||||
# 缓存命中时不能只是跳过安装: 缓存里可能缺少 ~/.rustup/settings.toml,
|
||||
# rustup 代理会报 "no default is configured", 导致 cargo / tauri 全部失败。
|
||||
# 因此这一步无条件运行, 但都是幂等操作 (已安装时不会重新下载)。
|
||||
- name: Setup Rust toolchain
|
||||
run: |
|
||||
if [ ! -x "$HOME/.cargo/bin/rustup" ]; then
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
|
||||
fi
|
||||
. "$HOME/.cargo/env"
|
||||
# 幂等: stable 已在 ~/.rustup/toolchains 时只补写 settings.toml
|
||||
rustup default stable
|
||||
# 只构建 aarch64 (现代 Android 手机); 需要模拟器/x86 设备时再加回其他 target
|
||||
rustup target add aarch64-linux-android
|
||||
command -v cargo-ndk >/dev/null 2>&1 || cargo install cargo-ndk --locked
|
||||
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Install Android SDK & NDK
|
||||
if: steps.cache-android.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
export ANDROID_HOME="$HOME/android-sdk"
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends unzip
|
||||
mkdir -p "$ANDROID_HOME/cmdline-tools"
|
||||
wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O /tmp/cmdline-tools.zip
|
||||
unzip -q /tmp/cmdline-tools.zip -d /tmp/
|
||||
mv /tmp/cmdline-tools "$ANDROID_HOME/cmdline-tools/latest"
|
||||
export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"
|
||||
yes | sdkmanager --licenses >/dev/null || true
|
||||
sdkmanager --install \
|
||||
"platforms;android-36" \
|
||||
"build-tools;36.0.0" \
|
||||
"platform-tools" \
|
||||
"ndk;27.2.12479018" \
|
||||
"cmake;3.22.1"
|
||||
|
||||
- name: Install frontend deps
|
||||
run: npm ci --no-audit --no-fund
|
||||
|
||||
# ---------- 全局 Android 环境 (对后续所有 step 生效, 无论缓存是否命中) ----------
|
||||
- name: Set Android env
|
||||
run: |
|
||||
echo "ANDROID_HOME=$HOME/android-sdk" >> "$GITHUB_ENV"
|
||||
echo "ANDROID_NDK_HOME=$HOME/android-sdk/ndk/27.2.12479018" >> "$GITHUB_ENV"
|
||||
echo "$HOME/android-sdk/cmdline-tools/latest/bin" >> "$GITHUB_PATH"
|
||||
echo "$HOME/android-sdk/platform-tools" >> "$GITHUB_PATH"
|
||||
|
||||
# ---------- 构建 ----------
|
||||
- name: Init Android project
|
||||
run: |
|
||||
# tauri android init 需要 cargo (运行 cargo metadata)。
|
||||
# Setup Rust toolchain 已把 ~/.cargo/bin 写入 GITHUB_PATH, 这里再 source 一次兜底。
|
||||
. "$HOME/.cargo/env"
|
||||
cargo --version
|
||||
cd src-tauri
|
||||
npx tauri android init --ci
|
||||
|
||||
- name: Inject release signing config
|
||||
run: bash scripts/ci/android-signing.sh
|
||||
env:
|
||||
ANDROID_KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_B64 }}
|
||||
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
|
||||
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
||||
|
||||
- name: Build release APK
|
||||
run: |
|
||||
export ANDROID_HOME="$HOME/android-sdk"
|
||||
export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/27.2.12479018"
|
||||
export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH"
|
||||
export CARGO_HOME="$HOME/.cargo"
|
||||
. "$HOME/.cargo/env"
|
||||
cd src-tauri
|
||||
npx tauri android build --apk --target aarch64
|
||||
# 断言签名配置仍在且 release 块实际引用了它 (防止 tauri 重新生成 gen/android 或正则未命中)
|
||||
if ! grep -q 'create("release")' gen/android/app/build.gradle.kts || ! grep -q 'signingConfig = signingConfigs.getByName("release")' gen/android/app/build.gradle.kts; then
|
||||
echo "ERROR: 构建后签名配置丢失/未生效, APK 未签名" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Locate APK
|
||||
id: apk
|
||||
run: |
|
||||
# 优先 universal 已签名 APK, 找不到再回退到其他已签名 APK
|
||||
APK=""
|
||||
UNIVERSAL=$(find src-tauri/gen/android/app/build/outputs/apk/universal/release -name '*.apk' ! -name '*unsigned*' 2>/dev/null | head -1)
|
||||
if [ -n "$UNIVERSAL" ]; then
|
||||
APK="$UNIVERSAL"
|
||||
else
|
||||
APK=$(find src-tauri/gen/android/app/build/outputs/apk -name '*.apk' ! -name '*unsigned*' | head -1)
|
||||
fi
|
||||
if [ -z "$APK" ]; then
|
||||
echo "ERROR: 未找到已签名 APK" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "path=$APK" >> $GITHUB_OUTPUT
|
||||
ls -lh "$APK"
|
||||
|
||||
# ---------- 上传到 Gitea Generic Package ----------
|
||||
- name: Publish APK to Gitea packages
|
||||
env:
|
||||
# 注意: 不能用 GITEA_TOKEN (内置 job token, 无组织 packages 写权限, 且命名规则禁止 GITEA_ 前缀)
|
||||
# 用一个自定义名 secret 存放有 packages 写权限的 user token
|
||||
PACKAGE_TOKEN: ${{ secrets.PACKAGE_TOKEN }}
|
||||
APK_PATH: ${{ steps.apk.outputs.path }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# 包版本: 用分支名去掉 android- 前缀 + 短 SHA, 保证每次 push 唯一
|
||||
BRANCH="${GITHUB_REF_NAME#android-}"
|
||||
PKG_VERSION="${BRANCH}-${GITHUB_SHA:0:8}"
|
||||
FILE_NAME="iboard-$(basename "$APK_PATH")"
|
||||
OWNER="${GITHUB_REPOSITORY_OWNER}"
|
||||
PKG_NAME="iboard"
|
||||
# Gitea Generic Package Registry 上传端点 (PUT)
|
||||
# 注意: GITHUB_SERVER_URL 是仓库 web 根地址, 例如 http://git.honor3.com
|
||||
URL="${GITHUB_SERVER_URL}/api/packages/${OWNER}/generic/${PKG_NAME}/${PKG_VERSION}/${FILE_NAME}"
|
||||
echo "上传到: ${URL}"
|
||||
curl -sS --fail-with-body \
|
||||
-H "Authorization: token ${PACKAGE_TOKEN}" \
|
||||
--upload-file "$APK_PATH" \
|
||||
"${URL}"
|
||||
echo ""
|
||||
echo "已发布: ${URL}"
|
||||
|
||||
@@ -18,6 +18,11 @@ on:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# 连续 push master 时取消还在跑的旧部署, 避免排队执行过时版本的完整部署
|
||||
concurrency:
|
||||
group: deploy-master
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-image:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -25,12 +30,12 @@ jobs:
|
||||
# 关键: 显式固定 Android/OTA 后端地址, 覆盖 runner 环境里可能存在的
|
||||
# 其他 VITE_API_BASE 值。否则 OTA 包里的前端可能连错后端, 导致
|
||||
# "OTA 更新后登录失败/进不去内部"。与 android-build.yml 保持一致。
|
||||
VITE_API_BASE: http://bh.vps.honor3.com:3001
|
||||
# OTA bundle 要求的最低原生二进制版本。
|
||||
# 与 package.json 的应用版本是**两个概念**: 前端 OTA 不碰原生层,
|
||||
# 现有 APK 仍是 0.1.0, 所以这里固定 0.1.0 (否则旧 APK 会被判定为不兼容而收不到更新)。
|
||||
# 重打 APK 并确认原生层无破坏后, 再提升此值。
|
||||
OTA_MIN_BINARY: '0.1.0'
|
||||
VITE_API_BASE: https://iboard.honor3.com
|
||||
# OTA bundle 要求的最低原生二进制版本 (hotswap 据此拒绝不兼容的旧外壳)。
|
||||
# 0.2.1 起必须为 0.2.1: 旧外壳 (0.1.0) 的 CSP 白名单编译进了原生二进制,
|
||||
# 不含 https://iboard.honor3.com, 若让它拉到 0.2.1 前端会因 CSP 拦截而白屏。
|
||||
# 旧外壳请整包升级 (Windows 重装 exe / Android 换装 APK), 不要靠 OTA。
|
||||
OTA_MIN_BINARY: '0.2.1'
|
||||
steps:
|
||||
- uses: https://gitea.com/actions/checkout@v4
|
||||
|
||||
@@ -38,6 +43,7 @@ jobs:
|
||||
uses: https://gitea.com/actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
|
||||
- name: Check version consistency & branch
|
||||
# 1) package.json / tauri.conf.json / Cargo.toml / Cargo.lock 四处版本必须完全相等且不带 -ota
|
||||
@@ -53,62 +59,110 @@ jobs:
|
||||
run: npm ci --no-audit --no-fund
|
||||
|
||||
- name: Build frontend
|
||||
# dist/ 供 OTA 打包用 (Dockerfile 内也会构建一份)
|
||||
# 全流程唯一一次前端构建: dist/ 既供 OTA 打包, 也通过 build context 进入镜像
|
||||
# (docker build 传 PREBUILT_DIST=true 跳过镜像内的重复构建)
|
||||
run: npm run build
|
||||
|
||||
- name: Install minisign
|
||||
run: sudo apt-get update && sudo apt-get install -y minisign
|
||||
# 静态二进制直接下载, 免去每次 apt-get update + install
|
||||
# 注意: release tarball 是 macOS 上打包的, 混有多个平台的 minisign 二进制,
|
||||
# 逐个试运行挑出本机可执行的, 都不行再回退 apt
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p /tmp/ms
|
||||
curl -sL https://github.com/jedisct1/minisign/releases/download/0.12/minisign-0.12-linux.tar.gz | tar xz -C /tmp/ms
|
||||
BIN=""
|
||||
for c in $(find /tmp/ms -type f -name minisign); do
|
||||
if "$c" -v >/dev/null 2>&1; then BIN="$c"; break; fi
|
||||
done
|
||||
if [ -n "$BIN" ]; then
|
||||
sudo install -m755 "$BIN" /usr/local/bin/minisign
|
||||
else
|
||||
echo "未找到可执行的静态二进制, 回退 apt 安装"
|
||||
sudo apt-get update && sudo apt-get install -y minisign
|
||||
fi
|
||||
minisign -v
|
||||
|
||||
- name: Build & sign OTA package
|
||||
# 每次主分支提交生成 OTA 升级包, sequence 默认时间戳 (单调递增)
|
||||
# 发布说明 = 触发构建的 commit message (标题+正文; 打包脚本内安全截断 400 字符),
|
||||
# commit sha 通过 --commit 单独写入 manifest, 前端分行展示
|
||||
# 产物 ota-package/ (tar.gz + latest.json + .minisig) 进入 docker build context, 由 Dockerfile COPY 进镜像
|
||||
env:
|
||||
MINISIGN_PRIVATE_KEY: ${{ secrets.MINISIGN_PRIVATE_KEY }}
|
||||
OTA_NOTES: ${{ github.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
node scripts/build-ota.mjs --out ota-package --notes ${OTA_NOTES} --min-binary ${OTA_MIN_BINARY}
|
||||
# 不用 head -c 截断: 那会把多字节汉字切成非法 UTF-8, latest.json 解析失败
|
||||
MSG="$(git log -1 --pretty=format:%B | sed -e '/^[[:space:]]*$/d')"
|
||||
echo "notes: ${MSG}"
|
||||
node scripts/build-ota.mjs --out ota-package --notes "${MSG}" --commit "${GITHUB_SHA}" --min-binary ${OTA_MIN_BINARY}
|
||||
echo "===== latest.json ====="
|
||||
cat ota-package/latest.json
|
||||
echo "===== bundle sha256 ====="
|
||||
sha256sum ota-package/iboard-ota.tar.gz
|
||||
|
||||
- name: Publish OTA package to server
|
||||
# CI 跑在容器内, 无法直接写宿主文件系统; 用 docker cp 经bind-mount容器
|
||||
# 把 ota-package 落到宿主 /app/iboard-prod/deploy/prod/data/ota
|
||||
# (该目录被生产 app 容器挂载为 /app/ota-package)。
|
||||
# docker 不可用或目录缺失时仅告警, 不影响发布。
|
||||
run: |
|
||||
set -euo pipefail
|
||||
HOST_DIR=/app/iboard-prod
|
||||
if ! docker info >/dev/null 2>&1; then
|
||||
echo "::warning::docker 不可用, 跳过 OTA 包同步"; exit 0
|
||||
fi
|
||||
docker run -d --name ota-bridge -v "${HOST_DIR}/deploy/prod/data/ota:/dst" alpine:3.20 sleep 30 >/dev/null
|
||||
docker cp ota-package/. ota-bridge:/dst/ || echo "::warning::OTA 包复制失败"
|
||||
docker rm -f ota-bridge >/dev/null 2>&1 || true
|
||||
echo "== 宿主 OTA 目录 =="
|
||||
docker run --rm -v "${HOST_DIR}/deploy/prod/data/ota:/d" alpine:3.20 sh -c 'ls -la /d | head -6'
|
||||
docker restart iboard-prod-app-1 >/dev/null 2>&1 || true
|
||||
|
||||
- name: Build Docker image
|
||||
# DATABASE_URL 仅在 prisma generate 阶段需要, 可用任何合法占位串
|
||||
run: docker build -t iboard:latest --build-arg DATABASE_URL=mysql://build:build@127.0.0.1:3306/build .
|
||||
# PREBUILT_DIST=true: 复用上一步构建好的 dist/, 镜像内跳过前端构建
|
||||
# DOCKER_BUILDKIT=1: Dockerfile 用了 cache mount 语法
|
||||
run: docker build -t iboard:latest --build-arg DATABASE_URL=mysql://build:build@127.0.0.1:3306/build --build-arg PREBUILT_DIST=true .
|
||||
env:
|
||||
DOCKER_BUILDKIT: '1'
|
||||
|
||||
- name: List images
|
||||
run: docker images
|
||||
|
||||
- name: Apply DB migrations
|
||||
# 数据库自动升级: 按 prisma/migrations/ 下的迁移顺序应用 (生产库由 master 部署时统一升级)
|
||||
# migrate deploy 幂等, 无新迁移时为 no-op
|
||||
# 位置关键: 在 "Remove old container" 之前 —— 建表/改表成功后才切换流量,
|
||||
# 迁移失败则整个 workflow 中断, 不会用新镜像顶掉仍在运行的旧容器
|
||||
- name: Deploy 0.2.1 production stack
|
||||
# 0.2.1 起生产为单端口架构: app(3001 对外) + pg(内网) + powersync(内网),
|
||||
# 客户端同步经 app 的 /sync 反代进入同步服务 (无独立同步端口)。
|
||||
# 部署逻辑在服务器的 /app/iboard-prod/scripts/deploy/prod.sh (幂等), 这里只负责:
|
||||
# 1) 把刚构建好的镜像交给该脚本
|
||||
# 2) 未配置 PG 相关 secrets 或脚本缺失时跳过部署 (发布不失败, 仅提示)
|
||||
env:
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
PG_PASSWORD: ${{ secrets.PG_PASSWORD }}
|
||||
PS_STORAGE_PASSWORD: ${{ secrets.PS_STORAGE_PASSWORD }}
|
||||
JWT_SECRET: ${{ secrets.JWT_SECRET }}
|
||||
POWERSYNC_JWT_SECRET: ${{ secrets.POWERSYNC_JWT_SECRET }}
|
||||
POWERSYNC_JWT_KEY_B64: ${{ secrets.POWERSYNC_JWT_KEY_B64 }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
npx prisma migrate status || true
|
||||
npx prisma migrate deploy
|
||||
|
||||
- name: Remove old container
|
||||
run: docker rm -vf iboard || true
|
||||
|
||||
- name: Recycle container
|
||||
run: docker system prune --volumes -f
|
||||
|
||||
- name: Run container
|
||||
# DATABASE_URL 通过 gitea Actions secret 注入, 不进镜像层
|
||||
# OTA_PACKAGE_DIR 已在 Dockerfile 设为 /app/ota-package
|
||||
# 米家凭证 (auth.txt) 不进 git: 运维放宿主机 /app/iboard/cache, bind-mount 进容器 (MIHOME_CACHE_DIR=/app/cache)
|
||||
run: |
|
||||
docker run -d --name iboard -p 3001:3001 \
|
||||
-e DATABASE_URL="${{ secrets.DATABASE_URL }}" \
|
||||
-e JWT_SECRET="${{ secrets.JWT_SECRET }}" \
|
||||
-e MIHOME_CACHE_DIR=/app/cache \
|
||||
-v /app/iboard/cache:/app/cache \
|
||||
iboard:latest
|
||||
env:
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
JWT_SECRET: ${{ secrets.JWT_SECRET }}
|
||||
HOST_DIR=/app/iboard-prod
|
||||
if [ -z "${PG_PASSWORD:-}" ]; then
|
||||
echo "::warning::未配置 PG_PASSWORD 等 secrets, 跳过自动部署 (镜像已构建完成)"
|
||||
echo "手工部署: ssh baihu 'bash $HOST_DIR/scripts/deploy/prod.sh'"
|
||||
exit 0
|
||||
fi
|
||||
if ! docker run --rm -v "$HOST_DIR:/w" alpine:3.20 test -f /w/scripts/deploy/prod.sh; then
|
||||
echo "::warning::$HOST_DIR/scripts/deploy/prod.sh 不存在, 跳过部署"
|
||||
exit 0
|
||||
fi
|
||||
docker run --rm \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v "$HOST_DIR:$HOST_DIR" \
|
||||
-w "$HOST_DIR" \
|
||||
-e PG_PASSWORD="$PG_PASSWORD" \
|
||||
-e PS_STORAGE_PASSWORD="$PS_STORAGE_PASSWORD" \
|
||||
-e JWT_SECRET="$JWT_SECRET" \
|
||||
-e POWERSYNC_JWT_SECRET="$POWERSYNC_JWT_SECRET" \
|
||||
-e POWERSYNC_JWT_KEY_B64="$POWERSYNC_JWT_KEY_B64" \
|
||||
-e PUBLIC_URL=https://iboard.honor3.com \
|
||||
-e ROOT="$HOST_DIR" \
|
||||
docker:27-cli sh -c 'apk add -q bash curl >/dev/null 2>&1; bash /app/iboard-prod/scripts/deploy/prod.sh'
|
||||
|
||||
@@ -1,75 +1,97 @@
|
||||
name: OTA Frontend Build & Sign
|
||||
|
||||
# 前端 OTA 升级包构建 + 签名 + 上传 artifact
|
||||
# 触发: 仅手动 (workflow_dispatch), 发版前用
|
||||
# - 主分支 push 的自动 OTA 生成已并入 docker-build.yml (随镜像发布), 本 workflow 不再监听 push 避免重复构建
|
||||
# 输入: sequence / notes / min_binary (可覆盖默认)
|
||||
# Secret 依赖: MINISIGN_PRIVATE_KEY (整段 .key 文件内容, 含首行 untrusted comment)
|
||||
# 注意: 此 workflow 不会修改 src-tauri/tauri.conf.json 里的 pubkey,
|
||||
# 换密钥请手动改 conf 并 commit.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
sequence:
|
||||
description: 'OTA sequence (单调递增整数, 留空用 timestamp)'
|
||||
required: false
|
||||
type: string
|
||||
notes:
|
||||
description: '发布说明 (写入 latest.json.notes)'
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
min_binary:
|
||||
description: '最低 binary 版本 (默认 0.1.0 = 现有 APK 的原生版本, 与前端应用版本无关)'
|
||||
required: false
|
||||
type: string
|
||||
default: '0.1.0'
|
||||
|
||||
jobs:
|
||||
build-ota:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://gitea.com/actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: https://gitea.com/actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Install deps
|
||||
run: npm ci --no-audit --no-fund
|
||||
|
||||
- name: Build frontend
|
||||
# 复用 vite build 产物; 不打 Docker (本 workflow 只关心前端)
|
||||
run: npm run build
|
||||
|
||||
- name: Install minisign
|
||||
run: sudo apt-get update && sudo apt-get install -y minisign
|
||||
|
||||
- name: Build & sign OTA package
|
||||
env:
|
||||
MINISIGN_PRIVATE_KEY: ${{ secrets.MINISIGN_PRIVATE_KEY }}
|
||||
OTA_NOTES: ${{ inputs.notes }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
OUT="ota-package"
|
||||
ARGS="--out ${OUT}"
|
||||
# sequence 留空 → 脚本默认用时间戳; min_binary 默认 0.1.0 (与前端版本解耦)
|
||||
if [ -n "${{ inputs.sequence }}" ]; then ARGS="${ARGS} --sequence ${{ inputs.sequence }}"; fi
|
||||
if [ -n "${OTA_NOTES}" ]; then ARGS="${ARGS} --notes ${OTA_NOTES}"; fi
|
||||
if [ -n "${{ inputs.min_binary }}" ]; then ARGS="${ARGS} --min-binary ${{ inputs.min_binary }}"; fi
|
||||
node scripts/build-ota.mjs ${ARGS}
|
||||
echo "===== latest.json ====="
|
||||
cat ${OUT}/latest.json
|
||||
echo "===== bundle sha256 ====="
|
||||
sha256sum ${OUT}/iboard-ota.tar.gz
|
||||
|
||||
- name: Upload OTA package as artifact
|
||||
uses: https://gitea.com/actions/upload-artifact@v4
|
||||
with:
|
||||
name: iboard-ota-${{ inputs.sequence || github.run_number }}
|
||||
path: ota-package/
|
||||
retention-days: 30
|
||||
if-no-files-found: error
|
||||
name: OTA Frontend Build & Sign
|
||||
|
||||
# 前端 OTA 升级包构建 + 签名 + 上传 artifact
|
||||
# 触发: 仅手动 (workflow_dispatch), 发版前用
|
||||
# - 主分支 push 的自动 OTA 生成已并入 docker-build.yml (随镜像发布), 本 workflow 不再监听 push 避免重复构建
|
||||
# 输入: sequence / notes / min_binary (可覆盖默认)
|
||||
# Secret 依赖: MINISIGN_PRIVATE_KEY (整段 .key 文件内容, 含首行 untrusted comment)
|
||||
# 注意: 此 workflow 不会修改 src-tauri/tauri.conf.json 里的 pubkey,
|
||||
# 换密钥请手动改 conf 并 commit.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
sequence:
|
||||
description: 'OTA sequence (单调递增整数, 留空用 timestamp)'
|
||||
required: false
|
||||
type: string
|
||||
notes:
|
||||
description: '发布说明 (写入 latest.json.notes)'
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
min_binary:
|
||||
description: '最低 binary 版本 (默认 0.1.0 = 现有 APK 的原生版本, 与前端应用版本无关)'
|
||||
required: false
|
||||
type: string
|
||||
default: '0.1.0'
|
||||
|
||||
jobs:
|
||||
build-ota:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://gitea.com/actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: https://gitea.com/actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Install deps
|
||||
run: npm ci --no-audit --no-fund
|
||||
|
||||
- name: Build frontend
|
||||
# 复用 vite build 产物; 不打 Docker (本 workflow 只关心前端)
|
||||
run: npm run build
|
||||
|
||||
- name: Install minisign
|
||||
# 静态二进制直接下载, 免去每次 apt-get update + install
|
||||
# 注意: release tarball 是 macOS 上打包的, 混有多个平台的 minisign 二进制,
|
||||
# 逐个试运行挑出本机可执行的, 都不行再回退 apt
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p /tmp/ms
|
||||
curl -sL https://github.com/jedisct1/minisign/releases/download/0.12/minisign-0.12-linux.tar.gz | tar xz -C /tmp/ms
|
||||
BIN=""
|
||||
for c in $(find /tmp/ms -type f -name minisign); do
|
||||
if "$c" -v >/dev/null 2>&1; then BIN="$c"; break; fi
|
||||
done
|
||||
if [ -n "$BIN" ]; then
|
||||
sudo install -m755 "$BIN" /usr/local/bin/minisign
|
||||
else
|
||||
echo "未找到可执行的静态二进制, 回退 apt 安装"
|
||||
sudo apt-get update && sudo apt-get install -y minisign
|
||||
fi
|
||||
minisign -v
|
||||
|
||||
- name: Build & sign OTA package
|
||||
# 发布说明默认 = 检出 commit 的 message (标题+正文), commit sha 单独写入;
|
||||
# 手动填写的 notes 优先
|
||||
env:
|
||||
MINISIGN_PRIVATE_KEY: ${{ secrets.MINISIGN_PRIVATE_KEY }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
OUT="ota-package"
|
||||
ARGS="--out ${OUT} --commit ${GITHUB_SHA}"
|
||||
if [ -n "${{ inputs.sequence }}" ]; then ARGS="${ARGS} --sequence ${{ inputs.sequence }}"; fi
|
||||
if [ -n "${{ inputs.notes }}" ]; then
|
||||
ARGS="${ARGS} --notes \"${{ inputs.notes }}\""
|
||||
else
|
||||
MSG="$(git log -1 --pretty=format:%B | sed -e '/^[[:space:]]*$/d')"
|
||||
ARGS="${ARGS} --notes \"${MSG}\""
|
||||
fi
|
||||
if [ -n "${{ inputs.min_binary }}" ]; then ARGS="${ARGS} --min-binary ${{ inputs.min_binary }}"; fi
|
||||
eval "node scripts/build-ota.mjs ${ARGS}"
|
||||
echo "===== latest.json ====="
|
||||
cat ${OUT}/latest.json
|
||||
echo "===== bundle sha256 ====="
|
||||
sha256sum ${OUT}/iboard-ota.tar.gz
|
||||
|
||||
- name: Upload OTA package as artifact
|
||||
uses: https://gitea.com/actions/upload-artifact@v4
|
||||
with:
|
||||
name: iboard-ota-${{ inputs.sequence || github.run_number }}
|
||||
path: ota-package/
|
||||
retention-days: 30
|
||||
if-no-files-found: error
|
||||
|
||||
+22
@@ -50,3 +50,25 @@ server/mihome/.cache/
|
||||
|
||||
# 鏈�湴 pnpm 瀹夎�浜х墿 (椤圭洰浣跨敤 package-lock.json)
|
||||
/.pnpm-store
|
||||
|
||||
# 0.2.1 楠岃瘉閮ㄧ讲: 鏈嶅姟鍣ㄦ湰鍦版暟鎹�嵎涓庡瘑閽� (缁濅笉鍏ュ簱)
|
||||
deploy/0.2.1/data/
|
||||
deploy/0.2.1/.env
|
||||
|
||||
# 构建/工具临时目录 (local)
|
||||
.esbuild-tmp/
|
||||
|
||||
|
||||
# Windows 打包产物 (本地生成, 不入库)
|
||||
/artifacts/
|
||||
|
||||
|
||||
# 生产部署密钥与数据卷
|
||||
/deploy/prod/.env
|
||||
/deploy/prod/data/
|
||||
|
||||
|
||||
# 本地临时脚本 (含口令, 不入库)
|
||||
scripts/_*.mjs
|
||||
.dsh-ops/
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// CDP 触摸手势驱动: 长按 400ms 激活 + 水平拖动, 观察 useRowDrag 行为
|
||||
import WebSocket from 'ws'
|
||||
import { spawn } from 'node:child_process'
|
||||
|
||||
const C = JSON.parse((await (await fetch('http://127.0.0.1:9223/json/version')).text()))
|
||||
const ws = new WebSocket(C.webSocketDebuggerUrl, { perMessageDeflate: false })
|
||||
let id = 0
|
||||
const pending = new Map()
|
||||
const send = (method, params = {}, sessionId) => new Promise((res) => {
|
||||
const i = ++id
|
||||
pending.set(i, res)
|
||||
ws.send(JSON.stringify({ id: i, method, params, sessionId }))
|
||||
})
|
||||
ws.on('message', (d) => {
|
||||
const m = JSON.parse(d)
|
||||
if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id) }
|
||||
if (m.method === 'Runtime.consoleAPICalled') {
|
||||
console.log('[console]', m.params.type, m.params.args.map((a) => a.value ?? a.description).join(' '))
|
||||
}
|
||||
})
|
||||
await new Promise((r) => ws.on('open', r))
|
||||
|
||||
const { targetId } = await send('Target.createTarget', { url: 'about:blank' })
|
||||
const { sessionId } = await send('Target.attachToTarget', { targetId, flatten: true })
|
||||
const S = (m, p) => send(m, p, sessionId)
|
||||
await S('Runtime.enable')
|
||||
await S('Page.enable')
|
||||
await S('Emulation.setDeviceMetricsOverride', { width: 400, height: 800, deviceScaleFactor: 2, mobile: true })
|
||||
await S('Emulation.setTouchEmulationEnabled', { enabled: true, maxTouchPoints: 5 })
|
||||
await S('Page.navigate', { url: 'http://127.0.0.1:5199/dragtest.html' })
|
||||
await new Promise((r) => setTimeout(r, 3000))
|
||||
|
||||
const sleep = (ms) => new Promise((res) => setTimeout(res, ms))
|
||||
const ev = async (expr) => (await S('Runtime.evaluate', { expression: expr, returnByValue: true })).result.value
|
||||
let rect = null
|
||||
for (let i = 0; i < 30 && !rect; i++) {
|
||||
await sleep(1000)
|
||||
const v = await ev(`(() => { const el = document.querySelector('[data-task-id="3"]'); return el ? JSON.stringify(el.getBoundingClientRect()) : null })()`)
|
||||
if (v) rect = JSON.parse(v)
|
||||
}
|
||||
if (!rect) { console.log('page never loaded'); ws.close(); process.exit(1) }
|
||||
console.log('row rect', JSON.stringify(rect))
|
||||
|
||||
const r = rect
|
||||
const x0 = r.left + r.width / 2
|
||||
const y0 = r.top + r.height / 2
|
||||
|
||||
const touch = (type, x, y) => S('Input.dispatchTouchEvent', {
|
||||
type,
|
||||
touchPoints: type === 'touchEnd' ? [] : [{ x, y, id: 1 }],
|
||||
})
|
||||
|
||||
// 1) touchStart + 按住 500ms (激活长按)
|
||||
await touch('touchStart', x0, y0)
|
||||
console.log('--- touchStart, holding 500ms')
|
||||
await sleep(500)
|
||||
|
||||
// 2) 水平拖动 +200px, 每步 +25px / 40ms (纯水平)
|
||||
for (let dx = 25; dx <= 200; dx += 25) {
|
||||
await touch('touchMove', x0 + dx, y0)
|
||||
await sleep(40)
|
||||
}
|
||||
console.log('--- moved to +200px (pure horizontal)')
|
||||
|
||||
// 3) 带纵向分量的拖动 (模拟真实手指): +160px 横 / +40px 纵
|
||||
await touch('touchMove', x0 + 60, y0 + 12)
|
||||
await sleep(40)
|
||||
await touch('touchMove', x0 + 120, y0 + 26)
|
||||
await sleep(40)
|
||||
await touch('touchMove', x0 + 160, y0 + 40)
|
||||
await sleep(40)
|
||||
|
||||
await touch('touchEnd')
|
||||
await sleep(600)
|
||||
|
||||
console.log('--- events on page:')
|
||||
console.log((await ev('window.__ev.join("\\n")')))
|
||||
ws.close()
|
||||
@@ -0,0 +1,77 @@
|
||||
// CDP 触摸手势驱动 v2: 把手立即拖动 + 长按路径, 组内/跨组落点
|
||||
import WebSocket from 'ws'
|
||||
|
||||
const C = JSON.parse((await (await fetch('http://127.0.0.1:9223/json/version')).text()))
|
||||
const ws = new WebSocket(C.webSocketDebuggerUrl, { perMessageDeflate: false })
|
||||
let id = 0
|
||||
const pending = new Map()
|
||||
const send = (method, params = {}, sessionId) => new Promise((res) => {
|
||||
const i = ++id
|
||||
pending.set(i, res)
|
||||
ws.send(JSON.stringify({ id: i, method, params, sessionId }))
|
||||
})
|
||||
ws.on('message', (d) => {
|
||||
const m = JSON.parse(d)
|
||||
if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id) }
|
||||
})
|
||||
await new Promise((r) => ws.on('open', r))
|
||||
|
||||
const { targetId } = await send('Target.createTarget', { url: 'about:blank' })
|
||||
const { sessionId } = await send('Target.attachToTarget', { targetId, flatten: true })
|
||||
const S = (m, p) => send(m, p, sessionId)
|
||||
await S('Runtime.enable'); await S('Page.enable')
|
||||
await S('Emulation.setDeviceMetricsOverride', { width: 400, height: 800, deviceScaleFactor: 2, mobile: true })
|
||||
await S('Emulation.setTouchEmulationEnabled', { enabled: true, maxTouchPoints: 5 })
|
||||
await S('Page.navigate', { url: 'http://127.0.0.1:5199/dragtest.html' })
|
||||
|
||||
const sleep = (ms) => new Promise((res) => setTimeout(res, ms))
|
||||
const ev = async (expr) => (await S('Runtime.evaluate', { expression: expr, returnByValue: true })).result.value
|
||||
|
||||
const center = async (sel) => {
|
||||
const r = JSON.parse(await ev(`JSON.stringify(document.querySelector('${sel}').getBoundingClientRect())`))
|
||||
return { x: r.left + r.width / 2, y: r.top + r.height / 2 }
|
||||
}
|
||||
for (let i = 0; i < 30; i++) { await sleep(1000); if (await ev(`!!document.querySelector('[data-habit-id="1"]')`)) break }
|
||||
const touch = (type, x, y) => S('Input.dispatchTouchEvent', {
|
||||
type, touchPoints: type === 'touchEnd' ? [] : [{ x, y, id: 1 }],
|
||||
})
|
||||
|
||||
// 用例 1: 按住 habit-1 的把手, 立即垂直拖到 habit-4 中部, 松手 → 应 DROP before/after 4
|
||||
let p = await center('[data-habit-id="1"] .grip')
|
||||
await touch('touchStart', p.x, p.y)
|
||||
await sleep(60)
|
||||
const t = await center('[data-habit-id="4"]')
|
||||
await touch('touchMove', p.x, p.y + 20)
|
||||
await sleep(30)
|
||||
await touch('touchMove', t.x, t.y)
|
||||
await sleep(60)
|
||||
const cls1 = await ev(`document.querySelector('[data-habit-id="4"]').className`)
|
||||
console.log('case1 target class:', cls1)
|
||||
await touch('touchEnd')
|
||||
await sleep(300)
|
||||
|
||||
// 用例 2: 按住 habit-1 把手拖到每周组 (habit-5) → 跨组, 应无落点无 DROP
|
||||
p = await center('[data-habit-id="1"] .grip')
|
||||
await touch('touchStart', p.x, p.y)
|
||||
await sleep(60)
|
||||
const w = await center('[data-habit-id="5"]')
|
||||
await touch('touchMove', w.x, w.y)
|
||||
await sleep(60)
|
||||
const cls2 = await ev(`document.querySelector('[data-habit-id="5"]').className`)
|
||||
console.log('case2 target class (应无 dragover):', cls2)
|
||||
await touch('touchEnd')
|
||||
await sleep(300)
|
||||
|
||||
// 用例 3: 长按卡片主体 500ms 后拖到 habit-3 下半 → DROP after 3
|
||||
p = await center('[data-habit-id="2"]')
|
||||
await touch('touchStart', p.x, p.y)
|
||||
await sleep(520)
|
||||
const t3 = await center('[data-habit-id="3"]')
|
||||
await touch('touchMove', t3.x, t3.y + 20)
|
||||
await sleep(60)
|
||||
await touch('touchEnd')
|
||||
await sleep(300)
|
||||
|
||||
console.log('--- events:')
|
||||
console.log(await ev('window.__ev.join("\\n")'))
|
||||
ws.close()
|
||||
@@ -0,0 +1,27 @@
|
||||
import WebSocket from 'ws'
|
||||
const C = JSON.parse((await (await fetch('http://127.0.0.1:9223/json/version')).text()))
|
||||
const ws = new WebSocket(C.webSocketDebuggerUrl, { perMessageDeflate: false })
|
||||
let id = 0; const pending = new Map()
|
||||
const send = (m, p = {}, s) => new Promise((r) => { const i = ++id; pending.set(i, r); ws.send(JSON.stringify({ id: i, method: m, params: p, sessionId: s })) })
|
||||
ws.on('message', (d) => { const m = JSON.parse(d); if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id) } })
|
||||
await new Promise((r) => ws.on('open', r))
|
||||
const targets = await send('Target.getTargets')
|
||||
const t = targets.targetInfos.find((x) => x.url.includes('dragtest'))
|
||||
const { sessionId } = await send('Target.attachToTarget', { targetId: t.targetId, flatten: true })
|
||||
const S = (m, p) => send(m, p, sessionId)
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
|
||||
const ev = async (e) => (await S('Runtime.evaluate', { expression: e, returnByValue: true })).result.value
|
||||
// 长按 habit-2 主体, 分别在 450ms 后检查激活态, 再拖动
|
||||
const r = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-habit-id="2"]').getBoundingClientRect())`))
|
||||
const x = r.left + r.width / 2, y = r.top + r.height / 2
|
||||
await S('Input.dispatchTouchEvent', { type: 'touchStart', touchPoints: [{ x, y, id: 1 }] })
|
||||
await sleep(460)
|
||||
console.log('after 460ms class:', await ev(`document.querySelector('[data-habit-id="2"]').className`))
|
||||
const r3 = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-habit-id="3"]').getBoundingClientRect())`))
|
||||
await S('Input.dispatchTouchEvent', { type: 'touchMove', touchPoints: [{ x: r3.left + r3.width / 2, y: r3.top + r3.height - 8, id: 1 }] })
|
||||
await sleep(80)
|
||||
console.log('class of 3 after move:', await ev(`document.querySelector('[data-habit-id="3"]').className`))
|
||||
await S('Input.dispatchTouchEvent', { type: 'touchEnd', touchPoints: [] })
|
||||
await sleep(300)
|
||||
console.log('events:', await ev('window.__ev.join(" | ")'))
|
||||
ws.close()
|
||||
@@ -0,0 +1,32 @@
|
||||
import WebSocket from 'ws'
|
||||
const C = JSON.parse((await (await fetch('http://127.0.0.1:9223/json/version')).text()))
|
||||
const ws = new WebSocket(C.webSocketDebuggerUrl, { perMessageDeflate: false })
|
||||
let id = 0; const pending = new Map()
|
||||
const send = (m, p = {}, s) => new Promise((r) => { const i = ++id; pending.set(i, r); ws.send(JSON.stringify({ id: i, method: m, params: p, sessionId: s })) })
|
||||
ws.on('message', (d) => { const m = JSON.parse(d); if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id) } })
|
||||
await new Promise((r) => ws.on('open', r))
|
||||
const { targetId } = await send('Target.createTarget', { url: 'about:blank' })
|
||||
const { sessionId } = await send('Target.attachToTarget', { targetId, flatten: true })
|
||||
const S = (m, p) => send(m, p, sessionId)
|
||||
await S('Runtime.enable'); await S('Page.enable')
|
||||
await S('Emulation.setDeviceMetricsOverride', { width: 400, height: 800, deviceScaleFactor: 2, mobile: true })
|
||||
await S('Emulation.setTouchEmulationEnabled', { enabled: true, maxTouchPoints: 5 })
|
||||
await S('Page.navigate', { url: 'http://127.0.0.1:5199/dragtest.html' })
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
|
||||
const ev = async (e) => (await S('Runtime.evaluate', { expression: e, returnByValue: true })).result.value
|
||||
for (let i = 0; i < 30; i++) { await sleep(1000); if (await ev(`!!document.querySelector('[data-task-id="101"]')`)) break }
|
||||
const touch = (type, x, y) => S('Input.dispatchTouchEvent', { type, touchPoints: type === 'touchEnd' ? [] : [{ x, y, id: 1 }] })
|
||||
|
||||
// 用例1: 任务B(102) 把手拖到任务D(104) 下半 → 期望 REORDER 102 after 104
|
||||
const g = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-task-id="102"] .grip').getBoundingClientRect())`))
|
||||
await touch('touchStart', g.left + g.width / 2, g.top + g.height / 2)
|
||||
await sleep(60)
|
||||
const d = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-task-id="104"]').getBoundingClientRect())`))
|
||||
await touch('touchMove', d.left + d.width / 2, d.top + d.height - 10)
|
||||
await sleep(120)
|
||||
console.log('row104 class:', await ev(`document.querySelector('[data-task-id="104"]').className`))
|
||||
console.log('drag state:', await ev(`JSON.stringify({drag: !!document.querySelector('.task-row.dragging'), ghost: !!document.querySelector('.drag-ghost')})`))
|
||||
await touch('touchEnd')
|
||||
await sleep(200)
|
||||
console.log('events:', await ev('window.__ev.join(" | ")'))
|
||||
ws.close()
|
||||
@@ -0,0 +1,40 @@
|
||||
import WebSocket from 'ws'
|
||||
const C = JSON.parse((await (await fetch('http://127.0.0.1:9223/json/version')).text()))
|
||||
const ws = new WebSocket(C.webSocketDebuggerUrl, { perMessageDeflate: false })
|
||||
let id = 0; const pending = new Map()
|
||||
const send = (m, p = {}, s) => new Promise((r) => { const i = ++id; pending.set(i, r); ws.send(JSON.stringify({ id: i, method: m, params: p, sessionId: s })) })
|
||||
ws.on('message', (d) => { const m = JSON.parse(d); if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id) } })
|
||||
await new Promise((r) => ws.on('open', r))
|
||||
const targets = await send('Target.getTargets')
|
||||
const t = targets.targetInfos.find((x) => x.url.includes('dragtest'))
|
||||
const { sessionId } = await send('Target.attachToTarget', { targetId: t.targetId, flatten: true })
|
||||
const S = (m, p) => send(m, p, sessionId)
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
|
||||
const ev = async (e) => (await S('Runtime.evaluate', { expression: e, returnByValue: true })).result.value
|
||||
const touch = (type, x, y) => S('Input.dispatchTouchEvent', { type, touchPoints: type === 'touchEnd' ? [] : [{ x, y, id: 1 }] })
|
||||
const r = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-habit-id="2"] .grip').getBoundingClientRect())`))
|
||||
const x = r.left + r.width / 2, y = r.top + r.height / 2
|
||||
await touch('touchStart', x, y)
|
||||
await sleep(60)
|
||||
const t4 = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-habit-id="4"]').getBoundingClientRect())`))
|
||||
const fx = t4.left + t4.width / 2, fy = t4.top + t4.height / 2
|
||||
await touch('touchMove', fx, fy)
|
||||
await sleep(150)
|
||||
const scan = await ev(`(() => {
|
||||
const out = []
|
||||
document.querySelectorAll('*').forEach((el) => {
|
||||
const cs = getComputedStyle(el)
|
||||
const b = el.getBoundingClientRect()
|
||||
if (b.width === 0 || b.height === 0) return
|
||||
const bg = cs.backgroundColor
|
||||
// 找带蓝色背景的元素
|
||||
if (/rgb\\((22|2[0-9]|1[0-9]), ?1[0-9]{2}/.test(bg) || bg.includes('22, 119, 255') || bg.includes('0, 149, 255')) {
|
||||
out.push({ tag: el.tagName, cls: el.className && String(el.className).slice(0, 40), bg, rect: [Math.round(b.left), Math.round(b.top), Math.round(b.width), Math.round(b.height)], text: (el.textContent || '').slice(0, 12) })
|
||||
}
|
||||
})
|
||||
return JSON.stringify(out)
|
||||
})()`)
|
||||
console.log('blue elements:', scan)
|
||||
console.log('ghost children:', await ev(`(() => { const g = document.querySelector('.drag-ghost'); return g ? g.children.length + ' kids, inner w ' + g.getBoundingClientRect().width : 'none' })()`))
|
||||
await touch('touchEnd')
|
||||
ws.close()
|
||||
@@ -0,0 +1,35 @@
|
||||
import WebSocket from 'ws'
|
||||
import fs from 'node:fs'
|
||||
const C = JSON.parse((await (await fetch('http://127.0.0.1:9223/json/version')).text()))
|
||||
const ws = new WebSocket(C.webSocketDebuggerUrl, { perMessageDeflate: false })
|
||||
let id = 0; const pending = new Map()
|
||||
const send = (m, p = {}, s) => new Promise((r) => { const i = ++id; pending.set(i, r); ws.send(JSON.stringify({ id: i, method: m, params: p, sessionId: s })) })
|
||||
ws.on('message', (d) => { const m = JSON.parse(d); if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id) } })
|
||||
await new Promise((r) => ws.on('open', r))
|
||||
const { targetId } = await send('Target.createTarget', { url: 'about:blank' })
|
||||
const { sessionId } = await send('Target.attachToTarget', { targetId, flatten: true })
|
||||
const S = (m, p) => send(m, p, sessionId)
|
||||
await S('Runtime.enable'); await S('Page.enable')
|
||||
await S('Emulation.setDeviceMetricsOverride', { width: 400, height: 800, deviceScaleFactor: 2, mobile: true })
|
||||
await S('Emulation.setTouchEmulationEnabled', { enabled: true, maxTouchPoints: 5 })
|
||||
await S('Page.navigate', { url: 'http://127.0.0.1:5199/dragtest.html' })
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
|
||||
const ev = async (e) => (await S('Runtime.evaluate', { expression: e, returnByValue: true })).result.value
|
||||
for (let i = 0; i < 30; i++) { await sleep(1000); if (await ev(`!!document.querySelector('[data-habit-id="1"]')`)) break }
|
||||
const touch = (type, x, y) => S('Input.dispatchTouchEvent', { type, touchPoints: type === 'touchEnd' ? [] : [{ x, y, id: 1 }] })
|
||||
const r = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-habit-id="2"] .grip').getBoundingClientRect())`))
|
||||
const x = r.left + r.width / 2, y = r.top + r.height / 2
|
||||
await touch('touchStart', x, y)
|
||||
await sleep(60)
|
||||
const t = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-habit-id="4"]').getBoundingClientRect())`))
|
||||
await touch('touchMove', t.x ?? t.left + t.width / 2, t.top + t.height / 2)
|
||||
await sleep(120)
|
||||
// 拖动中截图
|
||||
const shot = await S('Page.captureScreenshot', { format: 'png' })
|
||||
fs.writeFileSync(process.env.USERPROFILE + '\\AppData\\Local\\Temp\\drag-mid.png', Buffer.from(shot.data, 'base64'))
|
||||
console.log('shot saved; ghost exists:', await ev(`!!document.querySelector('.drag-ghost')`))
|
||||
console.log('ghost html head:', (await ev(`document.querySelector('.drag-ghost') ? document.querySelector('.drag-ghost').outerHTML.slice(0, 200) : 'NONE'`)))
|
||||
await touch('touchEnd')
|
||||
await sleep(200)
|
||||
console.log('events:', await ev('window.__ev.join(" | ")'))
|
||||
ws.close()
|
||||
@@ -0,0 +1,36 @@
|
||||
import WebSocket from 'ws'
|
||||
const C = JSON.parse((await (await fetch('http://127.0.0.1:9223/json/version')).text()))
|
||||
const ws = new WebSocket(C.webSocketDebuggerUrl, { perMessageDeflate: false })
|
||||
let id = 0; const pending = new Map()
|
||||
const send = (m, p = {}, s) => new Promise((r) => { const i = ++id; pending.set(i, r); ws.send(JSON.stringify({ id: i, method: m, params: p, sessionId: s })) })
|
||||
ws.on('message', (d) => { const m = JSON.parse(d); if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id) } })
|
||||
await new Promise((r) => ws.on('open', r))
|
||||
const { targetId } = await send('Target.createTarget', { url: 'about:blank' })
|
||||
const { sessionId } = await send('Target.attachToTarget', { targetId, flatten: true })
|
||||
const S = (m, p) => send(m, p, sessionId)
|
||||
await S('Runtime.enable'); await S('Page.enable')
|
||||
await S('Emulation.setDeviceMetricsOverride', { width: 400, height: 800, deviceScaleFactor: 2, mobile: true })
|
||||
await S('Emulation.setTouchEmulationEnabled', { enabled: true, maxTouchPoints: 5 })
|
||||
await S('Page.navigate', { url: 'http://127.0.0.1:5199/dragtest.html' })
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
|
||||
const ev = async (e) => (await S('Runtime.evaluate', { expression: e, returnByValue: true })).result.value
|
||||
for (let i = 0; i < 30; i++) { await sleep(1000); if (await ev(`!!document.querySelector('[data-task-id="3"]')`)) break }
|
||||
await sleep(1000) // 等延迟数据加载完
|
||||
const touch = (type, x, y) => S('Input.dispatchTouchEvent', { type, touchPoints: type === 'touchEnd' ? [] : [{ x, y, id: 1 }] })
|
||||
|
||||
// 用例: 按住任务3的把手 → 水平拖 +200px (带 +30 纵向漂移, 复现真机抖动) → 松手
|
||||
const r = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-task-id="3"] .grip').getBoundingClientRect())`))
|
||||
let x = r.left + r.width / 2, y = r.top + r.height / 2
|
||||
console.log('grip rect', JSON.stringify(r))
|
||||
await touch('touchStart', x, y)
|
||||
await sleep(60)
|
||||
for (const [dx, dy] of [[25, 4], [60, 10], [110, 18], [160, 26], [200, 30]]) {
|
||||
await touch('touchMove', x + dx, y + dy)
|
||||
await sleep(50)
|
||||
}
|
||||
console.log('row class after moves:', await ev(`document.querySelector('[data-task-id="3"]').className`))
|
||||
console.log('meta:', await ev(`document.querySelector('[data-task-id="3"] .task-meta').textContent`))
|
||||
await touch('touchEnd')
|
||||
await sleep(300)
|
||||
console.log('events:', await ev('window.__ev.join(" | ")'))
|
||||
ws.close()
|
||||
@@ -0,0 +1,30 @@
|
||||
import WebSocket from 'ws'
|
||||
import fs from 'node:fs'
|
||||
const C = JSON.parse((await (await fetch('http://127.0.0.1:9223/json/version')).text()))
|
||||
const ws = new WebSocket(C.webSocketDebuggerUrl, { perMessageDeflate: false })
|
||||
let id = 0; const pending = new Map()
|
||||
const send = (m, p = {}, s) => new Promise((r) => { const i = ++id; pending.set(i, r); ws.send(JSON.stringify({ id: i, method: m, params: p, sessionId: s })) })
|
||||
ws.on('message', (d) => { const m = JSON.parse(d); if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result); pending.delete(m.id) } })
|
||||
await new Promise((r) => ws.on('open', r))
|
||||
const targets = await send('Target.getTargets')
|
||||
const t = targets.targetInfos.find((x) => x.url.includes('dragtest'))
|
||||
const { sessionId } = await send('Target.attachToTarget', { targetId: t.targetId, flatten: true })
|
||||
const S = (m, p) => send(m, p, sessionId)
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
|
||||
const ev = async (e) => (await S('Runtime.evaluate', { expression: e, returnByValue: true })).result.value
|
||||
await S('Page.reload')
|
||||
await sleep(2500)
|
||||
const touch = (type, x, y) => S('Input.dispatchTouchEvent', { type, touchPoints: type === 'touchEnd' ? [] : [{ x, y, id: 1 }] })
|
||||
const r = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-habit-id="2"] .grip').getBoundingClientRect())`))
|
||||
const x = r.left + r.width / 2, y = r.top + r.height / 2
|
||||
await touch('touchStart', x, y)
|
||||
await sleep(60)
|
||||
const t4 = JSON.parse(await ev(`JSON.stringify(document.querySelector('[data-habit-id="4"]').getBoundingClientRect())`))
|
||||
await touch('touchMove', t4.left + t4.width / 2, t4.top + t4.height / 2)
|
||||
await sleep(150)
|
||||
console.log('ghost bg:', await ev(`getComputedStyle(document.querySelector('.drag-ghost')).backgroundColor`))
|
||||
console.log('ghost rect:', await ev(`JSON.stringify(document.querySelector('.drag-ghost').getBoundingClientRect())`))
|
||||
const shot = await S('Page.captureScreenshot', { format: 'png' })
|
||||
fs.writeFileSync(process.env.USERPROFILE + '\\AppData\\Local\\Temp\\drag-fixed.png', Buffer.from(shot.data, 'base64'))
|
||||
await touch('touchEnd')
|
||||
ws.close()
|
||||
@@ -0,0 +1,8 @@
|
||||
fix(V0.2): 拖动浮层残留蓝色背景 —— 类名与遗留 .drag-ghost 规则冲突
|
||||
|
||||
globals.css 里有一条无人使用的旧 .drag-ghost 规则 (蓝色气泡,
|
||||
max-width 180px, 看板早期方案遗留), 排在新增规则之后同特异性胜出,
|
||||
克隆卡片容器被套上蓝色背景并被压缩到 180px 宽, 即真机上手下面的
|
||||
蓝色 div。删除遗留规则; 新规则显式清零背景/内边距并禁用
|
||||
tap-highlight/user-select (克隆脱离 .mv-view/.task-content 祖先后
|
||||
需自带防选中)。CDP 实测拖动中容器背景已透明、宽度正常。
|
||||
@@ -0,0 +1,4 @@
|
||||
const s = '问题,;、!'
|
||||
console.log(JSON.stringify(s.replace(/[\s\p{P}]+$/u, '')))
|
||||
console.log(JSON.stringify('正常标题'.replace(/[\s\p{P}]+$/u, '')))
|
||||
console.log(JSON.stringify('a-b_c'.replace(/[\s\p{P}]+$/u, '')))
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
Exception during registration:
|
||||
System.ComponentModel.Win32Exception (0x80004005): 系统找不到指定的文件。
|
||||
在 System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
|
||||
在 System.CommandLine.Invocation.Process.StartProcess(String command, String args, String workingDir, Action`1 stdOut, Action`1 stdErr, ValueTuple`2[] environmentVariables)
|
||||
在 System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<<RegisterWithDotnetSuggest>b__5_1>d.MoveNext()
|
||||
@@ -0,0 +1,31 @@
|
||||
# CHANGELOG
|
||||
|
||||
版本号规则见 [README.md](README.md)「版本管理」。前端与外壳版本号**互相独立**、独立发版,
|
||||
分三部分记录;PATCH 仅在明确提出时自增, 否则沿用, 每次变更在对应部分追加一行。
|
||||
|
||||
---
|
||||
|
||||
## 程序版本 (Web / OTA)
|
||||
|
||||
**当前值: 0.2.0** (管理点: `package.json` 的 `version`; OTA 包 = 同值 + `-ota`)
|
||||
|
||||
| 日期 | 版本 | commit | 要点 |
|
||||
| --- | --- | --- | --- |
|
||||
| 2026-10-25 | 0.2.0 | — | V0.2 基线。前端与外壳版本解绑; build 标识统一 `YYYYMMDDHHmmss` (北京时间); PATCH 规则确立 (仅明确提出时自增) |
|
||||
| 2026-10-25 | 0.2.1 | 93b7c7b | 主要解决离线编辑与不同终端数据合并问题 |
|
||||
|
||||
## Android 外壳版本
|
||||
|
||||
**当前值: 0.2.0** (管理点: `src-tauri/tauri.conf.json` 的 `version`, 须与 Cargo.toml/Cargo.lock 组内一致; 产物 = APK versionName)
|
||||
|
||||
| 日期 | 版本 | commit | 要点 |
|
||||
| --- | --- | --- | --- |
|
||||
| 2026-10-25 | 0.2.0 | — | V0.2 基线。外壳与前端版本解绑, 独立维护 (android-* 分支出包) |
|
||||
|
||||
## Windows 外壳版本
|
||||
|
||||
**当前值: —** (尚未建立; 建立后管理点同为 `src-tauri/tauri.conf.json` + Cargo 三件套, 走独立分支/打包工作流)
|
||||
|
||||
| 日期 | 版本 | commit | 要点 |
|
||||
| --- | --- | --- | --- |
|
||||
| — | — | — | 暂无 |
|
||||
+84
-72
@@ -1,73 +1,85 @@
|
||||
# ===============================================================
|
||||
# iBoard 一体化镜像: 静态前端 (dist/) + Express API (server/) 同进程
|
||||
# 构建: docker build --build-arg DATABASE_URL=... -t iboard:latest .
|
||||
# 可选 --build-arg VITE_API_BASE=https://api.example.com 让前端走绝对地址
|
||||
# 运行: docker run -d --name iboard -p 3001:3001 -e DATABASE_URL=... iboard:latest
|
||||
# ===============================================================
|
||||
FROM node:22-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 国内构建时可换源
|
||||
RUN npm config set registry https://registry.npmmirror.com
|
||||
|
||||
# 先拷贝 package 文件, 利用 Docker 缓存
|
||||
COPY package*.json ./
|
||||
COPY prisma ./prisma/
|
||||
COPY prisma.config.ts ./
|
||||
COPY generated ./generated/
|
||||
|
||||
# 装全量依赖 (dev 也装, 后面 build 要用)
|
||||
RUN npm install
|
||||
|
||||
# 拷贝源码
|
||||
COPY . .
|
||||
|
||||
# 构建前端 (输出到 dist/) 与 Prisma 客户端
|
||||
RUN npx prisma generate
|
||||
# build 时把 VITE_API_BASE 写进 bundle, 默认空 = 走相对路径 /api (同源 Express)
|
||||
ARG VITE_API_BASE=
|
||||
ENV VITE_API_BASE=$VITE_API_BASE
|
||||
RUN npm run build
|
||||
|
||||
# 运行时阶段
|
||||
FROM node:22-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 阿里源加速
|
||||
RUN npm config set registry https://registry.npmmirror.com
|
||||
|
||||
# 只装运行时依赖
|
||||
COPY package*.json ./
|
||||
RUN npm install --omit=dev
|
||||
|
||||
# 拷贝构建产物
|
||||
COPY --from=builder /app/dist ./dist
|
||||
COPY --from=builder /app/server ./server
|
||||
COPY --from=builder /app/generated ./generated
|
||||
COPY --from=builder /app/prisma ./prisma
|
||||
COPY --from=builder /app/prisma.config.ts ./prisma.config.ts
|
||||
COPY --from=builder /app/scripts ./scripts
|
||||
COPY --from=builder /app/content ./content
|
||||
COPY --from=builder /app/public ./public
|
||||
|
||||
# OTA 升级包: CI (docker-build.yml) 生成的 ota-package/, 随镜像发布
|
||||
# 本地构建时只有占位文件 (ota-package/README.md), server 找不到 latest.json 则 OTA 返回"无更新"
|
||||
COPY ota-package ./ota-package
|
||||
ENV OTA_PACKAGE_DIR=/app/ota-package
|
||||
# OTA bundle 下载用的绝对 base (manifest.url 必须是绝对 URL, 插件不拼 base, 相对路径会导致下载 0% 失败)
|
||||
# 生产为 http 明文 (无 https), 客户端 tauri.conf.json 的 hotswap.require_https 已设为 false 放行 http
|
||||
ENV OTA_PUBLIC_BASE=http://bh.vps.honor3.com:3001
|
||||
|
||||
# 健康检查
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget -qO- http://127.0.0.1:3001/api/health || exit 1
|
||||
|
||||
EXPOSE 3001
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3001
|
||||
|
||||
# Node 22 默认未开 strip-types, 显式开启以便加载 generated/prisma/*.ts
|
||||
# syntax=docker/dockerfile:1
|
||||
# ===============================================================
|
||||
# iBoard 一体化镜像: 静态前端 (dist/) + Express API (server/) 同进程
|
||||
# 构建: docker build --build-arg DATABASE_URL=... -t iboard:latest .
|
||||
# 可选 --build-arg VITE_API_BASE=https://api.example.com 让前端走绝对地址
|
||||
# CI 传入 --build-arg PREBUILT_DIST=true 复用 CI 已构建的 dist/ (跳过镜像内前端构建)
|
||||
# 运行: docker run -d --name iboard -p 3001:3001 -e DATABASE_URL=... iboard:latest
|
||||
# 需要 BuildKit (cache mount 语法; Docker 23+ 默认开启)
|
||||
# ===============================================================
|
||||
FROM node:22-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 国内构建时可换源
|
||||
RUN npm config set registry https://registry.npmmirror.com
|
||||
|
||||
# 先拷贝 package 文件, 利用 Docker 缓存
|
||||
COPY package*.json ./
|
||||
COPY prisma ./prisma/
|
||||
COPY prisma.config.ts ./
|
||||
COPY generated ./generated/
|
||||
|
||||
# 装全量依赖 (dev 也装, 后面 prisma generate / 本地构建前端要用)
|
||||
# cache mount 复用 npm 下载缓存, 不进镜像层
|
||||
RUN --mount=type=cache,target=/root/.npm npm ci
|
||||
|
||||
# 拷贝源码
|
||||
COPY . .
|
||||
|
||||
# 构建 Prisma 客户端
|
||||
RUN --mount=type=cache,target=/root/.npm npx prisma generate
|
||||
|
||||
# 构建前端 (输出到 dist/)
|
||||
# PREBUILT_DIST=true: CI 已在镜像外构建过 dist/ (供 OTA 打包), 直接复用, 跳过镜像内构建
|
||||
ARG PREBUILT_DIST=false
|
||||
# build 时把 VITE_API_BASE 写进 bundle, 默认空 = 走相对路径 /api (同源 Express)
|
||||
ARG VITE_API_BASE=
|
||||
ENV VITE_API_BASE=$VITE_API_BASE
|
||||
RUN if [ "$PREBUILT_DIST" = "true" ]; then \
|
||||
test -f dist/index.html || { echo "PREBUILT_DIST=true 但 dist/ 缺失"; exit 1; }; \
|
||||
else \
|
||||
npm run build; \
|
||||
fi
|
||||
|
||||
# 运行时阶段
|
||||
FROM node:22-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 阿里源加速
|
||||
RUN npm config set registry https://registry.npmmirror.com
|
||||
|
||||
# 只装运行时依赖 (better-sqlite3 是 @powersync/node 的 devOptional peer, 需同时 omit optional 才能跳过)
|
||||
COPY package*.json ./
|
||||
RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev --omit=optional
|
||||
|
||||
# 拷贝构建产物
|
||||
COPY --from=builder /app/dist ./dist
|
||||
COPY --from=builder /app/server ./server
|
||||
COPY --from=builder /app/generated ./generated
|
||||
COPY --from=builder /app/prisma ./prisma
|
||||
COPY --from=builder /app/prisma.config.ts ./prisma.config.ts
|
||||
COPY --from=builder /app/scripts ./scripts
|
||||
COPY --from=builder /app/content ./content
|
||||
COPY --from=builder /app/public ./public
|
||||
|
||||
# OTA 升级包: CI (docker-build.yml) 生成的 ota-package/, 随镜像发布
|
||||
# 本地构建时只有占位文件 (ota-package/README.md), server 找不到 latest.json 则 OTA 返回"无更新"
|
||||
COPY ota-package ./ota-package
|
||||
ENV OTA_PACKAGE_DIR=/app/ota-package
|
||||
# OTA bundle 下载用的绝对 base (manifest.url 必须是绝对 URL, 插件不拼 base, 相对路径会导致下载 0% 失败)
|
||||
# 生产为 http 明文 (无 https), 客户端 tauri.conf.json 的 hotswap.require_https 已设为 false 放行 http
|
||||
ENV OTA_PUBLIC_BASE=https://iboard.honor3.com
|
||||
|
||||
# 健康检查 (端口跟随 PORT 环境变量, 0.2.1 部署为 13001)
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget -qO- http://127.0.0.1:${PORT:-3001}/api/health || exit 1
|
||||
|
||||
EXPOSE 3001
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3001
|
||||
|
||||
# Node 22 默认未开 strip-types, 显式开启以便加载 generated/prisma/*.ts
|
||||
CMD ["node", "--experimental-strip-types", "--no-warnings", "server/index.js"]
|
||||
@@ -0,0 +1,316 @@
|
||||
# iBoard
|
||||
|
||||
跨平台个人看板 (Tauri + React + Express + MariaDB)。用于收集和整理经济数据,但是以策略为主体重新组织。
|
||||
|
||||
- 宏观:新闻 / 经济文章(本地),以及外接 ifinance 模块的分类(经济数据 / 策略研究 / 看板,运行时动态挂载为 tab)
|
||||
- 数据与策略由外接 ifinance 模块承担(静态包,见 `../ifinance/readme.md`)
|
||||
- 报警并入「监控」;油价模块已下线
|
||||
|
||||
## 宏观
|
||||
|
||||
宏观页固定「新闻 / 经济文章」两个本地 tab,并动态挂载外接 ifinance `info.json` 的 `categories[]` 作为额外 tab:
|
||||
|
||||
- 新闻:每日夜间 3 点通过妙想 MCP 获取国内外最新经济政治新闻,按影响大小排序 10 条,存入数据库;列表按时间倒序无限滚动。
|
||||
- 经济文章:由 `content/articles/*.md` 承载的长文。
|
||||
- 经济数据 / 策略研究 / …:交给外接 ifinance 模块,宿主只读取 `info.json` 并用 iframe 打开条目页(纯前端直连,见 `ifinance/readme.md` §9)。iBoard 本地不再保存指标 / 策略数据。
|
||||
|
||||
## 监控
|
||||
|
||||
监控分为主机监控 / 服务监控 / 环境监控 / 报警四个 tab。主机监控对接 komari:<https://komari.honor3.com/>;报警 tab 展示跨源数据差异与定时任务异常,按时间倒序。
|
||||
|
||||
---
|
||||
|
||||
## 分支规则
|
||||
|
||||
### 分支类型与职责
|
||||
|
||||
| 分支 | 职责 | 部署 | 合并方向 |
|
||||
| --- | --- | --- | --- |
|
||||
| `master` | **主分支**(生产分支) | ✅ 唯一部署出口,push 即发布生产 | — |
|
||||
| `dev-*` | **开发集成分支**,如 `dev-0.2` | ❌ 不部署(只跑 CI 校验) | 合到 `master` 后由 master 部署 |
|
||||
| `tmp-*` | **临时分支**,针对单个问题的修改 | ❌ 不部署 | 合到对应 `dev-*` 后删除 |
|
||||
| `android-*` | **Android 外壳构建分支** | ❌ 不部署,只触发 APK 构建 | 构建前须与 `master` 同步 |
|
||||
|
||||
> **关于 `main`**:团队约定里的「main 主分支」在本仓库落地为 **`master`**(仓库默认分支即 `master`,不另建 `main`)。
|
||||
> 下文所有「main 主分支」均指 `master`。
|
||||
|
||||
### 流程
|
||||
|
||||
```
|
||||
tmp-xxx ──合并──▶ dev-0.2 ──合并──▶ master ──push──▶ 自动部署生产
|
||||
│ │
|
||||
│ └──push──▶ 只跑 CI(版本一致性校验),不部署
|
||||
│
|
||||
└── 问题解决后删除
|
||||
|
||||
android-1.0 ── 从 master 同步代码后 push ──▶ 只触发 APK 构建 / 发布到 Packages
|
||||
```
|
||||
|
||||
1. **日常开发**:从 `dev-*` 拉 `tmp-*`,改完合回 `dev-*`,`tmp-*` 用完即删。
|
||||
2. **上线**:`dev-*` 验收通过后合并到 `master`,**推送 `master` 才部署生产**。
|
||||
3. **打 Android 包**:`android-*` 与 `master` 同步(`git merge master` 或 `git reset --hard origin/master`)后推送,
|
||||
触发 [android-build.yml](.gitea/workflows/android-build.yml),产物发布到 Gitea Generic Packages。
|
||||
|
||||
### CI 触发范围
|
||||
|
||||
| 工作流 | 触发分支 | 动作 |
|
||||
| --- | --- | --- |
|
||||
| [docker-build.yml](.gitea/workflows/docker-build.yml) | **仅 `master`** | 构建前端 + 生成签名 OTA 包 + 构建镜像 + 迁移数据库 + 部署生产 |
|
||||
| [android-build.yml](.gitea/workflows/android-build.yml) | `android-*` | 构建并签名 release APK,上传到 Packages |
|
||||
| [ota-build.yml](.gitea/workflows/ota-build.yml) | 仅手动 `workflow_dispatch` | 单独构建/签发 OTA 包 |
|
||||
| [sync-registry.yml](.gitea/workflows/sync-registry.yml) | 见文件 | 同步注册中心 |
|
||||
|
||||
> ⚠️ **重要变更(2026-10)**:`dev-*` 分支**已取消** push 自动部署。
|
||||
> 此前 `dev-*` 与 `master` 共用同一套生产环境,推送开发分支等于直接发布,容易把半成品带上线。
|
||||
> 现在开发分支的提交只有合并进 `master` 并推送后才会部署。
|
||||
|
||||
### 日常操作规则(工作目录约定)
|
||||
|
||||
- 主工作目录(`iboard/`)**默认停留在 `dev-0.2`**,所有修改都在 `dev-0.2` 上提交;`dev-0.2` 可随时 push(只跑 CI 校验,不部署)。
|
||||
- 独立功能开发使用**独立分支 + 独立工作目录**(如离线编辑在 `iboard-dev-0.2.1/` 的 `tmp-offline-edit` 分支),不占用 `dev-0.2`。
|
||||
- **发布流程(仅在明确需要发布时执行,结束后立刻回到 `dev-0.2`)**:
|
||||
|
||||
```bash
|
||||
git switch master
|
||||
git merge --ff-only dev-0.2
|
||||
git push origin master # push 即部署生产
|
||||
git switch dev-0.2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本管理
|
||||
|
||||
### 版本号构成与口径(2026-10-25 决议)
|
||||
|
||||
```
|
||||
V MAJOR.MINOR.PATCH 例: V0.2.0
|
||||
│ └── PATCH 小版本号: 仅在明确提出时自增, 否则沿用 (记录见 CHANGELOG.md)
|
||||
└── MINOR 大版本迭代, 对应一个开发分支 (V0.2 ↔ dev-0.2)
|
||||
```
|
||||
|
||||
**前端与外壳解绑,独立维护、独立发版**(2026-10-25 决议):
|
||||
|
||||
| 组 | 版本 | 管理点 | 产物 | 发版节奏 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **前端** (Web/OTA) | `0.2.0` | `package.json` 的 `version`(唯一手动管理点,只写裸版本号,不允许带 `-ota`) | 前端运行时 `APP_VERSION` = `0.2.0`;OTA 包 `version` = `0.2.0-ota` | 随 master push 高频发版 |
|
||||
| **外壳** | `0.2.0` | `src-tauri/tauri.conf.json` 的 `version`(须与 `Cargo.toml`/`Cargo.lock` 组内一致) | APK versionName、桌面安装包版本;后续 Windows 外壳同理 | 独立分支(如 `android-*`)低频发版 |
|
||||
|
||||
- 两组版本号**不要求相等**,CI(`scripts/ci/check-version.mjs`)只校验组内一致 + 前端版本与 `dev-<major>.<minor>` 分支前缀对应。
|
||||
- 分支口径:**dev-0.2 开发,发布一律走 master**。
|
||||
|
||||
### build 标识
|
||||
|
||||
- 格式统一为 **`YYYYMMDDHHmmss`(北京时间)**:
|
||||
- 前端 `BUILD_ID`:vite 编译时生成(`vite.config.js#makeBuildId`),无需手动维护;
|
||||
- OTA 包 `build_id`:打包时按同一格式生成(`scripts/build-ota.mjs`)。
|
||||
- OTA 包的 `sequence`(Unix 毫秒,单调递增)保留,仅用于判新与热插拔协议,不再用于展示。
|
||||
|
||||
### PATCH 小版本号
|
||||
|
||||
- 仅在**明确提出**时自增(改 `package.json` version 第三位),否则一直沿用。
|
||||
- 每次自增在 [CHANGELOG.md](CHANGELOG.md) 追加一行(patch 号 / 日期 / commit / 要点)。
|
||||
|
||||
### 改版本号时要同步的位置
|
||||
|
||||
| 组 | 文件 | 字段 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| 前端 | `package.json` | `version` | **前端唯一源**,`src/version.js` 与 OTA 打包脚本均读取此值 |
|
||||
| 外壳 | `src-tauri/tauri.conf.json` | `version` | APK 的 versionName、桌面安装包版本 |
|
||||
| 外壳 | `src-tauri/Cargo.toml` | `[package].version` | Rust 包版本,须与 tauri.conf.json 一致 |
|
||||
| 外壳 | `src-tauri/Cargo.lock` | `name = "iboard"` 条目 | 随 Cargo.toml 一起改 |
|
||||
|
||||
### APK 版本号是怎么来的
|
||||
|
||||
`tauri android init` 会按 `src-tauri/tauri.conf.json` 的 `version` 生成
|
||||
`src-tauri/gen/android/app/tauri.properties`(该目录被 gitignore,CI 每次 `android init` 重新生成):
|
||||
|
||||
```
|
||||
tauri.android.versionName = 0.2.0 # 人类可见版本,与 tauri.conf.json 一致
|
||||
tauri.android.versionCode = 2000 # 整数,升级判定用 = major*1000000 + minor*1000 + patch
|
||||
```
|
||||
|
||||
注意:
|
||||
- `versionCode` 必须**单调递增**才能覆盖安装,所以外壳版本号只能往上走(`0.2.0` → 2000 > `0.1.0` → 1000)。
|
||||
- 本地如果 APK 装完显示旧版本号,是 `src-tauri/gen/android` 缓存没刷新,
|
||||
删掉该目录重新 `npx tauri android init` 即可。
|
||||
|
||||
### 其它
|
||||
|
||||
- `src/version.js` 从 `package.json` 取值,前端运行时与 OTA 检查接口展示的即此版本,不要另写死版本号。
|
||||
- **`min_binary_version` 与这里的版本号是两件事**:它是 OTA 包要求的最低原生二进制版本
|
||||
(CI 里由 `OTA_MIN_BINARY` 显式指定,当前 `0.1.0`)。前端版本升到 0.2.0 时它不应跟着抬,
|
||||
否则装了 0.1.0 APK 的设备会直接收不到热更。
|
||||
- `ota-package/latest.json` 为运行时产物,不纳入版本管理。
|
||||
|
||||
### "我的"页版本展示口径
|
||||
|
||||
| 行 | 展示 |
|
||||
| --- | --- |
|
||||
| 当前版本 | `v0.2.0 · build 20261025204300`(前端 APP_VERSION + BUILD_ID 原始格式) |
|
||||
| 运行资源 | OTA 覆盖包版本 / 内置 |
|
||||
| 新版本 | `v0.2.0-ota · build 20261025210000`(OTA 包 build_id) |
|
||||
| 发布说明 | 触发构建的 commit message |
|
||||
| 提交 | commit sha 前 10 位 |
|
||||
| 页面底部 | `外壳 v0.2.0`(Tauri API 读取;Web 端显示 `—`) |
|
||||
|
||||
---
|
||||
|
||||
## 前端 OTA 升级
|
||||
|
||||
只升级前端资源 (`dist/`),**不触碰** Tauri 框架/原生层,不重打 APK。
|
||||
底层使用 [`tauri-plugin-hotswap`](https://github.com/denniskribl/tauri-plugin-hotswap)
|
||||
(签名/回滚/资源替换全部由插件处理)。
|
||||
|
||||
### 工作流
|
||||
|
||||
```
|
||||
开发者 服务端
|
||||
npm run build ──┐
|
||||
scripts/build-ota.mjs │ 生成 iboard-ota.tar.gz + minisign 签名
|
||||
│ 输出 latest.json (sequence/version/...)
|
||||
▼
|
||||
ota-package/ ── 部署到服务器 ──▶ /srv/iboard/ota-package
|
||||
│
|
||||
▼
|
||||
GET /api/ota/check/<seq>
|
||||
客户端 (Tauri) │
|
||||
启动 ─▶ notifyReady() ▼
|
||||
我页 ─▶ checkUpdate() ◀──── 200 + manifest (含 url/signature) ─────┘
|
||||
applyUpdate() ─▶ 下载 tar.gz ─▶ minisign 校验 ─▶ 解压到 AppData/ota-dist
|
||||
location.reload() ─▶ 后续资源从 tauri://localhost 优先命中覆盖包
|
||||
```
|
||||
|
||||
### 发布说明与提交号(2026-10-25 起)
|
||||
|
||||
- CI(docker-build.yml)把**触发构建的 commit message**(标题+正文,去空行,截断 400 字)作为 `notes` 写入 latest.json;
|
||||
- commit sha 经 `--commit` 参数单独写入 `commit` 字段;
|
||||
- "我的"页:发布说明展示 commit message,「提交」行展示短 sha。
|
||||
|
||||
### 首次部署
|
||||
|
||||
1. **生成签名密钥** (一次性):
|
||||
```bash
|
||||
# 安装 minisign: Windows (scoop) / macOS (brew) / Linux (apt)
|
||||
node scripts/ota-genkey.mjs
|
||||
# 产物: ota-package/hotswap.{key, pub}
|
||||
```
|
||||
2. **把公钥写入 `src-tauri/tauri.conf.json`** 的 `plugins.hotswap.pubkey` 字段(脚本会打印)。
|
||||
3. **配置 endpoint** (`src-tauri/tauri.conf.json`):
|
||||
```json
|
||||
"hotswap": {
|
||||
"endpoint": "https://your-server/api/ota/check/{{current_sequence}}",
|
||||
"pubkey": "RWR...",
|
||||
"require_https": true
|
||||
}
|
||||
```
|
||||
4. **服务端环境变量** `OTA_PACKAGE_DIR` 指向 `ota-package/` 所在目录。
|
||||
|
||||
### 发布新版前端
|
||||
|
||||
```bash
|
||||
npm run build # 生成 dist/
|
||||
node scripts/build-ota.mjs # 打成 tar.gz + 签名 + latest.json
|
||||
# sequence 默认 = Date.now(), 也可 --sequence 43 手动指定
|
||||
# 也可加 --notes "修复登录按钮" --commit <sha> --min-binary 0.1.0
|
||||
```
|
||||
|
||||
然后把 `ota-package/` 下两个文件 (`iboard-ota.tar.gz` + `latest.json`) 同步到
|
||||
服务端 `OTA_PACKAGE_DIR` 指向的目录。sequence 单调递增即可。
|
||||
|
||||
### 客户端使用
|
||||
|
||||
打开 App 的"我"页,点 [检查更新] → [立即升级] 即可。失败会自动回滚到内置 dist。
|
||||
|
||||
> **注意:** Tauri 2 的 dev 模式 (`tauri dev`) 会代理到 Vite dev server,**OTA 不生效**。
|
||||
> OTA 仅在 release/打包构建中可用,测试请用 `tauri build` 产物。
|
||||
|
||||
### 客户端判新口径(V0.2)
|
||||
|
||||
"我"页 OTA 组内有一个 **开发版本** 开关(默认关闭),切换"怎么算新版本":
|
||||
|
||||
| 开关 | 判新依据 | 适用 |
|
||||
| --- | --- | --- |
|
||||
| 关(默认) | **版本号**逐字段比大小(`0.2.0-ota` > `0.1.0` 才算新) | 生产。旧分支被重建(sequence 变大但版本号更小)时不会把设备拉回旧版 |
|
||||
| 开 | **build sequence**(服务端 sequence 比本地大就算新) | 开发联调。每次构建都能推到设备 |
|
||||
|
||||
- 开关值持久化在设备端 `localStorage.iboard_ota_dev_mode`(`'1'`/`'0'`),默认 `'0'`。
|
||||
- 版本比较在 `src/utils/version.js`(纯函数,`src/api/ota.js` 统一封装两种模式)。
|
||||
- **`min_binary_version` 与应用版本是两件事**:前者是前端 bundle 要求的最低原生二进制版本,
|
||||
当前 APK 原生层仍是 `0.1.0`,所以 CI 里固定传 `--min-binary 0.1.0`(见 `docker-build.yml` 的
|
||||
`OTA_MIN_BINARY`)。只改前端版本号时**不要**跟着抬这个值,否则旧 APK 会收不到更新。
|
||||
|
||||
### Web 端
|
||||
|
||||
Web 浏览器访问 (同源) 时,服务端返回简化版 manifest,仅显示"有新版本请刷新"。
|
||||
Web 端不会自动下载包,也不会替换前端代码。
|
||||
|
||||
### CI 自动签名 (Gitea Actions)
|
||||
|
||||
.gitea/workflows/ota-build.yml 是手动触发的 OTA 打包工作流(每次发版前点一次)。
|
||||
|
||||
**配置 secret** (Gitea 仓库 -> Settings -> Actions -> Secrets):
|
||||
- MINISIGN_PRIVATE_KEY: 完整 .key 文件内容,**整段**(含首行 `untrusted comment: minisign encrypted secret key` 和 base64 行),整段作为多行 secret 粘贴即可。
|
||||
|
||||
**使用流程**:
|
||||
1. 在 Gitea Actions 页面选 OTA Frontend Build & Sign -> Run workflow
|
||||
2. 填 sequence(可选,不填用时间戳)和 notes(可选)
|
||||
3. CI 跑完会在 artifact 里看到 ota-package/iboard-ota.tar.gz + latest.json + iboard-ota.tar.gz.minisig
|
||||
4. 下载 artifact,scp 到服务器 OTA_PACKAGE_DIR 目录
|
||||
5. 客户端"我"页 [检查更新] 即可触发升级
|
||||
|
||||
**注意**:
|
||||
- CI 用的私钥必须**与** `tauri.conf.json` 里 plugins.hotswap.pubkey **配对**;换密钥时需要同时更新 CI secret 和 conf。
|
||||
- 不要把私钥 commit 到 git,整个 ota-package/hotswap.key 已被 .gitignore 排除。
|
||||
|
||||
### 数据库升级约定
|
||||
|
||||
- 唯一入口是 `prisma/migrations/` 下的迁移文件,由 `npx prisma migrate deploy` 在**启新容器之前**执行
|
||||
(幂等,无新迁移时为 no-op);迁移失败会中断 workflow,不会用新镜像顶掉仍在运行的旧容器。
|
||||
- 由于只有 `master` 会部署,**迁移真正作用到生产库的唯一时机就是 master 发布**;
|
||||
开发分支上的迁移不会被自动应用,请在合并到 `master` 前确认可回滚。
|
||||
- 本地改了 `prisma/schema.prisma` 后:写迁移 SQL → `npx prisma migrate deploy` 验证 → 提交迁移目录。
|
||||
|
||||
---
|
||||
|
||||
## Windows 本地实时预览(不打包)
|
||||
|
||||
改完前端 UI 想立刻在 **Windows 外壳**里看效果,不必 `tauri build`、不必打 NSIS/便携包、也不必重装。
|
||||
|
||||
```bash
|
||||
npm run win:dev # 构建(shell 模式) + 挂载 + 重启外壳
|
||||
npm run win:dev -- --no-build # 复用现有 dist, 只挂载+重启 (最快, 约 2 秒)
|
||||
npm run win:dev -- --no-launch # 只挂载, 自己手动启动外壳
|
||||
npm run win:dev -- --exe "D:\path\iBoard.exe"
|
||||
npm run win:dev -- --clear # 卸载覆盖, 外壳回到内置前端
|
||||
```
|
||||
|
||||
### 原理
|
||||
|
||||
外壳用 `tauri-plugin-hotswap`,启动时**先看覆盖目录有没有生效的前端包,没有才回退到 exe 内置的 `dist`**。
|
||||
这和线上 OTA 走的是同一条路径,所以只要把新构建的 `dist` 写进覆盖目录再重启外壳即可:
|
||||
|
||||
```
|
||||
%APPDATA%\com.iboard.app\hotswap\
|
||||
current ← 文本文件, 内容为生效的目录名 (如 seq-1793068631298)
|
||||
seq-1793068631298\ ← 本次前端 (dist 内容) + hotswap-meta.json
|
||||
```
|
||||
|
||||
`hotswap-meta.json` 里有两个字段是**硬约束**,改错会导致覆盖包被丢弃或立刻回滚:
|
||||
|
||||
| 字段 | 取值 | 原因 |
|
||||
| --- | --- | --- |
|
||||
| `min_binary_version` | **必须等于**外壳版本(当前 `0.2.1`) | 默认策略 `DiscardOnUpgrade` 在 `binary > min` 时丢弃缓存;安全线又要求 `binary >= min`,故只能取等号 |
|
||||
| `confirmed` | **必须为 `true`** | 默认策略 `SingleLaunch` 对未确认版本会在启动时立即回滚 |
|
||||
|
||||
另外 `sequence` 取 `Date.now()`(大值),让 OTA 检查认为"已是最新",避免线上更新把本地版本顶掉。
|
||||
|
||||
### 注意
|
||||
|
||||
- 覆盖目录就是外壳**真实的 OTA 落地目录**,挂载期间"我的"页会显示本地版本(`0.2.1-local`);
|
||||
测完建议 `npm run win:dev -- --clear` 复原,避免影响后续正常 OTA。
|
||||
- 脚本按 `src-tauri/tauri.conf.json` 的 `identifier` 推导覆盖目录,换 identifier 后无需改脚本。
|
||||
- 这只适用于**前端**改动。改了 `src-tauri/`(Rust/原生层、权限、CSP)必须重新打包外壳。
|
||||
- 想要带 HMR 的完整开发循环(改代码即时刷新、免手动重启)用 `npm run tauri:dev`,
|
||||
代价是首次要编译一次 debug 版 Rust 二进制。
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# 复制为 .env (服务器上 chmod 600, 已 gitignore)。
|
||||
# POWERSYNC_JWT_KEY_B64 = base64url(POWERSYNC_JWT_SECRET 的 UTF-8 字节)
|
||||
# 生成: printf %s "$POWERSYNC_JWT_SECRET" | openssl base64 -A | tr '+/' '-_' | tr -d '='
|
||||
|
||||
PG_PASSWORD=change-me
|
||||
PS_STORAGE_PASSWORD=change-me
|
||||
JWT_SECRET_021=change-me
|
||||
POWERSYNC_JWT_SECRET=change-me
|
||||
POWERSYNC_JWT_KEY_B64=change-me
|
||||
POWERSYNC_URL=https://bh.vps.honor3.com:13443
|
||||
OTA_PUBLIC_BASE=http://bh.vps.honor3.com:13001
|
||||
@@ -0,0 +1,61 @@
|
||||
# 0.2.1 独立验证部署 (baihu /app/iboard-0.2.1)
|
||||
|
||||
与现网 `/app/iboard`(3001/宿主 MySQL) 完全隔离:本栈使用独立 PG 容器、端口 13443/13001/13002,不写原库。
|
||||
|
||||
**浏览器入口: `https://bh.vps.honor3.com:13443`**(自签证书,首次访问需在浏览器"高级→继续访问"接受一次;PowerSync 网页端必须运行在安全上下文,http://13001 入口仅供接口调试,任务模块会报 Navigator locks 错误)。
|
||||
正式启用需替换受信证书:把 `bh.pem/bh.key` 换成受信证书后 `docker compose restart tls-front`(例如 Let's Encrypt DNS-01,80/443 已被 gateway 占用无法 HTTP-01)。
|
||||
|
||||
## 栈
|
||||
|
||||
| 服务 | 说明 |
|
||||
| --- | --- |
|
||||
| `pg` | postgres:16-alpine,`wal_level=logical`(供 PowerSync 逻辑复制),数据在 `./data/pg`,**不对外暴露端口** |
|
||||
| `app` | 本仓库 Dockerfile 构建,13001 对外;启动时先 `prisma migrate deploy`;`DISABLE_CRON=1` 关闭定时任务 |
|
||||
| `powersync` | journeyapps/powersync-service,13002 对外;源库=pg,bucket storage=同实例 PG 的 powersync schema |
|
||||
|
||||
## 首次部署
|
||||
|
||||
```bash
|
||||
# 服务器上 (源码已上传到 /app/iboard-0.2.1)
|
||||
cd /app/iboard-0.2.1/deploy/0.2.1
|
||||
|
||||
# 1. 生成 .env (参照 .env.example; POWERSYNC_JWT_KEY_B64 由 secret 派生)
|
||||
umask 077
|
||||
PG_PASSWORD=$(openssl rand -hex 16)
|
||||
PS_STORAGE_PASSWORD=$(openssl rand -hex 16)
|
||||
JWT_SECRET_021=$(openssl rand -hex 32)
|
||||
POWERSYNC_JWT_SECRET=$(openssl rand -hex 32)
|
||||
POWERSYNC_JWT_KEY_B64=$(printf %s "$POWERSYNC_JWT_SECRET" | openssl base64 -A | tr '+/' '-_' | tr -d '=')
|
||||
cat > .env <<EOF
|
||||
PG_PASSWORD=$PG_PASSWORD
|
||||
PS_STORAGE_PASSWORD=$PS_STORAGE_PASSWORD
|
||||
JWT_SECRET_021=$JWT_SECRET_021
|
||||
POWERSYNC_JWT_SECRET=$POWERSYNC_JWT_SECRET
|
||||
POWERSYNC_JWT_KEY_B64=$POWERSYNC_JWT_KEY_B64
|
||||
POWERSYNC_URL=http://bh.vps.honor3.com:13002
|
||||
OTA_PUBLIC_BASE=http://bh.vps.honor3.com:13001
|
||||
EOF
|
||||
|
||||
# 2. 构建并启动
|
||||
docker compose build app
|
||||
docker compose up -d
|
||||
|
||||
# 3. 验证
|
||||
docker compose ps
|
||||
curl -s http://127.0.0.1:13001/api/health
|
||||
docker compose logs powersync --tail 50 # 应看到连接源库/建 publication/sync config 部署成功
|
||||
```
|
||||
|
||||
## 日常更新
|
||||
|
||||
```bash
|
||||
cd /app/iboard-0.2.1
|
||||
git archive 方式重新上传源码 tar 后解包覆盖 (data/.env 不受影响)
|
||||
cd deploy/0.2.1 && docker compose build app && docker compose up -d app
|
||||
```
|
||||
|
||||
## 注意
|
||||
|
||||
- `./data/pg` 为 0.2.1 独立数据卷;`./.env` 为密钥,两者均已 gitignore,更新源码时不会覆盖 `.env`(tar 解包不产生它)。
|
||||
- PowerSync 的 publication / replication slot 由 service 自动管理(账号 iboard021 为容器内 superuser)。
|
||||
- 同步规则改动:编辑 `powersync/sync-config.yaml` 后 `docker compose restart powersync`。
|
||||
@@ -0,0 +1,84 @@
|
||||
name: iboard-021
|
||||
|
||||
# 0.2.1 独立验证栈: PG (逻辑复制) + PowerSync + App, 与现网 /app/iboard 完全隔离
|
||||
# 见 docs/V0.2/离线编辑模块/0.2.1实施方案.md; 端口 13001 (app) / 13002 (powersync), PG 不对外
|
||||
|
||||
services:
|
||||
pg:
|
||||
image: postgres:16-alpine
|
||||
restart: unless-stopped
|
||||
command: ["postgres", "-c", "wal_level=logical"]
|
||||
environment:
|
||||
POSTGRES_USER: iboard021
|
||||
POSTGRES_PASSWORD: ${PG_PASSWORD:?PG_PASSWORD missing}
|
||||
POSTGRES_DB: iboard021
|
||||
# initdb 脚本用: 创建 powersync bucket storage 用户
|
||||
PS_STORAGE_PASSWORD: ${PS_STORAGE_PASSWORD:?PS_STORAGE_PASSWORD missing}
|
||||
volumes:
|
||||
- ./data/pg:/var/lib/postgresql/data
|
||||
- ./initdb:/docker-entrypoint-initdb.d:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U iboard021 -d iboard021"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
|
||||
app:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: Dockerfile
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
pg:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DATABASE_URL: postgresql://iboard021:${PG_PASSWORD}@pg:5432/iboard021
|
||||
PORT: "13001"
|
||||
JWT_SECRET: ${JWT_SECRET_021:?JWT_SECRET_021 missing}
|
||||
POWERSYNC_JWT_SECRET: ${POWERSYNC_JWT_SECRET:?POWERSYNC_JWT_SECRET missing}
|
||||
POWERSYNC_URL: ${POWERSYNC_URL:-http://bh.vps.honor3.com:13002}
|
||||
OTA_PUBLIC_BASE: ${OTA_PUBLIC_BASE:-http://bh.vps.honor3.com:13001}
|
||||
# 0.2.1 验证部署关闭定时任务 (新闻/油价/米家等), 避免重复外呼
|
||||
DISABLE_CRON: "1"
|
||||
command: ["sh", "-c", "npx prisma migrate deploy && node --experimental-strip-types --no-warnings server/index.js"]
|
||||
ports:
|
||||
- "13001:13001"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:13001/api/health || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 20s
|
||||
|
||||
# TLS 前置 (自签证书): PowerSync 网页端必须在安全上下文运行 (navigator.locks),
|
||||
# 因此浏览器入口统一走 https://bh.vps.honor3.com:13443 (证书接受一次即可, app+sync 同源同证书)
|
||||
tls-front:
|
||||
image: nginx:alpine
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- app
|
||||
- powersync
|
||||
volumes:
|
||||
- ./tls/nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
- ./data/tls:/etc/nginx/certs:ro
|
||||
ports:
|
||||
- "13443:443"
|
||||
|
||||
powersync:
|
||||
image: journeyapps/powersync-service:latest
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
pg:
|
||||
condition: service_healthy
|
||||
command: ["start", "-r", "unified"]
|
||||
volumes:
|
||||
- ./powersync/service.yaml:/config/service.yaml:ro
|
||||
- ./powersync/sync-config.yaml:/config/sync-config.yaml:ro
|
||||
environment:
|
||||
POWERSYNC_CONFIG_PATH: /config/service.yaml
|
||||
# service.yaml 内 !env PS_* 引用 (整值替换; URI 在此组装)
|
||||
PS_SOURCE_URI: postgresql://iboard021:${PG_PASSWORD}@pg:5432/iboard021?sslmode=disable
|
||||
PS_STORAGE_URI: postgresql://powersync_storage:${PS_STORAGE_PASSWORD}@pg:5432/iboard021?sslmode=disable
|
||||
PS_JWT_KEY_B64: ${POWERSYNC_JWT_KEY_B64:?POWERSYNC_JWT_KEY_B64 missing}
|
||||
ports:
|
||||
- "13002:8080"
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
# 一次性 ETL: 清空 0.2.1 库 -> 从现网 MariaDB 只读快照导入
|
||||
# 现网连接串从 iboard 容器环境变量读取 (不回显), 源库仅 SELECT
|
||||
set -e
|
||||
cd /app/iboard-0.2.1/deploy/0.2.1
|
||||
SRC=$(docker inspect iboard --format '{{range .Config.Env}}{{println .}}{{end}}' | grep '^DATABASE_URL=' | cut -d= -f2-)
|
||||
[ -n "$SRC" ] || { echo '未取到现网 DATABASE_URL'; exit 1; }
|
||||
|
||||
echo '--- truncate 0.2.1 库 ---'
|
||||
docker compose exec -T pg psql -U iboard021 -d iboard021 -c "TRUNCATE TABLE \"User\",\"Project\",\"ProjectMember\",\"BoardColumn\",\"Task\",\"TaskMember\",\"TaskTag\",\"Habit\",\"CheckIn\",\"CheckList\",\"CheckListItem\",\"CheckListTemplate\",\"Article\",\"Strategy\",\"Indicator\",\"IndicatorPoint\",\"News\",\"Alert\",\"ServerNode\",\"ServerNodeHistory\",\"ServiceCheck\",\"Econ_SowInventory\",\"ScraperCache\",\"OilPriceHistory\",\"MihomeAccount\",\"MihomeHome\",\"MihomeDevice\",\"MihomeEnvSample\",\"MihomeEnvHour\" RESTART IDENTITY CASCADE;"
|
||||
|
||||
echo '--- etl run ---'
|
||||
docker compose exec -T -e SRC_DATABASE_URL="$SRC" app node scripts/etl-mariadb-to-pg.mjs
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
# 一次性初始化 (仅首次建库时执行): 创建 powersync bucket storage 专用用户
|
||||
# PG >= 14 允许源库与 bucket storage 同实例 (powersync schema 由 service 自建)
|
||||
set -e
|
||||
: "${PS_STORAGE_PASSWORD:?PS_STORAGE_PASSWORD missing}"
|
||||
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
DO \$\$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'powersync_storage') THEN
|
||||
CREATE USER powersync_storage WITH PASSWORD '$PS_STORAGE_PASSWORD';
|
||||
END IF;
|
||||
END
|
||||
\$\$;
|
||||
GRANT CREATE ON DATABASE $POSTGRES_DB TO powersync_storage;
|
||||
EOSQL
|
||||
|
||||
echo "[initdb] powersync_storage user ready."
|
||||
@@ -0,0 +1,10 @@
|
||||
-- 一次性初始化: PowerSync 逻辑复制所需 publication (服务不自建, 见 PSYNC_S1141)
|
||||
-- 仅发布任务域 + User; 新增同步表时需 ALTER PUBLICATION powersync ADD TABLE ...
|
||||
CREATE PUBLICATION powersync FOR TABLE
|
||||
"Project",
|
||||
"ProjectMember",
|
||||
"BoardColumn",
|
||||
"Task",
|
||||
"TaskMember",
|
||||
"TaskTag",
|
||||
"User";
|
||||
@@ -0,0 +1,41 @@
|
||||
# PowerSync Service 主配置 (0.2.1 验证部署)
|
||||
# 参考: https://docs.powersync.com/configuration/powersync-service/self-hosted-instances
|
||||
# 注意: !env 是 YAML 整值标签, 只能整个值替换变量, 不能内嵌在 URI 字符串中;
|
||||
# 完整连接串由 docker-compose 以 PS_* 环境变量注入。
|
||||
|
||||
replication:
|
||||
connections:
|
||||
- type: postgresql
|
||||
# 源库: 本栈 pg 容器 (0.2.1 独立实例), 复制走 iboard021 账号
|
||||
uri: !env PS_SOURCE_URI
|
||||
sslmode: disable
|
||||
|
||||
# bucket storage: 同实例 PG (16 >= 14 允许同库不同 schema, service 自建 powersync schema)
|
||||
storage:
|
||||
type: postgresql
|
||||
uri: !env PS_STORAGE_URI
|
||||
sslmode: disable
|
||||
|
||||
port: 8080
|
||||
|
||||
sync_config:
|
||||
path: /config/sync-config.yaml
|
||||
|
||||
# 客户端鉴权: app 后端 /api/powersync/token 签发 HS256 JWT (aud=powersync)
|
||||
# oct 密钥 k = base64url(POWERSYNC_JWT_SECRET)
|
||||
client_auth:
|
||||
jwks:
|
||||
keys:
|
||||
- kty: oct
|
||||
k: !env PS_JWT_KEY_B64
|
||||
alg: HS256
|
||||
audience:
|
||||
- powersync
|
||||
|
||||
telemetry:
|
||||
disable_telemetry_sharing: true
|
||||
|
||||
system:
|
||||
logging:
|
||||
level: info
|
||||
format: text
|
||||
@@ -0,0 +1,25 @@
|
||||
# Sync Streams (edition 3): 任务模块 6 表 + User (展示用)
|
||||
# Sequential ID Mapping (见 docs/V0.2/离线编辑模块/0.2.1实施方案.md §3):
|
||||
# - 客户端主键 = uuid 列 (离线新建行同步回填后 id 稳定); int 自增 id 不下发
|
||||
# - 外键输出 uuid 镜像列, 别名回 projectId/columnId/parentId/taskId (客户端字段名不变)
|
||||
# - 引号保留 PG PascalCase 标识; 可见性按 ProjectMember 过滤
|
||||
|
||||
config:
|
||||
edition: 3
|
||||
|
||||
streams:
|
||||
iboard_users:
|
||||
auto_subscribe: true
|
||||
query: SELECT CAST("id" AS TEXT) AS "id", "name", "email" FROM "User"
|
||||
|
||||
iboard_task_data:
|
||||
auto_subscribe: true
|
||||
with:
|
||||
my_projects: SELECT "projectId" FROM "ProjectMember" WHERE CAST("userId" AS TEXT) = auth.user_id()
|
||||
queries:
|
||||
- SELECT "uuid" AS "id", "uuid", "code", "name", CAST("ownerId" AS TEXT) AS "ownerId", "isPersonal", "archived", "notes", "taskKinds", "icon", "autoArchive", "createdAt", "updatedAt" FROM "Project" WHERE "id" IN my_projects
|
||||
- SELECT "uuid" AS "id", "uuid", "projectUuid" AS "projectId", CAST("userId" AS TEXT) AS "userId", "permission", "createdAt" FROM "ProjectMember" WHERE "projectId" IN my_projects
|
||||
- SELECT "uuid" AS "id", "uuid", "projectUuid" AS "projectId", "name", "sortOrder", "boundStatus", "retention", "createdAt", "updatedAt" FROM "BoardColumn" WHERE "projectId" IN my_projects
|
||||
- SELECT "uuid" AS "id", "uuid", "projectUuid" AS "projectId", "columnUuid" AS "columnId", "parentUuid" AS "parentId", "title", "description", "kind", "markDate", "status", "doneAt", CAST("assigneeId" AS TEXT) AS "assigneeId", CAST("creatorId" AS TEXT) AS "creatorId", "extras", "sortOrder", "number", "archived", "archivedAt", "archiveSource", "createdAt", "updatedAt" FROM "Task" WHERE "projectId" IN my_projects
|
||||
- SELECT "uuid" AS "id", "uuid", "taskUuid" AS "taskId", CAST("userId" AS TEXT) AS "userId" FROM "TaskMember" WHERE "taskId" IN (SELECT "id" FROM "Task" WHERE "projectId" IN my_projects)
|
||||
- SELECT "uuid" AS "id", "uuid", "taskUuid" AS "taskId", "name" FROM "TaskTag" WHERE "taskId" IN (SELECT "id" FROM "Task" WHERE "projectId" IN my_projects)
|
||||
@@ -0,0 +1,37 @@
|
||||
# 0.2.1 TLS 前置: 单端口 13443, 按路径分流 (自签证书, 见 README)
|
||||
# PowerSync 页面必须在安全上下文运行 (navigator.locks 仅 HTTPS/localhost), 故 app 与 sync 统一走 TLS
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name bh.vps.honor3.com;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/bh.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/bh.key;
|
||||
|
||||
# PowerSync (HTTP 流式 + WebSocket 兜底)
|
||||
location /sync/ {
|
||||
proxy_pass http://powersync:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection '';
|
||||
proxy_buffering off;
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
location /socket/ {
|
||||
proxy_pass http://powersync:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
location /health {
|
||||
proxy_pass http://powersync:8080;
|
||||
}
|
||||
|
||||
# 应用 (静态前端 + API)
|
||||
location / {
|
||||
proxy_pass http://app:13001;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# 生产环境变量 (deploy/prod/.env, 权限 600, 绝不入库)
|
||||
# POWERSYNC_JWT_KEY_B64 = base64url(POWERSYNC_JWT_SECRET 的 UTF-8 字节)
|
||||
# printf %s "$POWERSYNC_JWT_SECRET" | openssl base64 -A | tr '+/' '-_' | tr -d '='
|
||||
PG_PASSWORD=change-me
|
||||
PS_STORAGE_PASSWORD=change-me
|
||||
JWT_SECRET=change-me
|
||||
POWERSYNC_JWT_SECRET=change-me
|
||||
POWERSYNC_JWT_KEY_B64=change-me
|
||||
PUBLIC_URL=https://iboard.honor3.com
|
||||
@@ -0,0 +1,59 @@
|
||||
# iBoard 0.2.1 生产部署 (单端口, 无独立同步端口)
|
||||
|
||||
```
|
||||
pg (内网) ← app (3001 对外, /sync 反代) → powersync (内网)
|
||||
```
|
||||
|
||||
对外只有 **3001** 一个端口,域名 `https://iboard.honor3.com` 由 NPM 反代到这里;
|
||||
同步服务不再单独暴露端口(客户端拿到的 `POWERSYNC_URL` 是域名本身,请求经 app 的
|
||||
`/sync`、`/socket` 反代进入内网 powersync)。
|
||||
|
||||
## 首次部署顺序 (关键: publication 要在建表之后)
|
||||
|
||||
```bash
|
||||
# 0) 目录: /app/iboard-prod (源码已上传), 镜像 iboard:latest 已构建
|
||||
cd /app/iboard-prod/deploy/prod
|
||||
cp .env.example .env && chmod 600 .env # 填真实密钥
|
||||
mkdir -p data/pg data/cache
|
||||
|
||||
# 1) 先只起数据库
|
||||
docker compose --env-file .env up -d pg
|
||||
|
||||
# 2) 跑迁移建表 (一次性)
|
||||
docker run --rm --network iboard-prod_default \
|
||||
-e DATABASE_URL="postgresql://iboard:<PG_PASSWORD>@pg:5432/iboard" \
|
||||
iboard:latest npx prisma migrate deploy
|
||||
|
||||
# 3) 建逻辑复制 publication (表已存在)
|
||||
docker compose --env-file .env exec -T pg \
|
||||
psql -U iboard -d iboard -f /dev/stdin < sql/publication.sql
|
||||
|
||||
# 4) 起应用与同步服务
|
||||
docker compose --env-file .env up -d app powersync
|
||||
|
||||
# 5) 验证
|
||||
curl -s http://127.0.0.1:3001/api/health
|
||||
docker compose logs --tail 20 powersync # 应看到 replication 激活
|
||||
```
|
||||
|
||||
## 数据迁移 (从现网 MySQL 快照)
|
||||
|
||||
```bash
|
||||
# 只读源库; 目标必须是空库 (有数据会跳过, ETL_FORCE=1 强制)
|
||||
docker run --rm --network iboard-prod_default \
|
||||
-e DATABASE_URL="postgresql://iboard:<PG_PASSWORD>@pg:5432/iboard" \
|
||||
-e SRC_DATABASE_URL="mysql://<user>:<pass>@<host>:3306/iboard" \
|
||||
iboard:latest node scripts/etl-mariadb-to-pg.mjs
|
||||
```
|
||||
|
||||
迁移后需要重启 powersync 让它重新快照:
|
||||
|
||||
```bash
|
||||
docker compose --env-file .env restart powersync
|
||||
```
|
||||
|
||||
## 版本与回滚
|
||||
|
||||
- 新版本: 构建新镜像 → `docker compose up -d app` (只重建 app, 数据卷不动)
|
||||
- 回滚: 用上一次的镜像标签重新 `up -d app`
|
||||
- 数据: `data/pg` 持久化; 变更前建议 `docker compose exec -T pg pg_dump -U iboard iboard > backup.sql`
|
||||
@@ -0,0 +1,82 @@
|
||||
name: iboard-prod
|
||||
|
||||
# iBoard 0.2.1 生产栈 (单端口对外)
|
||||
# - app : 3001 对外 (唯一入口), 内部把 /sync 反代到 powersync
|
||||
# - pg : PostgreSQL 16, 仅容器内网 (逻辑复制供同步服务消费)
|
||||
# - powersync : 同步服务, 仅容器内网 (不对外暴露端口)
|
||||
#
|
||||
# 与旧的生产方式区别: 不再依赖宿主 MySQL, 数据库与同步服务都在栈内;
|
||||
# 对外只有 3001 一个端口, 域名 https://iboard.honor3.com (NPM 反代) 即可。
|
||||
#
|
||||
# 用法 (baihu:/app/iboard-prod):
|
||||
# cp deploy/prod/.env.example deploy/prod/.env # 填密钥
|
||||
# docker compose --env-file deploy/prod/.env -f deploy/prod/docker-compose.yml up -d
|
||||
|
||||
services:
|
||||
pg:
|
||||
image: postgres:16-alpine
|
||||
restart: unless-stopped
|
||||
command: ["postgres", "-c", "wal_level=logical"]
|
||||
environment:
|
||||
POSTGRES_USER: iboard
|
||||
POSTGRES_PASSWORD: ${PG_PASSWORD:?PG_PASSWORD missing}
|
||||
POSTGRES_DB: iboard
|
||||
PS_STORAGE_PASSWORD: ${PS_STORAGE_PASSWORD:?PS_STORAGE_PASSWORD missing}
|
||||
volumes:
|
||||
- ./data/pg:/var/lib/postgresql/data
|
||||
- ./initdb:/docker-entrypoint-initdb.d:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U iboard -d iboard"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
|
||||
app:
|
||||
image: iboard:latest
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: Dockerfile
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
pg:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DATABASE_URL: postgresql://iboard:${PG_PASSWORD}@pg:5432/iboard
|
||||
PORT: "3001"
|
||||
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET missing}
|
||||
POWERSYNC_JWT_SECRET: ${POWERSYNC_JWT_SECRET:?POWERSYNC_JWT_SECRET missing}
|
||||
# 客户端拿到的同步地址 = 同域 (经由 app 的 /sync 反代)
|
||||
POWERSYNC_URL: ${PUBLIC_URL:-https://iboard.honor3.com}
|
||||
# app 内部转发同步请求到内网 powersync
|
||||
POWERSYNC_UPSTREAM: http://powersync:8080
|
||||
OTA_PUBLIC_BASE: ${PUBLIC_URL:-https://iboard.honor3.com}
|
||||
OTA_PACKAGE_DIR: /app/ota-package
|
||||
MIHOME_CACHE_DIR: /app/cache
|
||||
volumes:
|
||||
- ./data/cache:/app/cache
|
||||
# OTA 热更包 (CI 构建产物, 由流水线写入宿主 data/ota)
|
||||
- ./data/ota:/app/ota-package
|
||||
ports:
|
||||
- "${HOST_PORT:-3001}:3001"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:3001/api/health || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 20s
|
||||
|
||||
powersync:
|
||||
image: journeyapps/powersync-service:latest
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
pg:
|
||||
condition: service_healthy
|
||||
command: ["start", "-r", "unified"]
|
||||
volumes:
|
||||
- ./powersync/service.yaml:/config/service.yaml:ro
|
||||
- ./powersync/sync-config.yaml:/config/sync-config.yaml:ro
|
||||
environment:
|
||||
POWERSYNC_CONFIG_PATH: /config/service.yaml
|
||||
PS_SOURCE_URI: postgresql://iboard:${PG_PASSWORD}@pg:5432/iboard?sslmode=disable
|
||||
PS_STORAGE_URI: postgresql://powersync_storage:${PS_STORAGE_PASSWORD}@pg:5432/iboard?sslmode=disable
|
||||
PS_JWT_KEY_B64: ${POWERSYNC_JWT_KEY_B64:?POWERSYNC_JWT_KEY_B64 missing}
|
||||
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
# 生产 PG 初始化: PowerSync bucket storage 专用用户
|
||||
set -e
|
||||
: "${PS_STORAGE_PASSWORD:?PS_STORAGE_PASSWORD missing}"
|
||||
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
DO \$\$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'powersync_storage') THEN
|
||||
CREATE USER powersync_storage WITH PASSWORD '$PS_STORAGE_PASSWORD';
|
||||
END IF;
|
||||
END
|
||||
\$\$;
|
||||
GRANT CREATE ON DATABASE $POSTGRES_DB TO powersync_storage;
|
||||
EOSQL
|
||||
|
||||
echo '[initdb] powersync_storage ready.'
|
||||
@@ -0,0 +1,41 @@
|
||||
# PowerSync Service 主配置 (0.2.1 验证部署)
|
||||
# 参考: https://docs.powersync.com/configuration/powersync-service/self-hosted-instances
|
||||
# 注意: !env 是 YAML 整值标签, 只能整个值替换变量, 不能内嵌在 URI 字符串中;
|
||||
# 完整连接串由 docker-compose 以 PS_* 环境变量注入。
|
||||
|
||||
replication:
|
||||
connections:
|
||||
- type: postgresql
|
||||
# 源库: 本栈 pg 容器 (0.2.1 独立实例), 复制走 iboard021 账号
|
||||
uri: !env PS_SOURCE_URI
|
||||
sslmode: disable
|
||||
|
||||
# bucket storage: 同实例 PG (16 >= 14 允许同库不同 schema, service 自建 powersync schema)
|
||||
storage:
|
||||
type: postgresql
|
||||
uri: !env PS_STORAGE_URI
|
||||
sslmode: disable
|
||||
|
||||
port: 8080
|
||||
|
||||
sync_config:
|
||||
path: /config/sync-config.yaml
|
||||
|
||||
# 客户端鉴权: app 后端 /api/powersync/token 签发 HS256 JWT (aud=powersync)
|
||||
# oct 密钥 k = base64url(POWERSYNC_JWT_SECRET)
|
||||
client_auth:
|
||||
jwks:
|
||||
keys:
|
||||
- kty: oct
|
||||
k: !env PS_JWT_KEY_B64
|
||||
alg: HS256
|
||||
audience:
|
||||
- powersync
|
||||
|
||||
telemetry:
|
||||
disable_telemetry_sharing: true
|
||||
|
||||
system:
|
||||
logging:
|
||||
level: info
|
||||
format: text
|
||||
@@ -0,0 +1,25 @@
|
||||
# Sync Streams (edition 3): 任务模块 6 表 + User (展示用)
|
||||
# Sequential ID Mapping (见 docs/V0.2/离线编辑模块/0.2.1实施方案.md §3):
|
||||
# - 客户端主键 = uuid 列 (离线新建行同步回填后 id 稳定); int 自增 id 不下发
|
||||
# - 外键输出 uuid 镜像列, 别名回 projectId/columnId/parentId/taskId (客户端字段名不变)
|
||||
# - 引号保留 PG PascalCase 标识; 可见性按 ProjectMember 过滤
|
||||
|
||||
config:
|
||||
edition: 3
|
||||
|
||||
streams:
|
||||
iboard_users:
|
||||
auto_subscribe: true
|
||||
query: SELECT CAST("id" AS TEXT) AS "id", "name", "email" FROM "User"
|
||||
|
||||
iboard_task_data:
|
||||
auto_subscribe: true
|
||||
with:
|
||||
my_projects: SELECT "projectId" FROM "ProjectMember" WHERE CAST("userId" AS TEXT) = auth.user_id()
|
||||
queries:
|
||||
- SELECT "uuid" AS "id", "uuid", "code", "name", CAST("ownerId" AS TEXT) AS "ownerId", "isPersonal", "archived", "notes", "taskKinds", "icon", "autoArchive", "createdAt", "updatedAt" FROM "Project" WHERE "id" IN my_projects
|
||||
- SELECT "uuid" AS "id", "uuid", "projectUuid" AS "projectId", CAST("userId" AS TEXT) AS "userId", "permission", "createdAt" FROM "ProjectMember" WHERE "projectId" IN my_projects
|
||||
- SELECT "uuid" AS "id", "uuid", "projectUuid" AS "projectId", "name", "sortOrder", "boundStatus", "retention", "createdAt", "updatedAt" FROM "BoardColumn" WHERE "projectId" IN my_projects
|
||||
- SELECT "uuid" AS "id", "uuid", "projectUuid" AS "projectId", "columnUuid" AS "columnId", "parentUuid" AS "parentId", "title", "description", "kind", "markDate", "status", "doneAt", CAST("assigneeId" AS TEXT) AS "assigneeId", CAST("creatorId" AS TEXT) AS "creatorId", "extras", "sortOrder", "number", "archived", "archivedAt", "archiveSource", "createdAt", "updatedAt" FROM "Task" WHERE "projectId" IN my_projects
|
||||
- SELECT "uuid" AS "id", "uuid", "taskUuid" AS "taskId", CAST("userId" AS TEXT) AS "userId" FROM "TaskMember" WHERE "taskId" IN (SELECT "id" FROM "Task" WHERE "projectId" IN my_projects)
|
||||
- SELECT "uuid" AS "id", "uuid", "taskUuid" AS "taskId", "name" FROM "TaskTag" WHERE "taskId" IN (SELECT "id" FROM "Task" WHERE "projectId" IN my_projects)
|
||||
@@ -0,0 +1,11 @@
|
||||
-- 生产 PG: PowerSync 逻辑复制 publication
|
||||
-- 必须在 prisma migrate deploy 建表之后执行 (initdb 阶段表还不存在, 故不放 initdb 目录)。
|
||||
-- 幂等: 已存在则跳过。
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_publication WHERE pubname = 'powersync') THEN
|
||||
CREATE PUBLICATION powersync FOR TABLE
|
||||
"Project", "ProjectMember", "BoardColumn", "Task", "TaskMember", "TaskTag", "User";
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
@@ -0,0 +1,82 @@
|
||||
-- iBoard 0.2.1: 外键 uuid 镜像列触发器
|
||||
-- 由 0.2.1 验证库导出; 服务端写入时自动维护 projectUuid/columnUuid/parentUuid/taskUuid,
|
||||
-- 同步流据此把镜像列以 projectId/columnId/parentId/taskId 的别名下发给客户端。
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.func_boardcolumn_uuid_map()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
BEGIN
|
||||
SELECT "uuid" INTO NEW."projectUuid" FROM "Project" WHERE "id" = NEW."projectId";
|
||||
IF NOT FOUND THEN RAISE EXCEPTION 'Project % 不存在', NEW."projectId"; END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$function$
|
||||
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.func_projectmember_uuid_map()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
BEGIN
|
||||
SELECT "uuid" INTO NEW."projectUuid" FROM "Project" WHERE "id" = NEW."projectId";
|
||||
IF NOT FOUND THEN RAISE EXCEPTION 'Project % 不存在', NEW."projectId"; END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$function$
|
||||
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.func_task_uuid_map()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
BEGIN
|
||||
SELECT "uuid" INTO NEW."projectUuid" FROM "Project" WHERE "id" = NEW."projectId";
|
||||
IF NOT FOUND THEN RAISE EXCEPTION 'Project % 不存在', NEW."projectId"; END IF;
|
||||
SELECT "uuid" INTO NEW."columnUuid" FROM "BoardColumn" WHERE "id" = NEW."columnId";
|
||||
IF NOT FOUND THEN RAISE EXCEPTION 'BoardColumn % 不存在', NEW."columnId"; END IF;
|
||||
IF NEW."parentId" IS NULL THEN
|
||||
NEW."parentUuid" := NULL;
|
||||
ELSE
|
||||
SELECT "uuid" INTO NEW."parentUuid" FROM "Task" WHERE "id" = NEW."parentId";
|
||||
IF NOT FOUND THEN RAISE EXCEPTION 'Task % 不存在', NEW."parentId"; END IF;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$function$
|
||||
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.func_taskmember_uuid_map()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
BEGIN
|
||||
SELECT "uuid" INTO NEW."taskUuid" FROM "Task" WHERE "id" = NEW."taskId";
|
||||
IF NOT FOUND THEN RAISE EXCEPTION 'Task % 不存在', NEW."taskId"; END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$function$
|
||||
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.func_tasktag_uuid_map()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
BEGIN
|
||||
SELECT "uuid" INTO NEW."taskUuid" FROM "Task" WHERE "id" = NEW."taskId";
|
||||
IF NOT FOUND THEN RAISE EXCEPTION 'Task % 不存在', NEW."taskId"; END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$function$
|
||||
|
||||
;
|
||||
|
||||
CREATE TRIGGER trg_boardcolumn_uuid_map BEFORE INSERT OR UPDATE ON public."BoardColumn" FOR EACH ROW EXECUTE FUNCTION func_boardcolumn_uuid_map();
|
||||
CREATE TRIGGER trg_projectmember_uuid_map BEFORE INSERT OR UPDATE ON public."ProjectMember" FOR EACH ROW EXECUTE FUNCTION func_projectmember_uuid_map();
|
||||
CREATE TRIGGER trg_task_uuid_map BEFORE INSERT OR UPDATE ON public."Task" FOR EACH ROW EXECUTE FUNCTION func_task_uuid_map();
|
||||
CREATE TRIGGER trg_taskmember_uuid_map BEFORE INSERT OR UPDATE ON public."TaskMember" FOR EACH ROW EXECUTE FUNCTION func_taskmember_uuid_map();
|
||||
CREATE TRIGGER trg_tasktag_uuid_map BEFORE INSERT OR UPDATE ON public."TaskTag" FOR EACH ROW EXECUTE FUNCTION func_tasktag_uuid_map();
|
||||
+379
@@ -0,0 +1,379 @@
|
||||
# 0.2.1 发布任务 — 目标与现状
|
||||
|
||||
> 最后更新:2026-10-27 08:20
|
||||
> 分支:`dev-0.2` 与 `master` 均已发布(`a1c267b`)
|
||||
> 状态:**全部完成** —— 生产已切 0.2.1 单端口架构,master 发布 CI 通过,Android APK 与 Windows 安装包均已产出
|
||||
|
||||
## 零、执行结果速览(2026-10-27 已完成)
|
||||
|
||||
| 项目 | 结果 |
|
||||
| --- | --- |
|
||||
| 生产地址 | `https://iboard.honor3.com` → baihu **3001**(NPM + 有效证书) |
|
||||
| 生产架构 | `iboard-prod-app-1`(3001 对外) + `iboard-prod-pg-1`(内网) + `iboard-prod-powersync-1`(内网) |
|
||||
| 端口 | **只剩 3001**;13001 / 13002 / 13443 全部下线 |
|
||||
| 数据迁移 | MySQL → PG 完成:Project 3、Task 25、BoardColumn 12、User 1、CheckIn 17、News 19、IndicatorPoint 245、Mihome 等全部迁入 |
|
||||
| 旧生产 | 容器 `iboard` 已 **stop(未删除)**,`docker start iboard` 可秒级回滚 |
|
||||
| 登录态 | `JWT_SECRET` 沿用 `iboard`,现有用户无需重新登录 |
|
||||
| 米家凭证 | `/app/iboard/cache` → `deploy/prod/data/cache`(`auth.txt` + `apizero/`) |
|
||||
| E2E 验证 | 在线:项目卡片 + `ADMIN-16`/`ADMIN-18` 编号;断网:徽标出现、任务页数据完整、无 fail fetch;恢复:徽标消失 |
|
||||
| master 发布 | 已推 `master`(`a1c267b`);CI `build-image` **10/10 步成功**(版本校验 / 前端 / OTA 签名包 / 镜像 / 部署步按设计跳过) |
|
||||
| Android | `android-1.0` CI **16/16 步成功**,APK 20MB(versionName 0.2.1)已发布到 Gitea packages |
|
||||
| Windows | 本地 `npm run build:win` 成功:NSIS 6.9MB + 便携 zip 7.9MB |
|
||||
| OTA 热更 | **已打通**:`https://iboard.honor3.com/api/ota/check/0` 返回 `0.2.1-ota`、`min_binary_version: 0.2.1`、签名与下载地址齐全 |
|
||||
|
||||
### 迁移过程中踩到并修掉的坑
|
||||
|
||||
1. `prisma/migrations/` 是 **MySQL 语法**(反引号),PG 无法 `migrate deploy` → 改用 `prisma db push --accept-data-loss` 按 schema 建表。
|
||||
2. `db push` 首次因参数不被识别而输出帮助(表结构停在旧 schema,缺 `autoArchive`)→ 去掉错误参数重跑,列已补齐。
|
||||
3. 新库缺 **uuid 镜像触发器**(`projectUuid`/`columnUuid`/`parentUuid`/`taskUuid`)→ 从 0.2.1 验证库导出 5 个函数 + 5 个触发器,落库并入库 `deploy/prod/sql/uuid-triggers.sql`。
|
||||
4. bash 把 `$$` 展开成 PID 导致 TRUNCATE 脚本报错 → 改用 SQL 文件传入。
|
||||
5. baihu 上 `docker build` 缺 buildx → 改用 `docker compose build`。
|
||||
6. **OTA 包此前完全没有**:手工构建的镜像内 `/app/ota-package` 只有 README(`ota-package/` 被 `.gitignore` 排除,CI 部署段又跳过),线上 OTA 接口返回空 → 已给 app 容器加 `data/ota` 挂载,并新增 CI 步骤用 `docker cp` 经 bind-mount 容器写入宿主(CI 在容器内无法直接写宿主文件)。
|
||||
7. CI 首次写入路径多写了一层 `data/ota`(与 compose 的 `deploy/prod/data/ota` 不一致)→ 路径已对齐。
|
||||
8. `OTA_MIN_BINARY` 由 `0.1.0` 提升到 **`0.2.1`**,防止旧外壳拉到新域名前端后被 CSP 拦截白屏。
|
||||
|
||||
---
|
||||
|
||||
## 一、任务目标(用户原始要求)
|
||||
|
||||
1. 域名 `iboard.honor3.com` 已指向 3000 端口、HTTPS 证书已配好 → 客户端全面切到该域名。
|
||||
2. **Windows 外壳 + 离线编辑功能正式合并到 `dev-0.2`**,完整版本号 **0.2.1**。
|
||||
3. 确保 **Android** 功能正常(通过 CI 编译验证,不在本地装 Android 工具链)。
|
||||
4. 确保 **CI/CD** 正常。
|
||||
5. Windows **本地打包正常**即可。
|
||||
6. `dev-0.2` 更新完成后 **通过 `master` 分支发布**。
|
||||
7. 追加要求:**不要再有 13002 这类独立端口**,所有版本合并成一套。
|
||||
|
||||
---
|
||||
|
||||
## 二、已完成
|
||||
|
||||
### 2.1 分支合并
|
||||
|
||||
| 内容 | 结果 |
|
||||
| --- | --- |
|
||||
| `dev-0.2.1`(离线编辑 + 项目编号 + PG/PowerSync)→ `dev-0.2` | 已合并 `e689e44` |
|
||||
| `dev-windows-0.1`(Windows 外壳:托盘/悬浮球/吸附/自启动)→ `dev-0.2` | 已按路径迁入 `eef2969` |
|
||||
| `dev-0.2` → `android-1.0`(触发 APK 构建) | 已合并 `4958359` |
|
||||
| 冲突处理 | `projects.js`/`tasks.js`/`api/task.js` 手工合并,**两边功能都保留**(`autoArchive` 归档 + `code` 编号) |
|
||||
| 归档功能补齐到本地库 | `src/api/task.js` 新增 `archive`/`unarchive`/`archivedList`;`schema.js` 加 `archived/archivedAt/archiveSource/autoArchive`;`sync-config.yaml` 与 upload 处理器同步 |
|
||||
| Prisma 客户端补齐 | dev-0.2 原来缺任务模块 6 个模型,已重新 generate `fc23919` |
|
||||
|
||||
### 2.2 版本与域名
|
||||
|
||||
- 版本统一 **0.2.1**:`package.json` / `src-tauri/tauri.conf.json` / `Cargo.toml` / `Cargo.lock`(`scripts/ci/check-version.mjs` 通过)
|
||||
- 域名切 `https://iboard.honor3.com`:
|
||||
- `src/api/client.js` 注释、`.env.shell`、`scripts/wsl/build-apk.sh`、`.gitea/workflows/android-build.yml`、`.gitea/workflows/docker-build.yml`、`Dockerfile`(`OTA_PUBLIC_BASE`)、`tauri.conf.json`(OTA endpoint + CSP)
|
||||
- 旧域名 `bh.vps.honor3.com:3001` 仅保留在 CSP 白名单里做滚动兼容
|
||||
|
||||
### 2.3 单端口架构(去掉 13002)
|
||||
|
||||
| 改动 | 位置 |
|
||||
| --- | --- |
|
||||
| Express 增加 `/sync`、`/socket` 同域反代到内网 powersync | `server/powersync-proxy.js` + `server/index.js`(挂在 `express.json()` 之前,支持长连接) |
|
||||
| 生产编排:app(3001 对外) + pg(内网) + powersync(内网) | `deploy/prod/docker-compose.yml` |
|
||||
| 生产部署脚本(幂等:迁移 → publication → 起栈 → 健康检查) | `scripts/deploy/prod.sh` |
|
||||
| PG 初始化(powersync_storage 用户) | `deploy/prod/initdb/01-powersync-storage.sh` |
|
||||
| 逻辑复制 publication(**建表后**执行) | `deploy/prod/sql/publication.sql` |
|
||||
| 部署文档 | `deploy/prod/README.md`、`.env.example` |
|
||||
| CI 部署段改为调用宿主脚本,未配 PG secrets 时自动跳过 | `.gitea/workflows/docker-build.yml` |
|
||||
|
||||
### 2.4 Windows 本地打包 — 通过 ✅
|
||||
|
||||
`npm run build:win` 全流程成功(本机需 Node 20.19+;已用便携 Node 20.18.1 之外的 Node 24 验证过 vite 构建):
|
||||
|
||||
| 产物 | 路径 | 大小 |
|
||||
| --- | --- | --- |
|
||||
| 安装包 | `artifacts/windows/iBoard_0.2.1_x64-setup.exe` | 6.9 MB |
|
||||
| 便携版 zip | `artifacts/windows/iBoard_0.2.1_x64-portable.zip` | 7.9 MB |
|
||||
|
||||
外壳能力(继承 dev-windows-0.1):托盘、悬浮球吸附、自动隐藏、单实例、自启动、窗口尺寸单值配置
|
||||
(`%APPDATA%\com.iboard.app\shell-config.json` 的 `window_width`,高度按 405:720 锁定推导)。
|
||||
|
||||
离线能力(继承 0.2.1):GET 响应本地缓存、同步凭证持久化(离线不清库)、左上角「离线」徽标。
|
||||
|
||||
### 2.5 Android CI — 通过 ✅
|
||||
|
||||
- 触发:`android-1.0` 分支 push(`4ded8e3`)
|
||||
- 结果:**16 个步骤全部 success**,`Build release APK` + `Publish APK to Gitea packages` 成功
|
||||
- 产物:`app-universal-release.apk`(20 MB),versionName = **0.2.1**
|
||||
已发布到 `http://git.honor3.com/api/packages/Fullstack/generic/iboard/1.0-4ded8e30/`
|
||||
- 首次失败原因:runner 宿主机(baihu)磁盘 **95%** → `No space left on device`;
|
||||
清理悬空镜像 + 构建缓存后释放 11.5 GB,重跑通过。**磁盘现已 60%(39 GB 可用)**
|
||||
|
||||
### 2.6 CI/CD 现状
|
||||
|
||||
| Workflow | 触发 | 状态 |
|
||||
| --- | --- | --- |
|
||||
| `docker-build.yml` | `master` push | 改造完成,未在 master 上验证(部署段会因缺 secrets 跳过) |
|
||||
| `android-build.yml` | `android-*` push | **已验证通过** |
|
||||
| `ota-build.yml` / `sync-registry.yml` | 参照原逻辑 | 未改动 |
|
||||
|
||||
---
|
||||
|
||||
## 三、当前卡点(生产切换未执行)
|
||||
|
||||
生产镜像 **已构建成功**:`iboard:latest` = `db6b27b851b8`(924 MB,2026-10-26 22:53)
|
||||
|
||||
baihu 上当前跑着两套:
|
||||
|
||||
| 容器 | 用途 | 端口 |
|
||||
| --- | --- | --- |
|
||||
| `iboard`(旧) | **现网生产**,MySQL(宿主 3306) | 3001 |
|
||||
| `iboard-021-app-1` 等 4 个 | 0.2.1 验证栈 | 13001 / 13002 / 13443 |
|
||||
| (未启动)`iboard-prod` | **新生产栈**(PG + PowerSync + app) | 3001(计划) |
|
||||
|
||||
`/app/iboard-prod` 已上传源码、`deploy/prod/.env` 已生成(6 个密钥,600 权限)。
|
||||
|
||||
**剩余步骤(约 4 步)**:
|
||||
|
||||
1. 起新栈(先用 `HOST_PORT=13011` 灰度,不动现网)
|
||||
2. 数据迁移:现网 MySQL → 新 PG(`scripts/etl-mariadb-to-pg.mjs`,只读源库)
|
||||
3. 验证:健康检查、登录、任务列表、离线编辑(断网后仍可读)、同步回流
|
||||
4. 切换:停 `iboard` 旧容器 → 新栈改绑 3001 → 域名 `iboard.honor3.com` 生效 → 下线验证栈(13001/13002/13443)
|
||||
|
||||
然后 `dev-0.2` → `master` 合并推送,触发 CI 发布。
|
||||
|
||||
---
|
||||
|
||||
## 四、需要确认的信息(阻塞项)
|
||||
|
||||
1. **Gitea Actions Secrets 是否要配 PG 密钥?**
|
||||
需配:`PG_PASSWORD`、`PS_STORAGE_PASSWORD`、`JWT_SECRET`、`POWERSYNC_JWT_SECRET`、`POWERSYNC_JWT_KEY_B64`。
|
||||
不配的话:master 的 CI 只构建镜像不部署,生产靠手工执行 `scripts/deploy/prod.sh`(当前默认路径)。
|
||||
|
||||
2. **生产数据迁移的时间窗口**:迁移需短暂停写(现网 MySQL 只读快照)。是否可以挑一个低峰时间、期间 (~10 分钟) 站点只读?
|
||||
|
||||
3. **现网 MySQL 里的米家凭证/缓存**:`/app/iboard/cache`(`auth.txt` 等)是否要一并搬到新栈的 `deploy/prod/data/cache`?
|
||||
|
||||
4. **用户密码**:迁移只搬数据,`JWT_SECRET` 若换新,所有人需重新登录;是否沿用现网 `JWT_SECRET`(`iboard`)以保持登录态?**建议换新**(当前值过弱)。
|
||||
|
||||
5. **域名端口说明**:`iboard.honor3.com` 实际反代到 baihu **3001**(NPM 配置:`114.134.187.170:3001`,SSL 强制)。
|
||||
你提到"已转向 3000 端口"——请确认指的是 `honor3.com` 根域(→ homepage)而非 `iboard` 子域,
|
||||
否则需要调整 NPM 的 forward_port。
|
||||
|
||||
---
|
||||
|
||||
## 五、关键路径与命令速查
|
||||
|
||||
```bash
|
||||
# 仓库
|
||||
C:\Users\Rabbit\Documents\workbetch\iboard # dev-0.2 主工作区
|
||||
C:\Users\Rabbit\Documents\workbetch\iboard-dev-0.2.1 # 研究分支 worktree
|
||||
C:\Users\Rabbit\Documents\workbetch\iboard-dev-windows # Windows 外壳 worktree
|
||||
|
||||
# 服务器
|
||||
ssh root@baihu # 生产 + CI runner(baihu 上跑 act_runner,注册名 baishe)
|
||||
ssh root@baishe # 反代(NPM)、Gitea、homepage
|
||||
|
||||
# 生产目录
|
||||
/app/iboard-prod/deploy/prod/{docker-compose.yml,.env,data/,powersync/,sql/,initdb/}
|
||||
|
||||
# 常用
|
||||
docker compose -f /app/iboard-prod/deploy/prod/docker-compose.yml --env-file /app/iboard-prod/deploy/prod/.env ps
|
||||
bash /app/iboard-prod/scripts/deploy/prod.sh
|
||||
curl -s https://iboard.honor3.com/api/health
|
||||
```
|
||||
|
||||
## 五之二、后续动作执行步骤(已确认方案,按序执行)
|
||||
|
||||
### 决策确认(2026-10-27)
|
||||
|
||||
| 项 | 决定 |
|
||||
| --- | --- |
|
||||
| PG 密钥 | **不进 Gitea Secrets**,直接写在服务器 `/app/iboard-prod/deploy/prod/.env`(600) |
|
||||
| 迁移时机 | 立即执行 |
|
||||
| 米家凭证 | 保留,`/app/iboard/cache` 搬到新栈 `deploy/prod/data/cache` |
|
||||
| `JWT_SECRET` | 沿用现网值(`iboard`),保持现有登录态 |
|
||||
| 域名 | `iboard.honor3.com` → baihu **3001**(NPM 已配好,SSL 强制) |
|
||||
|
||||
### 步骤 A:准备(不改动现网)
|
||||
|
||||
```bash
|
||||
# A1. 沿用现网 JWT_SECRET (其余密钥保持已生成的随机值)
|
||||
cd /app/iboard-prod/deploy/prod
|
||||
sed -i 's/^JWT_SECRET=.*/JWT_SECRET=iboard/' .env
|
||||
|
||||
# A2. 搬米家缓存 (auth.txt 等)
|
||||
mkdir -p data/cache
|
||||
rsync -a /app/iboard/cache/ data/cache/
|
||||
|
||||
# A3. 保留旧镜像用于回滚
|
||||
docker tag iboard:latest iboard:mysql-final # 需在重新构建前执行; 若已覆盖见"回滚预案"
|
||||
```
|
||||
|
||||
### 步骤 B:起灰度栈(不动现网 3001)
|
||||
|
||||
```bash
|
||||
cd /app/iboard-prod/deploy/prod
|
||||
HOST_PORT=13011 docker compose --project-name iboard-prod \
|
||||
--env-file .env -f docker-compose.yml up -d pg
|
||||
|
||||
# 迁移建表
|
||||
docker run --rm --network iboard-prod_default \
|
||||
-e DATABASE_URL="postgresql://iboard:$(grep ^PG_PASSWORD .env | cut -d= -f2)@pg:5432/iboard" \
|
||||
iboard:latest npx prisma migrate deploy
|
||||
|
||||
# 建 publication (必须在建表之后)
|
||||
docker compose --project-name iboard-prod --env-file .env -f docker-compose.yml exec -T pg \
|
||||
psql -U iboard -d iboard -v ON_ERROR_STOP=1 -f /dev/stdin < sql/publication.sql
|
||||
|
||||
# 起 app + powersync (只有 13011 对外)
|
||||
HOST_PORT=13011 docker compose --project-name iboard-prod \
|
||||
--env-file .env -f docker-compose.yml up -d app powersync
|
||||
|
||||
# 验收
|
||||
curl -s http://127.0.0.1:13011/api/health # {"ok":true,...}
|
||||
ss -tlnp | grep -E ':(13011|5432|8080)\b' # 只应看到 13011
|
||||
```
|
||||
|
||||
### 步骤 C:数据迁移(MySQL → PG)
|
||||
|
||||
```bash
|
||||
docker run --rm --network iboard-prod_default \
|
||||
-e DATABASE_URL="postgresql://iboard:<PG_PASSWORD>@pg:5432/iboard" \
|
||||
-e SRC_DATABASE_URL="mysql://root:<MYSQL_PASSWORD>@<host>:3306/iboard" \
|
||||
iboard:latest node scripts/etl-mariadb-to-pg.mjs
|
||||
|
||||
# 迁移后重启同步服务重建快照
|
||||
docker compose --project-name iboard-prod --env-file .env -f docker-compose.yml restart powersync
|
||||
```
|
||||
|
||||
**验收**:项目 3 个(ADMIN / IBOARD / P3F921)、任务 ~22 条、用户 1 个(admin@iboard.local);
|
||||
`docker compose logs powersync` 显示 replication 已激活且无报错。
|
||||
|
||||
### 步骤 D:功能验证(13011)
|
||||
|
||||
- 登录 `admin@iboard.local`
|
||||
- 任务页:项目卡片、任务编号(如 `ADMIN-16`)
|
||||
- **离线验证**:断网后列表仍可读、左上角出现「离线」徽标,恢复网络自动同步回流
|
||||
- 监控 / 宏观 / 打卡页有数据
|
||||
|
||||
### 步骤 E:切换生产
|
||||
|
||||
```bash
|
||||
# E1. 停旧容器 (MySQL 版)
|
||||
docker rm -vf iboard || true
|
||||
|
||||
# E2. 新栈改绑 3001
|
||||
cd /app/iboard-prod/deploy/prod
|
||||
HOST_PORT=3001 docker compose --project-name iboard-prod \
|
||||
--env-file .env -f docker-compose.yml up -d app
|
||||
curl -s https://iboard.honor3.com/api/health # 经 NPM + 证书
|
||||
|
||||
# E3. 下线 0.2.1 验证栈 (释放 13001/13002/13443)
|
||||
cd /app/iboard-0.2.1/deploy/0.2.1 && docker compose down
|
||||
```
|
||||
|
||||
**验收**:`https://iboard.honor3.com` 可登录、任务正常、离线可编辑;
|
||||
`ss -tlnp` 只剩 3001(外加仅供本机的 3306)。
|
||||
|
||||
### 步骤 F:发布
|
||||
|
||||
```bash
|
||||
git checkout master && git merge dev-0.2 --no-edit
|
||||
git push origin master # 触发 CI: 构建镜像 + OTA
|
||||
```
|
||||
|
||||
CI 未配 PG secrets → 只构建不部署;生产已由步骤 B~E 手工就绪。
|
||||
|
||||
### 回滚预案
|
||||
|
||||
| 场景 | 动作 |
|
||||
| --- | --- |
|
||||
| 新栈起不来 | 旧容器未停,现网不受影响,回步骤 B 排查 |
|
||||
| 迁移数据不对 | 清空 `deploy/prod/data/pg` 后重跑 ETL |
|
||||
| 切换后严重故障 | **`docker start iboard`**(旧容器仍在,见 6.2 警告:旧镜像已被 prune,不能重建) |
|
||||
|
||||
## 六、遗留清单与清理建议(2026-10-27 实测)
|
||||
|
||||
### 6.1 本机(Windows)
|
||||
|
||||
| 路径 | 占用 | 建议 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `workbetch/iboard`(主 worktree) | 3.81 GB | **保留** | 含 `node_modules` 0.49 GB + `src-tauri/target` 2.56 GB |
|
||||
| `workbetch/iboard-dev-0.2.1` | 0.53 GB | **可删** | 内容已全部并入 `dev-0.2` |
|
||||
| `workbetch/iboard-dev-windows` | 8.88 GB | **可删** | 同上,最大占用(含 Rust 编译产物) |
|
||||
| `iboard/src-tauri/target` | 2.56 GB | 可选 `cargo clean` | 删后下次打包需重编译(约 8 分钟) |
|
||||
| `iboard/artifacts/windows` | 32 MB | 保留最新安装包 | 0.2.1 exe/zip |
|
||||
| `%TEMP%\node-portable` | 78 MB | **保留** | 本机无系统 Node/npm,`npm run build:win` 依赖它 |
|
||||
| `%TEMP%\esbuild-*`(4 个) | 8 MB | 可删 | 构建残留 |
|
||||
| `%TEMP%\iboard-e2e`、`iboard-shell-offline` | < 1 MB | 可删 | 验证截图 |
|
||||
| `%TEMP%\edge-e2e-profile` | ~100 MB | 可删 | 无头浏览器测试配置 |
|
||||
|
||||
```bash
|
||||
# 删除 worktree 与已合并分支(内容已在 dev-0.2)
|
||||
git worktree remove ../iboard-dev-0.2.1
|
||||
git worktree remove ../iboard-dev-windows
|
||||
git branch -d dev-0.2.1 dev-windows-0.1
|
||||
git branch -D tmp-offline-edit
|
||||
```
|
||||
|
||||
### 6.2 服务器 baihu
|
||||
|
||||
| 路径/对象 | 占用 | 建议 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `/app/iboard-prod` | 72 MB | **保留** | 当前生产 |
|
||||
| `/app/iboard` | 24 KB | 暂留 → 可删 | 旧生产只剩 `cache/`(`auth.txt` 已复制到新栈)+ `start.sh` |
|
||||
| `/app/iboard-0.2.1` | 71 MB | **可删** | 验证栈已 down;其中 `deploy/0.2.1/data` 67 MB 是废弃 PG 数据 |
|
||||
| `/app/cicd` | 36 KB | **保留** | act_runner 配置 |
|
||||
| 容器 `iboard`(Exited) | — | **暂留** | 当前唯一回滚手段(见下方警告) |
|
||||
| 镜像 `iboard-021-app:latest` | 924 MB | **可删** | 验证栈镜像 |
|
||||
| 镜像 `iboard:latest` | 934 MB | 保留 | CI 构建产物 |
|
||||
| 悬空镜像 + 构建缓存 | 18.7 GB + 2.4 GB | 可清 | `docker image prune -f && docker builder prune -af` |
|
||||
|
||||
> **⚠ 回滚能力警告(实测更正)**
|
||||
> 原计划保留的 `iboard:mysql-final` 旧镜像**已被 `docker image prune` 清除** —— 旧镜像失去 tag 后成为悬空镜像被回收。
|
||||
> 现在唯一回滚途径是重启仍存在的旧容器 `iboard`(镜像层随容器保留):
|
||||
>
|
||||
> ```bash
|
||||
> docker stop iboard-prod-app-1
|
||||
> docker start iboard # 旧 MySQL 版立即恢复 3001
|
||||
> ```
|
||||
>
|
||||
> **在再次执行 `docker image prune` 前,务必先确认旧容器仍在**;容器一旦删除,MySQL 版镜像只能从 git 历史重新构建。
|
||||
|
||||
### 6.3 代码与其他
|
||||
|
||||
- 临时测试脚本(`scripts/_prod-e2e.mjs`、`_browser-test.mjs`、`_shell-offline-test.mjs`)含真实口令,已加入 `.gitignore`(`scripts/_*.mjs`),**不要入库**
|
||||
- `0.2.1` 验证库里的测试任务(如 `浏览器验证任务`)已按 MySQL 现状一并迁入 PG,如需清理直接在 PG 删除
|
||||
- `prisma/migrations/` 仍是 MySQL 语法,**新环境建表必须用 `prisma db push --accept-data-loss`**(已写入 `deploy/prod/README.md`)
|
||||
- `JWT_SECRET` 仍为弱值(`iboard`),建议择机换新(换新需全员重新登录)
|
||||
- `node_modules` 在多个 Node 版本间切换过,遇 esbuild 权限报错时清空重装
|
||||
|
||||
## 七、外壳升级策略:必须整包升级,不能只靠 OTA
|
||||
|
||||
### 结论
|
||||
|
||||
| 平台 | 结论 | 关键原因 |
|
||||
| --- | --- | --- |
|
||||
| Windows 外壳(0.1.0) | **必须重装 exe** | ① CSP 白名单编译进原生二进制,0.1.0 不含 `https://iboard.honor3.com`;② 旧外壳 OTA 检查地址指向 `13001`,**该端口已下线**,连检查都失败 |
|
||||
| Android(0.1.0 APK) | **必须换装 APK** | CSP 同样是原生配置;旧 APK 的 OTA 端点(3001)虽然有效,但拉到 0.2.1 前端会被 CSP 拦截 → 白屏 |
|
||||
|
||||
### 为什么 OTA 不够
|
||||
|
||||
`tauri-plugin-hotswap` 的 OTA 包内容只有 `bundle.tar.gz`(即 `dist/` 前端资源),**不触碰原生层**:
|
||||
|
||||
| 配置项 | 位置 | OTA 能否修改 |
|
||||
| --- | --- | --- |
|
||||
| CSP 白名单(`connect-src`) | `tauri.conf.json` → 编译期 `generate_context!` 注入二进制 | ❌ |
|
||||
| OTA 检查 endpoint | 同上 | ❌ |
|
||||
| 窗口尺寸/最小尺寸 | 同上(另可被 `shell-config.json` 覆盖) | ❌ |
|
||||
| 托盘 / 悬浮球 / 自启动 | Rust `src-tauri/src/shell/*` | ❌ |
|
||||
| 页面 JS/CSS/WASM(含离线编辑、徽标、编号展示) | `dist/` | ✅ |
|
||||
|
||||
因此旧外壳无法通过 OTA 获得访问新域名的能力。
|
||||
|
||||
### 已采取的保护措施
|
||||
|
||||
CI 的 `OTA_MIN_BINARY` 已从 `0.1.0` 提升到 **`0.2.1`**:
|
||||
|
||||
- 旧外壳(0.1.0 < 0.2.1)会被 hotswap 判定为不兼容 → **拒绝接收新前端**
|
||||
- 旧外壳继续运行旧前端,仍访问 `http://bh.vps.honor3.com:3001`(该地址现指向新栈,`/api` 完全兼容)
|
||||
- 结果:**旧外壳不会白屏**,只是缺少离线编辑、项目编号等 0.2.1 新能力
|
||||
|
||||
### 升级后
|
||||
|
||||
- 新外壳版本 0.2.1,CSP 含 `https://iboard.honor3.com`,OTA 恢复正常(后续纯前端改动可继续走 OTA)
|
||||
- Windows 窗口尺寸通过 `%APPDATA%\com.iboard.app\shell-config.json` 的 `window_width` 单值配置
|
||||
|
||||
@@ -155,6 +155,9 @@
|
||||
| T-TK-11 | `TaskMember` 关联表 | 多对多,`@@unique([taskId,userId])` 去重 |
|
||||
| T-TK-12 | `TaskTag` 表 + 卡片标签渲染 | 多标签,展示于卡片/条目 |
|
||||
| T-TK-13 | 服务端维护 | `creatorId`/`createdAt` 不可改;`updatedAt` 自动刷新 |
|
||||
| T-TK-14 | 标题 30 字上限 + 快速添加截断 | 服务端 create/update 校验 `title ≤ 30`;前端 `quickAdd.js#splitTitle`:≤30 原样,超长在 30 字内(≥8 字)找最后一个分隔符截断,前段为标题、**完整原文**写入 `description`;快速添加输入框 `maxLength=200` |
|
||||
| T-TK-15 | `Task.markDate DATETIME(3) NULL`(迁移 20261025120000) | create/update 接受 `YYYY-MM-DD`(空/null 清除),完整日期存储;详情页 DatePicker(precision=day)选择/清除;卡片展示 `MM-dd` 小标签(`date-tag`) |
|
||||
| T-TK-16 | `quickAdd.js#parseTimePrefix` | 前缀词表:今天/明天/后天/本周/下周/本月/下月(须后跟分隔符或结束);今天=当日,明天/后天=+1/+2,本周=本周日、下周=下周日(周一为一周开始),本月/下月=当月/下月最后一天;命中后前缀移出标题并写入 `markDate` |
|
||||
|
||||
---
|
||||
|
||||
@@ -316,7 +319,63 @@
|
||||
|
||||
---
|
||||
|
||||
## 15. 覆盖自检表
|
||||
## 15. 任务归档(T-ARC)
|
||||
|
||||
> 归档与 T-FL 显示控制的分层:**隐藏 = 展示过滤(可逆、不落库)**;**归档 = 移出主工作集(落库、可恢复)**。
|
||||
> 自动归档的倒计时起点是任务**被隐藏的时刻**(由 `doneAt + 列保留窗口` 推导,不新增 hiddenAt 字段)。
|
||||
|
||||
### 15.1 数据
|
||||
|
||||
| 落点 | 说明 |
|
||||
| --- | --- |
|
||||
| `Project.autoArchive VARCHAR(8) NOT NULL DEFAULT 'never'` | 自动归档档位:`3d` / `1w` / `1m` / `never`;项目级共享(同 T-FL-12 口径) |
|
||||
| `Task.archived BOOLEAN NOT NULL DEFAULT false` | 归档标记;归档**不迁移表**,主查询统一追加 `archived = false` |
|
||||
| `Task.archivedAt DATETIME(3) NULL` | 归档时刻(查询过滤与排序键) |
|
||||
| `Task.archiveSource VARCHAR(8) NULL` | `'manual'` / `'auto'`,溯源用 |
|
||||
| 索引 | `idx_task_project_archived (projectId, archived, archivedAt)`;`idx_task_project_status_archived (projectId, status, archived)` |
|
||||
| 迁移(待实施) | `20261026000000_task_archive`:三列 + 两索引(MySQL DDL 见数据库设计文档 §3.1/§3.4) |
|
||||
|
||||
### 15.2 API
|
||||
|
||||
| 接口 | 说明 |
|
||||
| --- | --- |
|
||||
| `POST /api/tasks/:id/archive` | 手动归档:父任务连带全部后代置 `archived/archivedAt/archiveSource='manual'`;幂等(已归档直接返回当前态) |
|
||||
| `POST /api/tasks/:id/unarchive` | 恢复:父任务连带后代清除归档三字段;原列因 `ON DELETE RESTRICT` 必存在,状态保持不变 |
|
||||
| `GET /api/projects/:id/tasks/archived?keyword&kind&columnId&from&to&cursor&limit` | 归档查询:`keyword` 标题 LIKE、`kind`、`columnId`、`from/to`(归档日期,含边界);`limit` 默认 20、≤100;游标 = `archivedAt+id` 复合降序,返回 `{ items, nextCursor }`,`nextCursor` 为空即到底 |
|
||||
| `PUT /api/projects/:id` | 增接受 `autoArchive`,枚举校验(非法 400) |
|
||||
| 既有查询 | `GET /projects/:id/tasks`、统计、列计数等全部追加 `archived: false`;`taskCount`(projectSummary)同口径 |
|
||||
|
||||
### 15.3 自动归档执行
|
||||
|
||||
- **归档到期判定**:`due = 隐藏时刻 + autoArchive 档位`;隐藏时刻 = `doneAt + 列保留窗口`(复用 `retentionStart` 口径,含 `today/3d/week/month`;`forever` 列任务永不隐藏、不进入自动归档)。
|
||||
- **执行时机(双保险)**:
|
||||
1. **读时惰性**:加载项目任务时对该项目先执行一轮到期归档(保证用户看到的数据与判定一致,刚到期的任务当场消失);
|
||||
2. **每小时 cron 兜底**:扫描全部 `autoArchive != 'never'` 项目的到期 done 任务批量归档(`archiveSource='auto'`)。
|
||||
- **幂等**:`archived = false` 且 `status = 'done'` 且 `doneAt` 非空才进入判定;重复执行无副作用。
|
||||
- **批量写**:cron 用 `updateMany`(按到期集合),读时惰性用单项目范围 `updateMany`。
|
||||
|
||||
### 15.4 前端
|
||||
|
||||
| 落点 | 说明 |
|
||||
| --- | --- |
|
||||
| 任务 `⋯` 菜单 | 新增「归档」(`Dialog.confirm`,含子任务时提示连带条数);成功后列表/看板乐观移除 |
|
||||
| 项目管理 | 「归档设置」分区:`Selector` 四档(3 天后/一周后/一月后/永不),保存即生效 |
|
||||
| 项目视图头部 | 项目栏新增「已归档」入口 → 全屏弹层:过滤条(**关键词 / 看板列 / 归档时段**;时段含「本周 / 本月 / 本季度」快捷项与自定义区间;条件编辑后点「筛选」统一提交,另有「重置」;**不提供类型过滤**)+ 已应用条件标签(可单独撤销)与命中条数 + 分页「加载更多」+ 行内「恢复 / 删除」 |
|
||||
| 主视图 | 列表/看板无需感知归档(数据源已排除);恢复后经 `load()` 回到原位 |
|
||||
|
||||
### 15.5 边界
|
||||
|
||||
| 场景 | 处理 |
|
||||
| --- | --- |
|
||||
| 归档父任务时新增/恢复子任务 | 子任务随父判定:父 archived 则子不单独出现在主列表(查询按父排除) |
|
||||
| 自动归档期间用户正在编辑该任务 | 详情弹层保存正常(归档不锁写);保存后任务仍在归档集 |
|
||||
| 恢复后任务再次到期隐藏 | 重新进入自动归档倒计时(`doneAt` 未变则可能立即再次到期,属预期) |
|
||||
| 「永不」档位 | 不做任何自动归档;手动归档不受影响 |
|
||||
| 拷贝任务 | 拷贝件不继承归档态(`copyTask` 不复制 archived 三字段) |
|
||||
|
||||
---
|
||||
|
||||
## 16. 覆盖自检表
|
||||
|
||||
| 模块 | 编号范围 | 条数 | 本文对应章节 |
|
||||
| --- | --- | --- | --- |
|
||||
@@ -334,6 +393,7 @@
|
||||
| 数据需求 | T-DT-01 ~ 09 | 9 | §12 |
|
||||
| 非功能需求 | T-NF-01 ~ 05 | 5 | §13 |
|
||||
| 异常与边界 | T-EX-01 ~ 19 | 19 | §14 |
|
||||
| **合计** | — | **149** | — |
|
||||
| 任务归档 | T-ARC-01 ~ 08 | 8 | §15 |
|
||||
| **合计** | — | **157** | — |
|
||||
|
||||
> 需求文档共 **149 条**需求点,本文逐条给出设计落点,覆盖率 **149/149**。(其中 `T-BD-14~16`、`T-COL-*`、`T-EX-18~19`、`T-VW-08~10`、`T-PJ-11/12`、`T-UI-11` 为评审后新增,编号不连续,非笔误。)
|
||||
> 需求文档共 **157 条**需求点,本文逐条给出设计落点,覆盖率 **157/157**。(其中 `T-BD-14~16`、`T-COL-*`、`T-EX-18~19`、`T-VW-08~10`、`T-PJ-11/12`、`T-UI-11`、`T-ARC-*` 为评审后新增,编号不连续,非笔误。)
|
||||
|
||||
@@ -93,6 +93,10 @@
|
||||
| `ownerId` | `INT` | 否 | — | 创建人(可退出项目,故与成员表分离) |
|
||||
| `isPersonal` | `BOOLEAN` | 否 | `false` | 个人默认项目:不可删、不可加成员(T-PJ-02/03) |
|
||||
| `archived` | `BOOLEAN` | 否 | `false` | 归档(T-PJ-09) |
|
||||
| `notes` | `JSON` | 否 | `[]` | 项目信息块 `[{title, content}]`(T-PJ-13) |
|
||||
| `taskKinds` | `JSON` | **是** | `NULL` | 任务类型标签列表;`NULL` 时服务端回落默认四类(T-TK-14) |
|
||||
| `icon` | `TEXT` | **是** | `NULL` | 项目图标 data URL(客户端裁剪 96x96 JPEG) |
|
||||
| `autoArchive` | `VARCHAR(8)` | 否 | `'never'` | 自动归档档位:`3d`/`1w`/`1m`/`never`(T-ARC-02) |
|
||||
| `createdAt` | `DATETIME(3)` | 否 | `now()` | — |
|
||||
| `updatedAt` | `DATETIME(3)` | 否 | — | `@updatedAt` |
|
||||
|
||||
@@ -176,14 +180,19 @@
|
||||
| `projectId` | `INT` | 否 | — | FK → `Project.id` `ON DELETE CASCADE` |
|
||||
| `columnId` | `INT` | 否 | — | FK → `BoardColumn.id` `ON DELETE RESTRICT`(T-BD-01) |
|
||||
| `parentId` | `INT` | **是** | `NULL` | 非空 = 一级子任务;FK → `Task.id` `ON DELETE CASCADE`(T-ST-01/11) |
|
||||
| `title` | `VARCHAR(200)` | 否 | — | 唯一必填(T-TK-02) |
|
||||
| `description` | `TEXT` | 否 | `''` | — |
|
||||
| `title` | `VARCHAR(200)` | 否 | — | 唯一必填(T-TK-02);应用层限 30 字(T-TK-14) |
|
||||
| `description` | `TEXT` | 否 | `''` | 快速添加超长文本的余文并入此处(T-TK-14) |
|
||||
| `kind` | `VARCHAR(20)` | **是** | `NULL` | 任务类型标签,取值见 `Project.taskKinds` |
|
||||
| `markDate` | `DATETIME(3)` | **是** | `NULL` | 时间标记,完整日期存储、展示 `MM-dd`(T-TK-15;迁移 20261025120000) |
|
||||
| `status` | `VARCHAR(8)` | 否 | `'todo'` | `todo`/`doing`/`done`(T-TK-03) |
|
||||
| `doneAt` | `DATETIME(3)` | **是** | `NULL` | 完成时刻;**保留选项判定依据**(T-FL-10) |
|
||||
| `assigneeId` | `INT` | **是** | `NULL` | 负责人(单人);新建默认创建者(T-TK-09) |
|
||||
| `creatorId` | `INT` | 否 | — | 创建人;拷贝时为操作者(T-CP-04) |
|
||||
| `extras` | `JSON` | 否 | `[]` | 扩展信息卡片 `[{label,value,url?}]`(T-TK-04/05) |
|
||||
| `sortOrder` | `INT` | 否 | `0` | 列内/列表内排序 |
|
||||
| `archived` | `BOOLEAN` | 否 | `false` | 归档标记;主查询一律 `archived = false`(T-ARC-01/03) |
|
||||
| `archivedAt` | `DATETIME(3)` | **是** | `NULL` | 归档时刻;归档查询的过滤/排序键(T-ARC-04) |
|
||||
| `archiveSource` | `VARCHAR(8)` | **是** | `NULL` | `'manual'` / `'auto'` 溯源(T-ARC-01/02) |
|
||||
| `createdAt` | `DATETIME(3)` | 否 | `now()` | — |
|
||||
| `updatedAt` | `DATETIME(3)` | 否 | — | — |
|
||||
|
||||
@@ -200,6 +209,8 @@
|
||||
| `idx_task_project_status` | `(projectId, status)` | — | 列表/统计 |
|
||||
| `idx_task_parent` | `(parentId)` | — | 取子任务 |
|
||||
| `idx_task_done_at` | `(projectId, doneAt)` | — | 保留窗口计算 |
|
||||
| `idx_task_project_archived` | `(projectId, archived, archivedAt)` | — | 已归档查询(T-ARC-04) |
|
||||
| `idx_task_project_status_arch` | `(projectId, status, archived)` | — | 主列表/看板排除归档(T-ARC-03) |
|
||||
|
||||
> \* `creatorId` 用 Cascade 会丢失"他人创建的任务"的历史;更稳妥的做法是 `Restrict` 并禁止删除有任务的用户。**建议改为 `Restrict`**,与既有 `User` 删除行为保持一致(本期系统无删除用户入口)。
|
||||
|
||||
@@ -212,6 +223,8 @@
|
||||
| 子任务 `projectId` == 父任务 `projectId`(T-EX-12) | 应用层 + 事务内校验 |
|
||||
| `parentId` 不得指向自身或其后代(T-EX-15) | 应用层 |
|
||||
| `status='done'` 时写 `doneAt=now()`;退出 `done` 时置 `NULL` | 应用层(领域统一裁决) |
|
||||
| 归档/恢复父任务连带全部后代,同事务内更新 `archived`/`archivedAt`/`archiveSource`(T-ARC-01/05) | 应用层 |
|
||||
| `autoArchive` 枚举 `3d`/`1w`/`1m`/`never`,非法 400(T-ARC-02) | 应用层 |
|
||||
| `assigneeId` 必须是 `ProjectMember` 中的用户(T-AS-03) | 应用层 |
|
||||
|
||||
### 3.5 `TaskMember` — 任务成员
|
||||
@@ -373,6 +386,7 @@ model User {
|
||||
| 权限校验 | `SELECT 1 FROM ProjectMember WHERE projectId=? AND userId=?` |
|
||||
| 保留窗口过滤 | 服务端可选:`status <> 'done' OR (doneAt >= ?)`;前端另有一份同口径实现 |
|
||||
| 自动归位选列 | 内存中对已加载的 `columns`(按 sortOrder)`find(c => c.boundStatus === status)` |
|
||||
| 已归档查询(T-ARC-04) | `WHERE projectId=? AND archived=1 [AND kind=?] [AND columnId=?] [AND archivedAt BETWEEN ? AND ?] [AND title LIKE ?] ORDER BY archivedAt DESC, id DESC LIMIT ?+1`;多取 1 条判定 `nextCursor` |
|
||||
|
||||
**防 N+1**:一次请求返回 `{ project, columns, members, tasks }`,子任务与父任务同批返回,由前端按 `parentId` 分组。
|
||||
|
||||
@@ -389,6 +403,16 @@ model User {
|
||||
3. 建唯一索引与普通索引;
|
||||
4. **不做数据回填**(本模块为新功能,无历史数据)。
|
||||
|
||||
后续增量迁移:
|
||||
|
||||
| 迁移 | 内容 | 状态 |
|
||||
| --- | --- | --- |
|
||||
| `20261022120000_add_checkin_checklist` | 打卡/清单模块 | 已上线 |
|
||||
| `20261025030000_task_kind` | `Project.taskKinds` / `Task.kind` | 已上线 |
|
||||
| `20261025120000_task_mark_date` | `Task.markDate` | 已上线 |
|
||||
| `20261025130000_project_icon` | `Project.icon` | 已上线 |
|
||||
| `20261026000000_task_archive` | 任务归档三字段 + 两索引(T-ARC,DDL 见 §3.1/§3.4 与功能设计 §15) | **待实施** |
|
||||
|
||||
### 6.2 个人默认项目初始化
|
||||
|
||||
```sql
|
||||
|
||||
@@ -240,6 +240,9 @@
|
||||
| T-TK-11 | 每条任务有**任务成员(多人)** | 可设置 0 ~ 多个成员,成员不重复 | P0 |
|
||||
| T-TK-12 | 任务支持**标签** | 可添加多个标签,展示于条目 / 卡片 | P1 |
|
||||
| T-TK-13 | 任务的创建人、创建时间、更新时间由系统维护,用户不可直接编辑 | 编辑任务不改变创建人与创建时间;任何修改刷新更新时间 | P1 |
|
||||
| T-TK-14 | 任务标题收紧至**30 个汉字**;快速添加输入框允许输入 200 字,超过 30 字时自动在 30 字以内的**分隔符(空格/标点等)**处截断,前段作为标题,整体文本并入描述 | 输入长文本创建后:标题 ≤30 字且不在词中间切断;描述包含完整原文余文;直接编辑标题超 30 字时拒绝保存并提示 | P0 |
|
||||
| T-TK-15 | 任务可选**时间标记**(完整日期存储),设置了时间标记的任务在列表行与看板卡片上以 `MM-dd` 小标签展示 | 详情页可选择/清除时间标记;设置后卡片出现 MM-dd 标签,清除后消失 | P1 |
|
||||
| T-TK-16 | 快速添加输入框支持**时间词前缀**:以 今天/明天/后天/本周/下周/本月/下月 开头(后跟分隔符)时,自动为任务设置对应时间标记并把该前缀从标题中移除 | 输入「明天 交周报」→ 创建任务标题为「交周报」、时间标记为明天的日期;无匹配前缀时行为不变 | P1 |
|
||||
|
||||
### 3.6 负责人 & 成员 UI 展示规则(T-AS)
|
||||
|
||||
@@ -381,12 +384,29 @@
|
||||
| T-EX-19 | 新增列时已达列数上限 | 拒绝新增并提示已达上限,不产生空列 | P1 |
|
||||
| T-EX-18 | 已完成任务跨周期(周一零点 / 月初零点)后应隐藏 | **当场消失**并带移除动画,其余任务平滑重排,不留空白 | P1 |
|
||||
|
||||
### 3.15 任务归档(T-ARC)
|
||||
|
||||
> 与「已完成显示控制(T-FL)」的关系:T-FL 决定任务**看不看得见**(可逆的展示过滤),
|
||||
> 归档决定任务**在不在主工作集**(数据层面的收纳)。隐藏是自动归档的前置条件,归档移出主列表,
|
||||
> 可通过「已归档查询」恢复。
|
||||
|
||||
| 编号 | 需求描述 | 验收要点 | 优先级 |
|
||||
| --- | --- | --- | --- |
|
||||
| T-ARC-01 | **手动归档**:任务 `⋯` 菜单增加「归档」;任意未归档任务(不限状态)均可归档;父任务归档时**连带全部子任务**一并归档;操作需二次确认(提示连带条数) | 归档后任务从列表/看板消失;子任务同步消失;确认弹窗条数正确 | P0 |
|
||||
| T-ARC-02 | **自动归档**:项目级配置「**隐藏的任务**多久归档」,可选 `3 天后 / 一周后 / 一月后 / 永不`(默认永不);已隐藏(T-FL 保留窗口过期)的 done 任务到期后由系统自动归档 | 配置保存后生效;隐藏任务到期被自动归档;「永不」时永不自动归档;未隐藏的任务不受影响 | P1 |
|
||||
| T-ARC-03 | **归档排除**:已归档任务从任务列表、看板、列计数、项目任务数统计中排除 | 归档后各视图与计数立即减少;恢复后回到原位 | P0 |
|
||||
| T-ARC-04 | **已归档查询**:独立入口查看本项目已归档任务;支持关键词(标题)、任务类型、看板列、归档时间范围过滤;**游标分页**支撑大量数据(千级) | 过滤条件生效;分页可连续加载到底;千级数据下查询流畅 | P0 |
|
||||
| T-ARC-05 | **恢复**:已归档任务可取消归档,回到原看板列(列被删除的场景因外键 `ON DELETE RESTRICT` 不存在),状态保持不变;父任务恢复连带子任务 | 恢复后任务与子任务重新出现在列表/看板原列 | P0 |
|
||||
| T-ARC-06 | **彻底删除**:已归档任务可物理删除(含子任务),二次确认 | 删除后不再出现在已归档查询 | P1 |
|
||||
| T-ARC-07 | **配置入口**:项目管理新增「归档设置」分区,展示与修改自动归档档位 | 修改后即时保存并对全项目生效 | P1 |
|
||||
| T-ARC-08 | **权限口径**:项目成员均可归档/恢复/删除归档任务(权限均等口径同 T-PM-04) | 与其他写操作权限一致,无额外限制 | P1 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 优先级汇总(本期交付范围)
|
||||
|
||||
- **P0(必须)**:T-VW-01~04、T-VW-06~09;T-COL-01~07、T-COL-09、T-COL-10;T-BD-01~10、13~16;T-FL-01~13、15;T-TK-01~11;T-AS-01~05、07;T-ST-01~09、11~14;T-PJ-01~08、10~13;T-PM-01、02、04~07;T-CP-01~06、08;T-UI-01~05、07~11;T-DT-01~08;T-NF-01~04;T-EX-01~05、07、09~12、15~17。
|
||||
- **P1(应该)**:T-VW-05、10;T-COL-08;T-BD-11、12;T-FL-14;T-TK-12、13;T-AS-06;T-ST-10、15;T-PJ-09;T-PM-03;T-CP-07、09;T-UI-06;T-DT-09;T-NF-05;T-EX-06、08、13、14、18、19。
|
||||
- **P0(必须)**:T-VW-01~04、T-VW-06~09;T-COL-01~07、T-COL-09、T-COL-10;T-BD-01~10、13~16;T-FL-01~13、15;T-TK-01~11;T-AS-01~05、07;T-ST-01~09、11~14;T-PJ-01~08、10~13;T-PM-01、02、04~07;T-CP-01~06、08;T-UI-01~05、07~11;T-DT-01~08;T-NF-01~04;T-EX-01~05、07、09~12、15~17;T-ARC-01、03、04、05。
|
||||
- **P1(应该)**:T-VW-05、10;T-COL-08;T-BD-11、12;T-FL-14;T-TK-12、13;T-AS-06;T-ST-10、15;T-PJ-09;T-PM-03;T-CP-07、09;T-UI-06;T-DT-09;T-NF-05;T-EX-06、08、13、14、18、19;T-ARC-02、06、07、08。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,627 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>已归档任务管理 · 界面重设计(原型待评审)</title>
|
||||
<style>
|
||||
:root{
|
||||
--bg:#f0f2f5; --page:#fff; --card:#fff;
|
||||
--text:#333; --muted:#666; --weak:#999; --line:#eee;
|
||||
--primary:#1677ff; --danger:#ff3141; --success:#00b578;
|
||||
--nav-h:45px; --tab-h:72px;
|
||||
}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{
|
||||
background:var(--bg); color:#1f2328; font-size:14px; line-height:1.6;
|
||||
font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Hiragino Sans GB','Microsoft YaHei',Roboto,sans-serif;
|
||||
-webkit-font-smoothing:antialiased;
|
||||
}
|
||||
.wrap{max-width:1400px;margin:0 auto;padding:36px 24px 72px}
|
||||
.doc-head{border-bottom:1px solid #e3e6ea;padding-bottom:20px;margin-bottom:28px}
|
||||
.doc-head h1{font-size:26px;font-weight:700;letter-spacing:.5px}
|
||||
.doc-head p{color:var(--muted);font-size:13px;margin-top:6px}
|
||||
.meta{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}
|
||||
.meta span{font-size:12px;background:#fff;border:1px solid #e3e6ea;border-radius:999px;padding:3px 12px;color:var(--muted)}
|
||||
h2{font-size:19px;font-weight:700;margin:40px 0 6px;display:flex;align-items:center;gap:10px}
|
||||
h2::before{content:"";width:4px;height:17px;background:var(--primary);border-radius:2px}
|
||||
h2 .sub{font-size:12px;font-weight:400;color:var(--weak)}
|
||||
p.desc{color:var(--muted);font-size:13px;margin-bottom:16px}
|
||||
|
||||
/* ---------- 改动要点 ---------- */
|
||||
.changes{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:14px}
|
||||
.chg{background:#fff;border:1px solid #e6e8eb;border-radius:12px;padding:14px 16px}
|
||||
.chg .k{font-size:11px;color:var(--primary);font-weight:700;letter-spacing:.6px}
|
||||
.chg h4{font-size:14px;font-weight:700;margin:5px 0 6px}
|
||||
.chg p{font-size:12.5px;color:var(--muted);line-height:1.65}
|
||||
.chg .old{color:var(--danger);font-size:12px;text-decoration:line-through}
|
||||
|
||||
/* ---------- 手机框 ---------- */
|
||||
.stage{display:flex;gap:34px;flex-wrap:wrap;align-items:flex-start;margin-top:18px}
|
||||
.phone{
|
||||
width:400px;height:812px;background:var(--page);position:relative;overflow:hidden;
|
||||
border:1px solid #d8dde3;border-radius:22px;box-shadow:0 10px 24px rgba(15,23,42,.08);
|
||||
display:flex;flex-direction:column;
|
||||
}
|
||||
.phone::after{content:"";position:absolute;top:8px;left:50%;transform:translateX(-50%);width:112px;height:19px;background:#111827;border-radius:12px;opacity:.9;z-index:5}
|
||||
.phone.sm{width:340px;height:700px}
|
||||
.phone .nav{
|
||||
height:var(--nav-h);margin-top:26px;flex-shrink:0;display:flex;align-items:center;gap:10px;
|
||||
padding:0 14px;background:#fff;border-bottom:1px solid var(--line);
|
||||
}
|
||||
.phone .nav .back{font-size:20px;color:#666;line-height:1;cursor:pointer}
|
||||
.phone .nav .title{font-size:16px;font-weight:700}
|
||||
.phone .nav .proj{margin-left:auto;font-size:11px;color:var(--weak);background:#f5f6f8;border-radius:999px;padding:2px 9px;max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.phone .body{flex:1;min-height:0;overflow-y:auto;background:#f5f5f5;padding-bottom:18px}
|
||||
|
||||
/* ---------- 筛选卡 ---------- */
|
||||
.fcard{background:#fff;margin:10px 12px 0;border-radius:12px;padding:4px 14px 12px;box-shadow:0 1px 2px rgba(0,0,0,.04)}
|
||||
.frow{display:flex;align-items:center;gap:10px;padding:10px 0;border-bottom:1px solid #f4f5f7}
|
||||
.frow:last-of-type{border-bottom:0}
|
||||
.frow > .lbl{width:56px;flex-shrink:0;font-size:12.5px;font-weight:600;color:var(--text)}
|
||||
.frow > .ctl{flex:1;min-width:0}
|
||||
/* 标题输入框前的提示 */
|
||||
.kwbox{display:flex;align-items:center;gap:7px;background:#f7f8fa;border:1px solid #eceef1;border-radius:8px;padding:6px 9px}
|
||||
.kwbox:focus-within{border-color:var(--primary);background:#fff}
|
||||
.kwbox .ph{font-size:11.5px;color:var(--weak);flex-shrink:0}
|
||||
.kwbox input{flex:1;min-width:0;border:0;background:transparent;font-size:13px;outline:none;color:var(--text)}
|
||||
.kwbox input::placeholder{color:#c2c6cc}
|
||||
.kwbox .clr{font-size:12px;color:#c2c6cc;cursor:pointer;flex-shrink:0}
|
||||
|
||||
.chips{display:flex;flex-wrap:wrap;gap:6px}
|
||||
.chip{
|
||||
font-size:12px;padding:4px 11px;border-radius:999px;border:1px solid #e6e8eb;
|
||||
background:#fafbfc;color:var(--muted);cursor:pointer;white-space:nowrap;transition:.15s;
|
||||
}
|
||||
.chip:hover{border-color:#c9d3e0}
|
||||
.chip.on{background:#eaf3ff;border-color:var(--primary);color:var(--primary);font-weight:600}
|
||||
.chip .x{margin-left:5px;color:#9aa4b2}
|
||||
|
||||
.range{display:flex;align-items:center;gap:7px;margin-top:8px}
|
||||
.range .dp{font-size:12px;padding:4px 10px;border:1px dashed #d9dee5;border-radius:7px;color:var(--weak);cursor:pointer;background:#fff}
|
||||
.range .dp.on{color:var(--text);border-style:solid;border-color:#d9dee5}
|
||||
.range .sep{color:#c2c6cc;font-size:12px}
|
||||
|
||||
.factions{display:flex;gap:9px;padding-top:12px}
|
||||
.btn{flex:1;height:36px;border-radius:9px;border:0;font-size:13.5px;font-weight:600;cursor:pointer;transition:.15s}
|
||||
.btn.ghost{background:#f4f5f7;color:var(--muted);flex:0 0 84px}
|
||||
.btn.primary{background:var(--primary);color:#fff;box-shadow:0 2px 8px rgba(22,119,255,.26)}
|
||||
.btn.primary:active{transform:translateY(1px)}
|
||||
.btn .badge{display:inline-block;min-width:16px;height:16px;line-height:16px;border-radius:8px;background:rgba(255,255,255,.28);font-size:10.5px;margin-left:5px;padding:0 4px}
|
||||
|
||||
/* ---------- 已应用条件 ---------- */
|
||||
.applied{display:flex;flex-wrap:wrap;gap:6px;align-items:center;padding:11px 14px 3px}
|
||||
.applied .tag{font-size:11.5px;background:#eaf3ff;color:var(--primary);border-radius:6px;padding:2px 7px;display:flex;align-items:center;gap:5px}
|
||||
.applied .tag b{font-weight:600}
|
||||
.applied .tag .x{cursor:pointer;opacity:.6}
|
||||
.applied .cnt{margin-left:auto;font-size:11.5px;color:var(--weak)}
|
||||
|
||||
/* ---------- 列表 ---------- */
|
||||
.list{padding:8px 12px 0;display:flex;flex-direction:column;gap:8px}
|
||||
.item{background:#fff;border-radius:10px;padding:11px 12px;box-shadow:0 1px 2px rgba(0,0,0,.04)}
|
||||
.item .top{display:flex;gap:8px;align-items:flex-start}
|
||||
.item .ttl{flex:1;min-width:0;font-size:13.5px;font-weight:600;line-height:1.42;word-break:break-word}
|
||||
.item .num{font-size:10px;color:#9aa4b2;background:#f5f6f8;border-radius:4px;padding:1px 5px;flex-shrink:0;margin-top:1px}
|
||||
.item .meta{display:flex;gap:7px;align-items:center;flex-wrap:wrap;margin-top:7px;font-size:10.5px;color:var(--weak)}
|
||||
.item .meta .col{color:var(--muted)}
|
||||
.item .meta .dot{width:3px;height:3px;border-radius:50%;background:#d5d9e0}
|
||||
.item .acts{display:flex;gap:7px;justify-content:flex-end;margin-top:9px;padding-top:9px;border-top:1px dashed #f0f1f3}
|
||||
.mini{font-size:11.5px;padding:3px 12px;border-radius:999px;border:1px solid var(--primary);color:var(--primary);background:#fff;cursor:pointer;white-space:nowrap}
|
||||
.mini.danger{border-color:#ffd4d7;color:var(--danger)}
|
||||
.kind{font-size:10px;line-height:1;font-weight:600;color:#fff;border-radius:4px;padding:3px 5px}
|
||||
|
||||
.empty{text-align:center;padding:56px 20px;color:#b8bcc4;font-size:13px}
|
||||
.empty .ico{font-size:34px;opacity:.35;margin-bottom:10px}
|
||||
.more{display:block;width:calc(100% - 24px);margin:12px auto 0;height:34px;border-radius:9px;border:1px solid #e6e8eb;background:#fff;color:var(--muted);font-size:12.5px;cursor:pointer}
|
||||
|
||||
.toast{
|
||||
position:absolute;left:50%;bottom:56px;transform:translateX(-50%) translateY(8px);
|
||||
background:rgba(0,0,0,.78);color:#fff;font-size:12.5px;padding:8px 16px;border-radius:8px;
|
||||
opacity:0;transition:.22s;z-index:9;pointer-events:none;white-space:nowrap;
|
||||
}
|
||||
.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
|
||||
|
||||
.caption{margin-top:12px;background:#fff;border:1px solid #e6e8eb;border-radius:10px;padding:11px 14px;width:400px}
|
||||
.caption b{font-size:13px;display:block;margin-bottom:4px}
|
||||
.caption span{font-size:12px;color:var(--muted);line-height:1.65}
|
||||
|
||||
.notes{flex:1;min-width:300px;display:flex;flex-direction:column;gap:12px}
|
||||
.note{background:#fff;border:1px solid #e6e8eb;border-radius:12px;padding:14px 16px}
|
||||
.note h4{font-size:13px;font-weight:700;margin-bottom:7px;display:flex;align-items:center;gap:7px}
|
||||
.note h4 i{width:18px;height:18px;border-radius:50%;background:#eaf3ff;color:var(--primary);font-size:11px;font-style:normal;display:flex;align-items:center;justify-content:center;font-weight:700}
|
||||
.note ul{list-style:none;font-size:12.5px;color:var(--muted);line-height:1.85}
|
||||
.note ul li::before{content:"·";color:#c2c6cc;margin-right:6px}
|
||||
.note code{background:#f5f6f8;border-radius:4px;padding:1px 5px;font-size:11.5px;color:#c7254e}
|
||||
|
||||
table{width:100%;border-collapse:collapse;background:#fff;border-radius:12px;overflow:hidden;border:1px solid #e6e8eb;font-size:12.5px}
|
||||
th,td{padding:10px 14px;text-align:left;border-bottom:1px solid #f0f1f3;vertical-align:top}
|
||||
th{background:#fafbfc;font-size:12px;color:var(--muted);font-weight:600}
|
||||
tr:last-child td{border-bottom:0}
|
||||
td .no{color:var(--danger)}
|
||||
td .yes{color:var(--success);font-weight:600}
|
||||
.variants{display:flex;gap:26px;flex-wrap:wrap;margin-top:16px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
|
||||
<div class="doc-head">
|
||||
<h1>已归档任务管理 · 界面重设计</h1>
|
||||
<p>对应需求 T-ARC-04/05/06。本文件是<strong>评审用原型</strong>,不含业务代码改动;确认后再落地。</p>
|
||||
<div class="meta">
|
||||
<span>原型 · 待评审</span>
|
||||
<span>2026-10-28</span>
|
||||
<span>可交互:点预设 / 筛选 / 恢复</span>
|
||||
<span>范围:任务模块 › 项目 › 已归档</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>改动要点 <span class="sub">对照你提出的 4 点</span></h2>
|
||||
<div class="changes">
|
||||
<div class="chg">
|
||||
<div class="k">01</div>
|
||||
<h4>去掉「类型」过滤</h4>
|
||||
<p>现版本的「全部类型」下拉整体移除。任务类型是弱维度,归档场景几乎不按类型找东西,反而占掉一横行高。</p>
|
||||
<p class="old">全部类型 ▾</p>
|
||||
</div>
|
||||
<div class="chg">
|
||||
<div class="k">02</div>
|
||||
<h4>输入框前加提示</h4>
|
||||
<p>关键词输入框前加固定标签「标题」,输入框内用灰字占位「输入关键词」——标签常驻,占位随输入消失,不会出现"不知道搜什么"。</p>
|
||||
<p class="old">搜索任务标题(无标签)</p>
|
||||
</div>
|
||||
<div class="chg">
|
||||
<div class="k">03</div>
|
||||
<h4>归档时间快捷选择</h4>
|
||||
<p>新增 <b>全部 / 本周 / 本月 / 本季度</b> 四个一键预设,选完自动算出起止日期并显示;需要精确区间时再选「自定义」。</p>
|
||||
<p class="old">开始日期 ~ 结束日期(纯手点)</p>
|
||||
</div>
|
||||
<div class="chg">
|
||||
<div class="k">04</div>
|
||||
<h4>补上「筛选」按钮</h4>
|
||||
<p>现版本改任何条件都立即查询、没有确认动作。改为:改条件 → 点<b>筛选</b>才生效,按钮上显示生效条件数;另给「重置」。</p>
|
||||
<p class="old">无按钮(自动即时查询)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>主界面 <span class="sub">可交互 · 点一下试试</span></h2>
|
||||
<div class="stage">
|
||||
|
||||
<div>
|
||||
<div class="phone" id="phone">
|
||||
<div class="nav">
|
||||
<span class="back">‹</span>
|
||||
<span class="title">已归档任务</span>
|
||||
<span class="proj">Admin的任务</span>
|
||||
</div>
|
||||
<div class="body">
|
||||
<!-- 筛选卡 -->
|
||||
<div class="fcard">
|
||||
<div class="frow">
|
||||
<span class="lbl">标题</span>
|
||||
<span class="ctl">
|
||||
<span class="kwbox">
|
||||
<input id="kw" placeholder="输入关键词" />
|
||||
<span class="clr" id="kwClear" style="display:none">✕</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="frow">
|
||||
<span class="lbl">看板列</span>
|
||||
<span class="ctl">
|
||||
<span class="chips" id="colChips">
|
||||
<span class="chip on" data-col="">全部</span>
|
||||
<span class="chip" data-col="Plan">Plan</span>
|
||||
<span class="chip" data-col="Doing">Doing</span>
|
||||
<span class="chip" data-col="Done">Done</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="frow">
|
||||
<span class="lbl">归档时间</span>
|
||||
<span class="ctl">
|
||||
<span class="chips" id="timeChips">
|
||||
<span class="chip on" data-t="all">全部</span>
|
||||
<span class="chip" data-t="week">本周</span>
|
||||
<span class="chip" data-t="month">本月</span>
|
||||
<span class="chip" data-t="quarter">本季度</span>
|
||||
<span class="chip" data-t="custom">自定义</span>
|
||||
</span>
|
||||
<span class="range" id="rangeRow" style="display:none">
|
||||
<span class="dp" id="dpFrom">开始日期</span>
|
||||
<span class="sep">~</span>
|
||||
<span class="dp" id="dpTo">结束日期</span>
|
||||
</span>
|
||||
<div id="rangeHint" style="font-size:11px;color:#9aa4b2;margin-top:6px"></div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="factions">
|
||||
<button class="btn ghost" id="resetBtn">重置</button>
|
||||
<button class="btn primary" id="filterBtn">筛选</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已应用条件 -->
|
||||
<div class="applied" id="appliedBar" style="display:none"></div>
|
||||
|
||||
<!-- 列表 -->
|
||||
<div class="list" id="list"></div>
|
||||
<button class="more" id="moreBtn">加载更多</button>
|
||||
</div>
|
||||
<div class="toast" id="toast"></div>
|
||||
</div>
|
||||
<div class="caption">
|
||||
<b>主界面</b>
|
||||
<span>筛选卡固定三行:标题 / 看板列 / 归档时间,外加操作行。改动不再即时生效,点「筛选」统一提交。</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notes">
|
||||
<div class="note">
|
||||
<h4><i>1</i>为什么改成"点按钮才筛选"</h4>
|
||||
<ul>
|
||||
<li>现在每改一个条件就重查一次,连点三个条件就发三次请求</li>
|
||||
<li>输入关键词时每敲一个字都可能触发(失焦也查),列表会闪</li>
|
||||
<li>改成显式提交后,用户能先把条件填完整,再一次性看结果</li>
|
||||
<li>按钮上带数字徽标,一眼知道当前生效了几个条件</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="note">
|
||||
<h4><i>2</i>快捷时间怎么算</h4>
|
||||
<ul>
|
||||
<li><b>本周</b>:周一 00:00 ~ 今天(不跨到未来)</li>
|
||||
<li><b>本月</b>:1 号 00:00 ~ 今天</li>
|
||||
<li><b>本季度</b>:当季首月 1 号 00:00 ~ 今天</li>
|
||||
<li>选完在下方灰字直接显示实际区间,避免"以为选的是整周"</li>
|
||||
<li>「自定义」才展开两个日期选择器,默认收起</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="note">
|
||||
<h4><i>3</i>已应用条件可单独撤销</h4>
|
||||
<ul>
|
||||
<li>筛选后在列表上方显示条件标签,每个带 <code>×</code> 可单独去掉</li>
|
||||
<li>右侧显示命中条数,避免"筛完不知道有没有筛到"</li>
|
||||
<li>无结果时给明确空状态,而不是一片留白</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="note">
|
||||
<h4><i>4</i>列表行重排</h4>
|
||||
<ul>
|
||||
<li>标题独占一行(原来和类型/列/时间挤在下面)</li>
|
||||
<li>元信息收成一行:<code>#编号</code> · 看板列 · 归档于 日期</li>
|
||||
<li>「恢复 / 删除」从右侧竖排改到底部右侧横排,减少每行高度</li>
|
||||
<li>类型标签保留展示(只是不再作为筛选项)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>状态变体</h2>
|
||||
<div class="variants">
|
||||
<div>
|
||||
<div class="phone sm">
|
||||
<div class="nav"><span class="back">‹</span><span class="title">已归档任务</span></div>
|
||||
<div class="body">
|
||||
<div class="fcard">
|
||||
<div class="frow"><span class="lbl">标题</span><span class="ctl"><span class="kwbox"><input placeholder="输入关键词" /></span></span></div>
|
||||
<div class="frow"><span class="lbl">看板列</span><span class="ctl"><span class="chips"><span class="chip on">全部</span><span class="chip">Plan</span><span class="chip">Done</span></span></span></div>
|
||||
<div class="frow"><span class="lbl">归档时间</span><span class="ctl">
|
||||
<span class="chips"><span class="chip">全部</span><span class="chip on">本周</span><span class="chip">本月</span><span class="chip">本季度</span><span class="chip">自定义</span></span>
|
||||
<div style="font-size:11px;color:#9aa4b2;margin-top:6px">2026-10-26 ~ 2026-10-28</div>
|
||||
</span></div>
|
||||
<div class="factions"><button class="btn ghost">重置</button><button class="btn primary">筛选<span class="badge">2</span></button></div>
|
||||
</div>
|
||||
<div class="applied">
|
||||
<span class="tag"><b>本周</b><span class="x">×</span></span>
|
||||
<span class="tag"><b>Done</b><span class="x">×</span></span>
|
||||
<span class="cnt">共 3 条</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<div class="item">
|
||||
<div class="top"><div class="ttl">任务详情页滚动优化</div><span class="num">#22</span></div>
|
||||
<div class="meta"><span class="kind" style="background:#1677ff">优化</span><span class="col">Done</span><span class="dot"></span><span>归档于 10-27</span></div>
|
||||
<div class="acts"><span class="mini">恢复</span><span class="mini danger">删除</span></div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="top"><div class="ttl">桌面端。</div><span class="num">#5</span></div>
|
||||
<div class="meta"><span class="kind" style="background:#fa8c16">需求</span><span class="col">Done</span><span class="dot"></span><span>归档于 10-26</span></div>
|
||||
<div class="acts"><span class="mini">恢复</span><span class="mini danger">删除</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="caption"><b>已应用条件 + 命中条数</b><span>条件可逐个撤销;时间预设自动显示实际区间。</span></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="phone sm">
|
||||
<div class="nav"><span class="back">‹</span><span class="title">已归档任务</span></div>
|
||||
<div class="body">
|
||||
<div class="fcard">
|
||||
<div class="frow"><span class="lbl">标题</span><span class="ctl"><span class="kwbox"><input value="日报" /><span class="clr">✕</span></span></span></div>
|
||||
<div class="frow"><span class="lbl">看板列</span><span class="ctl"><span class="chips"><span class="chip on">全部</span><span class="chip">Done</span></span></span></div>
|
||||
<div class="frow"><span class="lbl">归档时间</span><span class="ctl">
|
||||
<span class="chips"><span class="chip">全部</span><span class="chip">本周</span><span class="chip">本月</span><span class="chip">本季度</span><span class="chip on">自定义</span></span>
|
||||
<span class="range"><span class="dp on">2026-09-01</span><span class="sep">~</span><span class="dp on">2026-09-30</span></span>
|
||||
</span></div>
|
||||
<div class="factions"><button class="btn ghost">重置</button><button class="btn primary">筛选<span class="badge">3</span></button></div>
|
||||
</div>
|
||||
<div class="applied">
|
||||
<span class="tag"><b>标题: 日报</b><span class="x">×</span></span>
|
||||
<span class="tag"><b>09-01~09-30</b><span class="x">×</span></span>
|
||||
<span class="cnt">共 0 条</span>
|
||||
</div>
|
||||
<div class="empty"><div class="ico">🗂</div>没有符合条件的归档任务<br /><span style="font-size:11.5px;color:#c2c6cc">试试放宽时间范围或清空关键词</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="caption"><b>自定义区间 + 空结果</b><span>选「自定义」才展开日期;空结果给可操作的建议文案。</span></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="phone sm">
|
||||
<div class="nav"><span class="back">‹</span><span class="title">已归档任务</span></div>
|
||||
<div class="body">
|
||||
<div class="fcard">
|
||||
<div class="frow"><span class="lbl">标题</span><span class="ctl"><span class="kwbox"><input placeholder="输入关键词" /></span></span></div>
|
||||
<div class="frow"><span class="lbl">看板列</span><span class="ctl"><span class="chips"><span class="chip on">全部</span><span class="chip">Plan</span><span class="chip">Doing</span><span class="chip">Done</span></span></span></div>
|
||||
<div class="frow"><span class="lbl">归档时间</span><span class="ctl"><span class="chips"><span class="chip on">全部</span><span class="chip">本周</span><span class="chip">本月</span><span class="chip">本季度</span><span class="chip">自定义</span></span></span></div>
|
||||
<div class="factions"><button class="btn ghost">重置</button><button class="btn primary">筛选</button></div>
|
||||
</div>
|
||||
<div class="empty"><div class="ico">📭</div>这个项目还没有归档任务<br /><span style="font-size:11.5px;color:#c2c6cc">任务完成后可在 ⋯ 菜单里归档</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="caption"><b>初始空状态</b><span>一个都没归档时,告诉用户归档入口在哪。</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>与现版本对照</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th style="width:130px">项</th><th style="width:38%">现在</th><th>建议</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>类型过滤</td><td class="no">「全部类型」下拉 + 独占一行</td><td class="yes">移除</td></tr>
|
||||
<tr><td>关键词</td><td class="no">无标签,占位「搜索任务标题」</td><td class="yes">左侧常驻标签「标题」+ 占位「输入关键词」</td></tr>
|
||||
<tr><td>归档时间</td><td class="no">两个日期按钮,无快捷项</td><td class="yes">全部 / 本周 / 本月 / 本季度 / 自定义</td></tr>
|
||||
<tr><td>查询触发</td><td class="no">改条件即时查询,无按钮</td><td class="yes">「重置 / 筛选」,按钮带生效条件数</td></tr>
|
||||
<tr><td>条件可见性</td><td class="no">条件散在控件里,看不出生效了什么</td><td class="yes">列表上方条件标签 + 命中条数,可单独撤销</td></tr>
|
||||
<tr><td>列表行</td><td class="no">标题与元信息挤在一起,右侧竖排两按钮</td><td class="yes">标题独占行;元信息一行;按钮移到底部右侧</td></tr>
|
||||
<tr><td>空状态</td><td class="no">一行灰字「没有符合条件的归档任务」</td><td class="yes">区分「还没归档过」与「筛选无结果」,给出下一步建议</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>待你确认 <span class="sub">确认后我再改代码</span></h2>
|
||||
<div class="notes" style="max-width:820px">
|
||||
<div class="note">
|
||||
<ul>
|
||||
<li>「筛选」改为显式提交,是否符合你的使用习惯?(也可以保留"改预设即时生效、只有关键词需要点按钮")</li>
|
||||
<li>看板列是否也保留?还是只留 标题 + 归档时间 两个条件更清爽?</li>
|
||||
<li>列表行的「恢复 / 删除」放底部右侧可以吗?还是希望点行进入详情/预览?</li>
|
||||
<li>快捷时间是否需要再加「近 7 天 / 近 30 天」?</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
// ---- 原型状态 ----
|
||||
const state = { kw:'', col:'', time:'all', from:'', to:'', applied:{ kw:'', col:'', time:'all', from:'', to:'' } }
|
||||
|
||||
const MOCK = [
|
||||
{ id:1, title:'任务详情页滚动优化', num:'#22', kind:'优化', kindColor:'#1677ff', col:'Done', at:'2026-10-27' },
|
||||
{ id:2, title:'桌面端。', num:'#5', kind:'需求', kindColor:'#fa8c16', col:'Done', at:'2026-10-26' },
|
||||
{ id:3, title:'任务详情中切换看板列是提示不存在。', num:'#23', kind:'Bug', kindColor:'#ff3141', col:'Done', at:'2026-10-25' },
|
||||
{ id:4, title:'windows 电脑加入监控', num:'#24', kind:'需求', kindColor:'#fa8c16', col:'Plan', at:'2026-10-21' },
|
||||
{ id:5, title:'补日报', num:'#28', kind:'需求', kindColor:'#fa8c16', col:'Doing', at:'2026-10-12' },
|
||||
]
|
||||
let rows = MOCK.slice()
|
||||
|
||||
const $ = (id) => document.getElementById(id)
|
||||
const toastEl = $('toast')
|
||||
let toastTimer = null
|
||||
function toast(msg){
|
||||
toastEl.textContent = msg
|
||||
toastEl.classList.add('show')
|
||||
clearTimeout(toastTimer)
|
||||
toastTimer = setTimeout(() => toastEl.classList.remove('show'), 1600)
|
||||
}
|
||||
|
||||
// ---- 快捷时间区间 ----
|
||||
function pad(n){ return String(n).padStart(2,'0') }
|
||||
function fmt(d){ return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}` }
|
||||
function presetRange(t){
|
||||
const now = new Date()
|
||||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
||||
if (t === 'week') {
|
||||
const dow = (today.getDay() + 6) % 7 // 周一=0
|
||||
const mon = new Date(today); mon.setDate(today.getDate() - dow)
|
||||
return { from: fmt(mon), to: fmt(today) }
|
||||
}
|
||||
if (t === 'month') return { from: fmt(new Date(today.getFullYear(), today.getMonth(), 1)), to: fmt(today) }
|
||||
if (t === 'quarter') {
|
||||
const qm = Math.floor(today.getMonth() / 3) * 3
|
||||
return { from: fmt(new Date(today.getFullYear(), qm, 1)), to: fmt(today) }
|
||||
}
|
||||
return { from:'', to:'' }
|
||||
}
|
||||
function rangeLabel(t){
|
||||
if (t === 'custom') return ''
|
||||
const r = presetRange(t)
|
||||
return r.from ? `${r.from} ~ ${r.to}` : ''
|
||||
}
|
||||
|
||||
// ---- 渲染 ----
|
||||
function render(){
|
||||
// 时间行
|
||||
const custom = state.time === 'custom'
|
||||
$('rangeRow').style.display = custom ? 'flex' : 'none'
|
||||
const hint = rangeLabel(state.time)
|
||||
$('rangeHint').textContent = hint
|
||||
$('dpFrom').textContent = state.from || '开始日期'
|
||||
$('dpTo').textContent = state.to || '结束日期'
|
||||
$('dpFrom').classList.toggle('on', !!state.from)
|
||||
$('dpTo').classList.toggle('on', !!state.to)
|
||||
$('kwClear').style.display = state.kw ? 'block' : 'none'
|
||||
|
||||
// 生效条件数
|
||||
const n = countApplied()
|
||||
const btn = $('filterBtn')
|
||||
btn.innerHTML = n ? `筛选<span class="badge">${n}</span>` : '筛选'
|
||||
|
||||
// 已应用条
|
||||
const bar = $('appliedBar')
|
||||
const tags = []
|
||||
const a = state.applied
|
||||
if (a.kw) tags.push(`<span class="tag"><b>标题: ${a.kw}</b><span class="x" data-clear="kw">×</span></span>`)
|
||||
if (a.col) tags.push(`<span class="tag"><b>看板列: ${a.col}</b><span class="x" data-clear="col">×</span></span>`)
|
||||
if (a.time === 'custom') {
|
||||
if (a.from || a.to) tags.push(`<span class="tag"><b>${a.from || '…'} ~ ${a.to || '…'}</b><span class="x" data-clear="time">×</span></span>`)
|
||||
} else if (a.time !== 'all') {
|
||||
const name = { week:'本周', month:'本月', quarter:'本季度' }[a.time]
|
||||
tags.push(`<span class="tag"><b>${name}</b><span class="x" data-clear="time">×</span></span>`)
|
||||
}
|
||||
if (tags.length) {
|
||||
bar.style.display = 'flex'
|
||||
bar.innerHTML = tags.join('') + `<span class="cnt">共 ${rows.length} 条</span>`
|
||||
} else {
|
||||
bar.style.display = 'none'
|
||||
}
|
||||
|
||||
// 列表
|
||||
const list = $('list')
|
||||
if (!rows.length) {
|
||||
const never = !tags.length
|
||||
list.innerHTML = never
|
||||
? `<div class="empty"><div class="ico">📭</div>这个项目还没有归档任务<br /><span style="font-size:11.5px;color:#c2c6cc">任务完成后可在 ⋯ 菜单里归档</span></div>`
|
||||
: `<div class="empty"><div class="ico">🗂</div>没有符合条件的归档任务<br /><span style="font-size:11.5px;color:#c2c6cc">试试放宽时间范围或清空关键词</span></div>`
|
||||
$('moreBtn').style.display = 'none'
|
||||
} else {
|
||||
$('moreBtn').style.display = 'block'
|
||||
list.innerHTML = rows.map(r => `
|
||||
<div class="item" data-id="${r.id}">
|
||||
<div class="top"><div class="ttl">${r.title}</div><span class="num">${r.num}</span></div>
|
||||
<div class="meta">
|
||||
<span class="kind" style="background:${r.kindColor}">${r.kind}</span>
|
||||
<span class="col">${r.col}</span><span class="dot"></span><span>归档于 ${r.at}</span>
|
||||
</div>
|
||||
<div class="acts"><span class="mini" data-act="restore">恢复</span><span class="mini danger" data-act="remove">删除</span></div>
|
||||
</div>`).join('')
|
||||
}
|
||||
}
|
||||
|
||||
function countApplied(){
|
||||
const a = state.applied
|
||||
let n = 0
|
||||
if (a.kw) n++
|
||||
if (a.col) n++
|
||||
if (a.time === 'custom') { if (a.from || a.to) n++ } else if (a.time !== 'all') n++
|
||||
return n
|
||||
}
|
||||
|
||||
function applyFilter(){
|
||||
state.applied = { kw: state.kw, col: state.col, time: state.time, from: state.from, to: state.to }
|
||||
const a = state.applied
|
||||
rows = MOCK.filter(r => {
|
||||
if (a.kw && !r.title.includes(a.kw)) return false
|
||||
if (a.col && r.col !== a.col) return false
|
||||
if (a.time !== 'custom' && a.time !== 'all') {
|
||||
const rg = presetRange(a.time)
|
||||
if (rg.from && (r.at < rg.from || r.at > rg.to)) return false
|
||||
}
|
||||
if (a.time === 'custom') {
|
||||
if (a.from && r.at < a.from) return false
|
||||
if (a.to && r.at > a.to) return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
render()
|
||||
toast(countApplied() ? `已应用 ${countApplied()} 个条件 · ${rows.length} 条` : `显示全部 ${rows.length} 条`)
|
||||
}
|
||||
|
||||
// ---- 事件 ----
|
||||
$('kw').addEventListener('input', (e) => { state.kw = e.target.value.trim(); $('kwClear').style.display = state.kw ? 'block' : 'none' })
|
||||
$('kw').addEventListener('keydown', (e) => { if (e.key === 'Enter') applyFilter() })
|
||||
$('kwClear').addEventListener('click', () => { state.kw = ''; $('kw').value = ''; render() })
|
||||
|
||||
document.querySelectorAll('#colChips .chip').forEach(el => {
|
||||
el.addEventListener('click', () => {
|
||||
document.querySelectorAll('#colChips .chip').forEach(x => x.classList.remove('on'))
|
||||
el.classList.add('on')
|
||||
state.col = el.dataset.col || ''
|
||||
})
|
||||
})
|
||||
|
||||
document.querySelectorAll('#timeChips .chip').forEach(el => {
|
||||
el.addEventListener('click', () => {
|
||||
document.querySelectorAll('#timeChips .chip').forEach(x => x.classList.remove('on'))
|
||||
el.classList.add('on')
|
||||
state.time = el.dataset.t
|
||||
if (state.time !== 'custom') { state.from = ''; state.to = '' }
|
||||
render()
|
||||
})
|
||||
})
|
||||
|
||||
$('dpFrom').addEventListener('click', () => {
|
||||
const v = prompt('开始日期 (YYYY-MM-DD)', state.from || '2026-10-01')
|
||||
if (v !== null) { state.from = v.trim(); render() }
|
||||
})
|
||||
$('dpTo').addEventListener('click', () => {
|
||||
const v = prompt('结束日期 (YYYY-MM-DD)', state.to || '2026-10-28')
|
||||
if (v !== null) { state.to = v.trim(); render() }
|
||||
})
|
||||
|
||||
$('filterBtn').addEventListener('click', applyFilter)
|
||||
|
||||
$('resetBtn').addEventListener('click', () => {
|
||||
state.kw = ''; state.col = ''; state.time = 'all'; state.from = ''; state.to = ''
|
||||
state.applied = { kw:'', col:'', time:'all', from:'', to:'' }
|
||||
$('kw').value = ''
|
||||
document.querySelectorAll('#colChips .chip').forEach((x, i) => x.classList.toggle('on', i === 0))
|
||||
document.querySelectorAll('#timeChips .chip').forEach((x, i) => x.classList.toggle('on', i === 0))
|
||||
rows = MOCK.slice()
|
||||
render()
|
||||
toast('已重置')
|
||||
})
|
||||
|
||||
$('appliedBar').addEventListener('click', (e) => {
|
||||
const x = e.target.closest('.x'); if (!x) return
|
||||
const k = x.dataset.clear
|
||||
if (k === 'kw') state.applied.kw = ''
|
||||
if (k === 'col') state.applied.col = ''
|
||||
if (k === 'time') { state.applied.time = 'all'; state.applied.from = ''; state.applied.to = '' }
|
||||
const a = state.applied
|
||||
rows = MOCK.filter(r => {
|
||||
if (a.kw && !r.title.includes(a.kw)) return false
|
||||
if (a.col && r.col !== a.col) return false
|
||||
return true
|
||||
})
|
||||
render()
|
||||
toast('已移除该条件')
|
||||
})
|
||||
|
||||
$('list').addEventListener('click', (e) => {
|
||||
const btn = e.target.closest('[data-act]'); if (!btn) return
|
||||
const item = btn.closest('.item')
|
||||
const id = Number(item.dataset.id)
|
||||
const row = rows.find(r => r.id === id)
|
||||
if (btn.dataset.act === 'restore') {
|
||||
toast(`已恢复「${row.title}」`)
|
||||
} else {
|
||||
toast(`已删除「${row.title}」(原型演示,不实际删除)`)
|
||||
}
|
||||
item.style.opacity = '.35'
|
||||
item.style.pointerEvents = 'none'
|
||||
})
|
||||
|
||||
$('moreBtn').addEventListener('click', () => toast('加载更多(原型演示)'))
|
||||
|
||||
render()
|
||||
})()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,108 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>设计稿 · 视图切换移入项目栏 (T-VW-02/03 调整)</title>
|
||||
<style>
|
||||
:root { --primary:#1677ff; --text:#333; --muted:#999; --border:#eee; }
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif; }
|
||||
body { background:#eceef1; padding: 24px; display:flex; gap:28px; flex-wrap:wrap; }
|
||||
.phone { width:375px; background:#f5f5f5; border-radius:18px; overflow:hidden; box-shadow:0 8px 30px rgba(0,0,0,.15); }
|
||||
.phone h3 { font-size:13px; color:#556; padding:10px 14px 0; font-weight:600; }
|
||||
.phone .cap { font-size:11px; color:#889; padding:2px 14px 10px; }
|
||||
.bar { display:flex; align-items:center; gap:8px; padding:10px 12px; background:#fff; border-bottom:1px solid #f0f0f0; }
|
||||
.pname { font-size:15px; font-weight:700; color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0; }
|
||||
/* 紧凑分段切换: 高 26px, 内边距收窄, 选中项白底浮起 */
|
||||
.vseg { display:flex; background:#f2f3f5; border-radius:8px; padding:2px; flex-shrink:0; }
|
||||
.vseg div { font-size:12px; line-height:1; padding:5px 11px; border-radius:6px; color:var(--muted); cursor:pointer; white-space:nowrap; }
|
||||
.vseg div.on { background:#fff; color:var(--text); font-weight:600; box-shadow:0 1px 2px rgba(0,0,0,.08); }
|
||||
.mini { font-size:11px; color:var(--primary); background:#f0f5ff; border-radius:6px; padding:5px 8px; white-space:nowrap; flex-shrink:0; cursor:pointer; }
|
||||
.mini.solid { background:var(--primary); color:#fff; }
|
||||
.mini.ghost { background:#fff; border:1px solid #e3e6ea; color:#666; }
|
||||
.bar-note { font-size:10px; color:#b0b6bf; padding:6px 12px; background:#fafbfc; border-bottom:1px dashed #e5e8ec; }
|
||||
.demo-body { padding:12px; }
|
||||
.card { background:#fff; border-radius:9px; padding:10px 12px; margin-bottom:7px; font-size:13px; color:#444; box-shadow:0 1px 2px rgba(0,0,0,.04); display:flex; gap:8px; align-items:center; }
|
||||
.card .dot { width:8px; height:8px; border-radius:50%; background:#c9ced6; flex-shrink:0; }
|
||||
.cols { display:flex; gap:8px; }
|
||||
.col { flex:1; background:#fff; border-radius:9px; padding:8px; }
|
||||
.col b { font-size:11px; color:#556; }
|
||||
.col .ccard { background:#f6f7f9; border-radius:6px; font-size:11px; color:#555; padding:6px; margin-top:6px; }
|
||||
.anno { font-size:11px; color:#7a828e; padding:8px 14px 14px; line-height:1.7; background:#fafbfc; }
|
||||
.anno b { color:#43506b; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="phone">
|
||||
<h3>状态 1 · 列表视图(默认)</h3>
|
||||
<div class="cap">切换段控放在项目名右侧, 与名称同一行</div>
|
||||
<div class="bar">
|
||||
<div class="pname">我的看板项目</div>
|
||||
<div class="vseg"><div class="on">列表</div><div>看板</div></div>
|
||||
<div class="mini">已归档</div>
|
||||
<div class="mini ghost">管理</div>
|
||||
<div class="mini solid">+ 新增</div>
|
||||
</div>
|
||||
<div class="bar-note">「项目管理」按钮文案收窄为「管理」, 与「已归档」同为次级样式, 突出右侧主操作</div>
|
||||
<div class="demo-body">
|
||||
<div class="card"><span class="dot"></span>梳理 V0.2 归档需求</div>
|
||||
<div class="card"><span class="dot"></span>OTA 发布说明样式修复</div>
|
||||
<div class="card"><span class="dot"></span>看板列滚动条位置调整</div>
|
||||
</div>
|
||||
<div class="anno">
|
||||
<b>规格</b><br/>
|
||||
· 分段控件高 26px, 选中项白底浮起(复用现有 .seg 体系收窄版)<br/>
|
||||
· 项目名 flex:1 + 单行省略, 最短保留 4 字符<br/>
|
||||
· 段控始终紧贴名称右侧, 距离 8px
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="phone">
|
||||
<h3>状态 2 · 看板视图</h3>
|
||||
<div class="cap">点「看板」后段控选中项滑换, 其余布局不变</div>
|
||||
<div class="bar">
|
||||
<div class="pname">我的看板项目</div>
|
||||
<div class="vseg"><div>列表</div><div class="on">看板</div></div>
|
||||
<div class="mini">已归档</div>
|
||||
<div class="mini ghost">管理</div>
|
||||
<div class="mini solid">+ 新增</div>
|
||||
</div>
|
||||
<div class="demo-body">
|
||||
<div class="cols">
|
||||
<div class="col"><b>Plan · 2</b><div class="ccard">梳理归档需求</div><div class="ccard">OTA 说明修复</div></div>
|
||||
<div class="col"><b>Doing · 1</b><div class="ccard">滚动条调整</div></div>
|
||||
<div class="col"><b>Done · 1</b><div class="ccard">版本方案落地</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="anno">
|
||||
<b>交互</b><br/>
|
||||
· 点按即切换并持久化(设备端记忆, T-VW-02/03 不变)<br/>
|
||||
· 切换不引发项目栏高度变化, 下方视图平滑重排<br/>
|
||||
· 长按/拖动手势不受影响(段控独立控件, 不在卡片上)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="phone">
|
||||
<h3>状态 3 · 长项目名</h3>
|
||||
<div class="cap">名称被压缩时单行省略, 段控与按钮永不换行/收缩</div>
|
||||
<div class="bar">
|
||||
<div class="pname">经济数据整理与策略研究-2026年第一季度重点项目</div>
|
||||
<div class="vseg"><div class="on">列表</div><div>看板</div></div>
|
||||
<div class="mini">已归档</div>
|
||||
<div class="mini ghost">管理</div>
|
||||
<div class="mini solid">+ 新增</div>
|
||||
</div>
|
||||
<div class="demo-body">
|
||||
<div class="card"><span class="dot"></span>完整项目名在详情/管理页仍可见</div>
|
||||
</div>
|
||||
<div class="anno">
|
||||
<b>宽度预算 (375px)</b><br/>
|
||||
· 左右内边距 24 + 段控 90 + 已归档 48 + 管理 42 + 新增 54 + 间距 32 ≈ 290<br/>
|
||||
· 名称可用 ≈ 85px(约 6 个汉字)起, 不足部分省略<br/>
|
||||
· 若仍嫌拥挤, 备选:「已归档」「管理」收进 ⋯ 溢出菜单(二期可选, 本稿未采用)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,103 @@
|
||||
# 0.2.1 实施方案:PostgreSQL 迁移 + 任务模块离线编辑 (PowerSync)
|
||||
|
||||
| 项 | 内容 |
|
||||
| --- | --- |
|
||||
| 分支 | `dev-0.2.1`(worktree `iboard-dev-0.2.1`,独立临时分支,与 master / dev-0.2 隔离) |
|
||||
| 日期 | 2026-10-25 |
|
||||
| 依据 | [PowerSync可行性研究报告](./PowerSync可行性研究报告.md),按其 5.2.2 路线执行 |
|
||||
| 目标 | 在现网应用与数据不受影响的前提下,验证任务模块离线编辑 + 联网后自动双向同步;数据库由 MariaDB 迁移至 PostgreSQL |
|
||||
|
||||
## 1. 已确认决策(2026-10-25 与需求方对齐)
|
||||
|
||||
| 决策点 | 结论 |
|
||||
| --- | --- |
|
||||
| 技术路线 | PowerSync 全链路(自部署 powersync-service) |
|
||||
| 离线范围 | 仅任务模块(Project / ProjectMember / BoardColumn / Task / TaskMember / TaskTag);打卡、清单本阶段不纳入;新闻/指标/监控等时序域仅做普通缓存 |
|
||||
| 客户端 | 先 Web / Windows 外壳(WebView2)跑通全链路;Android 真机后续验证 |
|
||||
| 编号 | 项目短前缀由用户自拟(2~8 位,全局唯一);任务号 = 项目内自增整数,展示 `前缀-序号`;编号由服务端统一分配,离线创建的任务在同步定号前显示临时标识 |
|
||||
| 初始数据 | 从现网 MariaDB 只读快照导出并转换导入新 PG;0.2.1 期间产生的新数据不回写现网 |
|
||||
| 端口 | Web/API 13001、PowerSync WebSocket 13002、PG 仅容器内网(调试绑 127.0.0.1:15432) |
|
||||
| 部署 | 源码上传 baihu `/app/iboard-0.2.1`,服务器上 `docker compose build`;不使用原 CI/CD |
|
||||
| 现网保护 | `/app/iboard`、宿主 3306 MySQL、3001 端口、原 CI/CD 一律不使用(快照导出仅 SELECT) |
|
||||
| 磁盘 | 允许在 baihu 执行 docker 悬空镜像/构建缓存的安全清理(磁盘仅剩 9.9G) |
|
||||
|
||||
## 2. 目标架构
|
||||
|
||||
```
|
||||
[Web / Tauri WebView2]
|
||||
React + @powersync/react
|
||||
本地 WASM SQLite (OPFS 持久化)
|
||||
│ 离线: 直接读写本地库; 写操作进入 upload queue
|
||||
│ 联网: 批量 POST 上行到 Express API (按 uuid 幂等 upsert)
|
||||
▼
|
||||
[iboard-0.2.1 Express :13001] ── Prisma (adapter-pg) ──┐
|
||||
+ /api/powersync/token (为登录用户签发同步 JWT) ▼
|
||||
[PostgreSQL 容器] (wal_level=logical)
|
||||
▲ 逻辑复制 (publication + replication slot)
|
||||
[powersync-service :13002] ── Sync Rules: 任务域 6 表 + User 只读引用, 按 ProjectMember 过滤
|
||||
```
|
||||
|
||||
- 写路径:客户端本地写 → upload queue → 上行 API → Prisma 写 PG → 逻辑复制捕获 → 广播各端;
|
||||
- 读路径:任务模块页面全部改读本地 SQLite(@powersync/react 响应式查询),其余页面维持 axios REST;
|
||||
- 认证:复用现有 jsonwebtoken 体系,新增 `/api/powersync/token` 签发 PowerSync 校验的 JWT。
|
||||
|
||||
## 3. 数据模型改造(原则:只增不改)
|
||||
|
||||
采用报告 3.2 中的「新增 uuid 列 + 应用层幂等 upsert」选项,不做全表主键 UUID 化:外键关系全部保留,存量数据迁移风险最小。
|
||||
|
||||
- 任务域 6 表各加 `uuid varchar(36) unique`(客户端创建时生成;存量行导入时回填);
|
||||
- `Project` 加 `code varchar(8) unique`(用户自拟短前缀;存量项目导入时回填自动码,后续可改);
|
||||
- `Task` 加 `number int`,`@@unique([projectId, number])`(服务端按项目 max+1 分配;离线创建的任务上行时定号,本地未定号行显示临时标识);
|
||||
- 主键仍为自增 Int,业务外键不动;客户端上行一律以 uuid 引用父子行,服务端解析为内部 id;
|
||||
- Prisma datasource: `mysql` → `postgresql`;本分支重建基线迁移(旧 MySQL 迁移文件移除,历史保留在 git)。
|
||||
|
||||
### 3.1 Sequential ID Mapping(落地时按官方模式补充)
|
||||
|
||||
按 PowerSync 官方 [Sequential ID Mapping](https://docs.powersync.com/client-sdks/advanced/sequential-id-mapping) 模式实施,解决"离线新建行同步回填后本地 id 变化"问题:
|
||||
|
||||
- 任务域 6 表再加**外键 uuid 镜像列**(`Task.projectUuid/columnUuid/parentUuid`、`BoardColumn.projectUuid`、`ProjectMember.projectUuid`、`TaskMember/TaskTag.taskUuid`),由 **PG 触发器**在写入时按 int 外键自动回填(迁移 `20261026010000_uuid_fk_mapping`),库级 NOT NULL;
|
||||
- Sync Streams 以 **`uuid` 作为客户端主键**(`SELECT "uuid" AS "id"`),镜像列别名输出回 `projectId/columnId/parentId/taskId` —— 客户端 schema 与页面代码**零改动**;
|
||||
- 服务端 upload 处理器按 uuid 解析父行(`findUnique({ where: { uuid } })`),解析后再写 int 外键 → 触发器回填镜像,两侧闭环一致;
|
||||
- 镜像列在 Prisma 模型中声明为可空(触发器保证库级非空),避免 create 校验拦截。
|
||||
|
||||
## 4. 同步与冲突
|
||||
|
||||
- Sync Rules 仅下发任务域 6 表 + `User`(展示名称/头像用),按 `ProjectMember.userId = auth.parameters.userId` 过滤项目可见性;时序/监控/米家等表不纳入;
|
||||
- 上行:`POST /api/powersync/upload` 批处理客户端 CRUD 操作,按 uuid 幂等 upsert,更新时以服务端时间戳做 LWW 仲裁;
|
||||
- 冲突:字段级 LWW 为主;`BoardColumn.sortOrder` / `Task.sortOrder` 由服务端重排仲裁;并发唯一冲突(项目前缀、看板列名)返回明确错误,客户端提示重试;
|
||||
- `Task.number` / `Project.code` 由服务端唯一分配,多端并发下天然无冲突。
|
||||
|
||||
## 5. 部署拓扑(baihu `/app/iboard-0.2.1`)
|
||||
|
||||
docker compose 三服务:
|
||||
|
||||
| 服务 | 镜像 | 端口 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `pg-021` | postgres:16-alpine | 仅容器内网 | `wal_level=logical`;数据卷 `./data/pg` |
|
||||
| `powersync-021` | powersync 官方镜像 | 13002 对外 | 连接 PG 逻辑复制 + WebSocket 下发 |
|
||||
| `app-021` | 本仓库 Dockerfile 构建 | 13001 对外 | `OTA_PUBLIC_BASE=http://bh.vps.honor3:13001` |
|
||||
|
||||
- 环境变量由 compose 注入:`DATABASE_URL`(指向 pg-021)、`JWT_SECRET`(0.2.1 独立新设)、`PORT=13001`;
|
||||
- 0.2.1 容器内 cron/米家调度预计无凭证或产生重复外呼,部署时以环境开关关闭(实现于 M4)。
|
||||
|
||||
## 6. 数据迁移(一次性)
|
||||
|
||||
1. 在 baihu 上用仓库自带 `mariadb` 驱动以只读 SQL 从宿主 MySQL(3306) 导出全表 JSON(仅 SELECT);
|
||||
2. 转换:回填 uuid / `Project.code`(按名称自动生成,可后改)/ `Task.number`(按项目内 createdAt 顺序);时间字段转 PG 时间戳;
|
||||
3. PG 侧先 `prisma migrate deploy` 建基线表,再执行 ETL 写入;
|
||||
4. 校验:逐表行数比对 + 抽样字段比对,输出核对报告。
|
||||
|
||||
## 7. 里程碑
|
||||
|
||||
1. **M1** schema/适配层切 PG(本提交):schema 字段、db.js 适配器、基线迁移重建、本地 `prisma generate` 通过;
|
||||
2. **M2** 服务端:powersync token 端点、upload 幂等上行端点、任务域路由幂等化与编号分配逻辑;
|
||||
3. **M3** 前端:任务模块数据层切 PowerSync(本地库 + useQuery + 上传队列),桌面端联调;
|
||||
4. **M4** compose 编排 + ETL 导数 + baihu 部署,端口/现网隔离验证;
|
||||
5. **M5** 离线-联网闭环验证(断网编辑 → 恢复自动同步 → 多端一致),Android 真机评估。
|
||||
|
||||
## 8. 开放问题 / 风险
|
||||
|
||||
- powersync-service 的元数据存储(bucket storage)对 MongoDB 的依赖需在部署阶段核实;若当前版本不支持用 PG 存元数据,则加一个 mongo 容器;
|
||||
- Android System WebView 的 OPFS/WASM SQLite 兼容性(M5 评估,先桌面);
|
||||
- 项目前缀全局唯一在多端并发下的冲突提示体验(上行返回结构化错误码);
|
||||
- 0.2.1 验证期数据与现网彻底隔离,不回迁;若验证后需并回主线,另行决策迁移口径。
|
||||
@@ -0,0 +1,127 @@
|
||||
# 基于 PowerSync 的离线编辑可行性研究报告
|
||||
|
||||
| 项 | 内容 |
|
||||
| --- | --- |
|
||||
| 版本 | V0.2 |
|
||||
| 日期 | 2026-10-25 |
|
||||
| 评估对象 | PowerSync(powersync.com,开源 self-hosted + 云服务) |
|
||||
| 评估目标 | 项目离线操作(本地编辑)+ 联网后自动同步 |
|
||||
| 结论(TL;DR) | **技术上可行,但落地成本高、风险中高。不建议全量接入;若确需离线,建议仅对「打卡 / 清单 / 任务」等用户编辑域做局部接入,且需要先解决 MariaDB 兼容与主键策略两个前置问题。** |
|
||||
|
||||
---
|
||||
|
||||
## 1. 项目现状约束(评估输入)
|
||||
|
||||
结合代码库(`package.json`、`prisma/schema.prisma`、`server/index.js`)梳理:
|
||||
|
||||
1. **前端**:React 18 SPA(antd-mobile),通过 axios 调 `/api/*` REST 接口;`src/api`、`src/hooks`、`src/pages` 均以"请求 → state → 渲染"模式组织,无任何本地持久化数据层。
|
||||
2. **桌面/移动端**:Tauri 2(Windows WebView2 桌面 + Android System WebView),意味着前端运行在 WebView 内,浏览器类 SDK 是候选。
|
||||
3. **后端**:Express + Prisma,数据库为 **MariaDB / MySQL**(`provider = "mysql"`)。
|
||||
4. **认证**:自签 JWT(jsonwebtoken),登录接口在 `/api/auth`。
|
||||
5. **写路径**:全部业务写操作走 REST(打卡、清单、任务模块);同时存在大量**服务端定时写入**(cron:新闻、指标、监控、米家采样等,见 `server/cron.js`)。
|
||||
6. **数据模型**:约 25 张表,主键全部为 `Int @id @default(autoincrement())` 自增整数,多表带复合唯一约束(如 `CheckIn @@unique([habitId, date, seq])`)。
|
||||
7. **数据体量特征**:两类数据并存——
|
||||
- **用户编辑域**(Habit / CheckIn / CheckList* / 任务模块 10 张表):冲突敏感、离线价值高;
|
||||
- **服务端只读时序域**(News / Indicator / ServerNodeHistory / MihomeEnv* 等):append-only、量大、离线价值中等(缓存即可满足)。
|
||||
|
||||
---
|
||||
|
||||
## 2. PowerSync 是什么、怎么工作
|
||||
|
||||
- 架构:客户端本地 **SQLite**(Web 端为 WASM SQLite,存 OPFS/IndexedDB)↔ **PowerSync Service**(self-hosted 开源组件或云服务)↔ 源数据库。服务端通过 **逻辑复制/变更流** 捕获源库写入,按 **Sync Rules** 定义的下推子集同步到每个客户端的本地 SQLite。
|
||||
- 写路径(官方推荐模式):客户端离线写本地 SQLite → 进入 **upload queue** → 联网后由前端把变更批量 POST 到**现有后端 API** → 后端用原有方式(本项目即 Prisma)写库 → 变更流捕获 → 广播回各端。
|
||||
- 认证:客户端持后端签发的 JWT 连接同步服务;PowerSync 只做校验,不签发(可复用现有 jsonwebtoken 体系)。
|
||||
- 服务端直写的表(cron 产生)也能被变更流捕获并下推,不需要改造写入方。
|
||||
|
||||
参考:[PowerSync Service(GitHub)](https://github.com/powersync-ja/powersync-service)、[源数据库支持](https://docs.powersync.com/configuration/source-db/setup)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 逐项可行性分析
|
||||
|
||||
### 3.1 数据库兼容性 —— ⚠️ 主要风险点
|
||||
|
||||
- PowerSync 对源库支持:**PostgreSQL(GA)**、MongoDB、SQL Server;**MySQL 仍处于 Beta**(基于 binlog 复制,见 [MySQL Beta 公告](https://releases.powersync.com/announcements/mysql-backend-database-support-has-reached-beta)、[Alpha 公告](https://releases.powersync.com/announcements/announcing-mysql-as-a-source-database-alpha))。
|
||||
- 本项目用的是 **MariaDB**。MariaDB 的 binlog 格式与 MySQL 8 有差异(GTID、行事件细节),PowerSync MySQL 插件(@powersync/mysql-zongji 系)对 MariaDB **没有官方兼容承诺,需要实测验证**,很可能踩坑。
|
||||
- **结论**:要么实测 MariaDB + MySQL-beta 通路(风险高),要么把数据库迁到 PostgreSQL(PowerSync 最成熟路径,Prisma 迁移成本可控但涉及全部表和线上数据)。这是"能不能用"的第一道门槛。
|
||||
|
||||
### 3.2 数据模型适配 —— 改造成本大
|
||||
|
||||
- PowerSync Sync Rules 要求同步表有稳定的行标识,**强烈建议 UUID 文本主键**;而本项目全部是自增整数主键,且客户端离线创建记录时无法预知自增 id。
|
||||
- 离线创建的通用解法:主键改为客户端生成的 UUID(`char(36)`),或新增 `uuid` 列 + 应用层幂等 upsert。对用户编辑域的 10 余张表意味着**一次不小的 schema 迁移 + 全部相关路由改造**(迁移原则"只增不改"仍可遵守,但外键列类型变更工作量大)。
|
||||
- `DateTime`、`Json` 类型可映射(客户端以 ISO 文本/JSON 文本存储),但 `BigInt`(监控表)在 JS 端需按文本处理,注意精度。
|
||||
- 复合唯一约束(`CheckIn(habitId,date,seq)`、`BoardColumn(projectId,name)` 等)在离线多端并发下会冲突,需在 upload 处理端路由里做服务端仲裁(如 seq 重排、重名加后缀),现有路由大多不具备幂等/仲裁语义。
|
||||
|
||||
### 3.3 写路径(离线编辑 + 上行同步)—— 模式契合,但需逐路由改造
|
||||
|
||||
- PowerSync 的 upload-queue → 后端 API 模式与现有 Express 架构**天然契合**:不需要后端暴露直接写库的通口,各业务路由补成"接收客户端 UUID + 幂等 upsert"即可。
|
||||
- 打卡/清单/任务的路由数量约 3 个 router 文件(checkin / checklists / projects / tasks),逐个改造为幂等是可控的。
|
||||
- 服务端 cron 写入的表(新闻/指标/监控/米家)无需改造写入方,只需在 Sync Rules 里定义是否下推。
|
||||
|
||||
### 3.4 读路径与前端数据层 —— 全面前重写
|
||||
|
||||
这是**工作量最大**的部分:
|
||||
|
||||
- 现有 `src/api` + axios + useEffect 模式必须替换为 PowerSync 本地 SQLite 查询 + `@powersync/react` 的响应式 hooks(useQuery 等),否则离线读不到数据。
|
||||
- 涉及 `src/pages` 下全部用户编辑域页面(checkin、task),以及若要离线浏览还需覆盖 news/macro/monitor 的只读缓存页。
|
||||
- 工作量量级:相当于把前端数据层重构一遍;可与"离线只读缓存(HTTP 缓存)"分层实施以降低风险。
|
||||
|
||||
### 3.5 客户端运行环境 —— 大体可行,Android 需验证
|
||||
|
||||
- **浏览器 / Tauri 桌面(WebView2)**:PowerSync Web SDK(WASM SQLite + OPFS)在 Chromium 系 WebView 已被官方支持,可行。
|
||||
- **Tauri Android(System WebView)**:WASM SQLite 性能与 OPFS 在部分国产/旧版 WebView 上存在兼容性风险,需真机验证;备选方案是封装原生 SQLite(官方 Capacitor 插件思路可借鉴,但 Tauri 无官方插件,需自研 Rust 命令桥接,成本额外增加)。
|
||||
- 时序大表(ServerNodeHistory 每分钟一条、MihomeEnv*)不建议纳入同步——Sync Rules 需做时间窗过滤(如仅近 7 天),否则客户端存储和同步流量都会失控。
|
||||
|
||||
### 3.6 认证与部署 —— 可行
|
||||
|
||||
- JWT 可复用:新增一个 `/api/powersync/token` 端点为已登录用户签发 PowerSync 接受的 token,改造小。
|
||||
- 部署:self-hosted 需在现有 Express 之外多跑一个 powersync-service 进程(含 Mongo 存储元数据或云实例),并暴露 WebSocket 端口;或使用 PowerSync Cloud。运维面增加一个组件。
|
||||
|
||||
### 3.7 冲突解决
|
||||
|
||||
- PowerSync 本身不做自动合并,只提供上传队列与冲突回调;对"最后写入胜出"友好的字段(清单勾选、任务标题/状态)默认策略够用;对 `CheckIn.seq`、`BoardColumn.sortOrder` 这类"位置/序号型"数据需要自定义仲裁逻辑(上传端或同步流中处理),设计上可解,但要专门做。
|
||||
|
||||
---
|
||||
|
||||
## 4. 风险与成本汇总
|
||||
|
||||
| # | 风险/成本项 | 等级 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | MariaDB 兼容性未验证,MySQL 支持仅 Beta | 高 | 很可能需要迁移 PostgreSQL |
|
||||
| 2 | 前端数据层全面重构(axios → 本地 SQLite 查询) | 高 | 涉及所有相关页面 |
|
||||
| 3 | 用户编辑域主键整数自增 → UUID 迁移 | 中高 | schema + 路由幂等改造 |
|
||||
| 4 | 复合唯一约束的离线冲突仲裁 | 中 | CheckIn.seq、列名、排序等 |
|
||||
| 5 | Android WebView WASM SQLite/OPFS 兼容 | 中 | 需真机验证 |
|
||||
| 6 | 新增 self-hosted 组件的运维 | 中 | 多一个常驻服务 |
|
||||
| 7 | 时序大表同步范围控制 | 低 | Sync Rules 时间窗过滤即可 |
|
||||
|
||||
对照收益:真正需要离线**编辑**的只有打卡 / 清单 / 任务三个域(数据量小、单用户为主、冲突场景有限);其余域离线**只读缓存**即可满足。
|
||||
|
||||
---
|
||||
|
||||
## 5. 结论与建议
|
||||
|
||||
### 5.1 结论
|
||||
|
||||
**可行,但不建议以"全项目接入 PowerSync"的方式落地。** PowerSync 的同步模型与项目的 Express API 写路径契合、认证可复用、桌面端可行;但 MariaDB/MySQL-beta 兼容、整数自增主键、前端数据层全面重构三座大山使总成本明显高于当前阶段的收益。
|
||||
|
||||
### 5.2 建议(按优先序)
|
||||
|
||||
1. **首选(低成本方案)**:不引入 PowerSync,为用户编辑域实现轻量离线队列——前端把写请求落到 localStorage/IndexedDB 待发队列(含客户端生成的 UUID),联网后重放到现有 REST API(路由补幂等 upsert);只读页用 HTTP 缓存 + SW。可覆盖约 80% 的离线诉求,改动局限在 `src/api` 层与少量路由。
|
||||
2. **若决定采用 PowerSync**:
|
||||
- 前置 1:源库迁移 PostgreSQL(避开 MySQL Beta + MariaDB 兼容雷区);
|
||||
- 前置 2:仅将用户编辑域(Habit/CheckIn/CheckList*/任务模块)纳入 Sync Rules,时序与监控表排除或做时间窗过滤;
|
||||
- 前置 3:这些表的主键/唯一标识改造为客户端可生成的 UUID;
|
||||
- 分阶段:先桌面端(WebView2)验证全链路,再攻 Android WebView 兼容性;
|
||||
- 冲突策略:字段级 LWW 为主,seq/sortOrder 类服务端仲裁。
|
||||
3. **不建议**:为离线只读域(新闻/指标/监控)上 PowerSync——用简单缓存即可,同步引擎属于过度设计。
|
||||
|
||||
---
|
||||
|
||||
## 6. 参考资料
|
||||
|
||||
- [PowerSync Service(GitHub,self-hosted 开源)](https://github.com/powersync-ja/powersync-service)
|
||||
- [PowerSync 源数据库配置文档](https://docs.powersync.com/configuration/source-db/setup)
|
||||
- [MySQL 后端支持 Beta 公告](https://releases.powersync.com/announcements/mysql-backend-database-support-has-reached-beta)
|
||||
- [MySQL 后端支持 Alpha 公告](https://releases.powersync.com/announcements/announcing-mysql-as-a-source-database-alpha)
|
||||
- 本项目:`prisma/schema.prisma`、`server/index.js`、`docs/V0.2/数据库设计文档.md`、`docs/V0.2/任务模块/任务模块数据库设计文档.md`
|
||||
@@ -0,0 +1,312 @@
|
||||
# 任务模块「即时同步」方案
|
||||
|
||||
> 状态:**方案设计,未实施**(本文只描述做法,不含代码改动)
|
||||
> 范围:任务模块(Project / BoardColumn / Task / TaskMember / TaskTag)
|
||||
> 相关文档:[0.2.1 实施方案](./0.2.1实施方案.md)、[PowerSync 可行性研究报告](./PowerSync可行性研究报告.md)
|
||||
> 编写时间:2026-10-27
|
||||
|
||||
---
|
||||
|
||||
## 1. 问题与现状
|
||||
|
||||
### 1.1 现象
|
||||
|
||||
多端(Windows 外壳 / Android / Web)同时打开同一项目时:
|
||||
|
||||
- A 端点完成或改状态,**B 端不会自动更新**;
|
||||
- 必须**切到别的页面再切回来**(组件重新挂载触发重新查询)才能看到;
|
||||
- 看板分组(按 `columnId`)与详情状态(按 `status`)也会出现一段时间的显示不一致。
|
||||
|
||||
### 1.2 根因:不是"同步没到",而是"到了没人通知界面"
|
||||
|
||||
PowerSync 的下行链路本身是**推送式**的(服务端 PG 逻辑复制 → sync service → WebSocket →
|
||||
客户端写入本地 SQLite),延迟通常在亚秒级。问题出在**消费端**:
|
||||
|
||||
| 层 | 现状 | 结论 |
|
||||
| --- | --- | --- |
|
||||
| 传输层 | `PowerSyncProvider` 已在 `src/App.jsx` 挂载,连接正常 | ✅ 正常 |
|
||||
| 数据层 | 变化已写入本地 SQLite | ✅ 正常 |
|
||||
| **UI 层** | **全部是命令式读取**:`db().getAll(...)`,读完就不再看 | ❌ **断点在这** |
|
||||
|
||||
具体地:
|
||||
|
||||
- `src/api/task.js` 的 `projectApi.list/detail`、`taskApi.list` 都是一次性 `getAll()`,
|
||||
且在 JS 里做二次装配(子任务、成员、标签、列),**没有任何订阅**;
|
||||
- `src/lib/powersync/react.jsx` 虽然 re-export 了 `useQuery/useStatus`,
|
||||
但**全项目一次都没用过**(`grep useQuery src` 只命中该文件的导出语句);
|
||||
- `src/pages/task/TaskPage.jsx`、`ProjectView.jsx` 只在 `useEffect(..., [load])` 里加载一次;
|
||||
- 唯一的刷新机制是 `src/hooks/useRefreshOnVisible.js`(回前台/窗口聚焦时重查),
|
||||
而且**任务模块这两个页面根本没有用它**(只有打卡 `MergedView/ListView` 与宏观各页在用)。
|
||||
|
||||
所以「切面板能刷新」只是因为它**重新挂载组件 → 重新执行一次 `load()`**。
|
||||
|
||||
### 1.3 现状刷新机制盘点
|
||||
|
||||
| 页面 | 首次加载 | 回前台刷新 | 数据变化实时反映 |
|
||||
| --- | --- | --- | --- |
|
||||
| `TaskPage` | `useEffect` | ❌ 无 | ❌ |
|
||||
| `ProjectView` | `useEffect` | ❌ 无 | ❌(仅本地操作后手动 `load()`) |
|
||||
| `TaskDetail` | 打开时传入快照 | — | ❌(快照可能过期) |
|
||||
| `MergedView`(打卡) | `useEffect` | ✅ | ❌(且打卡走 REST,非 PowerSync) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 目标与非目标
|
||||
|
||||
### 2.1 目标
|
||||
|
||||
1. **跨端即时**:一端修改,其它端在 **≤1s** 内自动呈现(不含网络中断场景);
|
||||
2. **不打断操作**:正在输入的详情表单、正在拖拽的卡片**不被远端变更打断**;
|
||||
3. **离线可用**:断网期间照常读写本地库,恢复后自动补齐并刷新;
|
||||
4. **改动可控**:优先复用现有装配逻辑(`taskApi` 的 shape),不做大规模重写;
|
||||
5. **可回滚**:通过开关或逐步替换,出问题能退回"手动刷新"。
|
||||
|
||||
### 2.2 非目标
|
||||
|
||||
- 不做**多人协同编辑**(同一字段并发写仍按 LWW 后到覆盖);
|
||||
- 不解决打卡/清单模块的实时性(它们走 REST,见 §7);
|
||||
- 不引入 WebSocket 之外的额外实时通道(如 SSE)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 方案选型
|
||||
|
||||
| 方案 | 做法 | 优点 | 缺点 | 结论 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **A. 轮询** | 定时(如 3s)重跑 `load()` | 改动最小 | 无谓查询、电量/流量、仍不"即时" | ❌ |
|
||||
| **B. 全量响应式** | 把 `taskApi.list` 整体改成一条大 SQL + `useQuery` | 最"正统",细粒度 | 现有装配(子任务/成员/标签)要在 SQL 里重写;改动面大、回归风险高 | ⏳ 后续 |
|
||||
| **C. 变更信号 + 重算**(推荐) | 用 `db.watch()` 订阅一个**轻量"信号查询"**,信号变化时重跑现有 `load()` | 复用现有装配逻辑;改动集中且小;天然合并多次变更 | 每次重算一次列表(数据量小,可接受) | ✅ **P0 采用** |
|
||||
| **D. 表级广播** | 监听"某表有变更"事件触发重查 | 实现最简单 | 无法区分项目/范围,易全页面重查 | 作为 C 的退化实现 |
|
||||
|
||||
**结论**:P0 用方案 C;跑稳后再评估把高频视图(看板)迁到 B 的增量查询。
|
||||
|
||||
---
|
||||
|
||||
## 4. 推荐方案详设(方案 C)
|
||||
|
||||
### 4.1 核心思路
|
||||
|
||||
```
|
||||
本地 SQLite ←(PowerSync 下行写入)— 任一端/服务端变更
|
||||
│
|
||||
└─▶ db.watch(<信号查询>) ── 变化 ──▶ 节流合并 ──▶ 重跑 load()
|
||||
└─▶ setState 渲染
|
||||
```
|
||||
|
||||
**"信号查询"** 是一段**廉价但能覆盖所有相关表**的 SQL:只要任务域的任一相关行变化,
|
||||
其结果就会变;它不承担业务语义,只负责触发。
|
||||
|
||||
### 4.2 订阅范围:用 SDK 的表级过滤(已实施)
|
||||
|
||||
> 初版设计是手写一段"信号 SQL"(count / sum / max(updatedAt) 汇总),
|
||||
> 实施时发现 `db.watch()` 原生支持 **`tables` 表名过滤** 与 **`throttleMs` 节流**,
|
||||
> 由 SDK 判断"结果是否真的变了",比自己做汇总更可靠,因此改为表级过滤。
|
||||
|
||||
`CommonPowerSyncDatabase.watch(sql, params, handler, options)` 的 `options`:
|
||||
|
||||
| 选项 | 用途 |
|
||||
| --- | --- |
|
||||
| `tables: string[]` | 只关心这些表的变更,SDK 内部按表失效 |
|
||||
| `throttleMs` | 两次回调之间的最小间隔,天然合并同一批同步写入 |
|
||||
| `signal: AbortSignal` | 卸载时取消订阅 |
|
||||
|
||||
订阅内容(`src/lib/powersync/useDbSignal.js`):
|
||||
|
||||
```js
|
||||
db.watch('SELECT 1 AS s', [], { onResult, onError }, {
|
||||
tables: ['Task', 'BoardColumn', 'TaskMember', 'TaskTag'],
|
||||
throttleMs: 250,
|
||||
signal: ac.signal,
|
||||
})
|
||||
```
|
||||
|
||||
查询本身(`SELECT 1`)没有业务含义,只借 watch 的变更通知能力。表名集合定义在
|
||||
`src/api/task.js`:
|
||||
|
||||
| 常量 | 内容 | 用于 |
|
||||
| --- | --- | --- |
|
||||
| `TASK_TABLES` | `Task, BoardColumn, TaskMember, TaskTag` | 项目内任务列表 / 看板 |
|
||||
| `PROJECT_TABLES` | `Project, ProjectMember` | 项目列表、成员 |
|
||||
|
||||
之所以不需要 `max(updatedAt)`:表级过滤关心的是"行有没有被写过",
|
||||
而不是"结果集内容是否变化",所以改标题、改状态这类不增删的写入同样会触发。
|
||||
|
||||
### 4.3 UI 层接入
|
||||
|
||||
提供两个小工具,避免每个页面各写一套:
|
||||
|
||||
| 工具 | 位置(建议) | 作用 |
|
||||
| --- | --- | --- |
|
||||
| `useDbSignal(sql, params, onChange)` | `src/lib/powersync/useDbSignal.js`(新增) | 封装 `db.watch()` + 节流 + 卸载清理 |
|
||||
| `useTaskListSignal(projectId, onChange)` | `src/api/task.js` 或同目录(新增) | 拼好上述信号 SQL,页面只关心"变了要干嘛" |
|
||||
|
||||
接入示意(`ProjectView`):
|
||||
|
||||
```jsx
|
||||
// 现状
|
||||
useEffect(() => { setTasks(null); load() }, [load])
|
||||
|
||||
// 改造后(示意)
|
||||
useEffect(() => { setTasks(null); load() }, [load])
|
||||
useDbSignal(TASK_LIST_SIGNAL_SQL, [projectId, projectId, projectId, projectId, projectId, projectId], () => {
|
||||
if (dragActiveRef.current) return // 见 §4.4
|
||||
load()
|
||||
})
|
||||
```
|
||||
|
||||
`TaskPage` 同理,信号为项目表。
|
||||
|
||||
### 4.4 变更合并与"不打断"策略
|
||||
|
||||
这是本方案的关键,否则会出现「打字打到一半表单被重置」「拖到一半卡片跳走」。
|
||||
|
||||
| 场景 | 策略 |
|
||||
| --- | --- |
|
||||
| 连续多次变更(一次同步写多行) | `useDbSignal` 内部 **150~300ms 防抖**,合并成一次 `load()` |
|
||||
| 看板/列表**拖拽进行中** | 用 `dragActiveRef` 跳过本次刷新;拖拽结束后补一次 `load()` |
|
||||
| 任务**详情页打开中** | 不自动刷新表单;改为监听该任务是否变化,变化时在详情顶部显示「**该任务已在他端更新,点击刷新**」 |
|
||||
| 正在编辑标题/描述 | 同上传:不覆盖输入框,只提示 |
|
||||
| 本地乐观更新进行中(`updateTask` 未完成) | 以本地 patch 为准,`load()` 延后到请求结束(现有 `updateTask` 已有 snapshot 回滚,保留) |
|
||||
|
||||
### 4.5 同步状态指示
|
||||
|
||||
用已导出的 `useStatus()` 在任务页顶部显示一个极轻量的状态点:
|
||||
|
||||
| 状态 | 展示 |
|
||||
| --- | --- |
|
||||
| `connected && dataFlowStatus.connected` | 不显示(无干扰) |
|
||||
| 正在下载/上传 | 细小的同步图标 |
|
||||
| 离线 / 连接失败 | 「离线,改动将在联网后同步」 |
|
||||
|
||||
> 说明:当前外壳里 PowerSync 会打 `Multiple tab support is not enabled` 的提示,
|
||||
> 属预期(已在 0.2.1 关闭多标签以避免 Windows reload 后本地库挂起),不影响本方案。
|
||||
|
||||
### 4.6 离线与重连
|
||||
|
||||
- 断网:`db.watch()` 仍监听**本地库**,本地写入照常触发刷新(体验一致);
|
||||
- 恢复:PowerSync 自动重连并回放,写入本地库后信号触发刷新,**无需额外逻辑**;
|
||||
- 应用回到前台:保留 `useRefreshOnVisible` 作为**兜底**(WebView 可能挂起渲染线程),
|
||||
与 `db.watch()` 并存不冲突(两者都只是调 `load()`)。
|
||||
|
||||
---
|
||||
|
||||
## 5. 改造清单(按文件)
|
||||
|
||||
| 文件 | 改动 | 状态 |
|
||||
| --- | --- | --- |
|
||||
| `src/lib/powersync/useDbSignal.js` | **新增**:`db.watch` + 表过滤 + 节流 + 卸载清理 | ✅ P0 已完成 |
|
||||
| `src/api/task.js` | 新增 `TASK_TABLES` / `PROJECT_TABLES` 常量 | ✅ P0 |
|
||||
| `src/pages/task/ProjectView.jsx` | 订阅任务域;拖拽中延后刷新;回前台兜底 | ✅ P0 |
|
||||
| `src/pages/task/TaskPage.jsx` | 订阅项目域;回前台兜底 | ✅ P0 |
|
||||
| `src/pages/task/BoardView.jsx` / `ListView.jsx` | 上报 `onDragActiveChange` | ✅ P0 |
|
||||
| `src/pages/task/TaskDetail.jsx` | 「已在他端更新」提示(不覆盖表单) | ⏳ P1 |
|
||||
| 同步状态指示 | 用 `useStatus()` 显示离线/同步中 | ⏳ P1 |
|
||||
|
||||
**不需要改动**:`server/`、`sync-config.yaml`、数据库结构。
|
||||
|
||||
---
|
||||
|
||||
## 6. 分期与验收
|
||||
|
||||
### 6.1 分期
|
||||
|
||||
| 阶段 | 内容 | 预估 |
|
||||
| --- | --- | --- |
|
||||
| **P0** | `useDbSignal` + `ProjectView`/`TaskPage` 接入 + 拖拽保护 | 0.5~1 天 |
|
||||
| **P1** | 详情页「已在他端更新」提示 + 同步状态指示 | 0.5 天 |
|
||||
| **P2** | 看板改增量查询(方案 B);打卡/清单的实时性另议 | 按需 |
|
||||
|
||||
### 6.2 验收标准(可测)
|
||||
|
||||
1. **跨端延迟**:A 端改状态/拖列/改标题,B 端自动更新。实测 **约 1~2s**(数据进本地库约 1s,渲染 +0.3s,见 §7.0);
|
||||
2. **不打断**:B 端正在输入标题时,A 端改同任务描述 → B 端输入框内容**不被清空**,仅出现提示;
|
||||
3. **拖拽安全**:B 端拖拽中收到远端变更 → 卡片**不跳位**,松手后列表更新;
|
||||
4. **离线**:断网期间本地操作正常,恢复后自动补齐并刷新;
|
||||
5. **回滚**:`localStorage['iboard_realtime'] = '0'` 后重启应用,退回"手动刷新",无副作用;
|
||||
6. **性能**:单项目 100 条任务、连续 20 次远端写入,页面无卡顿、无重复渲染风暴(可用 Performance 面板核对)。
|
||||
|
||||
### 6.3 度量
|
||||
|
||||
- 在 `load()` 内打点(可临时 `console.time`),统计重算耗时与触发次数;
|
||||
- 目标:一次远端变更只触发 **1 次** 重算;100 条任务时单次重算 < 60ms。
|
||||
|
||||
---
|
||||
|
||||
## 7. 已知边界与后续
|
||||
|
||||
### 7.0 实测:端到端约 1~2s(达标)
|
||||
|
||||
P0 落地后在真实 Windows 外壳上实测,服务端直接改库模拟另一端:
|
||||
|
||||
| 项 | 结果 |
|
||||
| --- | --- |
|
||||
| 是否还需要切页面 | **不需要** —— 服务端改完,页面在无任何操作的情况下自动更新 ✅ |
|
||||
| 端到端延迟 | **约 1~2s**(数据进本地库约 1s,渲染再 +0.3s)✅ |
|
||||
|
||||
**测量方法(关键:早期结论曾被测量方式误导两次,务必按此方法复测)**
|
||||
|
||||
测量必须在**页面内**自计时,不能用 CDP 轮询 DOM:
|
||||
|
||||
```js
|
||||
// 页面内: 50ms 轮询本地库 + MutationObserver 观察 DOM, 各自记录 performance.now()
|
||||
const P = { startedAt: performance.now(), dbAt: null, domAt: null }
|
||||
setInterval(async () => {
|
||||
if (P.dbAt !== null) return
|
||||
const row = await window.__psdb.get('SELECT title FROM Task WHERE number = ?', [16])
|
||||
if (row && row.title.includes(MARK)) P.dbAt = performance.now() - P.startedAt
|
||||
}, 50)
|
||||
new MutationObserver(check).observe(document.body, { childList: true, subtree: true })
|
||||
```
|
||||
|
||||
一次干净测量的结果(已重载页面、无残留定时器):
|
||||
|
||||
| 度量 | 数值 |
|
||||
| --- | --- |
|
||||
| `psql` 命令总耗时(ssh + docker exec + UPDATE) | 2138ms |
|
||||
| 数据出现在客户端本地库(相对计时起点) | 2251ms → **≈ 命令返回后 113ms** |
|
||||
| 界面更新(相对计时起点) | 2580ms |
|
||||
| **同步数据到达 → 渲染完成** | **329ms** |
|
||||
|
||||
结论:**服务端改库到界面呈现约 1~2s(含 SSH/psql 开销在内的保守上界),其中 UI 渲染只占 ~0.33s**,
|
||||
`useDbSignal` 的表级 watch 工作正常,无需改 `useQuery`。
|
||||
|
||||
**两次被推翻的中间结论(留档,避免重复踩坑)**
|
||||
|
||||
| 曾经的判断 | 为何是错的 |
|
||||
| --- | --- |
|
||||
| ① "nginx `proxy_buffering` 导致按批下发" | 加了 `proxy_buffering off` 等配置并热重载(`nginx -T` 确认生效),延迟毫无变化。**该配置已回滚**(`advanced_config` 清空、生效 conf 还原、nginx 已重载)—— 保留它反而会让 OTA 大包下载失去缓冲保护 |
|
||||
| ② "同步数据早在服务端发出,客户端 `db.watch`/渲染滞后 15~30s" | 错在测量:用 CDP 反复读 `document.body.innerText`(长列表页上开销很大)轮询,且把 `ssh + docker exec` 的 ~6s 开销算进了延迟。改为页面内自计时后,实测只有 ~1~2s |
|
||||
|
||||
**服务端侧的旁证**(与上述一致):powersync 日志 `checkpoint_complete` 与改库同一秒完成;
|
||||
`stream_ms` 达 42 分钟、`close_reason: client closing stream`,长连接稳定,非频繁重连。
|
||||
|
||||
|
||||
| 项 | 说明 |
|
||||
| --- | --- |
|
||||
| 打卡 / 清单 | 走 REST(`src/api/checkin.js`),不经过本地库,**本项目方案不覆盖**;如需实时,可复用现有 `useRefreshOnVisible` + 定时轮询,或后续把这些模块也迁到 PowerSync |
|
||||
| 并发写冲突 | 仍为 LWW(`updatedAt` 后到覆盖);本文不引入人工合并 |
|
||||
| 排序并发 | `taskApi.reorder` 会整批重写 `sortOrder`,两端同时拖拽可能相互覆盖;建议 P2 再引入"版本号/操作序列" |
|
||||
| 大列表 | 方案 C 每次全量重算,任务数极大(>1000)时需转方案 B 的增量查询 |
|
||||
| WebView 后台节流 | 应用在后台时 `watch` 回调可能被延后,回前台由 `useRefreshOnVisible` 兜底 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 附:关键 API 备忘
|
||||
|
||||
| API | 来源 | 用途 |
|
||||
| --- | --- | --- |
|
||||
| `db.watch(sql, params, options)` | `@powersync/common` `CommonPowerSyncDatabase` | 订阅查询结果,返回 `AsyncIterable` |
|
||||
| `useQuery(sql, params, options)` | `@powersync/react` | React 版订阅;`options.rowComparator` 可做**增量**更新(P2 用) |
|
||||
| `useStatus()` | `@powersync/react` | 连接/同步状态 |
|
||||
| `getPowersyncDb()` | `src/lib/powersync/db.js` | 本地库单例(当前唯一入口) |
|
||||
|
||||
> 注:`src/lib/powersync/react.jsx` 已 re-export `useQuery/useStatus/usePowerSync`,
|
||||
> 本次改造**无需新增依赖**。
|
||||
|
||||
---
|
||||
|
||||
## 9. 结论
|
||||
|
||||
当前"必须切面板才刷新"**不是同步没到,而是 UI 从未订阅本地库变化**。
|
||||
采用「**变更信号 + 复用现有装配重算**」(方案 C)可在改动面很小的情况下获得亚秒级跨端同步,
|
||||
并把"不打断编辑/拖拽"作为硬性要求一并纳入。P0 约半天到一天即可上线验证。
|
||||
+30
-20
@@ -27,21 +27,6 @@ export type User = Prisma.UserModel
|
||||
*
|
||||
*/
|
||||
export type Article = Prisma.ArticleModel
|
||||
/**
|
||||
* Model Strategy
|
||||
*
|
||||
*/
|
||||
export type Strategy = Prisma.StrategyModel
|
||||
/**
|
||||
* Model Indicator
|
||||
*
|
||||
*/
|
||||
export type Indicator = Prisma.IndicatorModel
|
||||
/**
|
||||
* Model IndicatorPoint
|
||||
*
|
||||
*/
|
||||
export type IndicatorPoint = Prisma.IndicatorPointModel
|
||||
/**
|
||||
* Model News
|
||||
*
|
||||
@@ -77,11 +62,6 @@ export type Econ_SowInventory = Prisma.Econ_SowInventoryModel
|
||||
*
|
||||
*/
|
||||
export type ScraperCache = Prisma.ScraperCacheModel
|
||||
/**
|
||||
* Model OilPriceHistory
|
||||
*
|
||||
*/
|
||||
export type OilPriceHistory = Prisma.OilPriceHistoryModel
|
||||
/**
|
||||
* Model MihomeAccount
|
||||
*
|
||||
@@ -132,3 +112,33 @@ export type CheckListItem = Prisma.CheckListItemModel
|
||||
*
|
||||
*/
|
||||
export type CheckListTemplate = Prisma.CheckListTemplateModel
|
||||
/**
|
||||
* Model Project
|
||||
*
|
||||
*/
|
||||
export type Project = Prisma.ProjectModel
|
||||
/**
|
||||
* Model ProjectMember
|
||||
*
|
||||
*/
|
||||
export type ProjectMember = Prisma.ProjectMemberModel
|
||||
/**
|
||||
* Model BoardColumn
|
||||
*
|
||||
*/
|
||||
export type BoardColumn = Prisma.BoardColumnModel
|
||||
/**
|
||||
* Model Task
|
||||
*
|
||||
*/
|
||||
export type Task = Prisma.TaskModel
|
||||
/**
|
||||
* Model TaskMember
|
||||
*
|
||||
*/
|
||||
export type TaskMember = Prisma.TaskMemberModel
|
||||
/**
|
||||
* Model TaskTag
|
||||
*
|
||||
*/
|
||||
export type TaskTag = Prisma.TaskTagModel
|
||||
|
||||
+30
-20
@@ -51,21 +51,6 @@ export type User = Prisma.UserModel
|
||||
*
|
||||
*/
|
||||
export type Article = Prisma.ArticleModel
|
||||
/**
|
||||
* Model Strategy
|
||||
*
|
||||
*/
|
||||
export type Strategy = Prisma.StrategyModel
|
||||
/**
|
||||
* Model Indicator
|
||||
*
|
||||
*/
|
||||
export type Indicator = Prisma.IndicatorModel
|
||||
/**
|
||||
* Model IndicatorPoint
|
||||
*
|
||||
*/
|
||||
export type IndicatorPoint = Prisma.IndicatorPointModel
|
||||
/**
|
||||
* Model News
|
||||
*
|
||||
@@ -101,11 +86,6 @@ export type Econ_SowInventory = Prisma.Econ_SowInventoryModel
|
||||
*
|
||||
*/
|
||||
export type ScraperCache = Prisma.ScraperCacheModel
|
||||
/**
|
||||
* Model OilPriceHistory
|
||||
*
|
||||
*/
|
||||
export type OilPriceHistory = Prisma.OilPriceHistoryModel
|
||||
/**
|
||||
* Model MihomeAccount
|
||||
*
|
||||
@@ -156,3 +136,33 @@ export type CheckListItem = Prisma.CheckListItemModel
|
||||
*
|
||||
*/
|
||||
export type CheckListTemplate = Prisma.CheckListTemplateModel
|
||||
/**
|
||||
* Model Project
|
||||
*
|
||||
*/
|
||||
export type Project = Prisma.ProjectModel
|
||||
/**
|
||||
* Model ProjectMember
|
||||
*
|
||||
*/
|
||||
export type ProjectMember = Prisma.ProjectMemberModel
|
||||
/**
|
||||
* Model BoardColumn
|
||||
*
|
||||
*/
|
||||
export type BoardColumn = Prisma.BoardColumnModel
|
||||
/**
|
||||
* Model Task
|
||||
*
|
||||
*/
|
||||
export type Task = Prisma.TaskModel
|
||||
/**
|
||||
* Model TaskMember
|
||||
*
|
||||
*/
|
||||
export type TaskMember = Prisma.TaskMemberModel
|
||||
/**
|
||||
* Model TaskTag
|
||||
*
|
||||
*/
|
||||
export type TaskTag = Prisma.TaskTagModel
|
||||
|
||||
@@ -16,8 +16,8 @@ import type * as Prisma from "./internal/prismaNamespace.ts"
|
||||
|
||||
export type IntFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
in?: number[]
|
||||
notIn?: number[]
|
||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
||||
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
@@ -27,8 +27,8 @@ export type IntFilter<$PrismaModel = never> = {
|
||||
|
||||
export type StringFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
in?: string[]
|
||||
notIn?: string[]
|
||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
@@ -36,14 +36,14 @@ export type StringFilter<$PrismaModel = never> = {
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
search?: string
|
||||
mode?: Prisma.QueryMode
|
||||
not?: Prisma.NestedStringFilter<$PrismaModel> | string
|
||||
}
|
||||
|
||||
export type DateTimeFilter<$PrismaModel = never> = {
|
||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
in?: Date[] | string[]
|
||||
notIn?: Date[] | string[]
|
||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
@@ -53,8 +53,8 @@ export type DateTimeFilter<$PrismaModel = never> = {
|
||||
|
||||
export type IntWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
in?: number[]
|
||||
notIn?: number[]
|
||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
||||
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
@@ -69,8 +69,8 @@ export type IntWithAggregatesFilter<$PrismaModel = never> = {
|
||||
|
||||
export type StringWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
in?: string[]
|
||||
notIn?: string[]
|
||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
@@ -78,7 +78,7 @@ export type StringWithAggregatesFilter<$PrismaModel = never> = {
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
search?: string
|
||||
mode?: Prisma.QueryMode
|
||||
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
|
||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
||||
@@ -87,8 +87,8 @@ export type StringWithAggregatesFilter<$PrismaModel = never> = {
|
||||
|
||||
export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
in?: Date[] | string[]
|
||||
notIn?: Date[] | string[]
|
||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
@@ -99,74 +99,48 @@ export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type JsonFilter<$PrismaModel = never> =
|
||||
| Prisma.PatchUndefined<
|
||||
Prisma.Either<Required<JsonFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonFilterBase<$PrismaModel>>, 'path'>>,
|
||||
Required<JsonFilterBase<$PrismaModel>>
|
||||
>
|
||||
| Prisma.OptionalFlat<Omit<Required<JsonFilterBase<$PrismaModel>>, 'path'>>
|
||||
|
||||
export type JsonFilterBase<$PrismaModel = never> = {
|
||||
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
path?: string
|
||||
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
||||
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
lt?: runtime.InputJsonValue
|
||||
lte?: runtime.InputJsonValue
|
||||
gt?: runtime.InputJsonValue
|
||||
gte?: runtime.InputJsonValue
|
||||
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
export type StringNullableFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
mode?: Prisma.QueryMode
|
||||
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
||||
}
|
||||
|
||||
export type JsonWithAggregatesFilter<$PrismaModel = never> =
|
||||
| Prisma.PatchUndefined<
|
||||
Prisma.Either<Required<JsonWithAggregatesFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonWithAggregatesFilterBase<$PrismaModel>>, 'path'>>,
|
||||
Required<JsonWithAggregatesFilterBase<$PrismaModel>>
|
||||
>
|
||||
| Prisma.OptionalFlat<Omit<Required<JsonWithAggregatesFilterBase<$PrismaModel>>, 'path'>>
|
||||
|
||||
export type JsonWithAggregatesFilterBase<$PrismaModel = never> = {
|
||||
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
path?: string
|
||||
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
||||
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
lt?: runtime.InputJsonValue
|
||||
lte?: runtime.InputJsonValue
|
||||
gt?: runtime.InputJsonValue
|
||||
gte?: runtime.InputJsonValue
|
||||
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedJsonFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedJsonFilter<$PrismaModel>
|
||||
export type SortOrderInput = {
|
||||
sort: Prisma.SortOrder
|
||||
nulls?: Prisma.NullsOrder
|
||||
}
|
||||
|
||||
export type BoolFilter<$PrismaModel = never> = {
|
||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
||||
}
|
||||
|
||||
export type BoolWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
|
||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
mode?: Prisma.QueryMode
|
||||
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
|
||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type FloatFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
in?: number[]
|
||||
notIn?: number[]
|
||||
in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel>
|
||||
notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel>
|
||||
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
@@ -176,8 +150,8 @@ export type FloatFilter<$PrismaModel = never> = {
|
||||
|
||||
export type FloatWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
in?: number[]
|
||||
notIn?: number[]
|
||||
in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel>
|
||||
notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel>
|
||||
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
@@ -190,48 +164,10 @@ export type FloatWithAggregatesFilter<$PrismaModel = never> = {
|
||||
_max?: Prisma.NestedFloatFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type StringNullableFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||
in?: string[] | null
|
||||
notIn?: string[] | null
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
search?: string
|
||||
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
||||
}
|
||||
|
||||
export type SortOrderInput = {
|
||||
sort: Prisma.SortOrder
|
||||
nulls?: Prisma.NullsOrder
|
||||
}
|
||||
|
||||
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||
in?: string[] | null
|
||||
notIn?: string[] | null
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
search?: string
|
||||
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
|
||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type BigIntFilter<$PrismaModel = never> = {
|
||||
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||
in?: bigint[] | number[]
|
||||
notIn?: bigint[] | number[]
|
||||
in?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel>
|
||||
notIn?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel>
|
||||
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||
@@ -241,8 +177,8 @@ export type BigIntFilter<$PrismaModel = never> = {
|
||||
|
||||
export type BigIntWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||
in?: bigint[] | number[]
|
||||
notIn?: bigint[] | number[]
|
||||
in?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel>
|
||||
notIn?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel>
|
||||
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||
@@ -264,7 +200,7 @@ export type JsonNullableFilter<$PrismaModel = never> =
|
||||
|
||||
export type JsonNullableFilterBase<$PrismaModel = never> = {
|
||||
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
path?: string
|
||||
path?: string[]
|
||||
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
||||
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
@@ -272,10 +208,10 @@ export type JsonNullableFilterBase<$PrismaModel = never> = {
|
||||
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
lt?: runtime.InputJsonValue
|
||||
lte?: runtime.InputJsonValue
|
||||
gt?: runtime.InputJsonValue
|
||||
gte?: runtime.InputJsonValue
|
||||
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
}
|
||||
|
||||
@@ -288,7 +224,7 @@ export type JsonNullableWithAggregatesFilter<$PrismaModel = never> =
|
||||
|
||||
export type JsonNullableWithAggregatesFilterBase<$PrismaModel = never> = {
|
||||
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
path?: string
|
||||
path?: string[]
|
||||
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
||||
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
@@ -296,10 +232,10 @@ export type JsonNullableWithAggregatesFilterBase<$PrismaModel = never> = {
|
||||
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
lt?: runtime.InputJsonValue
|
||||
lte?: runtime.InputJsonValue
|
||||
gt?: runtime.InputJsonValue
|
||||
gte?: runtime.InputJsonValue
|
||||
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedJsonNullableFilter<$PrismaModel>
|
||||
@@ -308,8 +244,8 @@ export type JsonNullableWithAggregatesFilterBase<$PrismaModel = never> = {
|
||||
|
||||
export type IntNullableFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | null
|
||||
notIn?: number[] | null
|
||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
|
||||
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
@@ -319,8 +255,8 @@ export type IntNullableFilter<$PrismaModel = never> = {
|
||||
|
||||
export type DateTimeNullableFilter<$PrismaModel = never> = {
|
||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
||||
in?: Date[] | string[] | null
|
||||
notIn?: Date[] | string[] | null
|
||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
@@ -328,10 +264,15 @@ export type DateTimeNullableFilter<$PrismaModel = never> = {
|
||||
not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null
|
||||
}
|
||||
|
||||
export type BoolFilter<$PrismaModel = never> = {
|
||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
||||
}
|
||||
|
||||
export type IntNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | null
|
||||
notIn?: number[] | null
|
||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
|
||||
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
@@ -346,8 +287,8 @@ export type IntNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
|
||||
export type DateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
||||
in?: Date[] | string[] | null
|
||||
notIn?: Date[] | string[] | null
|
||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
@@ -358,10 +299,18 @@ export type DateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type BoolWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
|
||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type FloatNullableFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | null
|
||||
notIn?: number[] | null
|
||||
in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null
|
||||
notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null
|
||||
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
@@ -371,8 +320,8 @@ export type FloatNullableFilter<$PrismaModel = never> = {
|
||||
|
||||
export type FloatNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | null
|
||||
notIn?: number[] | null
|
||||
in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null
|
||||
notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null
|
||||
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
@@ -385,10 +334,61 @@ export type FloatNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
_max?: Prisma.NestedFloatNullableFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type JsonFilter<$PrismaModel = never> =
|
||||
| Prisma.PatchUndefined<
|
||||
Prisma.Either<Required<JsonFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonFilterBase<$PrismaModel>>, 'path'>>,
|
||||
Required<JsonFilterBase<$PrismaModel>>
|
||||
>
|
||||
| Prisma.OptionalFlat<Omit<Required<JsonFilterBase<$PrismaModel>>, 'path'>>
|
||||
|
||||
export type JsonFilterBase<$PrismaModel = never> = {
|
||||
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
path?: string[]
|
||||
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
||||
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
}
|
||||
|
||||
export type JsonWithAggregatesFilter<$PrismaModel = never> =
|
||||
| Prisma.PatchUndefined<
|
||||
Prisma.Either<Required<JsonWithAggregatesFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonWithAggregatesFilterBase<$PrismaModel>>, 'path'>>,
|
||||
Required<JsonWithAggregatesFilterBase<$PrismaModel>>
|
||||
>
|
||||
| Prisma.OptionalFlat<Omit<Required<JsonWithAggregatesFilterBase<$PrismaModel>>, 'path'>>
|
||||
|
||||
export type JsonWithAggregatesFilterBase<$PrismaModel = never> = {
|
||||
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
path?: string[]
|
||||
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
||||
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedJsonFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedJsonFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type NestedIntFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
in?: number[]
|
||||
notIn?: number[]
|
||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
||||
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
@@ -398,8 +398,8 @@ export type NestedIntFilter<$PrismaModel = never> = {
|
||||
|
||||
export type NestedStringFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
in?: string[]
|
||||
notIn?: string[]
|
||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
@@ -407,14 +407,13 @@ export type NestedStringFilter<$PrismaModel = never> = {
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
search?: string
|
||||
not?: Prisma.NestedStringFilter<$PrismaModel> | string
|
||||
}
|
||||
|
||||
export type NestedDateTimeFilter<$PrismaModel = never> = {
|
||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
in?: Date[] | string[]
|
||||
notIn?: Date[] | string[]
|
||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
@@ -424,8 +423,8 @@ export type NestedDateTimeFilter<$PrismaModel = never> = {
|
||||
|
||||
export type NestedIntWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
in?: number[]
|
||||
notIn?: number[]
|
||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
||||
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
@@ -440,8 +439,8 @@ export type NestedIntWithAggregatesFilter<$PrismaModel = never> = {
|
||||
|
||||
export type NestedFloatFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
in?: number[]
|
||||
notIn?: number[]
|
||||
in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel>
|
||||
notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel>
|
||||
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
@@ -451,8 +450,8 @@ export type NestedFloatFilter<$PrismaModel = never> = {
|
||||
|
||||
export type NestedStringWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
in?: string[]
|
||||
notIn?: string[]
|
||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
@@ -460,7 +459,6 @@ export type NestedStringWithAggregatesFilter<$PrismaModel = never> = {
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
search?: string
|
||||
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
|
||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
||||
@@ -469,8 +467,8 @@ export type NestedStringWithAggregatesFilter<$PrismaModel = never> = {
|
||||
|
||||
export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
in?: Date[] | string[]
|
||||
notIn?: Date[] | string[]
|
||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
@@ -481,47 +479,52 @@ export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type NestedJsonFilter<$PrismaModel = never> =
|
||||
| Prisma.PatchUndefined<
|
||||
Prisma.Either<Required<NestedJsonFilterBase<$PrismaModel>>, Exclude<keyof Required<NestedJsonFilterBase<$PrismaModel>>, 'path'>>,
|
||||
Required<NestedJsonFilterBase<$PrismaModel>>
|
||||
>
|
||||
| Prisma.OptionalFlat<Omit<Required<NestedJsonFilterBase<$PrismaModel>>, 'path'>>
|
||||
|
||||
export type NestedJsonFilterBase<$PrismaModel = never> = {
|
||||
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
path?: string
|
||||
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
||||
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
lt?: runtime.InputJsonValue
|
||||
lte?: runtime.InputJsonValue
|
||||
gt?: runtime.InputJsonValue
|
||||
gte?: runtime.InputJsonValue
|
||||
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
export type NestedStringNullableFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
||||
}
|
||||
|
||||
export type NestedBoolFilter<$PrismaModel = never> = {
|
||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
||||
export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
|
||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type NestedBoolWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
|
||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||
export type NestedIntNullableFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
|
||||
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null
|
||||
}
|
||||
|
||||
export type NestedFloatWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
in?: number[]
|
||||
notIn?: number[]
|
||||
in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel>
|
||||
notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel>
|
||||
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
@@ -534,54 +537,10 @@ export type NestedFloatWithAggregatesFilter<$PrismaModel = never> = {
|
||||
_max?: Prisma.NestedFloatFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type NestedStringNullableFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||
in?: string[] | null
|
||||
notIn?: string[] | null
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
search?: string
|
||||
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
||||
}
|
||||
|
||||
export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||
in?: string[] | null
|
||||
notIn?: string[] | null
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
search?: string
|
||||
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
|
||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type NestedIntNullableFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | null
|
||||
notIn?: number[] | null
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null
|
||||
}
|
||||
|
||||
export type NestedBigIntFilter<$PrismaModel = never> = {
|
||||
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||
in?: bigint[] | number[]
|
||||
notIn?: bigint[] | number[]
|
||||
in?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel>
|
||||
notIn?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel>
|
||||
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||
@@ -591,8 +550,8 @@ export type NestedBigIntFilter<$PrismaModel = never> = {
|
||||
|
||||
export type NestedBigIntWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||
in?: bigint[] | number[]
|
||||
notIn?: bigint[] | number[]
|
||||
in?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel>
|
||||
notIn?: bigint[] | number[] | Prisma.ListBigIntFieldRefInput<$PrismaModel>
|
||||
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||
@@ -614,7 +573,7 @@ export type NestedJsonNullableFilter<$PrismaModel = never> =
|
||||
|
||||
export type NestedJsonNullableFilterBase<$PrismaModel = never> = {
|
||||
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
path?: string
|
||||
path?: string[]
|
||||
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
||||
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
@@ -622,17 +581,17 @@ export type NestedJsonNullableFilterBase<$PrismaModel = never> = {
|
||||
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
lt?: runtime.InputJsonValue
|
||||
lte?: runtime.InputJsonValue
|
||||
gt?: runtime.InputJsonValue
|
||||
gte?: runtime.InputJsonValue
|
||||
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
}
|
||||
|
||||
export type NestedDateTimeNullableFilter<$PrismaModel = never> = {
|
||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
||||
in?: Date[] | string[] | null
|
||||
notIn?: Date[] | string[] | null
|
||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
@@ -640,10 +599,15 @@ export type NestedDateTimeNullableFilter<$PrismaModel = never> = {
|
||||
not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null
|
||||
}
|
||||
|
||||
export type NestedBoolFilter<$PrismaModel = never> = {
|
||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
||||
}
|
||||
|
||||
export type NestedIntNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | null
|
||||
notIn?: number[] | null
|
||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
|
||||
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
@@ -658,8 +622,8 @@ export type NestedIntNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
|
||||
export type NestedFloatNullableFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | null
|
||||
notIn?: number[] | null
|
||||
in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null
|
||||
notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null
|
||||
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
@@ -669,8 +633,8 @@ export type NestedFloatNullableFilter<$PrismaModel = never> = {
|
||||
|
||||
export type NestedDateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
||||
in?: Date[] | string[] | null
|
||||
notIn?: Date[] | string[] | null
|
||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
@@ -681,10 +645,18 @@ export type NestedDateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type NestedBoolWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
|
||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type NestedFloatNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | null
|
||||
notIn?: number[] | null
|
||||
in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null
|
||||
notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel> | null
|
||||
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
@@ -697,4 +669,28 @@ export type NestedFloatNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
_max?: Prisma.NestedFloatNullableFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type NestedJsonFilter<$PrismaModel = never> =
|
||||
| Prisma.PatchUndefined<
|
||||
Prisma.Either<Required<NestedJsonFilterBase<$PrismaModel>>, Exclude<keyof Required<NestedJsonFilterBase<$PrismaModel>>, 'path'>>,
|
||||
Required<NestedJsonFilterBase<$PrismaModel>>
|
||||
>
|
||||
| Prisma.OptionalFlat<Omit<Required<NestedJsonFilterBase<$PrismaModel>>, 'path'>>
|
||||
|
||||
export type NestedJsonFilterBase<$PrismaModel = never> = {
|
||||
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
path?: string[]
|
||||
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
||||
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||
}
|
||||
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -53,9 +53,6 @@ export const AnyNull = runtime.AnyNull
|
||||
export const ModelName = {
|
||||
User: 'User',
|
||||
Article: 'Article',
|
||||
Strategy: 'Strategy',
|
||||
Indicator: 'Indicator',
|
||||
IndicatorPoint: 'IndicatorPoint',
|
||||
News: 'News',
|
||||
Alert: 'Alert',
|
||||
ServerNode: 'ServerNode',
|
||||
@@ -63,7 +60,6 @@ export const ModelName = {
|
||||
ServiceCheck: 'ServiceCheck',
|
||||
Econ_SowInventory: 'Econ_SowInventory',
|
||||
ScraperCache: 'ScraperCache',
|
||||
OilPriceHistory: 'OilPriceHistory',
|
||||
MihomeAccount: 'MihomeAccount',
|
||||
MihomeHome: 'MihomeHome',
|
||||
MihomeDevice: 'MihomeDevice',
|
||||
@@ -73,7 +69,13 @@ export const ModelName = {
|
||||
CheckIn: 'CheckIn',
|
||||
CheckList: 'CheckList',
|
||||
CheckListItem: 'CheckListItem',
|
||||
CheckListTemplate: 'CheckListTemplate'
|
||||
CheckListTemplate: 'CheckListTemplate',
|
||||
Project: 'Project',
|
||||
ProjectMember: 'ProjectMember',
|
||||
BoardColumn: 'BoardColumn',
|
||||
Task: 'Task',
|
||||
TaskMember: 'TaskMember',
|
||||
TaskTag: 'TaskTag'
|
||||
} as const
|
||||
|
||||
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
||||
@@ -117,46 +119,6 @@ export const ArticleScalarFieldEnum = {
|
||||
export type ArticleScalarFieldEnum = (typeof ArticleScalarFieldEnum)[keyof typeof ArticleScalarFieldEnum]
|
||||
|
||||
|
||||
export const StrategyScalarFieldEnum = {
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
description: 'description',
|
||||
data: 'data',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
} as const
|
||||
|
||||
export type StrategyScalarFieldEnum = (typeof StrategyScalarFieldEnum)[keyof typeof StrategyScalarFieldEnum]
|
||||
|
||||
|
||||
export const IndicatorScalarFieldEnum = {
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
groupName: 'groupName',
|
||||
title: 'title',
|
||||
unit: 'unit',
|
||||
source: 'source',
|
||||
frequency: 'frequency',
|
||||
hidden: 'hidden',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
} as const
|
||||
|
||||
export type IndicatorScalarFieldEnum = (typeof IndicatorScalarFieldEnum)[keyof typeof IndicatorScalarFieldEnum]
|
||||
|
||||
|
||||
export const IndicatorPointScalarFieldEnum = {
|
||||
id: 'id',
|
||||
indicatorId: 'indicatorId',
|
||||
period: 'period',
|
||||
value: 'value',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
} as const
|
||||
|
||||
export type IndicatorPointScalarFieldEnum = (typeof IndicatorPointScalarFieldEnum)[keyof typeof IndicatorPointScalarFieldEnum]
|
||||
|
||||
|
||||
export const NewsScalarFieldEnum = {
|
||||
id: 'id',
|
||||
title: 'title',
|
||||
@@ -276,19 +238,6 @@ export const ScraperCacheScalarFieldEnum = {
|
||||
export type ScraperCacheScalarFieldEnum = (typeof ScraperCacheScalarFieldEnum)[keyof typeof ScraperCacheScalarFieldEnum]
|
||||
|
||||
|
||||
export const OilPriceHistoryScalarFieldEnum = {
|
||||
id: 'id',
|
||||
date: 'date',
|
||||
province: 'province',
|
||||
fuel: 'fuel',
|
||||
price: 'price',
|
||||
source: 'source',
|
||||
createdAt: 'createdAt'
|
||||
} as const
|
||||
|
||||
export type OilPriceHistoryScalarFieldEnum = (typeof OilPriceHistoryScalarFieldEnum)[keyof typeof OilPriceHistoryScalarFieldEnum]
|
||||
|
||||
|
||||
export const MihomeAccountScalarFieldEnum = {
|
||||
id: 'id',
|
||||
key: 'key',
|
||||
@@ -443,6 +392,106 @@ export const CheckListTemplateScalarFieldEnum = {
|
||||
export type CheckListTemplateScalarFieldEnum = (typeof CheckListTemplateScalarFieldEnum)[keyof typeof CheckListTemplateScalarFieldEnum]
|
||||
|
||||
|
||||
export const ProjectScalarFieldEnum = {
|
||||
id: 'id',
|
||||
uuid: 'uuid',
|
||||
code: 'code',
|
||||
name: 'name',
|
||||
ownerId: 'ownerId',
|
||||
isPersonal: 'isPersonal',
|
||||
archived: 'archived',
|
||||
notes: 'notes',
|
||||
taskKinds: 'taskKinds',
|
||||
icon: 'icon',
|
||||
autoArchive: 'autoArchive',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
} as const
|
||||
|
||||
export type ProjectScalarFieldEnum = (typeof ProjectScalarFieldEnum)[keyof typeof ProjectScalarFieldEnum]
|
||||
|
||||
|
||||
export const ProjectMemberScalarFieldEnum = {
|
||||
id: 'id',
|
||||
uuid: 'uuid',
|
||||
projectUuid: 'projectUuid',
|
||||
projectId: 'projectId',
|
||||
userId: 'userId',
|
||||
permission: 'permission',
|
||||
createdAt: 'createdAt'
|
||||
} as const
|
||||
|
||||
export type ProjectMemberScalarFieldEnum = (typeof ProjectMemberScalarFieldEnum)[keyof typeof ProjectMemberScalarFieldEnum]
|
||||
|
||||
|
||||
export const BoardColumnScalarFieldEnum = {
|
||||
id: 'id',
|
||||
uuid: 'uuid',
|
||||
projectUuid: 'projectUuid',
|
||||
projectId: 'projectId',
|
||||
name: 'name',
|
||||
sortOrder: 'sortOrder',
|
||||
boundStatus: 'boundStatus',
|
||||
retention: 'retention',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
} as const
|
||||
|
||||
export type BoardColumnScalarFieldEnum = (typeof BoardColumnScalarFieldEnum)[keyof typeof BoardColumnScalarFieldEnum]
|
||||
|
||||
|
||||
export const TaskScalarFieldEnum = {
|
||||
id: 'id',
|
||||
uuid: 'uuid',
|
||||
projectUuid: 'projectUuid',
|
||||
columnUuid: 'columnUuid',
|
||||
parentUuid: 'parentUuid',
|
||||
projectId: 'projectId',
|
||||
columnId: 'columnId',
|
||||
parentId: 'parentId',
|
||||
title: 'title',
|
||||
description: 'description',
|
||||
kind: 'kind',
|
||||
markDate: 'markDate',
|
||||
status: 'status',
|
||||
doneAt: 'doneAt',
|
||||
assigneeId: 'assigneeId',
|
||||
creatorId: 'creatorId',
|
||||
extras: 'extras',
|
||||
sortOrder: 'sortOrder',
|
||||
archived: 'archived',
|
||||
archivedAt: 'archivedAt',
|
||||
archiveSource: 'archiveSource',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt',
|
||||
number: 'number'
|
||||
} as const
|
||||
|
||||
export type TaskScalarFieldEnum = (typeof TaskScalarFieldEnum)[keyof typeof TaskScalarFieldEnum]
|
||||
|
||||
|
||||
export const TaskMemberScalarFieldEnum = {
|
||||
id: 'id',
|
||||
uuid: 'uuid',
|
||||
taskUuid: 'taskUuid',
|
||||
taskId: 'taskId',
|
||||
userId: 'userId'
|
||||
} as const
|
||||
|
||||
export type TaskMemberScalarFieldEnum = (typeof TaskMemberScalarFieldEnum)[keyof typeof TaskMemberScalarFieldEnum]
|
||||
|
||||
|
||||
export const TaskTagScalarFieldEnum = {
|
||||
id: 'id',
|
||||
uuid: 'uuid',
|
||||
taskUuid: 'taskUuid',
|
||||
taskId: 'taskId',
|
||||
name: 'name'
|
||||
} as const
|
||||
|
||||
export type TaskTagScalarFieldEnum = (typeof TaskTagScalarFieldEnum)[keyof typeof TaskTagScalarFieldEnum]
|
||||
|
||||
|
||||
export const SortOrder = {
|
||||
asc: 'asc',
|
||||
desc: 'desc'
|
||||
@@ -451,13 +500,6 @@ export const SortOrder = {
|
||||
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]
|
||||
|
||||
|
||||
export const JsonNullValueInput = {
|
||||
JsonNull: JsonNull
|
||||
} as const
|
||||
|
||||
export type JsonNullValueInput = (typeof JsonNullValueInput)[keyof typeof JsonNullValueInput]
|
||||
|
||||
|
||||
export const NullableJsonNullValueInput = {
|
||||
DbNull: DbNull,
|
||||
JsonNull: JsonNull
|
||||
@@ -466,32 +508,11 @@ export const NullableJsonNullValueInput = {
|
||||
export type NullableJsonNullValueInput = (typeof NullableJsonNullValueInput)[keyof typeof NullableJsonNullValueInput]
|
||||
|
||||
|
||||
export const UserOrderByRelevanceFieldEnum = {
|
||||
name: 'name',
|
||||
email: 'email',
|
||||
password: 'password'
|
||||
export const JsonNullValueInput = {
|
||||
JsonNull: JsonNull
|
||||
} as const
|
||||
|
||||
export type UserOrderByRelevanceFieldEnum = (typeof UserOrderByRelevanceFieldEnum)[keyof typeof UserOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const ArticleOrderByRelevanceFieldEnum = {
|
||||
title: 'title',
|
||||
slug: 'slug',
|
||||
content: 'content',
|
||||
date: 'date'
|
||||
} as const
|
||||
|
||||
export type ArticleOrderByRelevanceFieldEnum = (typeof ArticleOrderByRelevanceFieldEnum)[keyof typeof ArticleOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const JsonNullValueFilter = {
|
||||
DbNull: DbNull,
|
||||
JsonNull: JsonNull,
|
||||
AnyNull: AnyNull
|
||||
} as const
|
||||
|
||||
export type JsonNullValueFilter = (typeof JsonNullValueFilter)[keyof typeof JsonNullValueFilter]
|
||||
export type JsonNullValueInput = (typeof JsonNullValueInput)[keyof typeof JsonNullValueInput]
|
||||
|
||||
|
||||
export const QueryMode = {
|
||||
@@ -502,33 +523,6 @@ export const QueryMode = {
|
||||
export type QueryMode = (typeof QueryMode)[keyof typeof QueryMode]
|
||||
|
||||
|
||||
export const StrategyOrderByRelevanceFieldEnum = {
|
||||
name: 'name',
|
||||
description: 'description'
|
||||
} as const
|
||||
|
||||
export type StrategyOrderByRelevanceFieldEnum = (typeof StrategyOrderByRelevanceFieldEnum)[keyof typeof StrategyOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const IndicatorOrderByRelevanceFieldEnum = {
|
||||
name: 'name',
|
||||
groupName: 'groupName',
|
||||
title: 'title',
|
||||
unit: 'unit',
|
||||
source: 'source',
|
||||
frequency: 'frequency'
|
||||
} as const
|
||||
|
||||
export type IndicatorOrderByRelevanceFieldEnum = (typeof IndicatorOrderByRelevanceFieldEnum)[keyof typeof IndicatorOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const IndicatorPointOrderByRelevanceFieldEnum = {
|
||||
period: 'period'
|
||||
} as const
|
||||
|
||||
export type IndicatorPointOrderByRelevanceFieldEnum = (typeof IndicatorPointOrderByRelevanceFieldEnum)[keyof typeof IndicatorPointOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const NullsOrder = {
|
||||
first: 'first',
|
||||
last: 'last'
|
||||
@@ -537,157 +531,11 @@ export const NullsOrder = {
|
||||
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
|
||||
|
||||
|
||||
export const NewsOrderByRelevanceFieldEnum = {
|
||||
title: 'title',
|
||||
summary: 'summary',
|
||||
content: 'content',
|
||||
source: 'source',
|
||||
url: 'url'
|
||||
export const JsonNullValueFilter = {
|
||||
DbNull: DbNull,
|
||||
JsonNull: JsonNull,
|
||||
AnyNull: AnyNull
|
||||
} as const
|
||||
|
||||
export type NewsOrderByRelevanceFieldEnum = (typeof NewsOrderByRelevanceFieldEnum)[keyof typeof NewsOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const AlertOrderByRelevanceFieldEnum = {
|
||||
indicatorName: 'indicatorName',
|
||||
period: 'period',
|
||||
sourceA: 'sourceA',
|
||||
sourceB: 'sourceB'
|
||||
} as const
|
||||
|
||||
export type AlertOrderByRelevanceFieldEnum = (typeof AlertOrderByRelevanceFieldEnum)[keyof typeof AlertOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const ServerNodeOrderByRelevanceFieldEnum = {
|
||||
uuid: 'uuid',
|
||||
name: 'name',
|
||||
cpuName: 'cpuName',
|
||||
os: 'os',
|
||||
region: 'region',
|
||||
status: 'status'
|
||||
} as const
|
||||
|
||||
export type ServerNodeOrderByRelevanceFieldEnum = (typeof ServerNodeOrderByRelevanceFieldEnum)[keyof typeof ServerNodeOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const ServerNodeHistoryOrderByRelevanceFieldEnum = {
|
||||
uuid: 'uuid'
|
||||
} as const
|
||||
|
||||
export type ServerNodeHistoryOrderByRelevanceFieldEnum = (typeof ServerNodeHistoryOrderByRelevanceFieldEnum)[keyof typeof ServerNodeHistoryOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const ServiceCheckOrderByRelevanceFieldEnum = {
|
||||
name: 'name',
|
||||
url: 'url',
|
||||
status: 'status',
|
||||
message: 'message'
|
||||
} as const
|
||||
|
||||
export type ServiceCheckOrderByRelevanceFieldEnum = (typeof ServiceCheckOrderByRelevanceFieldEnum)[keyof typeof ServiceCheckOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const Econ_SowInventoryOrderByRelevanceFieldEnum = {
|
||||
month: 'month'
|
||||
} as const
|
||||
|
||||
export type Econ_SowInventoryOrderByRelevanceFieldEnum = (typeof Econ_SowInventoryOrderByRelevanceFieldEnum)[keyof typeof Econ_SowInventoryOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const ScraperCacheOrderByRelevanceFieldEnum = {
|
||||
scraperName: 'scraperName',
|
||||
lastDataHash: 'lastDataHash'
|
||||
} as const
|
||||
|
||||
export type ScraperCacheOrderByRelevanceFieldEnum = (typeof ScraperCacheOrderByRelevanceFieldEnum)[keyof typeof ScraperCacheOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const OilPriceHistoryOrderByRelevanceFieldEnum = {
|
||||
province: 'province',
|
||||
fuel: 'fuel',
|
||||
source: 'source'
|
||||
} as const
|
||||
|
||||
export type OilPriceHistoryOrderByRelevanceFieldEnum = (typeof OilPriceHistoryOrderByRelevanceFieldEnum)[keyof typeof OilPriceHistoryOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const MihomeAccountOrderByRelevanceFieldEnum = {
|
||||
key: 'key',
|
||||
userId: 'userId',
|
||||
status: 'status',
|
||||
lastError: 'lastError'
|
||||
} as const
|
||||
|
||||
export type MihomeAccountOrderByRelevanceFieldEnum = (typeof MihomeAccountOrderByRelevanceFieldEnum)[keyof typeof MihomeAccountOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const MihomeHomeOrderByRelevanceFieldEnum = {
|
||||
homeId: 'homeId',
|
||||
name: 'name'
|
||||
} as const
|
||||
|
||||
export type MihomeHomeOrderByRelevanceFieldEnum = (typeof MihomeHomeOrderByRelevanceFieldEnum)[keyof typeof MihomeHomeOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const MihomeDeviceOrderByRelevanceFieldEnum = {
|
||||
did: 'did',
|
||||
homeId: 'homeId',
|
||||
model: 'model',
|
||||
name: 'name',
|
||||
alias: 'alias'
|
||||
} as const
|
||||
|
||||
export type MihomeDeviceOrderByRelevanceFieldEnum = (typeof MihomeDeviceOrderByRelevanceFieldEnum)[keyof typeof MihomeDeviceOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const MihomeEnvSampleOrderByRelevanceFieldEnum = {
|
||||
origin: 'origin'
|
||||
} as const
|
||||
|
||||
export type MihomeEnvSampleOrderByRelevanceFieldEnum = (typeof MihomeEnvSampleOrderByRelevanceFieldEnum)[keyof typeof MihomeEnvSampleOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const HabitOrderByRelevanceFieldEnum = {
|
||||
name: 'name',
|
||||
period: 'period',
|
||||
color: 'color',
|
||||
icon: 'icon',
|
||||
note: 'note'
|
||||
} as const
|
||||
|
||||
export type HabitOrderByRelevanceFieldEnum = (typeof HabitOrderByRelevanceFieldEnum)[keyof typeof HabitOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const CheckInOrderByRelevanceFieldEnum = {
|
||||
date: 'date',
|
||||
note: 'note',
|
||||
source: 'source'
|
||||
} as const
|
||||
|
||||
export type CheckInOrderByRelevanceFieldEnum = (typeof CheckInOrderByRelevanceFieldEnum)[keyof typeof CheckInOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const CheckListOrderByRelevanceFieldEnum = {
|
||||
title: 'title',
|
||||
scene: 'scene',
|
||||
note: 'note'
|
||||
} as const
|
||||
|
||||
export type CheckListOrderByRelevanceFieldEnum = (typeof CheckListOrderByRelevanceFieldEnum)[keyof typeof CheckListOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const CheckListItemOrderByRelevanceFieldEnum = {
|
||||
title: 'title',
|
||||
note: 'note'
|
||||
} as const
|
||||
|
||||
export type CheckListItemOrderByRelevanceFieldEnum = (typeof CheckListItemOrderByRelevanceFieldEnum)[keyof typeof CheckListItemOrderByRelevanceFieldEnum]
|
||||
|
||||
|
||||
export const CheckListTemplateOrderByRelevanceFieldEnum = {
|
||||
name: 'name',
|
||||
scene: 'scene'
|
||||
} as const
|
||||
|
||||
export type CheckListTemplateOrderByRelevanceFieldEnum = (typeof CheckListTemplateOrderByRelevanceFieldEnum)[keyof typeof CheckListTemplateOrderByRelevanceFieldEnum]
|
||||
export type JsonNullValueFilter = (typeof JsonNullValueFilter)[keyof typeof JsonNullValueFilter]
|
||||
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
*/
|
||||
export type * from './models/User.ts'
|
||||
export type * from './models/Article.ts'
|
||||
export type * from './models/Strategy.ts'
|
||||
export type * from './models/Indicator.ts'
|
||||
export type * from './models/IndicatorPoint.ts'
|
||||
export type * from './models/News.ts'
|
||||
export type * from './models/Alert.ts'
|
||||
export type * from './models/ServerNode.ts'
|
||||
@@ -20,7 +17,6 @@ export type * from './models/ServerNodeHistory.ts'
|
||||
export type * from './models/ServiceCheck.ts'
|
||||
export type * from './models/Econ_SowInventory.ts'
|
||||
export type * from './models/ScraperCache.ts'
|
||||
export type * from './models/OilPriceHistory.ts'
|
||||
export type * from './models/MihomeAccount.ts'
|
||||
export type * from './models/MihomeHome.ts'
|
||||
export type * from './models/MihomeDevice.ts'
|
||||
@@ -31,4 +27,10 @@ export type * from './models/CheckIn.ts'
|
||||
export type * from './models/CheckList.ts'
|
||||
export type * from './models/CheckListItem.ts'
|
||||
export type * from './models/CheckListTemplate.ts'
|
||||
export type * from './models/Project.ts'
|
||||
export type * from './models/ProjectMember.ts'
|
||||
export type * from './models/BoardColumn.ts'
|
||||
export type * from './models/Task.ts'
|
||||
export type * from './models/TaskMember.ts'
|
||||
export type * from './models/TaskTag.ts'
|
||||
export type * from './commonInputTypes.ts'
|
||||
@@ -285,7 +285,6 @@ export type AlertOrderByWithRelationInput = {
|
||||
diff?: Prisma.SortOrder
|
||||
diffRatio?: Prisma.SortOrder
|
||||
createdAt?: Prisma.SortOrder
|
||||
_relevance?: Prisma.AlertOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type AlertWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -426,12 +425,6 @@ export type AlertUncheckedUpdateManyInput = {
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
}
|
||||
|
||||
export type AlertOrderByRelevanceInput = {
|
||||
fields: Prisma.AlertOrderByRelevanceFieldEnum | Prisma.AlertOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type AlertCountOrderByAggregateInput = {
|
||||
id?: Prisma.SortOrder
|
||||
indicatorName?: Prisma.SortOrder
|
||||
@@ -487,6 +480,14 @@ export type AlertSumOrderByAggregateInput = {
|
||||
diffRatio?: Prisma.SortOrder
|
||||
}
|
||||
|
||||
export type FloatFieldUpdateOperationsInput = {
|
||||
set?: number
|
||||
increment?: number
|
||||
decrement?: number
|
||||
multiply?: number
|
||||
divide?: number
|
||||
}
|
||||
|
||||
|
||||
|
||||
export type AlertSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
@@ -502,7 +503,31 @@ export type AlertSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs =
|
||||
createdAt?: boolean
|
||||
}, ExtArgs["result"]["alert"]>
|
||||
|
||||
export type AlertSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
indicatorName?: boolean
|
||||
period?: boolean
|
||||
valueA?: boolean
|
||||
valueB?: boolean
|
||||
sourceA?: boolean
|
||||
sourceB?: boolean
|
||||
diff?: boolean
|
||||
diffRatio?: boolean
|
||||
createdAt?: boolean
|
||||
}, ExtArgs["result"]["alert"]>
|
||||
|
||||
export type AlertSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
indicatorName?: boolean
|
||||
period?: boolean
|
||||
valueA?: boolean
|
||||
valueB?: boolean
|
||||
sourceA?: boolean
|
||||
sourceB?: boolean
|
||||
diff?: boolean
|
||||
diffRatio?: boolean
|
||||
createdAt?: boolean
|
||||
}, ExtArgs["result"]["alert"]>
|
||||
|
||||
export type AlertSelectScalar = {
|
||||
id?: boolean
|
||||
@@ -650,6 +675,30 @@ export interface AlertDelegate<ExtArgs extends runtime.Types.Extensions.Internal
|
||||
*/
|
||||
createMany<T extends AlertCreateManyArgs>(args?: Prisma.SelectSubset<T, AlertCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many Alerts and returns the data saved in the database.
|
||||
* @param {AlertCreateManyAndReturnArgs} args - Arguments to create many Alerts.
|
||||
* @example
|
||||
* // Create many Alerts
|
||||
* const alert = await prisma.alert.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many Alerts and only return the `id`
|
||||
* const alertWithIdOnly = await prisma.alert.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends AlertCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, AlertCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$AlertPayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a Alert.
|
||||
* @param {AlertDeleteArgs} args - Arguments to delete one Alert.
|
||||
@@ -714,6 +763,36 @@ export interface AlertDelegate<ExtArgs extends runtime.Types.Extensions.Internal
|
||||
*/
|
||||
updateMany<T extends AlertUpdateManyArgs>(args: Prisma.SelectSubset<T, AlertUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more Alerts and returns the data updated in the database.
|
||||
* @param {AlertUpdateManyAndReturnArgs} args - Arguments to update many Alerts.
|
||||
* @example
|
||||
* // Update many Alerts
|
||||
* const alert = await prisma.alert.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more Alerts and only return the `id`
|
||||
* const alertWithIdOnly = await prisma.alert.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends AlertUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, AlertUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$AlertPayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one Alert.
|
||||
* @param {AlertUpsertArgs} args - Arguments to update or create a Alert.
|
||||
@@ -1125,6 +1204,25 @@ export type AlertCreateManyArgs<ExtArgs extends runtime.Types.Extensions.Interna
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Alert createManyAndReturn
|
||||
*/
|
||||
export type AlertCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the Alert
|
||||
*/
|
||||
select?: Prisma.AlertSelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the Alert
|
||||
*/
|
||||
omit?: Prisma.AlertOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many Alerts.
|
||||
*/
|
||||
data: Prisma.AlertCreateManyInput | Prisma.AlertCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Alert update
|
||||
*/
|
||||
@@ -1165,6 +1263,32 @@ export type AlertUpdateManyArgs<ExtArgs extends runtime.Types.Extensions.Interna
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Alert updateManyAndReturn
|
||||
*/
|
||||
export type AlertUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the Alert
|
||||
*/
|
||||
select?: Prisma.AlertSelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the Alert
|
||||
*/
|
||||
omit?: Prisma.AlertOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update Alerts.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.AlertUpdateManyMutationInput, Prisma.AlertUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which Alerts to update
|
||||
*/
|
||||
where?: Prisma.AlertWhereInput
|
||||
/**
|
||||
* Limit how many Alerts to update.
|
||||
*/
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Alert upsert
|
||||
*/
|
||||
|
||||
@@ -242,7 +242,6 @@ export type ArticleOrderByWithRelationInput = {
|
||||
date?: Prisma.SortOrder
|
||||
createdAt?: Prisma.SortOrder
|
||||
updatedAt?: Prisma.SortOrder
|
||||
_relevance?: Prisma.ArticleOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type ArticleWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -353,12 +352,6 @@ export type ArticleUncheckedUpdateManyInput = {
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
}
|
||||
|
||||
export type ArticleOrderByRelevanceInput = {
|
||||
fields: Prisma.ArticleOrderByRelevanceFieldEnum | Prisma.ArticleOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type ArticleCountOrderByAggregateInput = {
|
||||
id?: Prisma.SortOrder
|
||||
title?: Prisma.SortOrder
|
||||
@@ -409,7 +402,25 @@ export type ArticleSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["article"]>
|
||||
|
||||
export type ArticleSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
title?: boolean
|
||||
slug?: boolean
|
||||
content?: boolean
|
||||
date?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["article"]>
|
||||
|
||||
export type ArticleSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
title?: boolean
|
||||
slug?: boolean
|
||||
content?: boolean
|
||||
date?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["article"]>
|
||||
|
||||
export type ArticleSelectScalar = {
|
||||
id?: boolean
|
||||
@@ -551,6 +562,30 @@ export interface ArticleDelegate<ExtArgs extends runtime.Types.Extensions.Intern
|
||||
*/
|
||||
createMany<T extends ArticleCreateManyArgs>(args?: Prisma.SelectSubset<T, ArticleCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many Articles and returns the data saved in the database.
|
||||
* @param {ArticleCreateManyAndReturnArgs} args - Arguments to create many Articles.
|
||||
* @example
|
||||
* // Create many Articles
|
||||
* const article = await prisma.article.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many Articles and only return the `id`
|
||||
* const articleWithIdOnly = await prisma.article.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends ArticleCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, ArticleCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ArticlePayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a Article.
|
||||
* @param {ArticleDeleteArgs} args - Arguments to delete one Article.
|
||||
@@ -615,6 +650,36 @@ export interface ArticleDelegate<ExtArgs extends runtime.Types.Extensions.Intern
|
||||
*/
|
||||
updateMany<T extends ArticleUpdateManyArgs>(args: Prisma.SelectSubset<T, ArticleUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more Articles and returns the data updated in the database.
|
||||
* @param {ArticleUpdateManyAndReturnArgs} args - Arguments to update many Articles.
|
||||
* @example
|
||||
* // Update many Articles
|
||||
* const article = await prisma.article.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more Articles and only return the `id`
|
||||
* const articleWithIdOnly = await prisma.article.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends ArticleUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, ArticleUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ArticlePayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one Article.
|
||||
* @param {ArticleUpsertArgs} args - Arguments to update or create a Article.
|
||||
@@ -1023,6 +1088,25 @@ export type ArticleCreateManyArgs<ExtArgs extends runtime.Types.Extensions.Inter
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Article createManyAndReturn
|
||||
*/
|
||||
export type ArticleCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the Article
|
||||
*/
|
||||
select?: Prisma.ArticleSelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the Article
|
||||
*/
|
||||
omit?: Prisma.ArticleOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many Articles.
|
||||
*/
|
||||
data: Prisma.ArticleCreateManyInput | Prisma.ArticleCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Article update
|
||||
*/
|
||||
@@ -1063,6 +1147,32 @@ export type ArticleUpdateManyArgs<ExtArgs extends runtime.Types.Extensions.Inter
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Article updateManyAndReturn
|
||||
*/
|
||||
export type ArticleUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the Article
|
||||
*/
|
||||
select?: Prisma.ArticleSelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the Article
|
||||
*/
|
||||
omit?: Prisma.ArticleOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update Articles.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.ArticleUpdateManyMutationInput, Prisma.ArticleUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which Articles to update
|
||||
*/
|
||||
where?: Prisma.ArticleWhereInput
|
||||
/**
|
||||
* Limit how many Articles to update.
|
||||
*/
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Article upsert
|
||||
*/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -267,7 +267,6 @@ export type CheckInOrderByWithRelationInput = {
|
||||
createdAt?: Prisma.SortOrder
|
||||
habit?: Prisma.HabitOrderByWithRelationInput
|
||||
user?: Prisma.UserOrderByWithRelationInput
|
||||
_relevance?: Prisma.CheckInOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type CheckInWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -399,12 +398,6 @@ export type CheckInOrderByRelationAggregateInput = {
|
||||
_count?: Prisma.SortOrder
|
||||
}
|
||||
|
||||
export type CheckInOrderByRelevanceInput = {
|
||||
fields: Prisma.CheckInOrderByRelevanceFieldEnum | Prisma.CheckInOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type CheckInHabitIdDateSeqCompoundUniqueInput = {
|
||||
habitId: number
|
||||
date: string
|
||||
@@ -739,7 +732,31 @@ export type CheckInSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs
|
||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["checkIn"]>
|
||||
|
||||
export type CheckInSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
habitId?: boolean
|
||||
userId?: boolean
|
||||
date?: boolean
|
||||
seq?: boolean
|
||||
note?: boolean
|
||||
source?: boolean
|
||||
createdAt?: boolean
|
||||
habit?: boolean | Prisma.HabitDefaultArgs<ExtArgs>
|
||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["checkIn"]>
|
||||
|
||||
export type CheckInSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
habitId?: boolean
|
||||
userId?: boolean
|
||||
date?: boolean
|
||||
seq?: boolean
|
||||
note?: boolean
|
||||
source?: boolean
|
||||
createdAt?: boolean
|
||||
habit?: boolean | Prisma.HabitDefaultArgs<ExtArgs>
|
||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["checkIn"]>
|
||||
|
||||
export type CheckInSelectScalar = {
|
||||
id?: boolean
|
||||
@@ -757,6 +774,14 @@ export type CheckInInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs
|
||||
habit?: boolean | Prisma.HabitDefaultArgs<ExtArgs>
|
||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type CheckInIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
habit?: boolean | Prisma.HabitDefaultArgs<ExtArgs>
|
||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type CheckInIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
habit?: boolean | Prisma.HabitDefaultArgs<ExtArgs>
|
||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
}
|
||||
|
||||
export type $CheckInPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
name: "CheckIn"
|
||||
@@ -890,6 +915,30 @@ export interface CheckInDelegate<ExtArgs extends runtime.Types.Extensions.Intern
|
||||
*/
|
||||
createMany<T extends CheckInCreateManyArgs>(args?: Prisma.SelectSubset<T, CheckInCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many CheckIns and returns the data saved in the database.
|
||||
* @param {CheckInCreateManyAndReturnArgs} args - Arguments to create many CheckIns.
|
||||
* @example
|
||||
* // Create many CheckIns
|
||||
* const checkIn = await prisma.checkIn.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many CheckIns and only return the `id`
|
||||
* const checkInWithIdOnly = await prisma.checkIn.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends CheckInCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, CheckInCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$CheckInPayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a CheckIn.
|
||||
* @param {CheckInDeleteArgs} args - Arguments to delete one CheckIn.
|
||||
@@ -954,6 +1003,36 @@ export interface CheckInDelegate<ExtArgs extends runtime.Types.Extensions.Intern
|
||||
*/
|
||||
updateMany<T extends CheckInUpdateManyArgs>(args: Prisma.SelectSubset<T, CheckInUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more CheckIns and returns the data updated in the database.
|
||||
* @param {CheckInUpdateManyAndReturnArgs} args - Arguments to update many CheckIns.
|
||||
* @example
|
||||
* // Update many CheckIns
|
||||
* const checkIn = await prisma.checkIn.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more CheckIns and only return the `id`
|
||||
* const checkInWithIdOnly = await prisma.checkIn.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends CheckInUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, CheckInUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$CheckInPayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one CheckIn.
|
||||
* @param {CheckInUpsertArgs} args - Arguments to update or create a CheckIn.
|
||||
@@ -1389,6 +1468,29 @@ export type CheckInCreateManyArgs<ExtArgs extends runtime.Types.Extensions.Inter
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckIn createManyAndReturn
|
||||
*/
|
||||
export type CheckInCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the CheckIn
|
||||
*/
|
||||
select?: Prisma.CheckInSelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the CheckIn
|
||||
*/
|
||||
omit?: Prisma.CheckInOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many CheckIns.
|
||||
*/
|
||||
data: Prisma.CheckInCreateManyInput | Prisma.CheckInCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.CheckInIncludeCreateManyAndReturn<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckIn update
|
||||
*/
|
||||
@@ -1433,6 +1535,36 @@ export type CheckInUpdateManyArgs<ExtArgs extends runtime.Types.Extensions.Inter
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckIn updateManyAndReturn
|
||||
*/
|
||||
export type CheckInUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the CheckIn
|
||||
*/
|
||||
select?: Prisma.CheckInSelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the CheckIn
|
||||
*/
|
||||
omit?: Prisma.CheckInOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update CheckIns.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.CheckInUpdateManyMutationInput, Prisma.CheckInUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which CheckIns to update
|
||||
*/
|
||||
where?: Prisma.CheckInWhereInput
|
||||
/**
|
||||
* Limit how many CheckIns to update.
|
||||
*/
|
||||
limit?: number
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.CheckInIncludeUpdateManyAndReturn<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckIn upsert
|
||||
*/
|
||||
|
||||
@@ -259,7 +259,6 @@ export type CheckListOrderByWithRelationInput = {
|
||||
updatedAt?: Prisma.SortOrder
|
||||
user?: Prisma.UserOrderByWithRelationInput
|
||||
items?: Prisma.CheckListItemOrderByRelationAggregateInput
|
||||
_relevance?: Prisma.CheckListOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type CheckListWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -395,12 +394,6 @@ export type CheckListOrderByRelationAggregateInput = {
|
||||
_count?: Prisma.SortOrder
|
||||
}
|
||||
|
||||
export type CheckListOrderByRelevanceInput = {
|
||||
fields: Prisma.CheckListOrderByRelevanceFieldEnum | Prisma.CheckListOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type CheckListCountOrderByAggregateInput = {
|
||||
id?: Prisma.SortOrder
|
||||
userId?: Prisma.SortOrder
|
||||
@@ -710,7 +703,29 @@ export type CheckListSelect<ExtArgs extends runtime.Types.Extensions.InternalArg
|
||||
_count?: boolean | Prisma.CheckListCountOutputTypeDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["checkList"]>
|
||||
|
||||
export type CheckListSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
userId?: boolean
|
||||
title?: boolean
|
||||
scene?: boolean
|
||||
note?: boolean
|
||||
archived?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["checkList"]>
|
||||
|
||||
export type CheckListSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
userId?: boolean
|
||||
title?: boolean
|
||||
scene?: boolean
|
||||
note?: boolean
|
||||
archived?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["checkList"]>
|
||||
|
||||
export type CheckListSelectScalar = {
|
||||
id?: boolean
|
||||
@@ -729,6 +744,12 @@ export type CheckListInclude<ExtArgs extends runtime.Types.Extensions.InternalAr
|
||||
items?: boolean | Prisma.CheckList$itemsArgs<ExtArgs>
|
||||
_count?: boolean | Prisma.CheckListCountOutputTypeDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type CheckListIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type CheckListIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
}
|
||||
|
||||
export type $CheckListPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
name: "CheckList"
|
||||
@@ -862,6 +883,30 @@ export interface CheckListDelegate<ExtArgs extends runtime.Types.Extensions.Inte
|
||||
*/
|
||||
createMany<T extends CheckListCreateManyArgs>(args?: Prisma.SelectSubset<T, CheckListCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many CheckLists and returns the data saved in the database.
|
||||
* @param {CheckListCreateManyAndReturnArgs} args - Arguments to create many CheckLists.
|
||||
* @example
|
||||
* // Create many CheckLists
|
||||
* const checkList = await prisma.checkList.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many CheckLists and only return the `id`
|
||||
* const checkListWithIdOnly = await prisma.checkList.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends CheckListCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, CheckListCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$CheckListPayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a CheckList.
|
||||
* @param {CheckListDeleteArgs} args - Arguments to delete one CheckList.
|
||||
@@ -926,6 +971,36 @@ export interface CheckListDelegate<ExtArgs extends runtime.Types.Extensions.Inte
|
||||
*/
|
||||
updateMany<T extends CheckListUpdateManyArgs>(args: Prisma.SelectSubset<T, CheckListUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more CheckLists and returns the data updated in the database.
|
||||
* @param {CheckListUpdateManyAndReturnArgs} args - Arguments to update many CheckLists.
|
||||
* @example
|
||||
* // Update many CheckLists
|
||||
* const checkList = await prisma.checkList.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more CheckLists and only return the `id`
|
||||
* const checkListWithIdOnly = await prisma.checkList.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends CheckListUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, CheckListUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$CheckListPayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one CheckList.
|
||||
* @param {CheckListUpsertArgs} args - Arguments to update or create a CheckList.
|
||||
@@ -1361,6 +1436,29 @@ export type CheckListCreateManyArgs<ExtArgs extends runtime.Types.Extensions.Int
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckList createManyAndReturn
|
||||
*/
|
||||
export type CheckListCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the CheckList
|
||||
*/
|
||||
select?: Prisma.CheckListSelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the CheckList
|
||||
*/
|
||||
omit?: Prisma.CheckListOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many CheckLists.
|
||||
*/
|
||||
data: Prisma.CheckListCreateManyInput | Prisma.CheckListCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.CheckListIncludeCreateManyAndReturn<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckList update
|
||||
*/
|
||||
@@ -1405,6 +1503,36 @@ export type CheckListUpdateManyArgs<ExtArgs extends runtime.Types.Extensions.Int
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckList updateManyAndReturn
|
||||
*/
|
||||
export type CheckListUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the CheckList
|
||||
*/
|
||||
select?: Prisma.CheckListSelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the CheckList
|
||||
*/
|
||||
omit?: Prisma.CheckListOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update CheckLists.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.CheckListUpdateManyMutationInput, Prisma.CheckListUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which CheckLists to update
|
||||
*/
|
||||
where?: Prisma.CheckListWhereInput
|
||||
/**
|
||||
* Limit how many CheckLists to update.
|
||||
*/
|
||||
limit?: number
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.CheckListIncludeUpdateManyAndReturn<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckList upsert
|
||||
*/
|
||||
|
||||
@@ -270,7 +270,6 @@ export type CheckListItemOrderByWithRelationInput = {
|
||||
createdAt?: Prisma.SortOrder
|
||||
updatedAt?: Prisma.SortOrder
|
||||
list?: Prisma.CheckListOrderByWithRelationInput
|
||||
_relevance?: Prisma.CheckListItemOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type CheckListItemWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -411,12 +410,6 @@ export type CheckListItemOrderByRelationAggregateInput = {
|
||||
_count?: Prisma.SortOrder
|
||||
}
|
||||
|
||||
export type CheckListItemOrderByRelevanceInput = {
|
||||
fields: Prisma.CheckListItemOrderByRelevanceFieldEnum | Prisma.CheckListItemOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type CheckListItemCountOrderByAggregateInput = {
|
||||
id?: Prisma.SortOrder
|
||||
listId?: Prisma.SortOrder
|
||||
@@ -627,7 +620,31 @@ export type CheckListItemSelect<ExtArgs extends runtime.Types.Extensions.Interna
|
||||
list?: boolean | Prisma.CheckListDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["checkListItem"]>
|
||||
|
||||
export type CheckListItemSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
listId?: boolean
|
||||
title?: boolean
|
||||
note?: boolean
|
||||
done?: boolean
|
||||
doneAt?: boolean
|
||||
sortOrder?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
list?: boolean | Prisma.CheckListDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["checkListItem"]>
|
||||
|
||||
export type CheckListItemSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
listId?: boolean
|
||||
title?: boolean
|
||||
note?: boolean
|
||||
done?: boolean
|
||||
doneAt?: boolean
|
||||
sortOrder?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
list?: boolean | Prisma.CheckListDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["checkListItem"]>
|
||||
|
||||
export type CheckListItemSelectScalar = {
|
||||
id?: boolean
|
||||
@@ -645,6 +662,12 @@ export type CheckListItemOmit<ExtArgs extends runtime.Types.Extensions.InternalA
|
||||
export type CheckListItemInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
list?: boolean | Prisma.CheckListDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type CheckListItemIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
list?: boolean | Prisma.CheckListDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type CheckListItemIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
list?: boolean | Prisma.CheckListDefaultArgs<ExtArgs>
|
||||
}
|
||||
|
||||
export type $CheckListItemPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
name: "CheckListItem"
|
||||
@@ -778,6 +801,30 @@ export interface CheckListItemDelegate<ExtArgs extends runtime.Types.Extensions.
|
||||
*/
|
||||
createMany<T extends CheckListItemCreateManyArgs>(args?: Prisma.SelectSubset<T, CheckListItemCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many CheckListItems and returns the data saved in the database.
|
||||
* @param {CheckListItemCreateManyAndReturnArgs} args - Arguments to create many CheckListItems.
|
||||
* @example
|
||||
* // Create many CheckListItems
|
||||
* const checkListItem = await prisma.checkListItem.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many CheckListItems and only return the `id`
|
||||
* const checkListItemWithIdOnly = await prisma.checkListItem.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends CheckListItemCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, CheckListItemCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$CheckListItemPayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a CheckListItem.
|
||||
* @param {CheckListItemDeleteArgs} args - Arguments to delete one CheckListItem.
|
||||
@@ -842,6 +889,36 @@ export interface CheckListItemDelegate<ExtArgs extends runtime.Types.Extensions.
|
||||
*/
|
||||
updateMany<T extends CheckListItemUpdateManyArgs>(args: Prisma.SelectSubset<T, CheckListItemUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more CheckListItems and returns the data updated in the database.
|
||||
* @param {CheckListItemUpdateManyAndReturnArgs} args - Arguments to update many CheckListItems.
|
||||
* @example
|
||||
* // Update many CheckListItems
|
||||
* const checkListItem = await prisma.checkListItem.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more CheckListItems and only return the `id`
|
||||
* const checkListItemWithIdOnly = await prisma.checkListItem.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends CheckListItemUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, CheckListItemUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$CheckListItemPayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one CheckListItem.
|
||||
* @param {CheckListItemUpsertArgs} args - Arguments to update or create a CheckListItem.
|
||||
@@ -1277,6 +1354,29 @@ export type CheckListItemCreateManyArgs<ExtArgs extends runtime.Types.Extensions
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckListItem createManyAndReturn
|
||||
*/
|
||||
export type CheckListItemCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the CheckListItem
|
||||
*/
|
||||
select?: Prisma.CheckListItemSelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the CheckListItem
|
||||
*/
|
||||
omit?: Prisma.CheckListItemOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many CheckListItems.
|
||||
*/
|
||||
data: Prisma.CheckListItemCreateManyInput | Prisma.CheckListItemCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.CheckListItemIncludeCreateManyAndReturn<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckListItem update
|
||||
*/
|
||||
@@ -1321,6 +1421,36 @@ export type CheckListItemUpdateManyArgs<ExtArgs extends runtime.Types.Extensions
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckListItem updateManyAndReturn
|
||||
*/
|
||||
export type CheckListItemUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the CheckListItem
|
||||
*/
|
||||
select?: Prisma.CheckListItemSelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the CheckListItem
|
||||
*/
|
||||
omit?: Prisma.CheckListItemOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update CheckListItems.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.CheckListItemUpdateManyMutationInput, Prisma.CheckListItemUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which CheckListItems to update
|
||||
*/
|
||||
where?: Prisma.CheckListItemWhereInput
|
||||
/**
|
||||
* Limit how many CheckListItems to update.
|
||||
*/
|
||||
limit?: number
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.CheckListItemIncludeUpdateManyAndReturn<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckListItem upsert
|
||||
*/
|
||||
|
||||
@@ -253,7 +253,6 @@ export type CheckListTemplateOrderByWithRelationInput = {
|
||||
createdAt?: Prisma.SortOrder
|
||||
updatedAt?: Prisma.SortOrder
|
||||
user?: Prisma.UserOrderByWithRelationInput
|
||||
_relevance?: Prisma.CheckListTemplateOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type CheckListTemplateWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -384,12 +383,6 @@ export type CheckListTemplateOrderByRelationAggregateInput = {
|
||||
_count?: Prisma.SortOrder
|
||||
}
|
||||
|
||||
export type CheckListTemplateOrderByRelevanceInput = {
|
||||
fields: Prisma.CheckListTemplateOrderByRelevanceFieldEnum | Prisma.CheckListTemplateOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type CheckListTemplateCountOrderByAggregateInput = {
|
||||
id?: Prisma.SortOrder
|
||||
userId?: Prisma.SortOrder
|
||||
@@ -585,7 +578,29 @@ export type CheckListTemplateSelect<ExtArgs extends runtime.Types.Extensions.Int
|
||||
user?: boolean | Prisma.CheckListTemplate$userArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["checkListTemplate"]>
|
||||
|
||||
export type CheckListTemplateSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
userId?: boolean
|
||||
name?: boolean
|
||||
scene?: boolean
|
||||
items?: boolean
|
||||
builtin?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
user?: boolean | Prisma.CheckListTemplate$userArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["checkListTemplate"]>
|
||||
|
||||
export type CheckListTemplateSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
userId?: boolean
|
||||
name?: boolean
|
||||
scene?: boolean
|
||||
items?: boolean
|
||||
builtin?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
user?: boolean | Prisma.CheckListTemplate$userArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["checkListTemplate"]>
|
||||
|
||||
export type CheckListTemplateSelectScalar = {
|
||||
id?: boolean
|
||||
@@ -602,6 +617,12 @@ export type CheckListTemplateOmit<ExtArgs extends runtime.Types.Extensions.Inter
|
||||
export type CheckListTemplateInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
user?: boolean | Prisma.CheckListTemplate$userArgs<ExtArgs>
|
||||
}
|
||||
export type CheckListTemplateIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
user?: boolean | Prisma.CheckListTemplate$userArgs<ExtArgs>
|
||||
}
|
||||
export type CheckListTemplateIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
user?: boolean | Prisma.CheckListTemplate$userArgs<ExtArgs>
|
||||
}
|
||||
|
||||
export type $CheckListTemplatePayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
name: "CheckListTemplate"
|
||||
@@ -734,6 +755,30 @@ export interface CheckListTemplateDelegate<ExtArgs extends runtime.Types.Extensi
|
||||
*/
|
||||
createMany<T extends CheckListTemplateCreateManyArgs>(args?: Prisma.SelectSubset<T, CheckListTemplateCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many CheckListTemplates and returns the data saved in the database.
|
||||
* @param {CheckListTemplateCreateManyAndReturnArgs} args - Arguments to create many CheckListTemplates.
|
||||
* @example
|
||||
* // Create many CheckListTemplates
|
||||
* const checkListTemplate = await prisma.checkListTemplate.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many CheckListTemplates and only return the `id`
|
||||
* const checkListTemplateWithIdOnly = await prisma.checkListTemplate.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends CheckListTemplateCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, CheckListTemplateCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$CheckListTemplatePayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a CheckListTemplate.
|
||||
* @param {CheckListTemplateDeleteArgs} args - Arguments to delete one CheckListTemplate.
|
||||
@@ -798,6 +843,36 @@ export interface CheckListTemplateDelegate<ExtArgs extends runtime.Types.Extensi
|
||||
*/
|
||||
updateMany<T extends CheckListTemplateUpdateManyArgs>(args: Prisma.SelectSubset<T, CheckListTemplateUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more CheckListTemplates and returns the data updated in the database.
|
||||
* @param {CheckListTemplateUpdateManyAndReturnArgs} args - Arguments to update many CheckListTemplates.
|
||||
* @example
|
||||
* // Update many CheckListTemplates
|
||||
* const checkListTemplate = await prisma.checkListTemplate.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more CheckListTemplates and only return the `id`
|
||||
* const checkListTemplateWithIdOnly = await prisma.checkListTemplate.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends CheckListTemplateUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, CheckListTemplateUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$CheckListTemplatePayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one CheckListTemplate.
|
||||
* @param {CheckListTemplateUpsertArgs} args - Arguments to update or create a CheckListTemplate.
|
||||
@@ -1232,6 +1307,29 @@ export type CheckListTemplateCreateManyArgs<ExtArgs extends runtime.Types.Extens
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckListTemplate createManyAndReturn
|
||||
*/
|
||||
export type CheckListTemplateCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the CheckListTemplate
|
||||
*/
|
||||
select?: Prisma.CheckListTemplateSelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the CheckListTemplate
|
||||
*/
|
||||
omit?: Prisma.CheckListTemplateOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many CheckListTemplates.
|
||||
*/
|
||||
data: Prisma.CheckListTemplateCreateManyInput | Prisma.CheckListTemplateCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.CheckListTemplateIncludeCreateManyAndReturn<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckListTemplate update
|
||||
*/
|
||||
@@ -1276,6 +1374,36 @@ export type CheckListTemplateUpdateManyArgs<ExtArgs extends runtime.Types.Extens
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckListTemplate updateManyAndReturn
|
||||
*/
|
||||
export type CheckListTemplateUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the CheckListTemplate
|
||||
*/
|
||||
select?: Prisma.CheckListTemplateSelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the CheckListTemplate
|
||||
*/
|
||||
omit?: Prisma.CheckListTemplateOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update CheckListTemplates.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.CheckListTemplateUpdateManyMutationInput, Prisma.CheckListTemplateUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which CheckListTemplates to update
|
||||
*/
|
||||
where?: Prisma.CheckListTemplateWhereInput
|
||||
/**
|
||||
* Limit how many CheckListTemplates to update.
|
||||
*/
|
||||
limit?: number
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.CheckListTemplateIncludeUpdateManyAndReturn<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckListTemplate upsert
|
||||
*/
|
||||
|
||||
@@ -228,7 +228,6 @@ export type Econ_SowInventoryOrderByWithRelationInput = {
|
||||
inventory?: Prisma.SortOrder
|
||||
createdAt?: Prisma.SortOrder
|
||||
updatedAt?: Prisma.SortOrder
|
||||
_relevance?: Prisma.Econ_SowInventoryOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type Econ_SowInventoryWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -319,12 +318,6 @@ export type Econ_SowInventoryUncheckedUpdateManyInput = {
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
}
|
||||
|
||||
export type Econ_SowInventoryOrderByRelevanceInput = {
|
||||
fields: Prisma.Econ_SowInventoryOrderByRelevanceFieldEnum | Prisma.Econ_SowInventoryOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type Econ_SowInventoryCountOrderByAggregateInput = {
|
||||
id?: Prisma.SortOrder
|
||||
month?: Prisma.SortOrder
|
||||
@@ -369,7 +362,21 @@ export type Econ_SowInventorySelect<ExtArgs extends runtime.Types.Extensions.Int
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["econ_SowInventory"]>
|
||||
|
||||
export type Econ_SowInventorySelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
month?: boolean
|
||||
inventory?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["econ_SowInventory"]>
|
||||
|
||||
export type Econ_SowInventorySelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
month?: boolean
|
||||
inventory?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["econ_SowInventory"]>
|
||||
|
||||
export type Econ_SowInventorySelectScalar = {
|
||||
id?: boolean
|
||||
@@ -507,6 +514,30 @@ export interface Econ_SowInventoryDelegate<ExtArgs extends runtime.Types.Extensi
|
||||
*/
|
||||
createMany<T extends Econ_SowInventoryCreateManyArgs>(args?: Prisma.SelectSubset<T, Econ_SowInventoryCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many Econ_SowInventories and returns the data saved in the database.
|
||||
* @param {Econ_SowInventoryCreateManyAndReturnArgs} args - Arguments to create many Econ_SowInventories.
|
||||
* @example
|
||||
* // Create many Econ_SowInventories
|
||||
* const econ_SowInventory = await prisma.econ_SowInventory.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many Econ_SowInventories and only return the `id`
|
||||
* const econ_SowInventoryWithIdOnly = await prisma.econ_SowInventory.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends Econ_SowInventoryCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, Econ_SowInventoryCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$Econ_SowInventoryPayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a Econ_SowInventory.
|
||||
* @param {Econ_SowInventoryDeleteArgs} args - Arguments to delete one Econ_SowInventory.
|
||||
@@ -571,6 +602,36 @@ export interface Econ_SowInventoryDelegate<ExtArgs extends runtime.Types.Extensi
|
||||
*/
|
||||
updateMany<T extends Econ_SowInventoryUpdateManyArgs>(args: Prisma.SelectSubset<T, Econ_SowInventoryUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more Econ_SowInventories and returns the data updated in the database.
|
||||
* @param {Econ_SowInventoryUpdateManyAndReturnArgs} args - Arguments to update many Econ_SowInventories.
|
||||
* @example
|
||||
* // Update many Econ_SowInventories
|
||||
* const econ_SowInventory = await prisma.econ_SowInventory.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more Econ_SowInventories and only return the `id`
|
||||
* const econ_SowInventoryWithIdOnly = await prisma.econ_SowInventory.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends Econ_SowInventoryUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, Econ_SowInventoryUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$Econ_SowInventoryPayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one Econ_SowInventory.
|
||||
* @param {Econ_SowInventoryUpsertArgs} args - Arguments to update or create a Econ_SowInventory.
|
||||
@@ -977,6 +1038,25 @@ export type Econ_SowInventoryCreateManyArgs<ExtArgs extends runtime.Types.Extens
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Econ_SowInventory createManyAndReturn
|
||||
*/
|
||||
export type Econ_SowInventoryCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the Econ_SowInventory
|
||||
*/
|
||||
select?: Prisma.Econ_SowInventorySelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the Econ_SowInventory
|
||||
*/
|
||||
omit?: Prisma.Econ_SowInventoryOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many Econ_SowInventories.
|
||||
*/
|
||||
data: Prisma.Econ_SowInventoryCreateManyInput | Prisma.Econ_SowInventoryCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Econ_SowInventory update
|
||||
*/
|
||||
@@ -1017,6 +1097,32 @@ export type Econ_SowInventoryUpdateManyArgs<ExtArgs extends runtime.Types.Extens
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Econ_SowInventory updateManyAndReturn
|
||||
*/
|
||||
export type Econ_SowInventoryUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the Econ_SowInventory
|
||||
*/
|
||||
select?: Prisma.Econ_SowInventorySelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the Econ_SowInventory
|
||||
*/
|
||||
omit?: Prisma.Econ_SowInventoryOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update Econ_SowInventories.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.Econ_SowInventoryUpdateManyMutationInput, Prisma.Econ_SowInventoryUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which Econ_SowInventories to update
|
||||
*/
|
||||
where?: Prisma.Econ_SowInventoryWhereInput
|
||||
/**
|
||||
* Limit how many Econ_SowInventories to update.
|
||||
*/
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Econ_SowInventory upsert
|
||||
*/
|
||||
|
||||
@@ -303,7 +303,6 @@ export type HabitOrderByWithRelationInput = {
|
||||
updatedAt?: Prisma.SortOrder
|
||||
user?: Prisma.UserOrderByWithRelationInput
|
||||
records?: Prisma.CheckInOrderByRelationAggregateInput
|
||||
_relevance?: Prisma.HabitOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type HabitWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -479,12 +478,6 @@ export type HabitOrderByRelationAggregateInput = {
|
||||
_count?: Prisma.SortOrder
|
||||
}
|
||||
|
||||
export type HabitOrderByRelevanceInput = {
|
||||
fields: Prisma.HabitOrderByRelevanceFieldEnum | Prisma.HabitOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type HabitCountOrderByAggregateInput = {
|
||||
id?: Prisma.SortOrder
|
||||
userId?: Prisma.SortOrder
|
||||
@@ -858,7 +851,37 @@ export type HabitSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs =
|
||||
_count?: boolean | Prisma.HabitCountOutputTypeDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["habit"]>
|
||||
|
||||
export type HabitSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
userId?: boolean
|
||||
name?: boolean
|
||||
period?: boolean
|
||||
target?: boolean
|
||||
color?: boolean
|
||||
icon?: boolean
|
||||
note?: boolean
|
||||
archived?: boolean
|
||||
sortOrder?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["habit"]>
|
||||
|
||||
export type HabitSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
userId?: boolean
|
||||
name?: boolean
|
||||
period?: boolean
|
||||
target?: boolean
|
||||
color?: boolean
|
||||
icon?: boolean
|
||||
note?: boolean
|
||||
archived?: boolean
|
||||
sortOrder?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["habit"]>
|
||||
|
||||
export type HabitSelectScalar = {
|
||||
id?: boolean
|
||||
@@ -881,6 +904,12 @@ export type HabitInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs =
|
||||
records?: boolean | Prisma.Habit$recordsArgs<ExtArgs>
|
||||
_count?: boolean | Prisma.HabitCountOutputTypeDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type HabitIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type HabitIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
}
|
||||
|
||||
export type $HabitPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
name: "Habit"
|
||||
@@ -1018,6 +1047,30 @@ export interface HabitDelegate<ExtArgs extends runtime.Types.Extensions.Internal
|
||||
*/
|
||||
createMany<T extends HabitCreateManyArgs>(args?: Prisma.SelectSubset<T, HabitCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many Habits and returns the data saved in the database.
|
||||
* @param {HabitCreateManyAndReturnArgs} args - Arguments to create many Habits.
|
||||
* @example
|
||||
* // Create many Habits
|
||||
* const habit = await prisma.habit.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many Habits and only return the `id`
|
||||
* const habitWithIdOnly = await prisma.habit.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends HabitCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, HabitCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$HabitPayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a Habit.
|
||||
* @param {HabitDeleteArgs} args - Arguments to delete one Habit.
|
||||
@@ -1082,6 +1135,36 @@ export interface HabitDelegate<ExtArgs extends runtime.Types.Extensions.Internal
|
||||
*/
|
||||
updateMany<T extends HabitUpdateManyArgs>(args: Prisma.SelectSubset<T, HabitUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more Habits and returns the data updated in the database.
|
||||
* @param {HabitUpdateManyAndReturnArgs} args - Arguments to update many Habits.
|
||||
* @example
|
||||
* // Update many Habits
|
||||
* const habit = await prisma.habit.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more Habits and only return the `id`
|
||||
* const habitWithIdOnly = await prisma.habit.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends HabitUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, HabitUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$HabitPayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one Habit.
|
||||
* @param {HabitUpsertArgs} args - Arguments to update or create a Habit.
|
||||
@@ -1521,6 +1604,29 @@ export type HabitCreateManyArgs<ExtArgs extends runtime.Types.Extensions.Interna
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Habit createManyAndReturn
|
||||
*/
|
||||
export type HabitCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the Habit
|
||||
*/
|
||||
select?: Prisma.HabitSelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the Habit
|
||||
*/
|
||||
omit?: Prisma.HabitOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many Habits.
|
||||
*/
|
||||
data: Prisma.HabitCreateManyInput | Prisma.HabitCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.HabitIncludeCreateManyAndReturn<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Habit update
|
||||
*/
|
||||
@@ -1565,6 +1671,36 @@ export type HabitUpdateManyArgs<ExtArgs extends runtime.Types.Extensions.Interna
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Habit updateManyAndReturn
|
||||
*/
|
||||
export type HabitUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the Habit
|
||||
*/
|
||||
select?: Prisma.HabitSelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the Habit
|
||||
*/
|
||||
omit?: Prisma.HabitOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update Habits.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.HabitUpdateManyMutationInput, Prisma.HabitUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which Habits to update
|
||||
*/
|
||||
where?: Prisma.HabitWhereInput
|
||||
/**
|
||||
* Limit how many Habits to update.
|
||||
*/
|
||||
limit?: number
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.HabitIncludeUpdateManyAndReturn<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Habit upsert
|
||||
*/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -233,7 +233,6 @@ export type MihomeAccountOrderByWithRelationInput = {
|
||||
status?: Prisma.SortOrder
|
||||
lastError?: Prisma.SortOrder
|
||||
updatedAt?: Prisma.SortOrder
|
||||
_relevance?: Prisma.MihomeAccountOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type MihomeAccountWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -334,12 +333,6 @@ export type MihomeAccountUncheckedUpdateManyInput = {
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
}
|
||||
|
||||
export type MihomeAccountOrderByRelevanceInput = {
|
||||
fields: Prisma.MihomeAccountOrderByRelevanceFieldEnum | Prisma.MihomeAccountOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type MihomeAccountCountOrderByAggregateInput = {
|
||||
id?: Prisma.SortOrder
|
||||
key?: Prisma.SortOrder
|
||||
@@ -386,7 +379,23 @@ export type MihomeAccountSelect<ExtArgs extends runtime.Types.Extensions.Interna
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["mihomeAccount"]>
|
||||
|
||||
export type MihomeAccountSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
key?: boolean
|
||||
userId?: boolean
|
||||
status?: boolean
|
||||
lastError?: boolean
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["mihomeAccount"]>
|
||||
|
||||
export type MihomeAccountSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
key?: boolean
|
||||
userId?: boolean
|
||||
status?: boolean
|
||||
lastError?: boolean
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["mihomeAccount"]>
|
||||
|
||||
export type MihomeAccountSelectScalar = {
|
||||
id?: boolean
|
||||
@@ -526,6 +535,30 @@ export interface MihomeAccountDelegate<ExtArgs extends runtime.Types.Extensions.
|
||||
*/
|
||||
createMany<T extends MihomeAccountCreateManyArgs>(args?: Prisma.SelectSubset<T, MihomeAccountCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many MihomeAccounts and returns the data saved in the database.
|
||||
* @param {MihomeAccountCreateManyAndReturnArgs} args - Arguments to create many MihomeAccounts.
|
||||
* @example
|
||||
* // Create many MihomeAccounts
|
||||
* const mihomeAccount = await prisma.mihomeAccount.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many MihomeAccounts and only return the `id`
|
||||
* const mihomeAccountWithIdOnly = await prisma.mihomeAccount.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends MihomeAccountCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, MihomeAccountCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$MihomeAccountPayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a MihomeAccount.
|
||||
* @param {MihomeAccountDeleteArgs} args - Arguments to delete one MihomeAccount.
|
||||
@@ -590,6 +623,36 @@ export interface MihomeAccountDelegate<ExtArgs extends runtime.Types.Extensions.
|
||||
*/
|
||||
updateMany<T extends MihomeAccountUpdateManyArgs>(args: Prisma.SelectSubset<T, MihomeAccountUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more MihomeAccounts and returns the data updated in the database.
|
||||
* @param {MihomeAccountUpdateManyAndReturnArgs} args - Arguments to update many MihomeAccounts.
|
||||
* @example
|
||||
* // Update many MihomeAccounts
|
||||
* const mihomeAccount = await prisma.mihomeAccount.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more MihomeAccounts and only return the `id`
|
||||
* const mihomeAccountWithIdOnly = await prisma.mihomeAccount.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends MihomeAccountUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, MihomeAccountUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$MihomeAccountPayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one MihomeAccount.
|
||||
* @param {MihomeAccountUpsertArgs} args - Arguments to update or create a MihomeAccount.
|
||||
@@ -997,6 +1060,25 @@ export type MihomeAccountCreateManyArgs<ExtArgs extends runtime.Types.Extensions
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeAccount createManyAndReturn
|
||||
*/
|
||||
export type MihomeAccountCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the MihomeAccount
|
||||
*/
|
||||
select?: Prisma.MihomeAccountSelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the MihomeAccount
|
||||
*/
|
||||
omit?: Prisma.MihomeAccountOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many MihomeAccounts.
|
||||
*/
|
||||
data: Prisma.MihomeAccountCreateManyInput | Prisma.MihomeAccountCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeAccount update
|
||||
*/
|
||||
@@ -1037,6 +1119,32 @@ export type MihomeAccountUpdateManyArgs<ExtArgs extends runtime.Types.Extensions
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeAccount updateManyAndReturn
|
||||
*/
|
||||
export type MihomeAccountUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the MihomeAccount
|
||||
*/
|
||||
select?: Prisma.MihomeAccountSelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the MihomeAccount
|
||||
*/
|
||||
omit?: Prisma.MihomeAccountOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update MihomeAccounts.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.MihomeAccountUpdateManyMutationInput, Prisma.MihomeAccountUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which MihomeAccounts to update
|
||||
*/
|
||||
where?: Prisma.MihomeAccountWhereInput
|
||||
/**
|
||||
* Limit how many MihomeAccounts to update.
|
||||
*/
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeAccount upsert
|
||||
*/
|
||||
|
||||
@@ -366,7 +366,6 @@ export type MihomeDeviceOrderByWithRelationInput = {
|
||||
home?: Prisma.MihomeHomeOrderByWithRelationInput
|
||||
samples?: Prisma.MihomeEnvSampleOrderByRelationAggregateInput
|
||||
hours?: Prisma.MihomeEnvHourOrderByRelationAggregateInput
|
||||
_relevance?: Prisma.MihomeDeviceOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type MihomeDeviceWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -597,12 +596,6 @@ export type MihomeDeviceOrderByRelationAggregateInput = {
|
||||
_count?: Prisma.SortOrder
|
||||
}
|
||||
|
||||
export type MihomeDeviceOrderByRelevanceInput = {
|
||||
fields: Prisma.MihomeDeviceOrderByRelevanceFieldEnum | Prisma.MihomeDeviceOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type MihomeDeviceCountOrderByAggregateInput = {
|
||||
id?: Prisma.SortOrder
|
||||
did?: Prisma.SortOrder
|
||||
@@ -744,6 +737,10 @@ export type NullableDateTimeFieldUpdateOperationsInput = {
|
||||
set?: Date | string | null
|
||||
}
|
||||
|
||||
export type BoolFieldUpdateOperationsInput = {
|
||||
set?: boolean
|
||||
}
|
||||
|
||||
export type MihomeDeviceCreateNestedOneWithoutSamplesInput = {
|
||||
create?: Prisma.XOR<Prisma.MihomeDeviceCreateWithoutSamplesInput, Prisma.MihomeDeviceUncheckedCreateWithoutSamplesInput>
|
||||
connectOrCreate?: Prisma.MihomeDeviceCreateOrConnectWithoutSamplesInput
|
||||
@@ -1201,7 +1198,47 @@ export type MihomeDeviceSelect<ExtArgs extends runtime.Types.Extensions.Internal
|
||||
_count?: boolean | Prisma.MihomeDeviceCountOutputTypeDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["mihomeDevice"]>
|
||||
|
||||
export type MihomeDeviceSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
did?: boolean
|
||||
homeId?: boolean
|
||||
model?: boolean
|
||||
name?: boolean
|
||||
alias?: boolean
|
||||
tempSiid?: boolean
|
||||
tempPiid?: boolean
|
||||
humSiid?: boolean
|
||||
humPiid?: boolean
|
||||
battSiid?: boolean
|
||||
battPiid?: boolean
|
||||
battery?: boolean
|
||||
batteryAt?: boolean
|
||||
enabled?: boolean
|
||||
lastSyncAt?: boolean
|
||||
createdAt?: boolean
|
||||
home?: boolean | Prisma.MihomeHomeDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["mihomeDevice"]>
|
||||
|
||||
export type MihomeDeviceSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
did?: boolean
|
||||
homeId?: boolean
|
||||
model?: boolean
|
||||
name?: boolean
|
||||
alias?: boolean
|
||||
tempSiid?: boolean
|
||||
tempPiid?: boolean
|
||||
humSiid?: boolean
|
||||
humPiid?: boolean
|
||||
battSiid?: boolean
|
||||
battPiid?: boolean
|
||||
battery?: boolean
|
||||
batteryAt?: boolean
|
||||
enabled?: boolean
|
||||
lastSyncAt?: boolean
|
||||
createdAt?: boolean
|
||||
home?: boolean | Prisma.MihomeHomeDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["mihomeDevice"]>
|
||||
|
||||
export type MihomeDeviceSelectScalar = {
|
||||
id?: boolean
|
||||
@@ -1230,6 +1267,12 @@ export type MihomeDeviceInclude<ExtArgs extends runtime.Types.Extensions.Interna
|
||||
hours?: boolean | Prisma.MihomeDevice$hoursArgs<ExtArgs>
|
||||
_count?: boolean | Prisma.MihomeDeviceCountOutputTypeDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type MihomeDeviceIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
home?: boolean | Prisma.MihomeHomeDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type MihomeDeviceIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
home?: boolean | Prisma.MihomeHomeDefaultArgs<ExtArgs>
|
||||
}
|
||||
|
||||
export type $MihomeDevicePayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
name: "MihomeDevice"
|
||||
@@ -1373,6 +1416,30 @@ export interface MihomeDeviceDelegate<ExtArgs extends runtime.Types.Extensions.I
|
||||
*/
|
||||
createMany<T extends MihomeDeviceCreateManyArgs>(args?: Prisma.SelectSubset<T, MihomeDeviceCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many MihomeDevices and returns the data saved in the database.
|
||||
* @param {MihomeDeviceCreateManyAndReturnArgs} args - Arguments to create many MihomeDevices.
|
||||
* @example
|
||||
* // Create many MihomeDevices
|
||||
* const mihomeDevice = await prisma.mihomeDevice.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many MihomeDevices and only return the `id`
|
||||
* const mihomeDeviceWithIdOnly = await prisma.mihomeDevice.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends MihomeDeviceCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, MihomeDeviceCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$MihomeDevicePayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a MihomeDevice.
|
||||
* @param {MihomeDeviceDeleteArgs} args - Arguments to delete one MihomeDevice.
|
||||
@@ -1437,6 +1504,36 @@ export interface MihomeDeviceDelegate<ExtArgs extends runtime.Types.Extensions.I
|
||||
*/
|
||||
updateMany<T extends MihomeDeviceUpdateManyArgs>(args: Prisma.SelectSubset<T, MihomeDeviceUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more MihomeDevices and returns the data updated in the database.
|
||||
* @param {MihomeDeviceUpdateManyAndReturnArgs} args - Arguments to update many MihomeDevices.
|
||||
* @example
|
||||
* // Update many MihomeDevices
|
||||
* const mihomeDevice = await prisma.mihomeDevice.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more MihomeDevices and only return the `id`
|
||||
* const mihomeDeviceWithIdOnly = await prisma.mihomeDevice.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends MihomeDeviceUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, MihomeDeviceUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$MihomeDevicePayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one MihomeDevice.
|
||||
* @param {MihomeDeviceUpsertArgs} args - Arguments to update or create a MihomeDevice.
|
||||
@@ -1882,6 +1979,29 @@ export type MihomeDeviceCreateManyArgs<ExtArgs extends runtime.Types.Extensions.
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeDevice createManyAndReturn
|
||||
*/
|
||||
export type MihomeDeviceCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the MihomeDevice
|
||||
*/
|
||||
select?: Prisma.MihomeDeviceSelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the MihomeDevice
|
||||
*/
|
||||
omit?: Prisma.MihomeDeviceOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many MihomeDevices.
|
||||
*/
|
||||
data: Prisma.MihomeDeviceCreateManyInput | Prisma.MihomeDeviceCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.MihomeDeviceIncludeCreateManyAndReturn<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeDevice update
|
||||
*/
|
||||
@@ -1926,6 +2046,36 @@ export type MihomeDeviceUpdateManyArgs<ExtArgs extends runtime.Types.Extensions.
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeDevice updateManyAndReturn
|
||||
*/
|
||||
export type MihomeDeviceUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the MihomeDevice
|
||||
*/
|
||||
select?: Prisma.MihomeDeviceSelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the MihomeDevice
|
||||
*/
|
||||
omit?: Prisma.MihomeDeviceOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update MihomeDevices.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.MihomeDeviceUpdateManyMutationInput, Prisma.MihomeDeviceUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which MihomeDevices to update
|
||||
*/
|
||||
where?: Prisma.MihomeDeviceWhereInput
|
||||
/**
|
||||
* Limit how many MihomeDevices to update.
|
||||
*/
|
||||
limit?: number
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.MihomeDeviceIncludeUpdateManyAndReturn<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeDevice upsert
|
||||
*/
|
||||
|
||||
@@ -566,7 +566,27 @@ export type MihomeEnvHourSelect<ExtArgs extends runtime.Types.Extensions.Interna
|
||||
device?: boolean | Prisma.MihomeDeviceDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["mihomeEnvHour"]>
|
||||
|
||||
export type MihomeEnvHourSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
deviceId?: boolean
|
||||
hour?: boolean
|
||||
temperatureAvg?: boolean
|
||||
humidityAvg?: boolean
|
||||
raw?: boolean
|
||||
createdAt?: boolean
|
||||
device?: boolean | Prisma.MihomeDeviceDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["mihomeEnvHour"]>
|
||||
|
||||
export type MihomeEnvHourSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
deviceId?: boolean
|
||||
hour?: boolean
|
||||
temperatureAvg?: boolean
|
||||
humidityAvg?: boolean
|
||||
raw?: boolean
|
||||
createdAt?: boolean
|
||||
device?: boolean | Prisma.MihomeDeviceDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["mihomeEnvHour"]>
|
||||
|
||||
export type MihomeEnvHourSelectScalar = {
|
||||
id?: boolean
|
||||
@@ -582,6 +602,12 @@ export type MihomeEnvHourOmit<ExtArgs extends runtime.Types.Extensions.InternalA
|
||||
export type MihomeEnvHourInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
device?: boolean | Prisma.MihomeDeviceDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type MihomeEnvHourIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
device?: boolean | Prisma.MihomeDeviceDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type MihomeEnvHourIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
device?: boolean | Prisma.MihomeDeviceDefaultArgs<ExtArgs>
|
||||
}
|
||||
|
||||
export type $MihomeEnvHourPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
name: "MihomeEnvHour"
|
||||
@@ -713,6 +739,30 @@ export interface MihomeEnvHourDelegate<ExtArgs extends runtime.Types.Extensions.
|
||||
*/
|
||||
createMany<T extends MihomeEnvHourCreateManyArgs>(args?: Prisma.SelectSubset<T, MihomeEnvHourCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many MihomeEnvHours and returns the data saved in the database.
|
||||
* @param {MihomeEnvHourCreateManyAndReturnArgs} args - Arguments to create many MihomeEnvHours.
|
||||
* @example
|
||||
* // Create many MihomeEnvHours
|
||||
* const mihomeEnvHour = await prisma.mihomeEnvHour.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many MihomeEnvHours and only return the `id`
|
||||
* const mihomeEnvHourWithIdOnly = await prisma.mihomeEnvHour.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends MihomeEnvHourCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, MihomeEnvHourCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$MihomeEnvHourPayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a MihomeEnvHour.
|
||||
* @param {MihomeEnvHourDeleteArgs} args - Arguments to delete one MihomeEnvHour.
|
||||
@@ -777,6 +827,36 @@ export interface MihomeEnvHourDelegate<ExtArgs extends runtime.Types.Extensions.
|
||||
*/
|
||||
updateMany<T extends MihomeEnvHourUpdateManyArgs>(args: Prisma.SelectSubset<T, MihomeEnvHourUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more MihomeEnvHours and returns the data updated in the database.
|
||||
* @param {MihomeEnvHourUpdateManyAndReturnArgs} args - Arguments to update many MihomeEnvHours.
|
||||
* @example
|
||||
* // Update many MihomeEnvHours
|
||||
* const mihomeEnvHour = await prisma.mihomeEnvHour.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more MihomeEnvHours and only return the `id`
|
||||
* const mihomeEnvHourWithIdOnly = await prisma.mihomeEnvHour.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends MihomeEnvHourUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, MihomeEnvHourUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$MihomeEnvHourPayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one MihomeEnvHour.
|
||||
* @param {MihomeEnvHourUpsertArgs} args - Arguments to update or create a MihomeEnvHour.
|
||||
@@ -1210,6 +1290,29 @@ export type MihomeEnvHourCreateManyArgs<ExtArgs extends runtime.Types.Extensions
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeEnvHour createManyAndReturn
|
||||
*/
|
||||
export type MihomeEnvHourCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the MihomeEnvHour
|
||||
*/
|
||||
select?: Prisma.MihomeEnvHourSelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the MihomeEnvHour
|
||||
*/
|
||||
omit?: Prisma.MihomeEnvHourOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many MihomeEnvHours.
|
||||
*/
|
||||
data: Prisma.MihomeEnvHourCreateManyInput | Prisma.MihomeEnvHourCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.MihomeEnvHourIncludeCreateManyAndReturn<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeEnvHour update
|
||||
*/
|
||||
@@ -1254,6 +1357,36 @@ export type MihomeEnvHourUpdateManyArgs<ExtArgs extends runtime.Types.Extensions
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeEnvHour updateManyAndReturn
|
||||
*/
|
||||
export type MihomeEnvHourUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the MihomeEnvHour
|
||||
*/
|
||||
select?: Prisma.MihomeEnvHourSelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the MihomeEnvHour
|
||||
*/
|
||||
omit?: Prisma.MihomeEnvHourOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update MihomeEnvHours.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.MihomeEnvHourUpdateManyMutationInput, Prisma.MihomeEnvHourUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which MihomeEnvHours to update
|
||||
*/
|
||||
where?: Prisma.MihomeEnvHourWhereInput
|
||||
/**
|
||||
* Limit how many MihomeEnvHours to update.
|
||||
*/
|
||||
limit?: number
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.MihomeEnvHourIncludeUpdateManyAndReturn<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeEnvHour upsert
|
||||
*/
|
||||
|
||||
@@ -348,7 +348,6 @@ export type MihomeEnvSampleOrderByWithRelationInput = {
|
||||
raw?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
createdAt?: Prisma.SortOrder
|
||||
device?: Prisma.MihomeDeviceOrderByWithRelationInput
|
||||
_relevance?: Prisma.MihomeEnvSampleOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type MihomeEnvSampleWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -550,12 +549,6 @@ export type MihomeEnvSampleOrderByRelationAggregateInput = {
|
||||
_count?: Prisma.SortOrder
|
||||
}
|
||||
|
||||
export type MihomeEnvSampleOrderByRelevanceInput = {
|
||||
fields: Prisma.MihomeEnvSampleOrderByRelevanceFieldEnum | Prisma.MihomeEnvSampleOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type MihomeEnvSampleDeviceIdDateCompoundUniqueInput = {
|
||||
deviceId: number
|
||||
date: Date | string
|
||||
@@ -857,7 +850,43 @@ export type MihomeEnvSampleSelect<ExtArgs extends runtime.Types.Extensions.Inter
|
||||
device?: boolean | Prisma.MihomeDeviceDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["mihomeEnvSample"]>
|
||||
|
||||
export type MihomeEnvSampleSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
deviceId?: boolean
|
||||
date?: boolean
|
||||
temperature?: boolean
|
||||
humidity?: boolean
|
||||
temperatureAvg?: boolean
|
||||
humidityAvg?: boolean
|
||||
temperatureMin?: boolean
|
||||
temperatureMax?: boolean
|
||||
humidityMin?: boolean
|
||||
humidityMax?: boolean
|
||||
origin?: boolean
|
||||
sampledAt?: boolean
|
||||
raw?: boolean
|
||||
createdAt?: boolean
|
||||
device?: boolean | Prisma.MihomeDeviceDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["mihomeEnvSample"]>
|
||||
|
||||
export type MihomeEnvSampleSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
deviceId?: boolean
|
||||
date?: boolean
|
||||
temperature?: boolean
|
||||
humidity?: boolean
|
||||
temperatureAvg?: boolean
|
||||
humidityAvg?: boolean
|
||||
temperatureMin?: boolean
|
||||
temperatureMax?: boolean
|
||||
humidityMin?: boolean
|
||||
humidityMax?: boolean
|
||||
origin?: boolean
|
||||
sampledAt?: boolean
|
||||
raw?: boolean
|
||||
createdAt?: boolean
|
||||
device?: boolean | Prisma.MihomeDeviceDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["mihomeEnvSample"]>
|
||||
|
||||
export type MihomeEnvSampleSelectScalar = {
|
||||
id?: boolean
|
||||
@@ -881,6 +910,12 @@ export type MihomeEnvSampleOmit<ExtArgs extends runtime.Types.Extensions.Interna
|
||||
export type MihomeEnvSampleInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
device?: boolean | Prisma.MihomeDeviceDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type MihomeEnvSampleIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
device?: boolean | Prisma.MihomeDeviceDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type MihomeEnvSampleIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
device?: boolean | Prisma.MihomeDeviceDefaultArgs<ExtArgs>
|
||||
}
|
||||
|
||||
export type $MihomeEnvSamplePayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
name: "MihomeEnvSample"
|
||||
@@ -1020,6 +1055,30 @@ export interface MihomeEnvSampleDelegate<ExtArgs extends runtime.Types.Extension
|
||||
*/
|
||||
createMany<T extends MihomeEnvSampleCreateManyArgs>(args?: Prisma.SelectSubset<T, MihomeEnvSampleCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many MihomeEnvSamples and returns the data saved in the database.
|
||||
* @param {MihomeEnvSampleCreateManyAndReturnArgs} args - Arguments to create many MihomeEnvSamples.
|
||||
* @example
|
||||
* // Create many MihomeEnvSamples
|
||||
* const mihomeEnvSample = await prisma.mihomeEnvSample.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many MihomeEnvSamples and only return the `id`
|
||||
* const mihomeEnvSampleWithIdOnly = await prisma.mihomeEnvSample.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends MihomeEnvSampleCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, MihomeEnvSampleCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$MihomeEnvSamplePayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a MihomeEnvSample.
|
||||
* @param {MihomeEnvSampleDeleteArgs} args - Arguments to delete one MihomeEnvSample.
|
||||
@@ -1084,6 +1143,36 @@ export interface MihomeEnvSampleDelegate<ExtArgs extends runtime.Types.Extension
|
||||
*/
|
||||
updateMany<T extends MihomeEnvSampleUpdateManyArgs>(args: Prisma.SelectSubset<T, MihomeEnvSampleUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more MihomeEnvSamples and returns the data updated in the database.
|
||||
* @param {MihomeEnvSampleUpdateManyAndReturnArgs} args - Arguments to update many MihomeEnvSamples.
|
||||
* @example
|
||||
* // Update many MihomeEnvSamples
|
||||
* const mihomeEnvSample = await prisma.mihomeEnvSample.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more MihomeEnvSamples and only return the `id`
|
||||
* const mihomeEnvSampleWithIdOnly = await prisma.mihomeEnvSample.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends MihomeEnvSampleUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, MihomeEnvSampleUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$MihomeEnvSamplePayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one MihomeEnvSample.
|
||||
* @param {MihomeEnvSampleUpsertArgs} args - Arguments to update or create a MihomeEnvSample.
|
||||
@@ -1525,6 +1614,29 @@ export type MihomeEnvSampleCreateManyArgs<ExtArgs extends runtime.Types.Extensio
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeEnvSample createManyAndReturn
|
||||
*/
|
||||
export type MihomeEnvSampleCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the MihomeEnvSample
|
||||
*/
|
||||
select?: Prisma.MihomeEnvSampleSelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the MihomeEnvSample
|
||||
*/
|
||||
omit?: Prisma.MihomeEnvSampleOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many MihomeEnvSamples.
|
||||
*/
|
||||
data: Prisma.MihomeEnvSampleCreateManyInput | Prisma.MihomeEnvSampleCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.MihomeEnvSampleIncludeCreateManyAndReturn<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeEnvSample update
|
||||
*/
|
||||
@@ -1569,6 +1681,36 @@ export type MihomeEnvSampleUpdateManyArgs<ExtArgs extends runtime.Types.Extensio
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeEnvSample updateManyAndReturn
|
||||
*/
|
||||
export type MihomeEnvSampleUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the MihomeEnvSample
|
||||
*/
|
||||
select?: Prisma.MihomeEnvSampleSelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the MihomeEnvSample
|
||||
*/
|
||||
omit?: Prisma.MihomeEnvSampleOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update MihomeEnvSamples.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.MihomeEnvSampleUpdateManyMutationInput, Prisma.MihomeEnvSampleUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which MihomeEnvSamples to update
|
||||
*/
|
||||
where?: Prisma.MihomeEnvSampleWhereInput
|
||||
/**
|
||||
* Limit how many MihomeEnvSamples to update.
|
||||
*/
|
||||
limit?: number
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.MihomeEnvSampleIncludeUpdateManyAndReturn<ExtArgs> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeEnvSample upsert
|
||||
*/
|
||||
|
||||
@@ -217,7 +217,6 @@ export type MihomeHomeOrderByWithRelationInput = {
|
||||
name?: Prisma.SortOrder
|
||||
createdAt?: Prisma.SortOrder
|
||||
devices?: Prisma.MihomeDeviceOrderByRelationAggregateInput
|
||||
_relevance?: Prisma.MihomeHomeOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type MihomeHomeWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -303,12 +302,6 @@ export type MihomeHomeUncheckedUpdateManyInput = {
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
}
|
||||
|
||||
export type MihomeHomeOrderByRelevanceInput = {
|
||||
fields: Prisma.MihomeHomeOrderByRelevanceFieldEnum | Prisma.MihomeHomeOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type MihomeHomeCountOrderByAggregateInput = {
|
||||
id?: Prisma.SortOrder
|
||||
homeId?: Prisma.SortOrder
|
||||
@@ -439,7 +432,19 @@ export type MihomeHomeSelect<ExtArgs extends runtime.Types.Extensions.InternalAr
|
||||
_count?: boolean | Prisma.MihomeHomeCountOutputTypeDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["mihomeHome"]>
|
||||
|
||||
export type MihomeHomeSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
homeId?: boolean
|
||||
name?: boolean
|
||||
createdAt?: boolean
|
||||
}, ExtArgs["result"]["mihomeHome"]>
|
||||
|
||||
export type MihomeHomeSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
homeId?: boolean
|
||||
name?: boolean
|
||||
createdAt?: boolean
|
||||
}, ExtArgs["result"]["mihomeHome"]>
|
||||
|
||||
export type MihomeHomeSelectScalar = {
|
||||
id?: boolean
|
||||
@@ -453,6 +458,8 @@ export type MihomeHomeInclude<ExtArgs extends runtime.Types.Extensions.InternalA
|
||||
devices?: boolean | Prisma.MihomeHome$devicesArgs<ExtArgs>
|
||||
_count?: boolean | Prisma.MihomeHomeCountOutputTypeDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type MihomeHomeIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {}
|
||||
export type MihomeHomeIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {}
|
||||
|
||||
export type $MihomeHomePayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
name: "MihomeHome"
|
||||
@@ -581,6 +588,30 @@ export interface MihomeHomeDelegate<ExtArgs extends runtime.Types.Extensions.Int
|
||||
*/
|
||||
createMany<T extends MihomeHomeCreateManyArgs>(args?: Prisma.SelectSubset<T, MihomeHomeCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many MihomeHomes and returns the data saved in the database.
|
||||
* @param {MihomeHomeCreateManyAndReturnArgs} args - Arguments to create many MihomeHomes.
|
||||
* @example
|
||||
* // Create many MihomeHomes
|
||||
* const mihomeHome = await prisma.mihomeHome.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many MihomeHomes and only return the `id`
|
||||
* const mihomeHomeWithIdOnly = await prisma.mihomeHome.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends MihomeHomeCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, MihomeHomeCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$MihomeHomePayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a MihomeHome.
|
||||
* @param {MihomeHomeDeleteArgs} args - Arguments to delete one MihomeHome.
|
||||
@@ -645,6 +676,36 @@ export interface MihomeHomeDelegate<ExtArgs extends runtime.Types.Extensions.Int
|
||||
*/
|
||||
updateMany<T extends MihomeHomeUpdateManyArgs>(args: Prisma.SelectSubset<T, MihomeHomeUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more MihomeHomes and returns the data updated in the database.
|
||||
* @param {MihomeHomeUpdateManyAndReturnArgs} args - Arguments to update many MihomeHomes.
|
||||
* @example
|
||||
* // Update many MihomeHomes
|
||||
* const mihomeHome = await prisma.mihomeHome.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more MihomeHomes and only return the `id`
|
||||
* const mihomeHomeWithIdOnly = await prisma.mihomeHome.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends MihomeHomeUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, MihomeHomeUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$MihomeHomePayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one MihomeHome.
|
||||
* @param {MihomeHomeUpsertArgs} args - Arguments to update or create a MihomeHome.
|
||||
@@ -1075,6 +1136,25 @@ export type MihomeHomeCreateManyArgs<ExtArgs extends runtime.Types.Extensions.In
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeHome createManyAndReturn
|
||||
*/
|
||||
export type MihomeHomeCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the MihomeHome
|
||||
*/
|
||||
select?: Prisma.MihomeHomeSelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the MihomeHome
|
||||
*/
|
||||
omit?: Prisma.MihomeHomeOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many MihomeHomes.
|
||||
*/
|
||||
data: Prisma.MihomeHomeCreateManyInput | Prisma.MihomeHomeCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeHome update
|
||||
*/
|
||||
@@ -1119,6 +1199,32 @@ export type MihomeHomeUpdateManyArgs<ExtArgs extends runtime.Types.Extensions.In
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeHome updateManyAndReturn
|
||||
*/
|
||||
export type MihomeHomeUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the MihomeHome
|
||||
*/
|
||||
select?: Prisma.MihomeHomeSelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the MihomeHome
|
||||
*/
|
||||
omit?: Prisma.MihomeHomeOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update MihomeHomes.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.MihomeHomeUpdateManyMutationInput, Prisma.MihomeHomeUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which MihomeHomes to update
|
||||
*/
|
||||
where?: Prisma.MihomeHomeWhereInput
|
||||
/**
|
||||
* Limit how many MihomeHomes to update.
|
||||
*/
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* MihomeHome upsert
|
||||
*/
|
||||
|
||||
@@ -264,7 +264,6 @@ export type NewsOrderByWithRelationInput = {
|
||||
publishedAt?: Prisma.SortOrder
|
||||
impactRank?: Prisma.SortOrder
|
||||
createdAt?: Prisma.SortOrder
|
||||
_relevance?: Prisma.NewsOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type NewsWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -396,12 +395,6 @@ export type NewsUncheckedUpdateManyInput = {
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
}
|
||||
|
||||
export type NewsOrderByRelevanceInput = {
|
||||
fields: Prisma.NewsOrderByRelevanceFieldEnum | Prisma.NewsOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type NewsTitlePublishedAtCompoundUniqueInput = {
|
||||
title: string
|
||||
publishedAt: Date | string
|
||||
@@ -471,7 +464,29 @@ export type NewsSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = r
|
||||
createdAt?: boolean
|
||||
}, ExtArgs["result"]["news"]>
|
||||
|
||||
export type NewsSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
title?: boolean
|
||||
summary?: boolean
|
||||
content?: boolean
|
||||
source?: boolean
|
||||
url?: boolean
|
||||
publishedAt?: boolean
|
||||
impactRank?: boolean
|
||||
createdAt?: boolean
|
||||
}, ExtArgs["result"]["news"]>
|
||||
|
||||
export type NewsSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
title?: boolean
|
||||
summary?: boolean
|
||||
content?: boolean
|
||||
source?: boolean
|
||||
url?: boolean
|
||||
publishedAt?: boolean
|
||||
impactRank?: boolean
|
||||
createdAt?: boolean
|
||||
}, ExtArgs["result"]["news"]>
|
||||
|
||||
export type NewsSelectScalar = {
|
||||
id?: boolean
|
||||
@@ -617,6 +632,30 @@ export interface NewsDelegate<ExtArgs extends runtime.Types.Extensions.InternalA
|
||||
*/
|
||||
createMany<T extends NewsCreateManyArgs>(args?: Prisma.SelectSubset<T, NewsCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many News and returns the data saved in the database.
|
||||
* @param {NewsCreateManyAndReturnArgs} args - Arguments to create many News.
|
||||
* @example
|
||||
* // Create many News
|
||||
* const news = await prisma.news.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many News and only return the `id`
|
||||
* const newsWithIdOnly = await prisma.news.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends NewsCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, NewsCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$NewsPayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a News.
|
||||
* @param {NewsDeleteArgs} args - Arguments to delete one News.
|
||||
@@ -681,6 +720,36 @@ export interface NewsDelegate<ExtArgs extends runtime.Types.Extensions.InternalA
|
||||
*/
|
||||
updateMany<T extends NewsUpdateManyArgs>(args: Prisma.SelectSubset<T, NewsUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more News and returns the data updated in the database.
|
||||
* @param {NewsUpdateManyAndReturnArgs} args - Arguments to update many News.
|
||||
* @example
|
||||
* // Update many News
|
||||
* const news = await prisma.news.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more News and only return the `id`
|
||||
* const newsWithIdOnly = await prisma.news.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends NewsUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, NewsUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$NewsPayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one News.
|
||||
* @param {NewsUpsertArgs} args - Arguments to update or create a News.
|
||||
@@ -1091,6 +1160,25 @@ export type NewsCreateManyArgs<ExtArgs extends runtime.Types.Extensions.Internal
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* News createManyAndReturn
|
||||
*/
|
||||
export type NewsCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the News
|
||||
*/
|
||||
select?: Prisma.NewsSelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the News
|
||||
*/
|
||||
omit?: Prisma.NewsOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many News.
|
||||
*/
|
||||
data: Prisma.NewsCreateManyInput | Prisma.NewsCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* News update
|
||||
*/
|
||||
@@ -1131,6 +1219,32 @@ export type NewsUpdateManyArgs<ExtArgs extends runtime.Types.Extensions.Internal
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* News updateManyAndReturn
|
||||
*/
|
||||
export type NewsUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the News
|
||||
*/
|
||||
select?: Prisma.NewsSelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the News
|
||||
*/
|
||||
omit?: Prisma.NewsOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update News.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.NewsUpdateManyMutationInput, Prisma.NewsUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which News to update
|
||||
*/
|
||||
where?: Prisma.NewsWhereInput
|
||||
/**
|
||||
* Limit how many News to update.
|
||||
*/
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* News upsert
|
||||
*/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -238,7 +238,6 @@ export type ScraperCacheOrderByWithRelationInput = {
|
||||
metadata?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
createdAt?: Prisma.SortOrder
|
||||
updatedAt?: Prisma.SortOrder
|
||||
_relevance?: Prisma.ScraperCacheOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type ScraperCacheWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -349,12 +348,6 @@ export type ScraperCacheUncheckedUpdateManyInput = {
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
}
|
||||
|
||||
export type ScraperCacheOrderByRelevanceInput = {
|
||||
fields: Prisma.ScraperCacheOrderByRelevanceFieldEnum | Prisma.ScraperCacheOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type ScraperCacheCountOrderByAggregateInput = {
|
||||
id?: Prisma.SortOrder
|
||||
scraperName?: Prisma.SortOrder
|
||||
@@ -403,7 +396,25 @@ export type ScraperCacheSelect<ExtArgs extends runtime.Types.Extensions.Internal
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["scraperCache"]>
|
||||
|
||||
export type ScraperCacheSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
scraperName?: boolean
|
||||
lastDataHash?: boolean
|
||||
lastUpdateAt?: boolean
|
||||
metadata?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["scraperCache"]>
|
||||
|
||||
export type ScraperCacheSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
scraperName?: boolean
|
||||
lastDataHash?: boolean
|
||||
lastUpdateAt?: boolean
|
||||
metadata?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["scraperCache"]>
|
||||
|
||||
export type ScraperCacheSelectScalar = {
|
||||
id?: boolean
|
||||
@@ -545,6 +556,30 @@ export interface ScraperCacheDelegate<ExtArgs extends runtime.Types.Extensions.I
|
||||
*/
|
||||
createMany<T extends ScraperCacheCreateManyArgs>(args?: Prisma.SelectSubset<T, ScraperCacheCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many ScraperCaches and returns the data saved in the database.
|
||||
* @param {ScraperCacheCreateManyAndReturnArgs} args - Arguments to create many ScraperCaches.
|
||||
* @example
|
||||
* // Create many ScraperCaches
|
||||
* const scraperCache = await prisma.scraperCache.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many ScraperCaches and only return the `id`
|
||||
* const scraperCacheWithIdOnly = await prisma.scraperCache.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends ScraperCacheCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, ScraperCacheCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ScraperCachePayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a ScraperCache.
|
||||
* @param {ScraperCacheDeleteArgs} args - Arguments to delete one ScraperCache.
|
||||
@@ -609,6 +644,36 @@ export interface ScraperCacheDelegate<ExtArgs extends runtime.Types.Extensions.I
|
||||
*/
|
||||
updateMany<T extends ScraperCacheUpdateManyArgs>(args: Prisma.SelectSubset<T, ScraperCacheUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more ScraperCaches and returns the data updated in the database.
|
||||
* @param {ScraperCacheUpdateManyAndReturnArgs} args - Arguments to update many ScraperCaches.
|
||||
* @example
|
||||
* // Update many ScraperCaches
|
||||
* const scraperCache = await prisma.scraperCache.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more ScraperCaches and only return the `id`
|
||||
* const scraperCacheWithIdOnly = await prisma.scraperCache.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends ScraperCacheUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, ScraperCacheUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ScraperCachePayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one ScraperCache.
|
||||
* @param {ScraperCacheUpsertArgs} args - Arguments to update or create a ScraperCache.
|
||||
@@ -1017,6 +1082,25 @@ export type ScraperCacheCreateManyArgs<ExtArgs extends runtime.Types.Extensions.
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* ScraperCache createManyAndReturn
|
||||
*/
|
||||
export type ScraperCacheCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the ScraperCache
|
||||
*/
|
||||
select?: Prisma.ScraperCacheSelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the ScraperCache
|
||||
*/
|
||||
omit?: Prisma.ScraperCacheOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many ScraperCaches.
|
||||
*/
|
||||
data: Prisma.ScraperCacheCreateManyInput | Prisma.ScraperCacheCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* ScraperCache update
|
||||
*/
|
||||
@@ -1057,6 +1141,32 @@ export type ScraperCacheUpdateManyArgs<ExtArgs extends runtime.Types.Extensions.
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* ScraperCache updateManyAndReturn
|
||||
*/
|
||||
export type ScraperCacheUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the ScraperCache
|
||||
*/
|
||||
select?: Prisma.ScraperCacheSelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the ScraperCache
|
||||
*/
|
||||
omit?: Prisma.ScraperCacheOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update ScraperCaches.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.ScraperCacheUpdateManyMutationInput, Prisma.ScraperCacheUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which ScraperCaches to update
|
||||
*/
|
||||
where?: Prisma.ScraperCacheWhereInput
|
||||
/**
|
||||
* Limit how many ScraperCaches to update.
|
||||
*/
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* ScraperCache upsert
|
||||
*/
|
||||
|
||||
@@ -464,7 +464,6 @@ export type ServerNodeOrderByWithRelationInput = {
|
||||
recordedAt?: Prisma.SortOrder
|
||||
createdAt?: Prisma.SortOrder
|
||||
updatedAt?: Prisma.SortOrder
|
||||
_relevance?: Prisma.ServerNodeOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type ServerNodeWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -755,12 +754,6 @@ export type ServerNodeUncheckedUpdateManyInput = {
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
}
|
||||
|
||||
export type ServerNodeOrderByRelevanceInput = {
|
||||
fields: Prisma.ServerNodeOrderByRelevanceFieldEnum | Prisma.ServerNodeOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type ServerNodeCountOrderByAggregateInput = {
|
||||
id?: Prisma.SortOrder
|
||||
uuid?: Prisma.SortOrder
|
||||
@@ -921,7 +914,61 @@ export type ServerNodeSelect<ExtArgs extends runtime.Types.Extensions.InternalAr
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["serverNode"]>
|
||||
|
||||
export type ServerNodeSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
uuid?: boolean
|
||||
name?: boolean
|
||||
cpuName?: boolean
|
||||
os?: boolean
|
||||
region?: boolean
|
||||
memTotal?: boolean
|
||||
diskTotal?: boolean
|
||||
cpuCores?: boolean
|
||||
cpuUsage?: boolean
|
||||
ramUsed?: boolean
|
||||
swapUsed?: boolean
|
||||
load1?: boolean
|
||||
load5?: boolean
|
||||
load15?: boolean
|
||||
diskUsed?: boolean
|
||||
netIn?: boolean
|
||||
netOut?: boolean
|
||||
uptime?: boolean
|
||||
process?: boolean
|
||||
connections?: boolean
|
||||
status?: boolean
|
||||
recordedAt?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["serverNode"]>
|
||||
|
||||
export type ServerNodeSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
uuid?: boolean
|
||||
name?: boolean
|
||||
cpuName?: boolean
|
||||
os?: boolean
|
||||
region?: boolean
|
||||
memTotal?: boolean
|
||||
diskTotal?: boolean
|
||||
cpuCores?: boolean
|
||||
cpuUsage?: boolean
|
||||
ramUsed?: boolean
|
||||
swapUsed?: boolean
|
||||
load1?: boolean
|
||||
load5?: boolean
|
||||
load15?: boolean
|
||||
diskUsed?: boolean
|
||||
netIn?: boolean
|
||||
netOut?: boolean
|
||||
uptime?: boolean
|
||||
process?: boolean
|
||||
connections?: boolean
|
||||
status?: boolean
|
||||
recordedAt?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["serverNode"]>
|
||||
|
||||
export type ServerNodeSelectScalar = {
|
||||
id?: boolean
|
||||
@@ -1099,6 +1146,30 @@ export interface ServerNodeDelegate<ExtArgs extends runtime.Types.Extensions.Int
|
||||
*/
|
||||
createMany<T extends ServerNodeCreateManyArgs>(args?: Prisma.SelectSubset<T, ServerNodeCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many ServerNodes and returns the data saved in the database.
|
||||
* @param {ServerNodeCreateManyAndReturnArgs} args - Arguments to create many ServerNodes.
|
||||
* @example
|
||||
* // Create many ServerNodes
|
||||
* const serverNode = await prisma.serverNode.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many ServerNodes and only return the `id`
|
||||
* const serverNodeWithIdOnly = await prisma.serverNode.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends ServerNodeCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, ServerNodeCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ServerNodePayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a ServerNode.
|
||||
* @param {ServerNodeDeleteArgs} args - Arguments to delete one ServerNode.
|
||||
@@ -1163,6 +1234,36 @@ export interface ServerNodeDelegate<ExtArgs extends runtime.Types.Extensions.Int
|
||||
*/
|
||||
updateMany<T extends ServerNodeUpdateManyArgs>(args: Prisma.SelectSubset<T, ServerNodeUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more ServerNodes and returns the data updated in the database.
|
||||
* @param {ServerNodeUpdateManyAndReturnArgs} args - Arguments to update many ServerNodes.
|
||||
* @example
|
||||
* // Update many ServerNodes
|
||||
* const serverNode = await prisma.serverNode.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more ServerNodes and only return the `id`
|
||||
* const serverNodeWithIdOnly = await prisma.serverNode.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends ServerNodeUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, ServerNodeUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ServerNodePayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one ServerNode.
|
||||
* @param {ServerNodeUpsertArgs} args - Arguments to update or create a ServerNode.
|
||||
@@ -1589,6 +1690,25 @@ export type ServerNodeCreateManyArgs<ExtArgs extends runtime.Types.Extensions.In
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* ServerNode createManyAndReturn
|
||||
*/
|
||||
export type ServerNodeCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the ServerNode
|
||||
*/
|
||||
select?: Prisma.ServerNodeSelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the ServerNode
|
||||
*/
|
||||
omit?: Prisma.ServerNodeOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many ServerNodes.
|
||||
*/
|
||||
data: Prisma.ServerNodeCreateManyInput | Prisma.ServerNodeCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* ServerNode update
|
||||
*/
|
||||
@@ -1629,6 +1749,32 @@ export type ServerNodeUpdateManyArgs<ExtArgs extends runtime.Types.Extensions.In
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* ServerNode updateManyAndReturn
|
||||
*/
|
||||
export type ServerNodeUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the ServerNode
|
||||
*/
|
||||
select?: Prisma.ServerNodeSelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the ServerNode
|
||||
*/
|
||||
omit?: Prisma.ServerNodeOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update ServerNodes.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.ServerNodeUpdateManyMutationInput, Prisma.ServerNodeUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which ServerNodes to update
|
||||
*/
|
||||
where?: Prisma.ServerNodeWhereInput
|
||||
/**
|
||||
* Limit how many ServerNodes to update.
|
||||
*/
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* ServerNode upsert
|
||||
*/
|
||||
|
||||
@@ -319,7 +319,6 @@ export type ServerNodeHistoryOrderByWithRelationInput = {
|
||||
process?: Prisma.SortOrder
|
||||
connections?: Prisma.SortOrder
|
||||
createdAt?: Prisma.SortOrder
|
||||
_relevance?: Prisma.ServerNodeHistoryOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type ServerNodeHistoryWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -481,12 +480,6 @@ export type ServerNodeHistoryUncheckedUpdateManyInput = {
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
}
|
||||
|
||||
export type ServerNodeHistoryOrderByRelevanceInput = {
|
||||
fields: Prisma.ServerNodeHistoryOrderByRelevanceFieldEnum | Prisma.ServerNodeHistoryOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type ServerNodeHistoryUuidPeriodCompoundUniqueInput = {
|
||||
uuid: string
|
||||
period: Date | string
|
||||
@@ -578,7 +571,35 @@ export type ServerNodeHistorySelect<ExtArgs extends runtime.Types.Extensions.Int
|
||||
createdAt?: boolean
|
||||
}, ExtArgs["result"]["serverNodeHistory"]>
|
||||
|
||||
export type ServerNodeHistorySelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
uuid?: boolean
|
||||
period?: boolean
|
||||
cpu?: boolean
|
||||
ram?: boolean
|
||||
load?: boolean
|
||||
disk?: boolean
|
||||
netIn?: boolean
|
||||
netOut?: boolean
|
||||
process?: boolean
|
||||
connections?: boolean
|
||||
createdAt?: boolean
|
||||
}, ExtArgs["result"]["serverNodeHistory"]>
|
||||
|
||||
export type ServerNodeHistorySelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
uuid?: boolean
|
||||
period?: boolean
|
||||
cpu?: boolean
|
||||
ram?: boolean
|
||||
load?: boolean
|
||||
disk?: boolean
|
||||
netIn?: boolean
|
||||
netOut?: boolean
|
||||
process?: boolean
|
||||
connections?: boolean
|
||||
createdAt?: boolean
|
||||
}, ExtArgs["result"]["serverNodeHistory"]>
|
||||
|
||||
export type ServerNodeHistorySelectScalar = {
|
||||
id?: boolean
|
||||
@@ -730,6 +751,30 @@ export interface ServerNodeHistoryDelegate<ExtArgs extends runtime.Types.Extensi
|
||||
*/
|
||||
createMany<T extends ServerNodeHistoryCreateManyArgs>(args?: Prisma.SelectSubset<T, ServerNodeHistoryCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many ServerNodeHistories and returns the data saved in the database.
|
||||
* @param {ServerNodeHistoryCreateManyAndReturnArgs} args - Arguments to create many ServerNodeHistories.
|
||||
* @example
|
||||
* // Create many ServerNodeHistories
|
||||
* const serverNodeHistory = await prisma.serverNodeHistory.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many ServerNodeHistories and only return the `id`
|
||||
* const serverNodeHistoryWithIdOnly = await prisma.serverNodeHistory.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends ServerNodeHistoryCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, ServerNodeHistoryCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ServerNodeHistoryPayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a ServerNodeHistory.
|
||||
* @param {ServerNodeHistoryDeleteArgs} args - Arguments to delete one ServerNodeHistory.
|
||||
@@ -794,6 +839,36 @@ export interface ServerNodeHistoryDelegate<ExtArgs extends runtime.Types.Extensi
|
||||
*/
|
||||
updateMany<T extends ServerNodeHistoryUpdateManyArgs>(args: Prisma.SelectSubset<T, ServerNodeHistoryUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more ServerNodeHistories and returns the data updated in the database.
|
||||
* @param {ServerNodeHistoryUpdateManyAndReturnArgs} args - Arguments to update many ServerNodeHistories.
|
||||
* @example
|
||||
* // Update many ServerNodeHistories
|
||||
* const serverNodeHistory = await prisma.serverNodeHistory.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more ServerNodeHistories and only return the `id`
|
||||
* const serverNodeHistoryWithIdOnly = await prisma.serverNodeHistory.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends ServerNodeHistoryUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, ServerNodeHistoryUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ServerNodeHistoryPayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one ServerNodeHistory.
|
||||
* @param {ServerNodeHistoryUpsertArgs} args - Arguments to update or create a ServerNodeHistory.
|
||||
@@ -1207,6 +1282,25 @@ export type ServerNodeHistoryCreateManyArgs<ExtArgs extends runtime.Types.Extens
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* ServerNodeHistory createManyAndReturn
|
||||
*/
|
||||
export type ServerNodeHistoryCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the ServerNodeHistory
|
||||
*/
|
||||
select?: Prisma.ServerNodeHistorySelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the ServerNodeHistory
|
||||
*/
|
||||
omit?: Prisma.ServerNodeHistoryOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many ServerNodeHistories.
|
||||
*/
|
||||
data: Prisma.ServerNodeHistoryCreateManyInput | Prisma.ServerNodeHistoryCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* ServerNodeHistory update
|
||||
*/
|
||||
@@ -1247,6 +1341,32 @@ export type ServerNodeHistoryUpdateManyArgs<ExtArgs extends runtime.Types.Extens
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* ServerNodeHistory updateManyAndReturn
|
||||
*/
|
||||
export type ServerNodeHistoryUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the ServerNodeHistory
|
||||
*/
|
||||
select?: Prisma.ServerNodeHistorySelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the ServerNodeHistory
|
||||
*/
|
||||
omit?: Prisma.ServerNodeHistoryOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update ServerNodeHistories.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.ServerNodeHistoryUpdateManyMutationInput, Prisma.ServerNodeHistoryUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which ServerNodeHistories to update
|
||||
*/
|
||||
where?: Prisma.ServerNodeHistoryWhereInput
|
||||
/**
|
||||
* Limit how many ServerNodeHistories to update.
|
||||
*/
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* ServerNodeHistory upsert
|
||||
*/
|
||||
|
||||
@@ -264,7 +264,6 @@ export type ServiceCheckOrderByWithRelationInput = {
|
||||
lastCheckAt?: Prisma.SortOrder
|
||||
createdAt?: Prisma.SortOrder
|
||||
updatedAt?: Prisma.SortOrder
|
||||
_relevance?: Prisma.ServiceCheckOrderByRelevanceInput
|
||||
}
|
||||
|
||||
export type ServiceCheckWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -395,12 +394,6 @@ export type ServiceCheckUncheckedUpdateManyInput = {
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
}
|
||||
|
||||
export type ServiceCheckOrderByRelevanceInput = {
|
||||
fields: Prisma.ServiceCheckOrderByRelevanceFieldEnum | Prisma.ServiceCheckOrderByRelevanceFieldEnum[]
|
||||
sort: Prisma.SortOrder
|
||||
search: string
|
||||
}
|
||||
|
||||
export type ServiceCheckCountOrderByAggregateInput = {
|
||||
id?: Prisma.SortOrder
|
||||
name?: Prisma.SortOrder
|
||||
@@ -461,7 +454,29 @@ export type ServiceCheckSelect<ExtArgs extends runtime.Types.Extensions.Internal
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["serviceCheck"]>
|
||||
|
||||
export type ServiceCheckSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
name?: boolean
|
||||
url?: boolean
|
||||
status?: boolean
|
||||
latencyMs?: boolean
|
||||
message?: boolean
|
||||
lastCheckAt?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["serviceCheck"]>
|
||||
|
||||
export type ServiceCheckSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
name?: boolean
|
||||
url?: boolean
|
||||
status?: boolean
|
||||
latencyMs?: boolean
|
||||
message?: boolean
|
||||
lastCheckAt?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
}, ExtArgs["result"]["serviceCheck"]>
|
||||
|
||||
export type ServiceCheckSelectScalar = {
|
||||
id?: boolean
|
||||
@@ -607,6 +622,30 @@ export interface ServiceCheckDelegate<ExtArgs extends runtime.Types.Extensions.I
|
||||
*/
|
||||
createMany<T extends ServiceCheckCreateManyArgs>(args?: Prisma.SelectSubset<T, ServiceCheckCreateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Create many ServiceChecks and returns the data saved in the database.
|
||||
* @param {ServiceCheckCreateManyAndReturnArgs} args - Arguments to create many ServiceChecks.
|
||||
* @example
|
||||
* // Create many ServiceChecks
|
||||
* const serviceCheck = await prisma.serviceCheck.createManyAndReturn({
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Create many ServiceChecks and only return the `id`
|
||||
* const serviceCheckWithIdOnly = await prisma.serviceCheck.createManyAndReturn({
|
||||
* select: { id: true },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
createManyAndReturn<T extends ServiceCheckCreateManyAndReturnArgs>(args?: Prisma.SelectSubset<T, ServiceCheckCreateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ServiceCheckPayload<ExtArgs>, T, "createManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Delete a ServiceCheck.
|
||||
* @param {ServiceCheckDeleteArgs} args - Arguments to delete one ServiceCheck.
|
||||
@@ -671,6 +710,36 @@ export interface ServiceCheckDelegate<ExtArgs extends runtime.Types.Extensions.I
|
||||
*/
|
||||
updateMany<T extends ServiceCheckUpdateManyArgs>(args: Prisma.SelectSubset<T, ServiceCheckUpdateManyArgs<ExtArgs>>): Prisma.PrismaPromise<Prisma.BatchPayload>
|
||||
|
||||
/**
|
||||
* Update zero or more ServiceChecks and returns the data updated in the database.
|
||||
* @param {ServiceCheckUpdateManyAndReturnArgs} args - Arguments to update many ServiceChecks.
|
||||
* @example
|
||||
* // Update many ServiceChecks
|
||||
* const serviceCheck = await prisma.serviceCheck.updateManyAndReturn({
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
*
|
||||
* // Update zero or more ServiceChecks and only return the `id`
|
||||
* const serviceCheckWithIdOnly = await prisma.serviceCheck.updateManyAndReturn({
|
||||
* select: { id: true },
|
||||
* where: {
|
||||
* // ... provide filter here
|
||||
* },
|
||||
* data: [
|
||||
* // ... provide data here
|
||||
* ]
|
||||
* })
|
||||
* Note, that providing `undefined` is treated as the value not being there.
|
||||
* Read more here: https://pris.ly/d/null-undefined
|
||||
*
|
||||
*/
|
||||
updateManyAndReturn<T extends ServiceCheckUpdateManyAndReturnArgs>(args: Prisma.SelectSubset<T, ServiceCheckUpdateManyAndReturnArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ServiceCheckPayload<ExtArgs>, T, "updateManyAndReturn", GlobalOmitOptions>>
|
||||
|
||||
/**
|
||||
* Create or update one ServiceCheck.
|
||||
* @param {ServiceCheckUpsertArgs} args - Arguments to update or create a ServiceCheck.
|
||||
@@ -1081,6 +1150,25 @@ export type ServiceCheckCreateManyArgs<ExtArgs extends runtime.Types.Extensions.
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceCheck createManyAndReturn
|
||||
*/
|
||||
export type ServiceCheckCreateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the ServiceCheck
|
||||
*/
|
||||
select?: Prisma.ServiceCheckSelectCreateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the ServiceCheck
|
||||
*/
|
||||
omit?: Prisma.ServiceCheckOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to create many ServiceChecks.
|
||||
*/
|
||||
data: Prisma.ServiceCheckCreateManyInput | Prisma.ServiceCheckCreateManyInput[]
|
||||
skipDuplicates?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceCheck update
|
||||
*/
|
||||
@@ -1121,6 +1209,32 @@ export type ServiceCheckUpdateManyArgs<ExtArgs extends runtime.Types.Extensions.
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceCheck updateManyAndReturn
|
||||
*/
|
||||
export type ServiceCheckUpdateManyAndReturnArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the ServiceCheck
|
||||
*/
|
||||
select?: Prisma.ServiceCheckSelectUpdateManyAndReturn<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the ServiceCheck
|
||||
*/
|
||||
omit?: Prisma.ServiceCheckOmit<ExtArgs> | null
|
||||
/**
|
||||
* The data used to update ServiceChecks.
|
||||
*/
|
||||
data: Prisma.XOR<Prisma.ServiceCheckUpdateManyMutationInput, Prisma.ServiceCheckUncheckedUpdateManyInput>
|
||||
/**
|
||||
* Filter which ServiceChecks to update
|
||||
*/
|
||||
where?: Prisma.ServiceCheckWhereInput
|
||||
/**
|
||||
* Limit how many ServiceChecks to update.
|
||||
*/
|
||||
limit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceCheck upsert
|
||||
*/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -5,7 +5,7 @@
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover" />
|
||||
<meta name="theme-color" content="#1f2937" />
|
||||
<title>iBoard - 经济数据与策略</title>
|
||||
<title>iBoard - 个人看板</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
Generated
+340
-122
@@ -1,14 +1,17 @@
|
||||
{
|
||||
"name": "iboard",
|
||||
"version": "1.0.0",
|
||||
"version": "0.2.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "iboard",
|
||||
"version": "1.0.0",
|
||||
"version": "0.2.1",
|
||||
"dependencies": {
|
||||
"@prisma/adapter-mariadb": "^7.9.1",
|
||||
"@journeyapps/wa-sqlite": "^2.0.7",
|
||||
"@powersync/react": "^2.1.0",
|
||||
"@powersync/web": "^2.5.0",
|
||||
"@prisma/adapter-pg": "^7.9.1",
|
||||
"@prisma/client": "^7.7.0",
|
||||
"@zythum02/mijia-api": "^0.1.4",
|
||||
"antd-mobile": "^5.42.3",
|
||||
@@ -30,8 +33,8 @@
|
||||
"ws": "^8.21.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/api": "^2.0.0",
|
||||
"@tauri-apps/cli": "^2.0.0",
|
||||
"@tauri-apps/api": "^2.12.4",
|
||||
"@tauri-apps/cli": "^2.12.4",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"cheerio": "^1.2.0",
|
||||
"concurrently": "^9.0.0",
|
||||
@@ -1187,6 +1190,11 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@journeyapps/wa-sqlite": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@journeyapps/wa-sqlite/-/wa-sqlite-2.0.8.tgz",
|
||||
"integrity": "sha512-WRXEms2515S/0DFSWzmlggHu9hSJomhugDUO17rWkL6VpokV/hfjIDLs954Egx/eqrk3WADmiOMzOc7MuJbznQ=="
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||
@@ -1615,64 +1623,91 @@
|
||||
"node": "^22.20 || ^24.12 || >=25"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/adapter-mariadb": {
|
||||
"version": "7.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/adapter-mariadb/-/adapter-mariadb-7.9.1.tgz",
|
||||
"integrity": "sha512-4aZUbIg2av7XFSQyM9cQeAp9YP9HiPr8H7ehe2rDo375IZXILEzQrNXZ/zLetgRQ2ojlaUl/brArFdOVS3n1xA==",
|
||||
"node_modules/@powersync/common": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@powersync/common/-/common-2.4.0.tgz",
|
||||
"integrity": "sha512-ngVogy+B8NQeI5l+0Es8qMxnGozUie8Q6R4+0/AHA3oBAhDG5UmeYw/LquTzZJSOPnCmvGgFSqovblV+JKzxKQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@powersync/react": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@powersync/react/-/react-2.1.0.tgz",
|
||||
"integrity": "sha512-ADIpakSLhstv0F9xrUIF+oCArFnAQPrcB9mNql6xH0Nup9EgBug38Ks+mv3yqbc33z60pBm9tuHs8uSIbkhuqg==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"@powersync/common": "^2.4.0",
|
||||
"react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@powersync/shared-internals": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@powersync/shared-internals/-/shared-internals-1.4.0.tgz",
|
||||
"integrity": "sha512-OiqqDuW6tePW4Ed7C5IWutBKrtcqKuTMlVMQNwUZtKtqr7ZrqKlb+YP5l4niuz8mpqDYo3/HNU0dpc+3mneSBQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"@powersync/common": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@powersync/web": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@powersync/web/-/web-2.5.0.tgz",
|
||||
"integrity": "sha512-/K2ZUjIdqvtFeoLfPetPcE+uioShBWzrTrOa8hpoZ7i62S8wgj47i+VPnKTXI7WQmXuxTGGZFUFnahinf6wjPg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@prisma/driver-adapter-utils": "7.9.1",
|
||||
"mariadb": "3.4.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/adapter-mariadb/node_modules/@types/node": {
|
||||
"version": "24.13.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz",
|
||||
"integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/adapter-mariadb/node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
"@journeyapps/wa-sqlite": "2.0.7",
|
||||
"@powersync/common": "2.4.0",
|
||||
"@powersync/shared-internals": "1.4.0",
|
||||
"comlink": "^4.4.2",
|
||||
"commander": "^12.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/adapter-mariadb/node_modules/lru-cache": {
|
||||
"version": "10.4.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@prisma/adapter-mariadb/node_modules/mariadb": {
|
||||
"version": "3.4.5",
|
||||
"resolved": "https://registry.npmjs.org/mariadb/-/mariadb-3.4.5.tgz",
|
||||
"integrity": "sha512-gThTYkhIS5rRqkVr+Y0cIdzr+GRqJ9sA2Q34e0yzmyhMCwyApf3OKAC1jnF23aSlIOqJuyaUFUcj7O1qZslmmQ==",
|
||||
"license": "LGPL-2.1-or-later",
|
||||
"dependencies": {
|
||||
"@types/geojson": "^7946.0.16",
|
||||
"@types/node": "^24.0.13",
|
||||
"denque": "^2.1.0",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"lru-cache": "^10.4.3"
|
||||
"bin": {
|
||||
"powersync-web": "bin/powersync.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
"peerDependencies": {
|
||||
"@powersync/common": "^2.4.0",
|
||||
"@powersync/shared-internals": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/adapter-mariadb/node_modules/undici-types": {
|
||||
"version": "7.18.2",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
||||
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
||||
"license": "MIT"
|
||||
"node_modules/@powersync/web/node_modules/@journeyapps/wa-sqlite": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@journeyapps/wa-sqlite/-/wa-sqlite-2.0.7.tgz",
|
||||
"integrity": "sha512-mc6xUricl4QB2s8QTRGS+FofSr1cjwDtf6dPXAdMBuGWodjfEgkSJGDPWQ7zVqgc7hMfXYLT7WIfEBAxara8YA=="
|
||||
},
|
||||
"node_modules/@powersync/web/node_modules/commander": {
|
||||
"version": "12.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
|
||||
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/adapter-pg": {
|
||||
"version": "7.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/adapter-pg/-/adapter-pg-7.10.1.tgz",
|
||||
"integrity": "sha512-EP9ruPYHjKP80WZlb5qVaEXKgZjJieBFjAxcBtQnDAHD6+16UN4keVmBh6L/RUpuDQT2FQRbvKNgq5d4Ix+IkQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@prisma/driver-adapter-utils": "7.10.1",
|
||||
"@types/pg": "^8.16.0",
|
||||
"pg": "^8.16.3",
|
||||
"postgres-array": "3.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/adapter-pg/node_modules/@prisma/debug": {
|
||||
"version": "7.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.10.1.tgz",
|
||||
"integrity": "sha512-eFCAkGZ13D/qPg48JBlG5IWi9cSBBOC7oG3abDPcAkYioDWlIjh0YJJLK2J7qo5S+xWWD8I6egAN/8KbCaiMCA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@prisma/adapter-pg/node_modules/@prisma/driver-adapter-utils": {
|
||||
"version": "7.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/driver-adapter-utils/-/driver-adapter-utils-7.10.1.tgz",
|
||||
"integrity": "sha512-DJ4c6xvVnm3s9ZUyql9nCUicADSqyvH+CjXRCM9L6m5zJxCNmebCElG6X4irICq3rosBwlX+S19Dxq/4IrZk3A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@prisma/debug": "7.10.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/client": {
|
||||
"version": "7.7.0",
|
||||
@@ -1747,21 +1782,6 @@
|
||||
"zeptomatch": "2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/driver-adapter-utils": {
|
||||
"version": "7.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/driver-adapter-utils/-/driver-adapter-utils-7.9.1.tgz",
|
||||
"integrity": "sha512-vmHehG7nn/heW32DXXpp13DxxAxVVe6n250oEt3dOL2E/4bt3olktKZN0mzSuxMMronyMSkbeW2uCOn3F4g8RQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@prisma/debug": "7.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/driver-adapter-utils/node_modules/@prisma/debug": {
|
||||
"version": "7.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.9.1.tgz",
|
||||
"integrity": "sha512-/cpVZ4itxtcgB8GHBvZtcmuEjq+lWsLrRJxFMbwZrT1RIdtuKmUm7PPGo/wzfbYpBrk+9WmmBE8CHJw2rybKDQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@prisma/engines": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.7.0.tgz",
|
||||
@@ -2505,9 +2525,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tauri-apps/api": {
|
||||
"version": "2.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.1.tgz",
|
||||
"integrity": "sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA==",
|
||||
"version": "2.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.12.4.tgz",
|
||||
"integrity": "sha512-pVmfis+4zT8H0Qhp3xPZx9Ju7kpzvA6rr/PSJPyuwfvucTQ8sVt61NYU9KMkK703UFlYadFmjKA4HhybOtN0gg==",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -2515,9 +2535,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli": {
|
||||
"version": "2.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.4.tgz",
|
||||
"integrity": "sha512-R8xGtMpwyetawSqm9kYOuMmEqkhUbvcUy8n0aNXIxollKBLESUu5f4Fx+64hgASYm1H+jSWq6jCW6zqTnH6hqQ==",
|
||||
"version": "2.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.12.4.tgz",
|
||||
"integrity": "sha512-JI9bHfudSRJikGOrjbXEm6RjzzvmKuLc+JixB1NE8hNzQkNlCCIC3pTES3uypkR9lxHrsbxzcnSwhI2IxBQphw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"bin": {
|
||||
@@ -2531,23 +2551,23 @@
|
||||
"url": "https://opencollective.com/tauri"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tauri-apps/cli-darwin-arm64": "2.11.4",
|
||||
"@tauri-apps/cli-darwin-x64": "2.11.4",
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf": "2.11.4",
|
||||
"@tauri-apps/cli-linux-arm64-gnu": "2.11.4",
|
||||
"@tauri-apps/cli-linux-arm64-musl": "2.11.4",
|
||||
"@tauri-apps/cli-linux-riscv64-gnu": "2.11.4",
|
||||
"@tauri-apps/cli-linux-x64-gnu": "2.11.4",
|
||||
"@tauri-apps/cli-linux-x64-musl": "2.11.4",
|
||||
"@tauri-apps/cli-win32-arm64-msvc": "2.11.4",
|
||||
"@tauri-apps/cli-win32-ia32-msvc": "2.11.4",
|
||||
"@tauri-apps/cli-win32-x64-msvc": "2.11.4"
|
||||
"@tauri-apps/cli-darwin-arm64": "2.12.4",
|
||||
"@tauri-apps/cli-darwin-x64": "2.12.4",
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf": "2.12.4",
|
||||
"@tauri-apps/cli-linux-arm64-gnu": "2.12.4",
|
||||
"@tauri-apps/cli-linux-arm64-musl": "2.12.4",
|
||||
"@tauri-apps/cli-linux-riscv64-gnu": "2.12.4",
|
||||
"@tauri-apps/cli-linux-x64-gnu": "2.12.4",
|
||||
"@tauri-apps/cli-linux-x64-musl": "2.12.4",
|
||||
"@tauri-apps/cli-win32-arm64-msvc": "2.12.4",
|
||||
"@tauri-apps/cli-win32-ia32-msvc": "2.12.4",
|
||||
"@tauri-apps/cli-win32-x64-msvc": "2.12.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-darwin-arm64": {
|
||||
"version": "2.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.4.tgz",
|
||||
"integrity": "sha512-1ryOF3ZhpZ/nemHV5zVwBQBz9jDGKmKPvWPADOhc83ig0P4bMc2iER4NbC6r9sjeIZ6RVQ4g3RZIYvezhcl4TQ==",
|
||||
"version": "2.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.12.4.tgz",
|
||||
"integrity": "sha512-OxqKSckIkoJ6vX2AjifIJpTasFC0Hbdoo7gEiCmAjauABO3U8m2MRsLWe+QTiBwDx1mIrvgA+rvI7tvDnbnHVg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2562,9 +2582,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-darwin-x64": {
|
||||
"version": "2.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.4.tgz",
|
||||
"integrity": "sha512-uFsGQAAfuyz1k/yGLmkWfkBlgKAqZfxqlHmLWx81QU27RJWfmbNHCIq8T8w1e+VClleIuZUjpHWfoE4E3DLo3A==",
|
||||
"version": "2.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.12.4.tgz",
|
||||
"integrity": "sha512-fz76/77wMMJOaaUN6n3CybRG0P/MTEjjqYk81mGPyhi9m6Ue5T+LWBnJP/AkPooNatBPI2vtulGqomyMJ44lCQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2579,9 +2599,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
|
||||
"version": "2.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.4.tgz",
|
||||
"integrity": "sha512-IaHZn5CdBL21oUmjiVOS1ctw6Ip1O0pjp70FwOWmYz1myWe0SY96ZIj2FYf7pT0m8bI2h/hrs5ZbEXXh44/MkQ==",
|
||||
"version": "2.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.12.4.tgz",
|
||||
"integrity": "sha512-K0w8Bof6Lt8QKLAYhRy0yy36ZH5iA/35VuSOvnxyjvCUZ+A3JKsUTlALviH/sVgAlisaPzzmeJSH8XOn4Q2HBA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -2596,9 +2616,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-arm64-gnu": {
|
||||
"version": "2.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.4.tgz",
|
||||
"integrity": "sha512-N41/ukTRVe6XSuUTESuFdGeOW2i7k62tK+6gHK5Kd5/q5RPvvi19GaWAVPPb9u95HSGmTChSolBfzynUsssFaA==",
|
||||
"version": "2.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.12.4.tgz",
|
||||
"integrity": "sha512-j5rOnjmRUwWrm1oosf0zBYl4D+fqydgUROFCk0hvKZxvbTT8SOERes0QB7rBV/XWNy+kM6pCKzb3oSm2sRglzg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2613,9 +2633,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-arm64-musl": {
|
||||
"version": "2.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.4.tgz",
|
||||
"integrity": "sha512-v277UnT/fB64xAfSroL5N3Km3tLmvATWqJJw/wRI+g6o+HkeD0slyE7gOhNs1MbjE41R7bQOTxMVoL3aomUJmw==",
|
||||
"version": "2.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.12.4.tgz",
|
||||
"integrity": "sha512-aOue3Fy6Jb7+oWcfAVUsVHfeTybSCt2CZoU06aO4jv4d2Ks/KnaTPwMAK3CLEduGOvBLY1lbBBrIyomqLLH5Sg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2630,9 +2650,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-riscv64-gnu": {
|
||||
"version": "2.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.4.tgz",
|
||||
"integrity": "sha512-qqgNkQ2u1yZHxjhxsZaxUtRDW8dIqIYm33rx/mzwQv0SfY9x1B+iraj8vWeFiXjjSVVhEMepXSOts1TqPzvXNQ==",
|
||||
"version": "2.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.12.4.tgz",
|
||||
"integrity": "sha512-Fx6GShY/RvST+t6LMlTHAwd66+1tEaAAGrnU7cysVfaiRP1BKng5Vl6rVmO6rSLOA1Sp1dRzNYM4f0OWQGb7qA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -2647,9 +2667,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-x64-gnu": {
|
||||
"version": "2.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.4.tgz",
|
||||
"integrity": "sha512-2VRNWl84FOH0m2giiDkO2h0QXlcMJeX+zJDpI5kDIQAx6s+geF3v48F4DXfJez4GS/FdoDGnPnw1C2iYGbQ7bQ==",
|
||||
"version": "2.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.12.4.tgz",
|
||||
"integrity": "sha512-tNzZSa79Z1nRpHxEpQ38lsAFpKlFlYvoXzjTV+lRRn/zSf3wrdUC3wnLcq2XEadchPu4ttK0zivhYzr+XGD19Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2664,9 +2684,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-x64-musl": {
|
||||
"version": "2.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.4.tgz",
|
||||
"integrity": "sha512-o9GyhYor/nc7xarmwDE3ka2szuW3uuZzXjHWh64Q8YX5AtSgxdQkFWzrY4O8KiGtVNvFBI14H3Q49Qj5TOIP/A==",
|
||||
"version": "2.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.12.4.tgz",
|
||||
"integrity": "sha512-jmZ1pr0xEqUhuEusWtt9Yt+GF6lCnGHUmIL+KIknpbjK/XQ2qkhDBhaxK/JMHM2A8dAacVXCxXJk2EXpwhQUfA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2681,9 +2701,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-win32-arm64-msvc": {
|
||||
"version": "2.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.4.tgz",
|
||||
"integrity": "sha512-ld5Ehb598m0VkYyylRPNeCFsBe/km0jxis6KgMpl3IGY6I/i1RwQXO05I1AsXUXO2WC6AvB/Lw4qTf/asiuEiQ==",
|
||||
"version": "2.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.12.4.tgz",
|
||||
"integrity": "sha512-fGo8pgEHZxdSYgZ/J98m1usJ7NHKmKbq7k6uT0x1rpOq6Bg2YVzzpaAblWk710f5LHYBJIL5QIFS5IrD+4gmTg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2698,9 +2718,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-win32-ia32-msvc": {
|
||||
"version": "2.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.4.tgz",
|
||||
"integrity": "sha512-12Hxi0XX/H5VFxO/bGgHkFWhml9VMgEOu9CidjeCeTNQ1l6fpUlbiGgSP7CLI3PFtW9/FfbeHieZ+kyWK5H7CA==",
|
||||
"version": "2.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.12.4.tgz",
|
||||
"integrity": "sha512-ezl8sQIcjYsye3TUnTH5VcWmWzccZASlQm7hlZt0dYyj1XD0qpVW47u2xYVrIKui0F8uQv/7QGZixdWiClBsbg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -2715,9 +2735,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-win32-x64-msvc": {
|
||||
"version": "2.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.4.tgz",
|
||||
"integrity": "sha512-+vDiqBIU5dMISg/wNvX3sF+ZHfgJGJ5T0AcO+EHNXV9GGAG+P5fzodlDXD3QdKCRgZxMoCm5PPvj3BqLNjBthw==",
|
||||
"version": "2.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.12.4.tgz",
|
||||
"integrity": "sha512-DJhDeCqjLDNMWYFy7ZS8cfeAHX2RK0RbQMLiA3bMVVkXRDJ4NBYiHriGqsUhsr9iG41aTb+dJGkFPmPw1gWwVw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2867,6 +2887,17 @@
|
||||
"undici-types": "~8.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pg": {
|
||||
"version": "8.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.23.1.tgz",
|
||||
"integrity": "sha512-fKVHpikPdg4GKks3JuLEhvwSyvwzF23hnabPy6DD8ljVbC7+6J5dQzdv4arV6jqq57djnMgs1HKBxX4P8aBI3A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"pg-protocol": "*",
|
||||
"pg-types": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
|
||||
@@ -3193,6 +3224,21 @@
|
||||
"integrity": "sha512-YOf0VSj5nUPI27doTtXF+BBnsiRq3qY7avHqfIWnppxTLGyvkLq1QV2RTxkwoZwJ60ywLfZ0raFF4J/G886i7A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/better-sqlite3": {
|
||||
"version": "13.0.3",
|
||||
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-13.0.3.tgz",
|
||||
"integrity": "sha512-RbOBxmLBG8uvFUc15X9+9SFemKcQ0WBuISBVkpuiaUB2qblC8UWlHEjdWVoZ8AdhSwmoEgsiXKfopX0CQxaACQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"node-addon-api": "^8.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.6",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz",
|
||||
@@ -3575,6 +3621,12 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/comlink": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.2.tgz",
|
||||
"integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "13.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
|
||||
@@ -5362,6 +5414,17 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "8.9.2",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.9.2.tgz",
|
||||
"integrity": "sha512-VijLXbi3UACN69I0JVXJsX4tjACjNoQDgv2gTF6sx2wWEi8tkSg2eX8p5gSIFi8z2+DL3oHmY6OyKce38SDolg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18 || ^20 || >= 21"
|
||||
}
|
||||
},
|
||||
"node_modules/node-cron": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.3.tgz",
|
||||
@@ -5579,6 +5642,104 @@
|
||||
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.23.1",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.23.1.tgz",
|
||||
"integrity": "sha512-aL96AHANtWjPLDOLqnhx+ngp9+UK7ETEU8VJrDCGvsSSi/mGLcWYsS6Herg7lmaBJe4uwrfqsa7gTEFaSizDoQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pg-connection-string": "^2.14.1",
|
||||
"pg-pool": "^3.14.0",
|
||||
"pg-protocol": "^1.16.1",
|
||||
"pg-types": "2.2.0",
|
||||
"pgpass": "1.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"pg-cloudflare": "^1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pg-native": ">=3.0.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"pg-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pg-cloudflare": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.1.tgz",
|
||||
"integrity": "sha512-6PQbsFWZcp9EmJEwy5cGQ2La+AMWpP46lgbb8X+U/XsHIUweYDNCpeuKck5RxL2MdVFi7krbbEi5nX4Zh7JhrQ==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/pg-connection-string": {
|
||||
"version": "2.14.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.1.tgz",
|
||||
"integrity": "sha512-qR3kGNPBLpCNtz0evbKA0Y/MRFXwSSdT+pTJvYp/bXTcReZbvX1kzF0IyTc1QnxqF7AZbOeBhNL8R5mYQZV/MA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pg-int8": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-pool": {
|
||||
"version": "3.14.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz",
|
||||
"integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"pg": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-protocol": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.16.1.tgz",
|
||||
"integrity": "sha512-p9VOFMiHB/ZbJATetbg+99PxssTVSQRnyuPSQ67mN1+1KBOjZaZ83ZQzltnxPhJwSsC3nwVjJ10DVJlerbFzLg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pg-types": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pg-int8": "1.0.1",
|
||||
"postgres-array": "~2.0.0",
|
||||
"postgres-bytea": "~1.0.0",
|
||||
"postgres-date": "~1.0.4",
|
||||
"postgres-interval": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-types/node_modules/postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/pgpass": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"split2": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
@@ -5619,6 +5780,45 @@
|
||||
"url": "https://github.com/sponsors/porsager"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-array": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz",
|
||||
"integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-bytea": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
|
||||
"integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-date": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-interval": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prisma": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-7.7.0.tgz",
|
||||
@@ -6470,6 +6670,15 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
@@ -6938,6 +7147,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
|
||||
+11
-6
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "iboard",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"tauri": "tauri",
|
||||
@@ -8,14 +8,16 @@
|
||||
"dev:server": "node --watch --experimental-strip-types --no-warnings server/index.js",
|
||||
"dev": "concurrently -n web,api -c blue,green \"npm run dev:web\" \"npm run dev:server\"",
|
||||
"build": "vite build",
|
||||
"build:shell": "vite build --mode shell",
|
||||
"build:win": "node scripts/build-windows.mjs",
|
||||
"build:win:debug": "node scripts/build-windows.mjs --debug",
|
||||
"win:dev": "node scripts/dev-windows-live.mjs",
|
||||
"build:ota": "node scripts/build-ota.mjs",
|
||||
"preview": "vite preview",
|
||||
"start": "node --experimental-strip-types --no-warnings server/index.js",
|
||||
"db:migrate": "prisma migrate deploy",
|
||||
"db:seed": "node scripts/seed.cjs",
|
||||
"registry:sync": "node scripts/sync-registry.mjs",
|
||||
"data:refresh": "node --no-warnings scripts/refresh-indicators.cjs",
|
||||
"data:discover": "node --no-warnings scripts/discover-mcp-tools.cjs",
|
||||
"icons": "node scripts/generate-icons.mjs",
|
||||
"tauri:dev": "tauri dev",
|
||||
"tauri:build": "tauri build",
|
||||
@@ -30,7 +32,10 @@
|
||||
"publish": "bash scripts/wsl/build-apk.sh release"
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/adapter-mariadb": "^7.9.1",
|
||||
"@journeyapps/wa-sqlite": "^2.0.7",
|
||||
"@powersync/react": "^2.1.0",
|
||||
"@powersync/web": "^2.5.0",
|
||||
"@prisma/adapter-pg": "^7.9.1",
|
||||
"@prisma/client": "^7.7.0",
|
||||
"@zythum02/mijia-api": "^0.1.4",
|
||||
"antd-mobile": "^5.42.3",
|
||||
@@ -52,8 +57,8 @@
|
||||
"ws": "^8.21.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/api": "^2.0.0",
|
||||
"@tauri-apps/cli": "^2.0.0",
|
||||
"@tauri-apps/api": "^2.12.4",
|
||||
"@tauri-apps/cli": "^2.12.4",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"cheerio": "^1.2.0",
|
||||
"concurrently": "^9.0.0",
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE `User` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(191) NOT NULL,
|
||||
`email` VARCHAR(191) NOT NULL,
|
||||
`password` VARCHAR(191) NOT NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `User_email_key`(`email`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `Article` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`title` VARCHAR(191) NOT NULL,
|
||||
`slug` VARCHAR(191) NOT NULL,
|
||||
`content` VARCHAR(191) NOT NULL,
|
||||
`date` VARCHAR(191) NOT NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `Article_slug_key`(`slug`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `Strategy` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(191) NOT NULL,
|
||||
`description` VARCHAR(191) NOT NULL,
|
||||
`data` JSON NOT NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `Indicator` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(191) NOT NULL,
|
||||
`groupName` VARCHAR(191) NOT NULL,
|
||||
`data` JSON NOT NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `SowInventory` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`month` VARCHAR(191) NOT NULL,
|
||||
`inventory` INTEGER NOT NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `SowInventory_month_key`(`month`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the `SowInventory` table. If the table is not empty, all the data it contains will be lost.
|
||||
|
||||
*/
|
||||
-- DropTable
|
||||
DROP TABLE `SowInventory`;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `Econ_SowInventory` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`month` VARCHAR(191) NOT NULL,
|
||||
`inventory` INTEGER NOT NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `Econ_SowInventory_month_key`(`month`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
@@ -1,13 +0,0 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE `ScraperCache` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`scraperName` VARCHAR(191) NOT NULL,
|
||||
`lastDataHash` VARCHAR(191) NULL,
|
||||
`lastUpdateAt` DATETIME(3) NOT NULL,
|
||||
`metadata` JSON NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `ScraperCache_scraperName_key`(`scraperName`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
@@ -1,5 +0,0 @@
|
||||
-- Add unique constraint on Strategy.name to enable idempotent upsert in seed script
|
||||
ALTER TABLE `Strategy` ADD UNIQUE INDEX `Strategy_name_key`(`name`);
|
||||
|
||||
-- Add unique constraint on Indicator.name to enable idempotent upsert in seed script
|
||||
ALTER TABLE `Indicator` ADD UNIQUE INDEX `Indicator_name_key`(`name`);
|
||||
@@ -1,24 +0,0 @@
|
||||
-- 经济指标: 明细数据拆到独立表, 兼容年/季/月/日频率
|
||||
-- 第一步: 建 IndicatorPoint 表 + Indicator 加元数据列 (data 列暂保留, 由迁移脚本搬运后删除)
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `IndicatorPoint` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`indicatorId` INTEGER NOT NULL,
|
||||
`period` VARCHAR(191) NOT NULL,
|
||||
`value` DOUBLE NOT NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `IndicatorPoint_indicatorId_period_key`(`indicatorId`, `period`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- AlterTable: 元数据列 (data JSON 拆出)
|
||||
ALTER TABLE `Indicator` ADD COLUMN `title` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
ADD COLUMN `unit` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
ADD COLUMN `source` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
ADD COLUMN `frequency` VARCHAR(191) NOT NULL DEFAULT 'month';
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `IndicatorPoint` ADD CONSTRAINT `IndicatorPoint_indicatorId_fkey` FOREIGN KEY (`indicatorId`) REFERENCES `Indicator`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -1,3 +0,0 @@
|
||||
-- 第二步: 明细数据已迁移到 IndicatorPoint, 删除 Indicator.data JSON 列
|
||||
-- AlterTable
|
||||
ALTER TABLE `Indicator` DROP COLUMN `data`;
|
||||
@@ -1,2 +0,0 @@
|
||||
-- 指标隐藏标记: 数据与获取脚本保留, 前端展示过滤 hidden=true
|
||||
ALTER TABLE `Indicator` ADD COLUMN `hidden` BOOLEAN NOT NULL DEFAULT false;
|
||||
@@ -1,32 +0,0 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE `News` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`title` VARCHAR(191) NOT NULL,
|
||||
`summary` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`source` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`url` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`publishedAt` DATETIME(3) NOT NULL,
|
||||
`impactRank` INTEGER NOT NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
|
||||
UNIQUE INDEX `News_title_publishedAt_key`(`title`, `publishedAt`),
|
||||
INDEX `News_publishedAt_idx`(`publishedAt`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `Alert` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`indicatorName` VARCHAR(191) NOT NULL,
|
||||
`period` VARCHAR(191) NOT NULL,
|
||||
`valueA` DOUBLE NOT NULL,
|
||||
`valueB` DOUBLE NOT NULL,
|
||||
`sourceA` VARCHAR(191) NOT NULL,
|
||||
`sourceB` VARCHAR(191) NOT NULL,
|
||||
`diff` DOUBLE NOT NULL,
|
||||
`diffRatio` DOUBLE NOT NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
|
||||
INDEX `Alert_createdAt_idx`(`createdAt`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
@@ -1,68 +0,0 @@
|
||||
-- 服务器监控 + 服务健康检查
|
||||
-- CreateTable
|
||||
CREATE TABLE `ServerNode` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`uuid` VARCHAR(191) NOT NULL,
|
||||
`name` VARCHAR(191) NOT NULL,
|
||||
`cpuName` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`os` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`region` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`memTotal` BIGINT NOT NULL DEFAULT 0,
|
||||
`diskTotal` BIGINT NOT NULL DEFAULT 0,
|
||||
`cpuCores` INTEGER NOT NULL DEFAULT 0,
|
||||
`cpuUsage` DOUBLE NOT NULL DEFAULT 0,
|
||||
`ramUsed` BIGINT NOT NULL DEFAULT 0,
|
||||
`swapUsed` BIGINT NOT NULL DEFAULT 0,
|
||||
`load1` DOUBLE NOT NULL DEFAULT 0,
|
||||
`load5` DOUBLE NOT NULL DEFAULT 0,
|
||||
`load15` DOUBLE NOT NULL DEFAULT 0,
|
||||
`diskUsed` BIGINT NOT NULL DEFAULT 0,
|
||||
`netIn` BIGINT NOT NULL DEFAULT 0,
|
||||
`netOut` BIGINT NOT NULL DEFAULT 0,
|
||||
`uptime` BIGINT NOT NULL DEFAULT 0,
|
||||
`process` INTEGER NOT NULL DEFAULT 0,
|
||||
`connections` INTEGER NOT NULL DEFAULT 0,
|
||||
`status` VARCHAR(191) NOT NULL DEFAULT 'unknown',
|
||||
`recordedAt` DATETIME(3) NOT NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `ServerNode_uuid_key`(`uuid`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `ServerNodeHistory` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`uuid` VARCHAR(191) NOT NULL,
|
||||
`period` DATETIME(3) NOT NULL,
|
||||
`cpu` DOUBLE NOT NULL DEFAULT 0,
|
||||
`ram` BIGINT NOT NULL DEFAULT 0,
|
||||
`load` DOUBLE NOT NULL DEFAULT 0,
|
||||
`disk` BIGINT NOT NULL DEFAULT 0,
|
||||
`netIn` BIGINT NOT NULL DEFAULT 0,
|
||||
`netOut` BIGINT NOT NULL DEFAULT 0,
|
||||
`process` INTEGER NOT NULL DEFAULT 0,
|
||||
`connections` INTEGER NOT NULL DEFAULT 0,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
|
||||
UNIQUE INDEX `ServerNodeHistory_uuid_period_key`(`uuid`, `period`),
|
||||
INDEX `ServerNodeHistory_uuid_period_idx`(`uuid`, `period`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `ServiceCheck` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(191) NOT NULL,
|
||||
`url` VARCHAR(191) NOT NULL,
|
||||
`status` VARCHAR(191) NOT NULL DEFAULT 'unknown',
|
||||
`latencyMs` INTEGER NOT NULL DEFAULT 0,
|
||||
`message` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`lastCheckAt` DATETIME(3) NOT NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `ServiceCheck_name_key`(`name`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
@@ -1,2 +0,0 @@
|
||||
-- AlterTable (news.content 全文, 须在 news_and_alerts 建表之后执行)
|
||||
ALTER TABLE `News` ADD COLUMN `content` TEXT NULL;
|
||||
@@ -1,17 +0,0 @@
|
||||
-- 浙江省油价历史表
|
||||
-- - date: 调价日次日 (即数据实际抓取日, 便于按时间排序)
|
||||
-- - province + fuel: 浙江 / 92/95/0
|
||||
-- - unique (date, province, fuel): 防 cron 重复入库
|
||||
-- - DOUBLE 容纳 999.999 元/升
|
||||
CREATE TABLE OilPriceHistory (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
date DATETIME(3) NOT NULL,
|
||||
province VARCHAR(191) NOT NULL,
|
||||
fuel VARCHAR(8) NOT NULL,
|
||||
price DOUBLE NOT NULL,
|
||||
source VARCHAR(191) NOT NULL DEFAULT 'apizero',
|
||||
createdAt DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE INDEX OilPriceHistory_date_province_fuel_key(date, province, fuel),
|
||||
INDEX OilPriceHistory_province_fuel_date_idx(province, fuel, date)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
@@ -1,95 +0,0 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE `MihomeAccount` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`key` VARCHAR(191) NOT NULL DEFAULT 'default',
|
||||
`userId` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`status` VARCHAR(191) NOT NULL DEFAULT 'unknown',
|
||||
`lastError` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `MihomeAccount_key_key`(`key`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `MihomeHome` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`homeId` VARCHAR(191) NOT NULL,
|
||||
`name` VARCHAR(191) NOT NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
|
||||
UNIQUE INDEX `MihomeHome_homeId_key`(`homeId`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `MihomeDevice` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`did` VARCHAR(191) NOT NULL,
|
||||
`homeId` VARCHAR(191) NOT NULL,
|
||||
`model` VARCHAR(191) NOT NULL,
|
||||
`name` VARCHAR(191) NOT NULL,
|
||||
`alias` VARCHAR(191) NULL,
|
||||
`tempSiid` INTEGER NOT NULL,
|
||||
`tempPiid` INTEGER NOT NULL,
|
||||
`humSiid` INTEGER NOT NULL,
|
||||
`humPiid` INTEGER NOT NULL,
|
||||
`battSiid` INTEGER NOT NULL,
|
||||
`battPiid` INTEGER NOT NULL,
|
||||
`battery` INTEGER NULL,
|
||||
`batteryAt` DATETIME(3) NULL,
|
||||
`enabled` BOOLEAN NOT NULL DEFAULT true,
|
||||
`lastSyncAt` DATETIME(3) NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
|
||||
UNIQUE INDEX `MihomeDevice_did_key`(`did`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `MihomeEnvSample` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`deviceId` INTEGER NOT NULL,
|
||||
`date` DATETIME(3) NOT NULL,
|
||||
`temperature` DOUBLE NULL,
|
||||
`humidity` DOUBLE NULL,
|
||||
`temperatureAvg` DOUBLE NULL,
|
||||
`humidityAvg` DOUBLE NULL,
|
||||
`temperatureMin` DOUBLE NULL,
|
||||
`temperatureMax` DOUBLE NULL,
|
||||
`humidityMin` DOUBLE NULL,
|
||||
`humidityMax` DOUBLE NULL,
|
||||
`origin` VARCHAR(191) NOT NULL DEFAULT 'live',
|
||||
`sampledAt` DATETIME(3) NULL,
|
||||
`raw` JSON NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
|
||||
INDEX `MihomeEnvSample_deviceId_date_idx`(`deviceId`, `date`),
|
||||
UNIQUE INDEX `MihomeEnvSample_deviceId_date_key`(`deviceId`, `date`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `MihomeEnvHour` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`deviceId` INTEGER NOT NULL,
|
||||
`hour` DATETIME(3) NOT NULL,
|
||||
`temperatureAvg` DOUBLE NULL,
|
||||
`humidityAvg` DOUBLE NULL,
|
||||
`raw` JSON NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
|
||||
INDEX `MihomeEnvHour_deviceId_hour_idx`(`deviceId`, `hour`),
|
||||
UNIQUE INDEX `MihomeEnvHour_deviceId_hour_key`(`deviceId`, `hour`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `MihomeDevice` ADD CONSTRAINT `MihomeDevice_homeId_fkey` FOREIGN KEY (`homeId`) REFERENCES `MihomeHome`(`homeId`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `MihomeEnvSample` ADD CONSTRAINT `MihomeEnvSample_deviceId_fkey` FOREIGN KEY (`deviceId`) REFERENCES `MihomeDevice`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `MihomeEnvHour` ADD CONSTRAINT `MihomeEnvHour_deviceId_fkey` FOREIGN KEY (`deviceId`) REFERENCES `MihomeDevice`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE `Habit` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`userId` INTEGER NOT NULL,
|
||||
`name` VARCHAR(191) NOT NULL,
|
||||
`period` VARCHAR(191) NOT NULL DEFAULT 'day',
|
||||
`target` INTEGER NOT NULL DEFAULT 1,
|
||||
`color` VARCHAR(191) NOT NULL DEFAULT '#1677ff',
|
||||
`icon` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`note` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`archived` BOOLEAN NOT NULL DEFAULT false,
|
||||
`sortOrder` INTEGER NOT NULL DEFAULT 0,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
INDEX `Habit_userId_archived_sortOrder_idx`(`userId`, `archived`, `sortOrder`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `CheckIn` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`habitId` INTEGER NOT NULL,
|
||||
`userId` INTEGER NOT NULL,
|
||||
`date` VARCHAR(10) NOT NULL,
|
||||
`seq` INTEGER NOT NULL DEFAULT 1,
|
||||
`note` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`source` VARCHAR(191) NOT NULL DEFAULT 'user',
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
|
||||
UNIQUE INDEX `CheckIn_habitId_date_seq_key`(`habitId`, `date`, `seq`),
|
||||
INDEX `CheckIn_userId_date_idx`(`userId`, `date`),
|
||||
INDEX `CheckIn_habitId_date_idx`(`habitId`, `date`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `CheckList` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`userId` INTEGER NOT NULL,
|
||||
`title` VARCHAR(191) NOT NULL,
|
||||
`scene` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`note` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`archived` BOOLEAN NOT NULL DEFAULT false,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
INDEX `CheckList_userId_archived_idx`(`userId`, `archived`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `CheckListItem` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`listId` INTEGER NOT NULL,
|
||||
`title` VARCHAR(191) NOT NULL,
|
||||
`note` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`done` BOOLEAN NOT NULL DEFAULT false,
|
||||
`doneAt` DATETIME(3) NULL,
|
||||
`sortOrder` INTEGER NOT NULL DEFAULT 0,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
INDEX `CheckListItem_listId_sortOrder_idx`(`listId`, `sortOrder`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `CheckListTemplate` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`userId` INTEGER NULL,
|
||||
`name` VARCHAR(191) NOT NULL,
|
||||
`scene` VARCHAR(191) NOT NULL DEFAULT '',
|
||||
`items` JSON NOT NULL,
|
||||
`builtin` BOOLEAN NOT NULL DEFAULT false,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
INDEX `CheckListTemplate_userId_builtin_idx`(`userId`, `builtin`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `Habit` ADD CONSTRAINT `Habit_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `CheckIn` ADD CONSTRAINT `CheckIn_habitId_fkey` FOREIGN KEY (`habitId`) REFERENCES `Habit`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `CheckIn` ADD CONSTRAINT `CheckIn_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `CheckList` ADD CONSTRAINT `CheckList_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `CheckListItem` ADD CONSTRAINT `CheckListItem_listId_fkey` FOREIGN KEY (`listId`) REFERENCES `CheckList`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `CheckListTemplate` ADD CONSTRAINT `CheckListTemplate_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -1,126 +0,0 @@
|
||||
-- 任务模块 (V0.2): 项目 / 成员 / 看板列 / 任务 / 任务成员 / 标签
|
||||
-- 设计口径见 docs/V0.2/任务模块/任务模块数据库设计文档.md
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `Project` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(50) NOT NULL,
|
||||
`ownerId` INTEGER NOT NULL,
|
||||
`isPersonal` BOOLEAN NOT NULL DEFAULT false,
|
||||
`archived` BOOLEAN NOT NULL DEFAULT false,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
INDEX `Project_ownerId_idx`(`ownerId`),
|
||||
INDEX `Project_archived_idx`(`archived`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `ProjectMember` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`projectId` INTEGER NOT NULL,
|
||||
`userId` INTEGER NOT NULL,
|
||||
`permission` VARCHAR(191) NOT NULL DEFAULT 'member',
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
|
||||
UNIQUE INDEX `ProjectMember_projectId_userId_key`(`projectId`, `userId`),
|
||||
INDEX `ProjectMember_userId_idx`(`userId`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `BoardColumn` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`projectId` INTEGER NOT NULL,
|
||||
`name` VARCHAR(20) NOT NULL,
|
||||
`sortOrder` INTEGER NOT NULL DEFAULT 0,
|
||||
`boundStatus` VARCHAR(8) NULL,
|
||||
`retention` VARCHAR(8) NOT NULL DEFAULT 'forever',
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `BoardColumn_projectId_name_key`(`projectId`, `name`),
|
||||
INDEX `BoardColumn_projectId_sortOrder_idx`(`projectId`, `sortOrder`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `Task` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`projectId` INTEGER NOT NULL,
|
||||
`columnId` INTEGER NOT NULL,
|
||||
`parentId` INTEGER NULL,
|
||||
`title` VARCHAR(200) NOT NULL,
|
||||
`description` TEXT NOT NULL,
|
||||
`status` VARCHAR(8) NOT NULL DEFAULT 'todo',
|
||||
`doneAt` DATETIME(3) NULL,
|
||||
`assigneeId` INTEGER NULL,
|
||||
`creatorId` INTEGER NOT NULL,
|
||||
`extras` JSON NOT NULL,
|
||||
`sortOrder` INTEGER NOT NULL DEFAULT 0,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
INDEX `Task_projectId_columnId_idx`(`projectId`, `columnId`),
|
||||
INDEX `Task_projectId_status_idx`(`projectId`, `status`),
|
||||
INDEX `Task_parentId_idx`(`parentId`),
|
||||
INDEX `Task_projectId_doneAt_idx`(`projectId`, `doneAt`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `TaskMember` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`taskId` INTEGER NOT NULL,
|
||||
`userId` INTEGER NOT NULL,
|
||||
|
||||
UNIQUE INDEX `TaskMember_taskId_userId_key`(`taskId`, `userId`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `TaskTag` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`taskId` INTEGER NOT NULL,
|
||||
`name` VARCHAR(20) NOT NULL,
|
||||
|
||||
INDEX `TaskTag_taskId_idx`(`taskId`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `Project` ADD CONSTRAINT `Project_ownerId_fkey` FOREIGN KEY (`ownerId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `ProjectMember` ADD CONSTRAINT `ProjectMember_projectId_fkey` FOREIGN KEY (`projectId`) REFERENCES `Project`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `ProjectMember` ADD CONSTRAINT `ProjectMember_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `BoardColumn` ADD CONSTRAINT `BoardColumn_projectId_fkey` FOREIGN KEY (`projectId`) REFERENCES `Project`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `Task` ADD CONSTRAINT `Task_projectId_fkey` FOREIGN KEY (`projectId`) REFERENCES `Project`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `Task` ADD CONSTRAINT `Task_columnId_fkey` FOREIGN KEY (`columnId`) REFERENCES `BoardColumn`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `Task` ADD CONSTRAINT `Task_parentId_fkey` FOREIGN KEY (`parentId`) REFERENCES `Task`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `Task` ADD CONSTRAINT `Task_assigneeId_fkey` FOREIGN KEY (`assigneeId`) REFERENCES `User`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `Task` ADD CONSTRAINT `Task_creatorId_fkey` FOREIGN KEY (`creatorId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `TaskMember` ADD CONSTRAINT `TaskMember_taskId_fkey` FOREIGN KEY (`taskId`) REFERENCES `Task`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `TaskMember` ADD CONSTRAINT `TaskMember_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `TaskTag` ADD CONSTRAINT `TaskTag_taskId_fkey` FOREIGN KEY (`taskId`) REFERENCES `Task`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -1,7 +0,0 @@
|
||||
-- 项目信息块 (T-PJ-13): Project 增加 notes JSON, 存 [{ title, content }] 任意多个长文本块
|
||||
-- 分三步保证存量行安全: 先可空加列 -> 回填 '[]' -> 收紧为 NOT NULL
|
||||
ALTER TABLE `Project` ADD COLUMN `notes` JSON NULL;
|
||||
|
||||
UPDATE `Project` SET `notes` = '[]' WHERE `notes` IS NULL;
|
||||
|
||||
ALTER TABLE `Project` MODIFY `notes` JSON NOT NULL;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user