This commit is contained in:
parent
3d84e8d8f7
commit
789505fb34
2
.env
2
.env
@ -1 +1 @@
|
|||||||
DATABASE_URL="mysql://root:fullstack@192.168.111.111:3306/iboard"
|
DATABASE_URL="mysql://root:sunyard123@114.134.187.170:3306/iboard"
|
||||||
@ -1,6 +1,7 @@
|
|||||||
# ===============================================================
|
# ===============================================================
|
||||||
# iBoard 一体化镜像: 静态前端 (dist/) + Express API (server/) 同进程
|
# iBoard 一体化镜像: 静态前端 (dist/) + Express API (server/) 同进程
|
||||||
# 构建: docker build --build-arg DATABASE_URL=... -t iboard:latest .
|
# 构建: 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
|
# 运行: docker run -d --name iboard -p 3001:3001 -e DATABASE_URL=... iboard:latest
|
||||||
# ===============================================================
|
# ===============================================================
|
||||||
FROM node:22-alpine AS builder
|
FROM node:22-alpine AS builder
|
||||||
@ -24,6 +25,9 @@ COPY . .
|
|||||||
|
|
||||||
# 构建前端 (输出到 dist/) 与 Prisma 客户端
|
# 构建前端 (输出到 dist/) 与 Prisma 客户端
|
||||||
RUN npx prisma generate
|
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
|
RUN npm run build
|
||||||
|
|
||||||
# 运行时阶段
|
# 运行时阶段
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# iBoard Android APK 打包
|
# iBoard Android APK 打包
|
||||||
# 用法: bash scripts/wsl/build-apk.sh [debug|release]
|
# 用法: bash scripts/wsl/build-apk.sh [debug|release]
|
||||||
|
# release 模式自动构建 4 个 ABI 并出 universal APK
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
WIN_PROJECT='/mnt/d/workbench/iboard'
|
WIN_PROJECT='/mnt/d/workbench/iboard'
|
||||||
@ -12,7 +13,15 @@ echo "构建变体: $VARIANT"
|
|||||||
# 环境变量
|
# 环境变量
|
||||||
export ANDROID_HOME="${ANDROID_HOME:-$HOME/android-sdk}"
|
export ANDROID_HOME="${ANDROID_HOME:-$HOME/android-sdk}"
|
||||||
export ANDROID_NDK_HOME="${ANDROID_NDK_HOME:-$ANDROID_HOME/ndk/27.2.12479018}"
|
export ANDROID_NDK_HOME="${ANDROID_NDK_HOME:-$ANDROID_HOME/ndk/27.2.12479018}"
|
||||||
export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$HOME/.cargo/bin:$PATH"
|
# 关键: 把 NDK toolchain bin 加进 PATH, .cargo/config.toml 里
|
||||||
|
# linker = "aarch64-linux-android24-clang" 这种短名才能被解析到
|
||||||
|
NDK_TOOLCHAIN_BIN="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin"
|
||||||
|
if [ ! -d "$NDK_TOOLCHAIN_BIN" ]; then
|
||||||
|
echo "ERROR: NDK toolchain bin 不存在: $NDK_TOOLCHAIN_BIN"
|
||||||
|
echo "请确认 ANDROID_NDK_HOME 正确指向 NDK 27.2.x 目录"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
export PATH="$NDK_TOOLCHAIN_BIN:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$HOME/.cargo/bin:$PATH"
|
||||||
|
|
||||||
# 0. 首次需要 android 工程
|
# 0. 首次需要 android 工程
|
||||||
cd src-tauri
|
cd src-tauri
|
||||||
@ -22,6 +31,10 @@ if [ ! -d "gen/android" ]; then
|
|||||||
fi
|
fi
|
||||||
cd "$WIN_PROJECT"
|
cd "$WIN_PROJECT"
|
||||||
|
|
||||||
|
# Android 后端地址, 写到前端 bundle 里 (覆盖默认的 /api 相对路径)
|
||||||
|
export VITE_API_BASE="${VITE_API_BASE:-http://bh.vps.honor3.com:3001}"
|
||||||
|
echo "VITE_API_BASE=$VITE_API_BASE"
|
||||||
|
|
||||||
# 1. 先构建前端, 让 Tauri android 工程能拿到 dist/
|
# 1. 先构建前端, 让 Tauri android 工程能拿到 dist/
|
||||||
echo "=== 1. 构建前端 ==="
|
echo "=== 1. 构建前端 ==="
|
||||||
npm run build
|
npm run build
|
||||||
@ -36,30 +49,34 @@ RUST_BUILD_TASKS=(
|
|||||||
":app:rustBuildX86_64Release"
|
":app:rustBuildX86_64Release"
|
||||||
":app:rustBuildI686Release"
|
":app:rustBuildI686Release"
|
||||||
)
|
)
|
||||||
RUST_BUILD_TASKS_DBG=(
|
|
||||||
":app:rustBuildArm64Debug"
|
|
||||||
":app:rustBuildArmv7Debug"
|
|
||||||
":app:rustBuildX86_64Debug"
|
|
||||||
":app:rustBuildI686Debug"
|
|
||||||
)
|
|
||||||
SKIP_ARGS=()
|
SKIP_ARGS=()
|
||||||
for t in "${RUST_BUILD_TASKS[@]}"; do SKIP_ARGS+=(-x "${t#:}"); done
|
for t in "${RUST_BUILD_TASKS[@]}"; do SKIP_ARGS+=(-x "${t#:}"); done
|
||||||
|
|
||||||
if [ "$VARIANT" = "release" ]; then
|
# 4 个 Android 目标的 cargo target triple
|
||||||
echo "[release] 手工 cargo 编 lib, gradle 只跑 packaging (跳过 rustBuild* 避免 --release 重复 bug)"
|
ANDROID_TARGETS=(
|
||||||
|
"aarch64-linux-android"
|
||||||
|
"armv7-linux-androideabi"
|
||||||
|
"x86_64-linux-android"
|
||||||
|
"i686-linux-android"
|
||||||
|
)
|
||||||
|
|
||||||
echo " [cargo] build lib release ..."
|
if [ "$VARIANT" = "release" ]; then
|
||||||
cargo build \
|
echo "[release] 手工 cargo 编 lib (4 个 ABI), gradle 只跑 packaging (跳过 rustBuild* 避免 --release 重复 bug)"
|
||||||
--target aarch64-linux-android \
|
|
||||||
--features custom-protocol \
|
for target in "${ANDROID_TARGETS[@]}"; do
|
||||||
--lib \
|
echo " [cargo] build lib release for $target ..."
|
||||||
--release
|
cargo build \
|
||||||
|
--target "$target" \
|
||||||
|
--features custom-protocol \
|
||||||
|
--lib \
|
||||||
|
--release
|
||||||
|
done
|
||||||
|
|
||||||
echo " [gradle] assembleRelease -x rustBuild* ..."
|
echo " [gradle] assembleRelease -x rustBuild* ..."
|
||||||
cd gen/android
|
cd gen/android
|
||||||
./gradlew --no-daemon assembleRelease "${SKIP_ARGS[@]}"
|
./gradlew --no-daemon assembleRelease "${SKIP_ARGS[@]}"
|
||||||
|
|
||||||
APK_DIR="$WIN_PROJECT/src-tauri/gen/android/app/build/outputs/apk/release"
|
APK_DIR="$WIN_PROJECT/src-tauri/gen/android/app/build/outputs/apk/universal/release"
|
||||||
echo
|
echo
|
||||||
echo "=== 产物 (未签名) ==="
|
echo "=== 产物 (未签名) ==="
|
||||||
ls -lh "$APK_DIR"/*.apk 2>/dev/null || echo "(未找到 APK)"
|
ls -lh "$APK_DIR"/*.apk 2>/dev/null || echo "(未找到 APK)"
|
||||||
@ -73,7 +90,12 @@ if [ "$VARIANT" = "release" ]; then
|
|||||||
echo " IBOARD_UPLOAD_KEY_ALIAS=iboard"
|
echo " IBOARD_UPLOAD_KEY_ALIAS=iboard"
|
||||||
echo " IBOARD_UPLOAD_STORE_PASSWORD=..."
|
echo " IBOARD_UPLOAD_STORE_PASSWORD=..."
|
||||||
echo " IBOARD_UPLOAD_KEY_PASSWORD=..."
|
echo " IBOARD_UPLOAD_KEY_PASSWORD=..."
|
||||||
echo "3. 重跑本脚本"
|
echo "3. 重跑本脚本 (Gradle 读 gradle.properties 里的 IBOARD_UPLOAD_* 自动用 releaseSigning)"
|
||||||
|
echo
|
||||||
|
echo "=== 临时用 debug keystore 手动签 (仅测试) ==="
|
||||||
|
echo " \$ANDROID_HOME/build-tools/*/apksigner sign --ks ~/.android/debug.keystore \\"
|
||||||
|
echo " --ks-pass pass:android --key-pass pass:android \\"
|
||||||
|
echo " $APK_DIR/app-universal-release-unsigned.apk"
|
||||||
else
|
else
|
||||||
echo "[debug] Tauri CLI debug 路径 (不传 --release 不会触发 bug), 直接走"
|
echo "[debug] Tauri CLI debug 路径 (不传 --release 不会触发 bug), 直接走"
|
||||||
npx tauri android build --apk
|
npx tauri android build --apk
|
||||||
|
|||||||
@ -6,7 +6,32 @@ VARIANT="${1:-debug}"
|
|||||||
WIN_PROJECT='/mnt/d/workbench/iboard'
|
WIN_PROJECT='/mnt/d/workbench/iboard'
|
||||||
export PATH="$HOME/android-sdk/platform-tools:$PATH"
|
export PATH="$HOME/android-sdk/platform-tools:$PATH"
|
||||||
|
|
||||||
APK_DIR="$WIN_PROJECT/src-tauri/target/$( [ "$VARIANT" = "release" ] && echo release || echo debug )/bundle/android/app/build/outputs/apk/$VARIANT"
|
# 候选 APK 路径 (按优先级, 找到第一个就用)
|
||||||
APK=$(ls -1 "$APK_DIR"/*.apk | head -1)
|
CANDIDATES=(
|
||||||
echo "安装: $APK"
|
# release 通用包 (脚本 build-apk.sh release 产物)
|
||||||
|
"$WIN_PROJECT/src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release.apk"
|
||||||
|
"$WIN_PROJECT/src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release-unsigned.apk"
|
||||||
|
# 各 ABI 单独包
|
||||||
|
"$WIN_PROJECT/src-tauri/gen/android/app/build/outputs/apk/arm64-v8a/release/app-arm64-v8a-release.apk"
|
||||||
|
"$WIN_PROJECT/src-tauri/gen/android/app/build/outputs/apk/arm64-v8a/release/app-arm64-v8a-release-unsigned.apk"
|
||||||
|
# debug 通用包
|
||||||
|
"$WIN_PROJECT/src-tauri/gen/android/app/build/outputs/apk/universal/debug/app-universal-debug.apk"
|
||||||
|
# Tauri CLI debug 默认路径
|
||||||
|
"$WIN_PROJECT/src-tauri/target/debug/bundle/android/app/build/outputs/apk/debug/app-debug.apk"
|
||||||
|
"$WIN_PROJECT/src-tauri/target/release/bundle/android/app/build/outputs/apk/release/app-release.apk"
|
||||||
|
)
|
||||||
|
|
||||||
|
APK=""
|
||||||
|
for p in "${CANDIDATES[@]}"; do
|
||||||
|
if [ -f "$p" ]; then APK="$p"; break; fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$APK" ]; then
|
||||||
|
echo "ERROR: 找不到 $VARIANT APK, 请先 bash scripts/wsl/build-apk.sh $VARIANT"
|
||||||
|
echo "扫描过路径:"
|
||||||
|
for p in "${CANDIDATES[@]}"; do echo " $p"; done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "安装 ($VARIANT): $APK"
|
||||||
adb install -r "$APK"
|
adb install -r "$APK"
|
||||||
@ -13,9 +13,7 @@ sudo apt update
|
|||||||
sudo apt install -y \
|
sudo apt install -y \
|
||||||
build-essential curl wget file libssl-dev pkg-config \
|
build-essential curl wget file libssl-dev pkg-config \
|
||||||
unzip zip git \
|
unzip zip git \
|
||||||
openjdk-17-jdk-headless \
|
openjdk-17-jdk-headless
|
||||||
clang libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev \
|
|
||||||
libsoup-3.0-dev libjavascriptcoregtk-4.1-dev
|
|
||||||
|
|
||||||
echo "=== 2. Rust ==="
|
echo "=== 2. Rust ==="
|
||||||
if ! command -v cargo >/dev/null 2>&1; then
|
if ! command -v cargo >/dev/null 2>&1; then
|
||||||
@ -24,30 +22,33 @@ if ! command -v cargo >/dev/null 2>&1; then
|
|||||||
fi
|
fi
|
||||||
rustc --version
|
rustc --version
|
||||||
cargo --version
|
cargo --version
|
||||||
|
# Tauri 2 + Android 需要 rustc 1.77+, 当前默认 toolchain 即可
|
||||||
|
|
||||||
echo "=== 3. Android SDK + NDK ==="
|
echo "=== 3. Android SDK + NDK ==="
|
||||||
export ANDROID_HOME="$HOME/Android/Sdk"
|
export ANDROID_HOME="$HOME/android-sdk"
|
||||||
export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/$(ls -1 $ANDROID_HOME/ndk 2>/dev/null | sort -V | tail -1 || echo 26.1.10909125)"
|
mkdir -p "$ANDROID_HOME/cmdline-tools"
|
||||||
mkdir -p "$HOME/android-sdk/cmdline-tools"
|
if [ ! -d "$ANDROID_HOME/cmdline-tools/latest" ]; then
|
||||||
cd /tmp
|
cd /tmp
|
||||||
if [ ! -d "$HOME/android-sdk/cmdline-tools/latest" ]; then
|
|
||||||
echo "下载 Android cmdline-tools..."
|
echo "下载 Android cmdline-tools..."
|
||||||
wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O cmdline-tools.zip
|
wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O cmdline-tools.zip
|
||||||
unzip -q cmdline-tools.zip
|
unzip -q cmdline-tools.zip
|
||||||
mv cmdline-tools "$HOME/android-sdk/cmdline-tools/latest"
|
mv cmdline-tools "$ANDROID_HOME/cmdline-tools/latest"
|
||||||
fi
|
fi
|
||||||
export PATH="$HOME/android-sdk/cmdline-tools/latest/bin:$PATH"
|
export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"
|
||||||
|
|
||||||
yes | sdkmanager --licenses >/dev/null
|
yes | sdkmanager --licenses >/dev/null || true
|
||||||
sdkmanager --install "platforms;android-34" "build-tools;34.0.0" "platform-tools" \
|
# 与 src-tauri/gen/android 中 Tauri 生成的配置对齐: compileSdk=36, NDK 27.2
|
||||||
"ndk;26.1.10909125" "cmake;3.22.1"
|
sdkmanager --install "platforms;android-36" "build-tools;36.0.0" "platform-tools" \
|
||||||
|
"ndk;27.2.12479018" "cmake;3.22.1"
|
||||||
|
|
||||||
|
# 默认 NDK 版本 (build-apk.sh 也用这个)
|
||||||
|
export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/27.2.12479018"
|
||||||
|
|
||||||
echo "ANDROID_HOME=$ANDROID_HOME"
|
echo "ANDROID_HOME=$ANDROID_HOME"
|
||||||
echo "ANDROID_NDK_HOME=$ANDROID_NDK_HOME"
|
echo "ANDROID_NDK_HOME=$ANDROID_NDK_HOME"
|
||||||
|
|
||||||
echo "=== 4. Node 依赖 (与 Windows 共用 node_modules 也行, 这里重新装) ==="
|
echo "=== 4. Node 依赖 (与 Windows 共用 node_modules 也行, 这里重新装) ==="
|
||||||
cd "$WIN_PROJECT"
|
cd "$WIN_PROJECT"
|
||||||
# 跳过 Windows 二进制不兼容的 sharp 等, npm 会自动 rebuild
|
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
echo "=== 5. Tauri Android 工程初始化 (首次执行) ==="
|
echo "=== 5. Tauri Android 工程初始化 (首次执行) ==="
|
||||||
@ -60,9 +61,18 @@ echo
|
|||||||
echo "=== 搭建完成 ==="
|
echo "=== 搭建完成 ==="
|
||||||
echo "接下来:"
|
echo "接下来:"
|
||||||
echo " bash scripts/wsl/build-apk.sh # 打 debug APK"
|
echo " bash scripts/wsl/build-apk.sh # 打 debug APK"
|
||||||
echo " bash scripts/wsl/build-apk.sh release # 打 release APK (需要签名)"
|
echo " bash scripts/wsl/build-apk.sh release # 打 release APK (默认未签名, 需配置 keystore 才能签名)"
|
||||||
|
echo
|
||||||
|
echo "Release 签名 (可选, 仅 release 用):"
|
||||||
|
echo " 1. keytool -genkey -v -keystore ~/iboard-release.keystore -alias iboard -keyalg RSA -keysize 2048 -validity 10000"
|
||||||
|
echo " 2. 在 ~/.gradle/gradle.properties 写:"
|
||||||
|
echo " IBOARD_UPLOAD_STORE_FILE=/home/\$USER/iboard-release.keystore"
|
||||||
|
echo " IBOARD_UPLOAD_KEY_ALIAS=iboard"
|
||||||
|
echo " IBOARD_UPLOAD_STORE_PASSWORD=..."
|
||||||
|
echo " IBOARD_UPLOAD_KEY_PASSWORD=..."
|
||||||
|
echo " 3. 重跑 build-apk.sh release 即可"
|
||||||
echo
|
echo
|
||||||
echo "如要永久生效, 把下面加进 ~/.bashrc:"
|
echo "如要永久生效, 把下面加进 ~/.bashrc:"
|
||||||
echo " export ANDROID_HOME=$ANDROID_HOME"
|
echo " export ANDROID_HOME=$ANDROID_HOME"
|
||||||
echo " export ANDROID_NDK_HOME=$ANDROID_NDK_HOME"
|
echo " export ANDROID_NDK_HOME=$ANDROID_NDK_HOME"
|
||||||
echo " export PATH=\$PATH:$HOME/android-sdk/cmdline-tools/latest/bin:$HOME/android-sdk/platform-tools"
|
echo " export PATH=\$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:\$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin"
|
||||||
37
src-tauri/.cargo/config.toml
Normal file
37
src-tauri/.cargo/config.toml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# iBoard Android 交叉编译配置
|
||||||
|
# 让 cargo 在编译 Android 目标时强制用 NDK 提供的 clang/clang++
|
||||||
|
# 否则会回退到宿主机 ld.bfd, 报 "file in wrong format"
|
||||||
|
#
|
||||||
|
# 链接器名直接写, 路径靠 PATH 解析 (在 build-apk.sh 里 export 过 NDK toolchain bin)
|
||||||
|
|
||||||
|
[target.aarch64-linux-android]
|
||||||
|
linker = "aarch64-linux-android24-clang"
|
||||||
|
rustflags = [
|
||||||
|
"-Clink-arg=-landroid",
|
||||||
|
"-Clink-arg=-llog",
|
||||||
|
"-Clink-arg=-lOpenSLES",
|
||||||
|
]
|
||||||
|
|
||||||
|
[target.armv7-linux-androideabi]
|
||||||
|
linker = "armv7a-linux-androideabi24-clang"
|
||||||
|
rustflags = [
|
||||||
|
"-Clink-arg=-landroid",
|
||||||
|
"-Clink-arg=-llog",
|
||||||
|
"-Clink-arg=-lOpenSLES",
|
||||||
|
]
|
||||||
|
|
||||||
|
[target.x86_64-linux-android]
|
||||||
|
linker = "x86_64-linux-android24-clang"
|
||||||
|
rustflags = [
|
||||||
|
"-Clink-arg=-landroid",
|
||||||
|
"-Clink-arg=-llog",
|
||||||
|
"-Clink-arg=-lOpenSLES",
|
||||||
|
]
|
||||||
|
|
||||||
|
[target.i686-linux-android]
|
||||||
|
linker = "i686-linux-android24-clang"
|
||||||
|
rustflags = [
|
||||||
|
"-Clink-arg=-landroid",
|
||||||
|
"-Clink-arg=-llog",
|
||||||
|
"-Clink-arg=-lOpenSLES",
|
||||||
|
]
|
||||||
@ -22,7 +22,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
"csp": null
|
"csp": "default-src 'self' ipc: http://ipc.localhost; img-src 'self' data: blob: asset: http://asset.localhost; connect-src 'self' ipc: http://ipc.localhost http://bh.vps.honor3.com:3001 https://bh.vps.honor3.com:3001; style-src 'self' 'unsafe-inline'; script-src 'self'"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bundle": {
|
"bundle": {
|
||||||
|
|||||||
@ -2,6 +2,12 @@
|
|||||||
const TOKEN_KEY = 'iboard_token'
|
const TOKEN_KEY = 'iboard_token'
|
||||||
const REMEMBER_KEY = 'iboard_remember'
|
const REMEMBER_KEY = 'iboard_remember'
|
||||||
|
|
||||||
|
// API 基址:
|
||||||
|
// - 默认相对路径 `/api` (web dev: Vite 代理; web prod: 同源 Express 一起托管)
|
||||||
|
// - 打包 Android 时设 VITE_API_BASE=http://bh.vps.honor3.com:3001 走外网后端
|
||||||
|
const API_BASE = (import.meta.env.VITE_API_BASE || '').replace(/\/+$/, '')
|
||||||
|
const API_PREFIX = API_BASE ? API_BASE + '/api' : '/api'
|
||||||
|
|
||||||
export function getToken() {
|
export function getToken() {
|
||||||
return localStorage.getItem(TOKEN_KEY) || ''
|
return localStorage.getItem(TOKEN_KEY) || ''
|
||||||
}
|
}
|
||||||
@ -20,9 +26,13 @@ export function setRememberFlag(v) {
|
|||||||
else localStorage.removeItem(REMEMBER_KEY)
|
else localStorage.removeItem(REMEMBER_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getApiBase() {
|
||||||
|
return API_BASE
|
||||||
|
}
|
||||||
|
|
||||||
export async function api(path, { method = 'GET', body, headers = {} } = {}) {
|
export async function api(path, { method = 'GET', body, headers = {} } = {}) {
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
const res = await fetch('/api' + path, {
|
const res = await fetch(API_PREFIX + path, {
|
||||||
method,
|
method,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user