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" + } + } + } +} 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', }, }) diff --git a/apps/web/src/app/privacy/page.tsx b/apps/web/src/app/privacy/page.tsx new file mode 100644 index 0000000..86b4a2a --- /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 = process.env.NEXT_PUBLIC_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