From 068615988c7ce4487194988f8eb41699fb53dca6 Mon Sep 17 00:00:00 2001 From: Retsomm <112182ssss@gmail.com> Date: Mon, 13 Jul 2026 17:42:30 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=9A=B1=E7=A7=81?= =?UTF-8?q?=E6=AC=8A=E6=94=BF=E7=AD=96=E9=A0=81=E9=9D=A2=EF=BC=8C=E6=BA=96?= =?UTF-8?q?=E5=82=99=20Google=20Play=20=E4=B8=8A=E6=9E=B6=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E6=A2=9D=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit apps/web/privacy:說明訪客模式本機資料、登入後透過 Clerk 蒐集的資料範圍、 第三方服務商(Clerk/Supabase/Vercel)、刪除帳號效果、App 權限用途, 使用者已在本機確認內容無誤。 Co-Authored-By: Claude Sonnet 5 --- apps/web/src/app/privacy/page.tsx | 112 ++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 apps/web/src/app/privacy/page.tsx diff --git a/apps/web/src/app/privacy/page.tsx b/apps/web/src/app/privacy/page.tsx new file mode 100644 index 0000000..e67d53c --- /dev/null +++ b/apps/web/src/app/privacy/page.tsx @@ -0,0 +1,112 @@ +import type { Metadata } from 'next' + +export const metadata: Metadata = { + title: '隱私權政策 — EasyLearn', +} + +const CONTACT_EMAIL = '112182ssss@gmail.com' + +const PrivacyPage = () => { + return ( +
+

隱私權政策

+

最後更新:2026-07-13

+ +

+ EasyLearn(以下稱「本服務」,包含網頁版 PWA 與 Android/iOS App)由個人開發者製作與維運。 + 本頁說明使用本服務時,我們會蒐集哪些資料、如何使用,以及你可以如何管理或刪除這些資料。 +

+ +

一、訪客模式(未登入)

+

+ 未登入時,你的學習進度(答題紀錄、錯題本、收藏題目、經驗值、連續學習天數等)只會儲存在 + 你自己的裝置本機(瀏覽器 localStorage 或 App 的裝置端儲存),不會傳送到我們的伺服器, + 我們也無法看到這些資料。移除瀏覽器資料或解除安裝 App 會一併清除這些本機資料。 +

+ +

二、登入後蒐集的資料

+

本服務使用第三方服務 Clerk 提供帳號驗證(目前僅支援 Google 登入)。登入後:

+ + +

三、資料的使用方式

+

+ 上述資料僅用於提供「跨裝置同步學習進度」這項功能本身(例如讓你在網頁版與 App + 之間看到同一份進度)。本服務不會將你的資料用於廣告投放,也不會出售或提供給第三方 + 行銷使用。目前本服務未整合任何第三方數據分析或廣告 SDK。 +

+ +

四、資料由誰代管

+

本服務使用以下第三方服務商處理資料,請參考各自的隱私權政策了解其資料處理方式:

+ + +

五、刪除帳號與資料

+

+ 你可以隨時在「個人設定」頁面刪除帳號。刪除後,上述儲存在本服務資料庫中與該帳號關聯的 + 所有學習資料(進度、錯題本、收藏、統計)會一併永久刪除,且無法復原;Clerk + 代管的帳號本身也會一併刪除。 +

+ +

六、App 權限(Android/iOS)

+

+ App 版本僅會請求「存取你的相簿」權限,用途是讓你選擇一張既有的照片作為大頭貼。 + 本服務不會請求相機、麥克風、定位或聯絡人等其他權限。 +

+ +

七、聯絡方式

+

+ 若對本隱私權政策或你的個人資料有任何疑問,歡迎透過 {CONTACT_EMAIL}{' '} + 與我們聯絡。 +

+
+ ) +} + +const styles = { + h2: { fontSize: '1.1rem', marginTop: 32, marginBottom: 8 }, + ul: { paddingLeft: 20 }, +} as const + +export default PrivacyPage From 5e20b0c1429a008f2471e65c01632a7a3187c5e5 Mon Sep 17 00:00:00 2001 From: Retsomm <112182ssss@gmail.com> Date: Mon, 13 Jul 2026 17:44:42 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E6=BA=96=E5=82=99=20Google=20Play=20?= =?UTF-8?q?=E4=B8=8A=E6=9E=B6=EF=BC=9A=E8=A8=AD=E5=AE=9A=20EAS=20Build=20?= =?UTF-8?q?=E5=B0=88=E6=A1=88=E8=88=87=20Android=20=E7=89=88=E6=9C=AC/?= =?UTF-8?q?=E6=AC=8A=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 建立 EAS 專案並連結 app.json(extra.eas.projectId) - 新增 eas.json:development/preview/production build profile, preview/production 指向正式後端 https://easy-learn-inky.vercel.app - 加上 android.versionCode,production profile 開 autoIncrement - expo-image-picker 明確關閉 cameraPermission/microphonePermission, 避免預設帶入用不到的 RECORD_AUDIO/CAMERA 權限 - .gitignore 排除 google-play-service-account.json Co-Authored-By: Claude Sonnet 5 --- apps/mobile/.gitignore | 3 +++ apps/mobile/app.json | 16 +++++++++++++--- apps/mobile/eas.json | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 apps/mobile/eas.json diff --git a/apps/mobile/.gitignore b/apps/mobile/.gitignore index d914c32..f418dda 100644 --- a/apps/mobile/.gitignore +++ b/apps/mobile/.gitignore @@ -18,6 +18,9 @@ expo-env.d.ts *.key *.mobileprovision +# Google Play service account credentials +google-play-service-account.json + # Metro .metro-health-check* diff --git a/apps/mobile/app.json b/apps/mobile/app.json index bcb32f7..cd30036 100644 --- a/apps/mobile/app.json +++ b/apps/mobile/app.json @@ -19,7 +19,8 @@ "monochromeImage": "./assets/images/android-icon-monochrome.png" }, "predictiveBackGestureEnabled": false, - "package": "com.retsnom.easylearnmobile" + "package": "com.retsnom.easylearnmobile", + "versionCode": 1 }, "web": { "bundler": "metro", @@ -41,12 +42,21 @@ [ "expo-image-picker", { - "photosPermission": "需要存取你的相簿才能選擇頭像照片" + "photosPermission": "需要存取你的相簿才能選擇頭像照片", + "cameraPermission": false, + "microphonePermission": false } ] ], "experiments": { "typedRoutes": true - } + }, + "extra": { + "router": {}, + "eas": { + "projectId": "3b061355-ca44-44b3-b59c-08dcba64fb4a" + } + }, + "owner": "retsnom" } } diff --git a/apps/mobile/eas.json b/apps/mobile/eas.json new file mode 100644 index 0000000..08f4c7c --- /dev/null +++ b/apps/mobile/eas.json @@ -0,0 +1,37 @@ +{ + "cli": { + "version": ">= 20.3.0", + "appVersionSource": "remote" + }, + "build": { + "development": { + "developmentClient": true, + "distribution": "internal" + }, + "preview": { + "distribution": "internal", + "android": { + "buildType": "apk" + }, + "env": { + "EXPO_PUBLIC_API_BASE_URL": "https://easy-learn-inky.vercel.app", + "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY": "pk_test_Z29sZGVuLWxhYi00OC5jbGVyay5hY2NvdW50cy5kZXYk" + } + }, + "production": { + "autoIncrement": true, + "env": { + "EXPO_PUBLIC_API_BASE_URL": "https://easy-learn-inky.vercel.app", + "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY": "pk_test_Z29sZGVuLWxhYi00OC5jbGVyay5hY2NvdW50cy5kZXYk" + } + } + }, + "submit": { + "production": { + "android": { + "serviceAccountKeyPath": "./google-play-service-account.json", + "track": "internal" + } + } + } +} From 76d03effee566abb20514e919b8ea34de453a987 Mon Sep 17 00:00:00 2001 From: Retsomm <112182ssss@gmail.com> Date: Mon, 13 Jul 2026 17:54:37 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20monorepo=20yarn=20inst?= =?UTF-8?q?all=20=E8=A7=B8=E7=99=BC=20apps/web=20postinstall=20=E5=B0=8E?= =?UTF-8?q?=E8=87=B4=20EAS=20Build=20=E5=A4=B1=E6=95=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit apps/web 的 postinstall(prisma generate)會在任何一次 repo 根目錄的 yarn install 被觸發(Yarn 1 workspaces 對所有 workspace 一起跑 postinstall),即使只是要裝 apps/mobile 的依賴。prisma.config.ts 用 @prisma/config 的 env('DIRECT_URL') 讀不到就直接 throw,導致 EAS Build (只建置 mobile,本來就不會有 apps/web 的資料庫連線字串)在 INSTALL_DEPENDENCIES 階段整個失敗。 改成直接讀 process.env.DIRECT_URL,讀不到時退回一個不會真的連線的假字串 ——prisma generate 本身不需要真的連得上資料庫,只有 prisma migrate/正式 執行期的 @prisma/client 才需要真正有效的連線字串,那些情境本來就會有 apps/web 自己的 .env.local 或 Vercel 環境變數,不受這個 fallback 影響。 已用 env -i(清空所有環境變數+暫時移開 .env)模擬 EAS Build 的環境, 確認 prisma generate 不再因缺 DIRECT_URL 而失敗;apps/web 的 typecheck/build 在正常環境下仍全部通過。 Co-Authored-By: Claude Sonnet 5 --- apps/web/prisma.config.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/web/prisma.config.ts b/apps/web/prisma.config.ts index 2701aeb..13a2ddc 100644 --- a/apps/web/prisma.config.ts +++ b/apps/web/prisma.config.ts @@ -1,11 +1,19 @@ import 'dotenv/config' -import { defineConfig, env } from '@prisma/config' +import { defineConfig } from '@prisma/config' // `prisma migrate`/`prisma generate` 用這裡設定的連線字串。用 DIRECT_URL(非連線池) // 是因為 Supabase 的 pgbouncer 連線池(transaction mode)不支援跑 migration 需要的 DDL/prepared statement。 +// +// 這裡沒有讀到 DIRECT_URL 時退回一個不會真的連線的假字串,不用 @prisma/config 的 env() +// helper(讀不到就直接 throw)——因為 Yarn 1 workspaces 的 `postinstall` 是整個 monorepo +// 一起跑,只想裝 apps/mobile 依賴的 `yarn install`(例如 EAS Build)也會觸發這支 +// `prisma generate`,但那個環境本來就不會、也不需要有 apps/web 的資料庫連線字串。 +// `prisma generate` 本身不需要真的連得上資料庫,只有 `prisma migrate`/正式執行期的 +// `@prisma/client` 才需要真正有效的 DIRECT_URL/DATABASE_URL(由 apps/web 自己的 +// .env.local 或 Vercel 環境變數提供,這裡的 fallback 不影響那邊)。 export default defineConfig({ schema: 'prisma/schema.prisma', datasource: { - url: env('DIRECT_URL'), + url: process.env.DIRECT_URL ?? 'postgresql://placeholder:placeholder@localhost:5432/placeholder', }, }) From 07687267489c831c4474a2dfc0736339e84a611f Mon Sep 17 00:00:00 2001 From: Retsomm <112182ssss@gmail.com> Date: Mon, 13 Jul 2026 19:32:12 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E9=9A=B1=E7=A7=81?= =?UTF-8?q?=E6=AC=8A=E6=94=BF=E7=AD=96=E9=A0=81=E9=9D=A2=20code=20review?= =?UTF-8?q?=20=E6=84=8F=E8=A6=8B=EF=BC=9A=E8=81=AF=E7=B5=A1=E4=BF=A1?= =?UTF-8?q?=E7=AE=B1=E6=94=B9=E8=AE=80=E7=92=B0=E5=A2=83=E8=AE=8A=E6=95=B8?= =?UTF-8?q?=E3=80=81=E5=A4=96=E9=83=A8=E9=80=A3=E7=B5=90=E8=A3=9C=E4=B8=8A?= =?UTF-8?q?=20noopener?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5 --- apps/web/src/app/privacy/page.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/privacy/page.tsx b/apps/web/src/app/privacy/page.tsx index e67d53c..86b4a2a 100644 --- a/apps/web/src/app/privacy/page.tsx +++ b/apps/web/src/app/privacy/page.tsx @@ -4,7 +4,7 @@ export const metadata: Metadata = { title: '隱私權政策 — EasyLearn', } -const CONTACT_EMAIL = '112182ssss@gmail.com' +const CONTACT_EMAIL = process.env.NEXT_PUBLIC_CONTACT_EMAIL ?? '112182ssss@gmail.com' const PrivacyPage = () => { return ( @@ -63,19 +63,19 @@ const PrivacyPage = () => {

本服務使用以下第三方服務商處理資料,請參考各自的隱私權政策了解其資料處理方式: