@coze-arch/cli 0.0.1-alpha.912cd5 → 0.0.1-alpha.9f719c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/__templates__/expo/.coze +5 -0
- package/lib/__templates__/expo/.cozeproj/scripts/deploy_build.sh +25 -25
- package/lib/__templates__/expo/.cozeproj/scripts/deploy_run.sh +31 -56
- package/lib/__templates__/expo/.cozeproj/scripts/prod_build.sh +47 -0
- package/lib/__templates__/expo/.cozeproj/scripts/prod_run.sh +35 -0
- package/lib/__templates__/expo/_npmrc +1 -1
- package/lib/__templates__/expo/babel.config.js +10 -0
- package/lib/__templates__/expo/client/constants/theme.ts +10 -0
- package/lib/__templates__/expo/client/contexts/AuthContext.tsx +14 -107
- package/lib/__templates__/expo/client/hooks/useTheme.ts +1 -1
- package/lib/__templates__/expo/client/screens/home/index.tsx +1 -4
- package/lib/__templates__/expo/client/screens/home/styles.ts +1 -273
- package/lib/__templates__/expo/client/utils/index.ts +1 -2
- package/lib/__templates__/expo/metro.config.js +3 -5
- package/lib/__templates__/expo/package.json +8 -2
- package/lib/__templates__/expo/pnpm-lock.yaml +87 -5
- package/lib/__templates__/expo/src/index.ts +2 -2
- package/lib/__templates__/expo/template.config.js +1 -1
- package/lib/__templates__/nextjs/.coze +3 -3
- package/lib/__templates__/nextjs/_npmrc +1 -1
- package/lib/__templates__/nextjs/package.json +9 -3
- package/lib/__templates__/nextjs/pnpm-lock.yaml +2501 -1120
- package/lib/__templates__/nextjs/scripts/dev.sh +8 -27
- package/lib/__templates__/nextjs/src/app/layout.tsx +18 -22
- package/lib/__templates__/nextjs/template.config.js +1 -1
- package/lib/__templates__/templates.json +7 -0
- package/lib/__templates__/vite/.coze +3 -3
- package/lib/__templates__/vite/README.md +204 -26
- package/lib/__templates__/vite/_npmrc +1 -1
- package/lib/__templates__/vite/package.json +1 -1
- package/lib/__templates__/vite/pnpm-lock.yaml +120 -120
- package/lib/__templates__/vite/scripts/dev.sh +7 -26
- package/lib/__templates__/vite/template.config.js +10 -1
- package/lib/__templates__/vite/vite.config.ts +3 -3
- package/lib/cli.js +408 -248
- package/package.json +3 -2
- package/lib/__templates__/nextjs/.babelrc +0 -15
- package/lib/__templates__/nextjs/server.mjs +0 -50
|
@@ -5,3 +5,8 @@ requires = ["nodejs-24"]
|
|
|
5
5
|
[dev]
|
|
6
6
|
build = ["bash", ".cozeproj/scripts/deploy_build.sh"]
|
|
7
7
|
run = ["bash", ".cozeproj/scripts/deploy_run.sh"]
|
|
8
|
+
|
|
9
|
+
[deploy]
|
|
10
|
+
build = ["bash", ".cozeproj/scripts/prod_build.sh"]
|
|
11
|
+
run = ["bash", ".cozeproj/scripts/prod_run.sh"]
|
|
12
|
+
build_app_dir = "."
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
if [ -z "${BASH_VERSION:-}" ]; then exec /usr/bin/env bash "$0" "$@"; fi
|
|
3
3
|
set -euo pipefail
|
|
4
4
|
ROOT_DIR="$(pwd)"
|
|
5
|
+
PREVIEW_DIR="${COZE_PREVIEW_DIR:-$ROOT_DIR}"
|
|
5
6
|
LOG_DIR="${COZE_LOG_DIR:-$ROOT_DIR/logs}"
|
|
6
7
|
LOG_FILE="$LOG_DIR/app.log"
|
|
7
8
|
mkdir -p "$LOG_DIR"
|
|
@@ -29,42 +30,41 @@ check_command() {
|
|
|
29
30
|
write_log() {
|
|
30
31
|
local level="${1:-INFO}"
|
|
31
32
|
local msg="${2:-}"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
esac
|
|
33
|
+
node -e '
|
|
34
|
+
const fs=require("fs");
|
|
35
|
+
const path=process.argv[1];
|
|
36
|
+
const level=(process.argv[2]||"").toUpperCase();
|
|
37
|
+
let msg=String(process.argv[3]||"");
|
|
38
|
+
const ts=Date.now();
|
|
39
|
+
const dt=new Date();
|
|
40
|
+
const parts=new Intl.DateTimeFormat("en-GB",{timeZone:"Asia/Shanghai",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:false}).formatToParts(dt);
|
|
41
|
+
const o={};
|
|
42
|
+
for(const p of parts){o[p.type]=p.value}
|
|
43
|
+
const dt_str=`${o.year}-${o.month}-${o.day} ${o.hour}:${o.minute}:${o.second}`;
|
|
44
|
+
msg=msg.replace(/\\n/g,"\n");
|
|
45
|
+
msg=`${dt_str} [${level}] ${msg}`;
|
|
46
|
+
const record=JSON.stringify({level, message: msg, timestamp: ts},null,0);
|
|
47
|
+
const fd=fs.openSync(path,"a");
|
|
48
|
+
fs.writeSync(fd, record+"\n", null, "utf8");
|
|
49
|
+
fs.fsyncSync(fd);
|
|
50
|
+
fs.closeSync(fd);
|
|
51
|
+
' "$LOG_FILE" "$level" "$msg"
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
# ==================== 前置检查 ====================
|
|
55
55
|
write_log "INFO" "==================== 开始构建 ===================="
|
|
56
56
|
|
|
57
57
|
write_log "INFO" "检查根目录 pre_install.py"
|
|
58
|
-
if [ -f "$
|
|
59
|
-
write_log "INFO" "执行:python $
|
|
60
|
-
python "$
|
|
58
|
+
if [ -f "$PREVIEW_DIR/pre_install.py" ]; then
|
|
59
|
+
write_log "INFO" "执行:python $PREVIEW_DIR/pre_install.py"
|
|
60
|
+
python "$PREVIEW_DIR/pre_install.py" || write_log "ERROR" "pre_install.py 执行失败"
|
|
61
61
|
fi
|
|
62
62
|
|
|
63
63
|
write_log "INFO" "开始执行构建脚本(build_dev.sh)..."
|
|
64
64
|
write_log "INFO" "正在检查依赖命令是否存在..."
|
|
65
65
|
# 检查核心命令
|
|
66
|
-
check_command "pip"
|
|
67
|
-
check_command "python"
|
|
66
|
+
# check_command "pip"
|
|
67
|
+
# check_command "python"
|
|
68
68
|
check_command "pnpm"
|
|
69
69
|
check_command "npm"
|
|
70
70
|
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
ROOT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
2
|
+
PREVIEW_DIR="${COZE_PREVIEW_DIR:-$ROOT_DIR}"
|
|
2
3
|
LOG_DIR="${COZE_LOG_DIR:-$ROOT_DIR/logs}"
|
|
3
4
|
LOG_FILE="$LOG_DIR/app.log"
|
|
4
5
|
mkdir -p "$LOG_DIR"
|
|
5
6
|
|
|
6
7
|
# ==================== 配置项 ====================
|
|
7
|
-
#
|
|
8
|
+
# Server 服务配置
|
|
8
9
|
SERVER_HOST="0.0.0.0"
|
|
9
10
|
SERVER_PORT="9091"
|
|
10
11
|
# Expo 项目配置
|
|
11
12
|
EXPO_HOST="0.0.0.0"
|
|
12
13
|
EXPO_DIR="expo"
|
|
13
|
-
EXPO_PORT="
|
|
14
|
+
EXPO_PORT="5000"
|
|
14
15
|
WEB_URL="${COZE_PROJECT_DOMAIN_DEFAULT:-http://127.0.0.1:${SERVER_PORT}}"
|
|
15
16
|
ASSUME_YES="1"
|
|
16
17
|
EXPO_PUBLIC_BACKEND_BASE_URL="${EXPO_PUBLIC_BACKEND_BASE_URL:-$WEB_URL}"
|
|
@@ -97,25 +98,25 @@ ensure_port() {
|
|
|
97
98
|
write_log() {
|
|
98
99
|
local level="${1:-INFO}"
|
|
99
100
|
local msg="${2:-}"
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
101
|
+
node -e '
|
|
102
|
+
const fs=require("fs");
|
|
103
|
+
const path=process.argv[1];
|
|
104
|
+
const level=(process.argv[2]||"").toUpperCase();
|
|
105
|
+
let msg=String(process.argv[3]||"");
|
|
106
|
+
const ts=Date.now();
|
|
107
|
+
msg=msg.replace(/\\n/g,"\n");
|
|
108
|
+
const dt=new Date();
|
|
109
|
+
const parts=new Intl.DateTimeFormat("en-GB",{timeZone:"Asia/Shanghai",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:false}).formatToParts(dt);
|
|
110
|
+
const o={};
|
|
111
|
+
for(const p of parts){o[p.type]=p.value}
|
|
112
|
+
const dt_str=`${o.year}-${o.month}-${o.day} ${o.hour}:${o.minute}:${o.second}`;
|
|
113
|
+
msg=`${dt_str} [${level}] ${msg}`;
|
|
114
|
+
const record=JSON.stringify({level, message: msg, timestamp: ts},null,0);
|
|
115
|
+
const fd=fs.openSync(path,"a");
|
|
116
|
+
fs.writeSync(fd, record+"\n", null, "utf8");
|
|
117
|
+
fs.fsyncSync(fd);
|
|
118
|
+
fs.closeSync(fd);
|
|
119
|
+
' "$LOG_FILE" "$level" "$msg"
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
wait_port_connectable() {
|
|
@@ -153,11 +154,17 @@ detect_expo_fetch_failed() {
|
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
# ==================== 前置检查 ====================
|
|
157
|
+
write_log "INFO" "检查根目录 pre_install.py"
|
|
158
|
+
if [ -f "$PREVIEW_DIR/pre_install.py" ]; then
|
|
159
|
+
write_log "INFO" "执行:python $PREVIEW_DIR/pre_install.py"
|
|
160
|
+
python "$PREVIEW_DIR/pre_install.py" || write_log "ERROR" "pre_install.py 执行失败"
|
|
161
|
+
fi
|
|
162
|
+
|
|
156
163
|
write_log "INFO" "==================== 开始启动 ===================="
|
|
157
164
|
write_log "INFO" "开始执行服务启动脚本(start_dev.sh)..."
|
|
158
165
|
write_log "INFO" "正在检查依赖命令和目录是否存在..."
|
|
159
166
|
# 检查核心命令
|
|
160
|
-
check_command "python"
|
|
167
|
+
# check_command "python"
|
|
161
168
|
check_command "npm"
|
|
162
169
|
check_command "pnpm"
|
|
163
170
|
check_command "lsof"
|
|
@@ -169,39 +176,7 @@ mkdir -p logs
|
|
|
169
176
|
ensure_port SERVER_PORT "$SERVER_PORT"
|
|
170
177
|
ensure_port EXPO_PORT "$EXPO_PORT"
|
|
171
178
|
|
|
172
|
-
# ====================
|
|
173
|
-
|
|
174
|
-
# ------------环境变量----------------------
|
|
175
|
-
export storage_type="s3"
|
|
176
|
-
export project_platform="app"
|
|
177
|
-
|
|
178
|
-
if [ -n "${COZE_WORKLOAD_IDENTITY_API_KEY:-}" ]; then
|
|
179
|
-
export OPENAI_API_KEY="$COZE_WORKLOAD_IDENTITY_API_KEY"
|
|
180
|
-
fi
|
|
181
|
-
if [ -n "${OPENAI_API_KEY:-}" ]; then
|
|
182
|
-
export ARK_API_KEY="$OPENAI_API_KEY"
|
|
183
|
-
fi
|
|
184
|
-
|
|
185
|
-
if [ -n "${COZE_INTEGRATION_BASE_URL:-}" ]; then
|
|
186
|
-
_base="${COZE_INTEGRATION_BASE_URL%/}"
|
|
187
|
-
export default_llm_base_url="${_base}/api/v3"
|
|
188
|
-
else
|
|
189
|
-
export default_llm_base_url="https://ark.cn-beijing.volces.com/api/v3"
|
|
190
|
-
fi
|
|
191
|
-
|
|
192
|
-
if [ -n "${PGDATABASE_URL:-}" ]; then
|
|
193
|
-
_pg="${PGDATABASE_URL}"
|
|
194
|
-
case "${_pg}" in
|
|
195
|
-
postgresql://*)
|
|
196
|
-
_pg="postgresql+asyncpg://${_pg#postgresql://}"
|
|
197
|
-
;;
|
|
198
|
-
esac
|
|
199
|
-
_pg="${_pg%%\?*}"
|
|
200
|
-
export DATABASE_URL="${_pg}"
|
|
201
|
-
fi
|
|
202
|
-
|
|
203
|
-
# ------------环境变量----------------------
|
|
204
|
-
|
|
179
|
+
# ==================== 启动 Server 服务 ====================
|
|
205
180
|
write_log "INFO" "==================== 启动 server 服务 ===================="
|
|
206
181
|
write_log "INFO" "正在执行:npm run server"
|
|
207
182
|
PORT="$SERVER_PORT" nohup npm run server > logs/app.log 2>&1 &
|
|
@@ -221,7 +196,7 @@ if detect_expo_fetch_failed 8; then
|
|
|
221
196
|
: > logs/expo.log
|
|
222
197
|
EXPO_PID=$(start_expo 1)
|
|
223
198
|
fi
|
|
224
|
-
# 输出以下环境变量,确保 Expo 项目能正确连接到
|
|
199
|
+
# 输出以下环境变量,确保 Expo 项目能正确连接到 Server 服务
|
|
225
200
|
write_log "INFO" "Expo 环境变量配置:"
|
|
226
201
|
write_log "INFO" "EXPO_PUBLIC_BACKEND_BASE_URL=${EXPO_PUBLIC_BACKEND_BASE_URL}"
|
|
227
202
|
write_log "INFO" "EXPO_PACKAGER_PROXY_URL=${EXPO_PACKAGER_PROXY_URL}"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
if [ -z "${BASH_VERSION:-}" ]; then exec /usr/bin/env bash "$0" "$@"; fi
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
ROOT_DIR="$(pwd)"
|
|
5
|
+
|
|
6
|
+
# ==================== 工具函数 ====================
|
|
7
|
+
info() {
|
|
8
|
+
echo "[INFO] $1"
|
|
9
|
+
}
|
|
10
|
+
warn() {
|
|
11
|
+
echo "[WARN] $1"
|
|
12
|
+
}
|
|
13
|
+
error() {
|
|
14
|
+
echo "[ERROR] $1"
|
|
15
|
+
exit 1
|
|
16
|
+
}
|
|
17
|
+
check_command() {
|
|
18
|
+
if ! command -v "$1" &> /dev/null; then
|
|
19
|
+
error "命令 $1 未找到,请先安装"
|
|
20
|
+
fi
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
info "==================== 开始构建 ===================="
|
|
24
|
+
info "开始执行构建脚本(build_prod.sh)..."
|
|
25
|
+
info "正在检查依赖命令是否存在..."
|
|
26
|
+
# 检查核心命令
|
|
27
|
+
check_command "pnpm"
|
|
28
|
+
check_command "npm"
|
|
29
|
+
|
|
30
|
+
# ==================== 安装 Node 依赖 ====================
|
|
31
|
+
info "==================== 安装 Node 依赖 ===================="
|
|
32
|
+
info "开始安装 Node 依赖"
|
|
33
|
+
if [ -f "$ROOT_DIR/package.json" ]; then
|
|
34
|
+
info "进入目录:$ROOT_DIR"
|
|
35
|
+
info "正在执行:pnpm install"
|
|
36
|
+
(cd "$ROOT_DIR" && pnpm install --registry=https://registry.npmmirror.com) || error "Node 依赖安装失败"
|
|
37
|
+
else
|
|
38
|
+
warn "未找到 $ROOT_DIR/package.json 文件,请检查路径是否正确"
|
|
39
|
+
fi
|
|
40
|
+
info "==================== 依赖安装完成!====================\n"
|
|
41
|
+
|
|
42
|
+
info "==================== dist打包 ===================="
|
|
43
|
+
info "开始执行:pnpm run server:build"
|
|
44
|
+
(cd "$ROOT_DIR" && pnpm run server:build) || error "dist打包失败"
|
|
45
|
+
info "==================== dist打包完成!====================\n"
|
|
46
|
+
|
|
47
|
+
info "下一步:执行 ./prod_run.sh 启动服务"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# 产物部署使用
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
ROOT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
HOST="${HOST:-0.0.0.0}"
|
|
9
|
+
PORT="${PORT:-5000}"
|
|
10
|
+
|
|
11
|
+
# ==================== 工具函数 ====================
|
|
12
|
+
info() {
|
|
13
|
+
echo "[INFO] $1"
|
|
14
|
+
}
|
|
15
|
+
warn() {
|
|
16
|
+
echo "[WARN] $1"
|
|
17
|
+
}
|
|
18
|
+
error() {
|
|
19
|
+
echo "[ERROR] $1"
|
|
20
|
+
exit 1
|
|
21
|
+
}
|
|
22
|
+
check_command() {
|
|
23
|
+
if ! command -v "$1" &> /dev/null; then
|
|
24
|
+
error "命令 $1 未找到,请先安装"
|
|
25
|
+
fi
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# ============== 启动服务 ======================
|
|
29
|
+
# 检查核心命令
|
|
30
|
+
check_command "pnpm"
|
|
31
|
+
check_command "npm"
|
|
32
|
+
|
|
33
|
+
info "开始执行:pnpm run server:prod"
|
|
34
|
+
(cd "$ROOT_DIR" && PORT="$PORT" pnpm run server:prod) || error "服务启动失败"
|
|
35
|
+
info "服务启动完成!\n"
|
|
@@ -3,6 +3,16 @@ module.exports = function (api) {
|
|
|
3
3
|
return {
|
|
4
4
|
presets: ["babel-preset-expo"],
|
|
5
5
|
plugins: [
|
|
6
|
+
[
|
|
7
|
+
"module-resolver",
|
|
8
|
+
{
|
|
9
|
+
root: ["./"],
|
|
10
|
+
alias: {
|
|
11
|
+
"@": "./client",
|
|
12
|
+
},
|
|
13
|
+
extensions: [".ios.js", ".android.js", ".js", ".ts", ".tsx", ".json"],
|
|
14
|
+
},
|
|
15
|
+
],
|
|
6
16
|
"react-native-reanimated/plugin",
|
|
7
17
|
],
|
|
8
18
|
};
|
|
@@ -6,25 +6,35 @@ const tintColorDark = "#0A84FF";
|
|
|
6
6
|
export const Colors = {
|
|
7
7
|
light: {
|
|
8
8
|
text: "#11181C",
|
|
9
|
+
textPrimary: "#11181C",
|
|
10
|
+
textSecondary: "#687076",
|
|
11
|
+
textMuted: "#9BA1A6",
|
|
9
12
|
buttonText: "#FFFFFF",
|
|
10
13
|
tabIconDefault: "#687076",
|
|
11
14
|
tabIconSelected: tintColorLight,
|
|
15
|
+
primary: tintColorLight,
|
|
12
16
|
link: "#007AFF",
|
|
13
17
|
backgroundRoot: "#FFFFFF", // Elevation 0
|
|
14
18
|
backgroundDefault: "#F2F2F2", // Elevation 1
|
|
15
19
|
backgroundSecondary: "#E6E6E6", // Elevation 2
|
|
16
20
|
backgroundTertiary: "#D9D9D9", // Elevation 3
|
|
21
|
+
border: "#E6E6E6",
|
|
17
22
|
},
|
|
18
23
|
dark: {
|
|
19
24
|
text: "#ECEDEE",
|
|
25
|
+
textPrimary: "#ECEDEE",
|
|
26
|
+
textSecondary: "#9BA1A6",
|
|
27
|
+
textMuted: "#6F767E",
|
|
20
28
|
buttonText: "#FFFFFF",
|
|
21
29
|
tabIconDefault: "#9BA1A6",
|
|
22
30
|
tabIconSelected: tintColorDark,
|
|
31
|
+
primary: tintColorDark,
|
|
23
32
|
link: "#0A84FF",
|
|
24
33
|
backgroundRoot: "#1F2123", // Elevation 0
|
|
25
34
|
backgroundDefault: "#2A2C2E", // Elevation 1
|
|
26
35
|
backgroundSecondary: "#353739", // Elevation 2
|
|
27
36
|
backgroundTertiary: "#404244", // Elevation 3
|
|
37
|
+
border: "#353739",
|
|
28
38
|
},
|
|
29
39
|
};
|
|
30
40
|
|
|
@@ -4,19 +4,15 @@
|
|
|
4
4
|
*
|
|
5
5
|
* 基于固定的 API 接口实现,可复用到其他项目
|
|
6
6
|
* 其他项目使用时,只需修改 @api 的导入路径指向项目的 api 模块
|
|
7
|
+
*
|
|
8
|
+
* 注意:
|
|
9
|
+
* - 如果需要登录/鉴权场景,请扩展本文件,完善 login/logout、token 管理、用户信息获取与刷新等逻辑
|
|
10
|
+
* - 将示例中的占位实现替换为项目实际的接口调用与状态管理
|
|
7
11
|
*/
|
|
8
|
-
import React, {
|
|
9
|
-
createContext,
|
|
10
|
-
useContext,
|
|
11
|
-
useState,
|
|
12
|
-
useEffect,
|
|
13
|
-
ReactNode,
|
|
14
|
-
} from "react";
|
|
15
|
-
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
16
|
-
//import { UserOut, UsersService, AuthenticationService } from "@api";
|
|
12
|
+
import React, { createContext, useContext, ReactNode } from "react";
|
|
17
13
|
|
|
18
14
|
interface UserOut {
|
|
19
|
-
|
|
15
|
+
|
|
20
16
|
}
|
|
21
17
|
|
|
22
18
|
interface AuthContextType {
|
|
@@ -31,105 +27,16 @@ interface AuthContextType {
|
|
|
31
27
|
|
|
32
28
|
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
|
33
29
|
|
|
34
|
-
export const AuthProvider: React.FC<{ children: ReactNode }> = ({
|
|
35
|
-
children,
|
|
36
|
-
}) => {
|
|
37
|
-
const [user, setUser] = useState<UserOut | null>(null);
|
|
38
|
-
const [token, setToken] = useState<string | null>(null);
|
|
39
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
loadAuthData();
|
|
43
|
-
}, []);
|
|
44
|
-
|
|
45
|
-
const loadAuthData = async () => {
|
|
46
|
-
try {
|
|
47
|
-
const results = await AsyncStorage.multiGet(["access_token", "user_data"]);
|
|
48
|
-
const storedToken = results?.[0]?.[1] ?? null;
|
|
49
|
-
const storedUser = results?.[1]?.[1] ?? null;
|
|
50
|
-
|
|
51
|
-
if (!storedToken) {
|
|
52
|
-
setToken(null);
|
|
53
|
-
setUser(null);
|
|
54
|
-
await AsyncStorage.multiRemove(["access_token", "user_data"]);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (storedToken && storedUser) {
|
|
59
|
-
setToken(storedToken);
|
|
60
|
-
setUser(JSON.parse(storedUser));
|
|
61
|
-
} else if (storedToken && !storedUser) {
|
|
62
|
-
// 若仅有 token,主动拉取当前用户信息
|
|
63
|
-
setToken(storedToken);
|
|
64
|
-
try {
|
|
65
|
-
// const me = await UsersService.getCurrentUserApiV1UsersMeGet();
|
|
66
|
-
//if (me?.success && me.data) {
|
|
67
|
-
// setUser(me.data);
|
|
68
|
-
// await AsyncStorage.setItem("user_data", JSON.stringify(me.data));
|
|
69
|
-
//}
|
|
70
|
-
} catch (e) {
|
|
71
|
-
// 拉取失败则保持未登录状态
|
|
72
|
-
console.error("Failed to fetch current user with stored token:", e);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
} catch (error) {
|
|
76
|
-
console.error("Failed to load auth data:", error);
|
|
77
|
-
} finally {
|
|
78
|
-
setIsLoading(false);
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const login = async (newToken: string) => {
|
|
83
|
-
try {
|
|
84
|
-
setToken(newToken);
|
|
85
|
-
// 统一写入 access_token,供 OpenAPI 读取并自动携带
|
|
86
|
-
await AsyncStorage.setItem("access_token", newToken);
|
|
87
|
-
// 登录后拉取当前用户并缓存
|
|
88
|
-
try {
|
|
89
|
-
const me = await UsersService.getCurrentUserApiV1UsersMeGet();
|
|
90
|
-
if (me?.success && me.data) {
|
|
91
|
-
setUser(me.data);
|
|
92
|
-
await AsyncStorage.setItem("user_data", JSON.stringify(me.data));
|
|
93
|
-
}
|
|
94
|
-
} catch (e) {
|
|
95
|
-
console.error("Fetch current user after login failed:", e);
|
|
96
|
-
}
|
|
97
|
-
} catch (error) {
|
|
98
|
-
console.error("Login failed:", error);
|
|
99
|
-
throw error;
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
const logout = async () => {
|
|
104
|
-
try {
|
|
105
|
-
// await AuthenticationService.logoutApiV1AuthLogoutPost(true);
|
|
106
|
-
} catch(error) {
|
|
107
|
-
console.warn('Logout failed:', error);
|
|
108
|
-
}
|
|
109
|
-
// remove token
|
|
110
|
-
setToken(null);
|
|
111
|
-
setUser(null);
|
|
112
|
-
await AsyncStorage.multiRemove(["auth_token", "access_token", "user_data"]);
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
const updateUser = (userData: Partial<UserOut>) => {
|
|
116
|
-
if (user) {
|
|
117
|
-
const updatedUser = { ...user, ...userData };
|
|
118
|
-
setUser(updatedUser);
|
|
119
|
-
AsyncStorage.setItem("user_data", JSON.stringify(updatedUser));
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
|
|
30
|
+
export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
|
123
31
|
const value: AuthContextType = {
|
|
124
|
-
user,
|
|
125
|
-
token,
|
|
126
|
-
isAuthenticated:
|
|
127
|
-
isLoading,
|
|
128
|
-
login,
|
|
129
|
-
logout,
|
|
130
|
-
updateUser,
|
|
32
|
+
user: null,
|
|
33
|
+
token: null,
|
|
34
|
+
isAuthenticated: false,
|
|
35
|
+
isLoading: false,
|
|
36
|
+
login: async (token: string) => {},
|
|
37
|
+
logout: async () => {},
|
|
38
|
+
updateUser: () => {},
|
|
131
39
|
};
|
|
132
|
-
|
|
133
40
|
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
|
134
41
|
};
|
|
135
42
|
|
|
@@ -4,7 +4,7 @@ import { useColorScheme } from "@/hooks/useColorScheme";
|
|
|
4
4
|
export function useTheme() {
|
|
5
5
|
const colorScheme = useColorScheme();
|
|
6
6
|
const isDark = colorScheme === "dark";
|
|
7
|
-
const theme = Colors[colorScheme ?? "light"];
|
|
7
|
+
const theme = Colors[(colorScheme as "light" | "dark") ?? "light"];
|
|
8
8
|
|
|
9
9
|
return {
|
|
10
10
|
theme,
|
|
@@ -3,10 +3,7 @@ import {
|
|
|
3
3
|
View,
|
|
4
4
|
Text,
|
|
5
5
|
ScrollView,
|
|
6
|
-
TouchableOpacity,
|
|
7
6
|
RefreshControl,
|
|
8
|
-
Image,
|
|
9
|
-
ActivityIndicator,
|
|
10
7
|
} from 'react-native';
|
|
11
8
|
import { useRouter, useFocusEffect } from 'expo-router';
|
|
12
9
|
import { FontAwesome6 } from '@expo/vector-icons';
|
|
@@ -15,7 +12,7 @@ import { useTheme } from "@/hooks/useTheme";
|
|
|
15
12
|
|
|
16
13
|
import { Screen } from '@/components/Screen';
|
|
17
14
|
|
|
18
|
-
import styles from './styles';
|
|
15
|
+
import { styles } from './styles';
|
|
19
16
|
|
|
20
17
|
|
|
21
18
|
export default function HomeScreen() {
|