feat: 添加数据库迁移脚本和 Supabase 工具配置

添加迁移脚本以支持数据库版本管理,配置 pnpm workspace 仅构建 supabase 依赖
新增用户登录字段和任务帮助请求功能,禁用 tasks 和 task_steps 表的 RLS
更新环境变量示例以支持自托管 Supabase 实例
This commit is contained in:
2026-01-29 02:41:10 +08:00
parent 2397ed46eb
commit 095f02700f
11 changed files with 362 additions and 4 deletions

View File

@@ -1,2 +1,8 @@
NEXT_PUBLIC_SUPABASE_URL=https://vmtosvsveuopmqruxrpl.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZtdG9zdnN2ZXVvcG1xcnV4cnBsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjkzMTUzOTQsImV4cCI6MjA4NDg5MTM5NH0.KsVxObf4xRnkEZegjUgiRyWgA9cwTuLSAR9gOebBryQ
# SUPABASE
# NEXT_PUBLIC_SUPABASE_URL=https://vmtosvsveuopmqruxrpl.supabase.co
# NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZtdG9zdnN2ZXVvcG1xcnV4cnBsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjkzMTUzOTQsImV4cCI6MjA4NDg5MTM5NH0.KsVxObf4xRnkEZegjUgiRyWgA9cwTuLSAR9gOebBryQ
# ALIYUN SUPABASE
NEXT_PUBLIC_SUPABASE_URL=http://8.161.175.98
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJvbGUiOiJhbm9uIiwiaWF0IjoxNzY5NjA3MTMwLCJleHAiOjEzMjgwMjQ3MTMwfQ.HYbEkBJhf8YeLuNTEtO44aBApUuEkvXmxvEWIA1gUTQ
SUPABASE_SERVICE_ROLE_KEY=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJvbGUiOiJzZXJ2aWNlX3JvbGUiLCJpYXQiOjE3Njk2MDcxMzAsImV4cCI6MTMyODAyNDcxMzB9.8nGI1fVpeCCDGZJ84Hmd1mHgPwpEKB8q6PDNp6Z3ZEM

View File

@@ -7,6 +7,7 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
"migrate": "node scripts/migrate.js",
"typecheck": "tsc --noEmit"
},
"dependencies": {
@@ -70,5 +71,9 @@
"typescript": "5.2.2",
"vaul": "^0.9.9",
"zod": "^3.23.8"
},
"devDependencies": {
"dotenv": "^17.2.3",
"supabase": "^2.72.9"
}
}

187
pnpm-lock.yaml generated
View File

@@ -188,6 +188,13 @@ importers:
zod:
specifier: ^3.23.8
version: 3.25.76
devDependencies:
dotenv:
specifier: ^17.2.3
version: 17.2.3
supabase:
specifier: ^2.72.9
version: 2.72.9
packages:
@@ -259,6 +266,10 @@ packages:
resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
deprecated: Use @eslint/object-schema instead
'@isaacs/fs-minipass@4.0.1':
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
engines: {node: '>=18.0.0'}
'@jridgewell/gen-mapping@0.3.13':
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
@@ -1249,6 +1260,10 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
agent-base@7.1.4:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
@@ -1350,6 +1365,10 @@ packages:
resolution: {integrity: sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA==}
hasBin: true
bin-links@6.0.0:
resolution: {integrity: sha512-X4CiKlcV2GjnCMwnKAfbVWpHa++65th9TuzAEYtZoATiOE2DQKhSp4CJlyLoTqdhBKlXjpXjCTYPNNFS33Fi6w==}
engines: {node: ^20.17.0 || >=22.9.0}
binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
@@ -1404,6 +1423,10 @@ packages:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
chownr@3.0.0:
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
engines: {node: '>=18'}
class-variance-authority@0.7.1:
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
@@ -1414,6 +1437,10 @@ packages:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
cmd-shim@8.0.0:
resolution: {integrity: sha512-Jk/BK6NCapZ58BKUxlSI+ouKRbjH1NLZCgJkYoab+vEHUY3f6OzpNBN9u7HFSv9J6TRDGs4PLOHezoKGaFRSCA==}
engines: {node: ^20.17.0 || >=22.9.0}
cmdk@1.1.1:
resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==}
peerDependencies:
@@ -1493,6 +1520,10 @@ packages:
damerau-levenshtein@1.0.8:
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
data-uri-to-buffer@4.0.1:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
data-view-buffer@1.0.2:
resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
engines: {node: '>= 0.4'}
@@ -1563,6 +1594,10 @@ packages:
dom-helpers@5.2.1:
resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
dotenv@17.2.3:
resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==}
engines: {node: '>=12'}
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
@@ -1766,6 +1801,10 @@ packages:
picomatch:
optional: true
fetch-blob@3.2.0:
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
engines: {node: ^12.20 || >= 14.13}
file-entry-cache@6.0.1:
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
engines: {node: ^10.12.0 || >=12.0.0}
@@ -1789,6 +1828,10 @@ packages:
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
engines: {node: '>= 0.4'}
formdata-polyfill@4.0.10:
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
engines: {node: '>=12.20.0'}
fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
@@ -1901,6 +1944,10 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
https-proxy-agent@7.0.6:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
iceberg-js@0.8.1:
resolution: {integrity: sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==}
engines: {node: '>=20.0.0'}
@@ -2153,6 +2200,14 @@ packages:
minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
minipass@7.1.2:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
minizlib@3.1.0:
resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==}
engines: {node: '>= 18'}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -2193,6 +2248,15 @@ packages:
sass:
optional: true
node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
deprecated: Use your platform's native DOMException instead
node-fetch@3.3.2:
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
node-releases@2.0.27:
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
@@ -2204,6 +2268,10 @@ packages:
resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
engines: {node: '>=0.10.0'}
npm-normalize-package-bin@5.0.0:
resolution: {integrity: sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==}
engines: {node: ^20.17.0 || >=22.9.0}
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -2354,6 +2422,10 @@ packages:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
proc-log@6.1.0:
resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==}
engines: {node: ^20.17.0 || >=22.9.0}
prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
@@ -2442,6 +2514,10 @@ packages:
read-cache@1.0.0:
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
read-cmd-shim@6.0.0:
resolution: {integrity: sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A==}
engines: {node: ^20.17.0 || >=22.9.0}
readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
@@ -2552,6 +2628,10 @@ packages:
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
engines: {node: '>= 0.4'}
signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
slash@3.0.0:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}
@@ -2630,6 +2710,11 @@ packages:
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
supabase@2.72.9:
resolution: {integrity: sha512-kifjjxhjs0K1c10YiXh3bZ2lRp9s+MnloT1sTUAoV7mIkkfYn6XCq59hEYoNxfaZyE4j8nuQKszHz5XCMmFt9w==}
engines: {npm: '>=8'}
hasBin: true
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
@@ -2651,6 +2736,10 @@ packages:
engines: {node: '>=14.0.0'}
hasBin: true
tar@7.5.7:
resolution: {integrity: sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==}
engines: {node: '>=18'}
text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
@@ -2773,6 +2862,10 @@ packages:
resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==}
engines: {node: '>=10.13.0'}
web-streams-polyfill@3.3.3:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'}
which-boxed-primitive@1.1.1:
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
engines: {node: '>= 0.4'}
@@ -2801,6 +2894,10 @@ packages:
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
write-file-atomic@7.0.0:
resolution: {integrity: sha512-YnlPC6JqnZl6aO4uRc+dx5PHguiR9S6WeoLtpxNT9wIG+BDya7ZNE1q7KOjVgaA73hKhKLpVPgJ5QA9THQ5BRg==}
engines: {node: ^20.17.0 || >=22.9.0}
ws@8.19.0:
resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==}
engines: {node: '>=10.0.0'}
@@ -2813,6 +2910,10 @@ packages:
utf-8-validate:
optional: true
yallist@5.0.0:
resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
engines: {node: '>=18'}
yaml@2.8.2:
resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==}
engines: {node: '>= 14.6'}
@@ -2906,6 +3007,10 @@ snapshots:
'@humanwhocodes/object-schema@2.0.3': {}
'@isaacs/fs-minipass@4.0.1':
dependencies:
minipass: 7.1.2
'@jridgewell/gen-mapping@0.3.13':
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -3877,6 +3982,8 @@ snapshots:
acorn@8.15.0: {}
agent-base@7.1.4: {}
ajv@6.12.6:
dependencies:
fast-deep-equal: 3.1.3
@@ -4002,6 +4109,14 @@ snapshots:
baseline-browser-mapping@2.9.18: {}
bin-links@6.0.0:
dependencies:
cmd-shim: 8.0.0
npm-normalize-package-bin: 5.0.0
proc-log: 6.1.0
read-cmd-shim: 6.0.0
write-file-atomic: 7.0.0
binary-extensions@2.3.0: {}
brace-expansion@1.1.12:
@@ -4069,6 +4184,8 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
chownr@3.0.0: {}
class-variance-authority@0.7.1:
dependencies:
clsx: 2.1.1
@@ -4077,6 +4194,8 @@ snapshots:
clsx@2.1.1: {}
cmd-shim@8.0.0: {}
cmdk@1.1.1(@types/react-dom@18.2.7)(@types/react@18.2.22)(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@18.2.22)(react@18.2.0)
@@ -4149,6 +4268,8 @@ snapshots:
damerau-levenshtein@1.0.8: {}
data-uri-to-buffer@4.0.1: {}
data-view-buffer@1.0.2:
dependencies:
call-bound: 1.0.4
@@ -4216,6 +4337,8 @@ snapshots:
'@babel/runtime': 7.28.6
csstype: 3.2.3
dotenv@17.2.3: {}
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -4563,6 +4686,11 @@ snapshots:
optionalDependencies:
picomatch: 4.0.3
fetch-blob@3.2.0:
dependencies:
node-domexception: 1.0.0
web-streams-polyfill: 3.3.3
file-entry-cache@6.0.1:
dependencies:
flat-cache: 3.2.0
@@ -4588,6 +4716,10 @@ snapshots:
dependencies:
is-callable: 1.2.7
formdata-polyfill@4.0.10:
dependencies:
fetch-blob: 3.2.0
fraction.js@4.3.7: {}
fs.realpath@1.0.0: {}
@@ -4714,6 +4846,13 @@ snapshots:
dependencies:
function-bind: 1.1.2
https-proxy-agent@7.0.6:
dependencies:
agent-base: 7.1.4
debug: 4.4.3
transitivePeerDependencies:
- supports-color
iceberg-js@0.8.1: {}
ignore@5.3.2: {}
@@ -4957,6 +5096,12 @@ snapshots:
minimist@1.2.8: {}
minipass@7.1.2: {}
minizlib@3.1.0:
dependencies:
minipass: 7.1.2
ms@2.1.3: {}
mz@2.7.0:
@@ -5002,12 +5147,22 @@ snapshots:
- '@babel/core'
- babel-plugin-macros
node-domexception@1.0.0: {}
node-fetch@3.3.2:
dependencies:
data-uri-to-buffer: 4.0.1
fetch-blob: 3.2.0
formdata-polyfill: 4.0.10
node-releases@2.0.27: {}
normalize-path@3.0.0: {}
normalize-range@0.1.2: {}
npm-normalize-package-bin@5.0.0: {}
object-assign@4.1.1: {}
object-hash@3.0.0: {}
@@ -5150,6 +5305,8 @@ snapshots:
prelude-ls@1.2.1: {}
proc-log@6.1.0: {}
prop-types@15.8.1:
dependencies:
loose-envify: 1.4.0
@@ -5236,6 +5393,8 @@ snapshots:
dependencies:
pify: 2.3.0
read-cmd-shim@6.0.0: {}
readdirp@3.6.0:
dependencies:
picomatch: 2.3.1
@@ -5386,6 +5545,8 @@ snapshots:
side-channel-map: 1.0.1
side-channel-weakmap: 1.0.2
signal-exit@4.1.0: {}
slash@3.0.0: {}
sonner@1.7.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
@@ -5477,6 +5638,15 @@ snapshots:
tinyglobby: 0.2.15
ts-interface-checker: 0.1.13
supabase@2.72.9:
dependencies:
bin-links: 6.0.0
https-proxy-agent: 7.0.6
node-fetch: 3.3.2
tar: 7.5.7
transitivePeerDependencies:
- supports-color
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
@@ -5516,6 +5686,14 @@ snapshots:
transitivePeerDependencies:
- ts-node
tar@7.5.7:
dependencies:
'@isaacs/fs-minipass': 4.0.1
chownr: 3.0.0
minipass: 7.1.2
minizlib: 3.1.0
yallist: 5.0.0
text-table@0.2.0: {}
thenify-all@1.6.0:
@@ -5686,6 +5864,8 @@ snapshots:
glob-to-regexp: 0.4.1
graceful-fs: 4.2.11
web-streams-polyfill@3.3.3: {}
which-boxed-primitive@1.1.1:
dependencies:
is-bigint: 1.1.0
@@ -5735,8 +5915,15 @@ snapshots:
wrappy@1.0.2: {}
write-file-atomic@7.0.0:
dependencies:
imurmurhash: 0.1.4
signal-exit: 4.1.0
ws@8.19.0: {}
yallist@5.0.0: {}
yaml@2.8.2: {}
yocto-queue@0.1.0: {}

2
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,2 @@
onlyBuiltDependencies:
- supabase

148
scripts/migrate.js Normal file
View File

@@ -0,0 +1,148 @@
const { createClient } = require('@supabase/supabase-js');
const fs = require('fs');
const path = require('path');
const dotenv = require('dotenv');
// 加载环境变量
dotenv.config({ path: '.env.local' });
dotenv.config();
const MIGRATIONS_DIR = path.join(process.cwd(), 'supabase', 'migrations');
async function migrate() {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
if (!supabaseUrl || !supabaseServiceKey) {
console.error('❌ 错误: 缺少必要的环境变量。');
console.error('请确保 .env.local 中包含以下变量:');
console.error(' - NEXT_PUBLIC_SUPABASE_URL');
console.error(' - SUPABASE_SERVICE_ROLE_KEY (需要从 Supabase Dashboard 获取)');
process.exit(1);
}
// 使用 Service Role Key 初始化 Supabase 客户端,以获得执行 DDL 的权限
const supabase = createClient(supabaseUrl, supabaseServiceKey, {
auth: {
autoRefreshToken: false,
persistSession: false,
},
});
// 封装 SQL 执行函数,通过 RPC 调用
const execSql = async (sql, params = []) => {
// 注意:这需要数据库中预先存在一个名为 'exec_sql' 的 RPC 函数
// create or replace function exec_sql(sql text) returns void as $$
// begin
// execute sql;
// end;
// $$ language plpgsql security definer;
// 由于 exec_sql RPC 仅接受 sql 文本,我们这里简单地替换参数(如果只是简单的值替换)
// 或者如果 RPC 支持参数化查询则更好。目前的简单实现假设 SQL 已经完整。
// 如果需要参数支持,建议直接拼接(仅限迁移脚本可控环境)或改进 RPC。
// 这里的实现假设传入的 SQL 是完整的脚本。
const { error } = await supabase.rpc('exec_sql', { sql });
if (error) {
throw new Error(error.message);
}
};
// 检查 RPC 是否可用
try {
await execSql('SELECT 1');
console.log('✅ 已连接到 Supabase (RPC exec_sql 可用)');
} catch (err) {
console.error('❌ 连接失败或 exec_sql 函数不存在。');
console.error('请确保您已在 Supabase 数据库中创建了 exec_sql 函数。');
console.error('请查看 supabase/init_rpc.sql 获取创建脚本。');
console.error('错误信息:', err.message);
process.exit(1);
}
try {
// 确保 uuid-ossp 扩展已启用 (虽然我们现在主要使用 gen_random_uuid,但保留此行以兼容旧代码)
await execSql('CREATE EXTENSION IF NOT EXISTS "uuid-ossp";');
// 创建迁移记录表
await execSql(`
CREATE TABLE IF NOT EXISTS _migrations (
id SERIAL PRIMARY KEY,
name TEXT UNIQUE NOT NULL,
applied_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 允许 Service Role 访问此表(虽然 Service Role 默认有权限,但明确授权更稳妥)
-- 或者简单地禁用此表的 RLS,因为它只在后端使用
ALTER TABLE _migrations ENABLE ROW LEVEL SECURITY;
-- 创建策略,允许所有操作(因为我们只通过 Service Role 访问)
-- 注意:如果策略已存在会报错,所以先尝试删除
DROP POLICY IF EXISTS "Allow full access to service role" ON _migrations;
CREATE POLICY "Allow full access to service role" ON _migrations
USING (true)
WITH CHECK (true);
`);
// 获取已执行的迁移
// 由于 exec_sql 不返回值,我们需要通过标准 Supabase 查询来获取记录
const { data: appliedMigrations, error: fetchError } = await supabase
.from('_migrations')
.select('name');
if (fetchError) {
throw new Error(`无法获取迁移记录: ${fetchError.message}`);
}
const appliedNames = new Set(appliedMigrations.map((row) => row.name));
// 读取迁移文件
const files = fs.readdirSync(MIGRATIONS_DIR)
.filter((file) => file.endsWith('.sql'))
.sort(); // 按文件名排序
let count = 0;
for (const file of files) {
if (appliedNames.has(file)) {
continue;
}
console.log(`🚀 正在执行迁移: ${file}...`);
const filePath = path.join(MIGRATIONS_DIR, file);
const sql = fs.readFileSync(filePath, 'utf8');
try {
// 执行迁移 SQL
await execSql(sql);
// 记录迁移状态
const { error: insertError } = await supabase
.from('_migrations')
.insert({ name: file });
if (insertError) {
throw new Error(`无法记录迁移状态: ${insertError.message}`);
}
console.log(`✅ 成功: ${file}`);
count++;
} catch (err) {
console.error(`❌ 失败: ${file}`);
console.error(err);
process.exit(1);
}
}
if (count === 0) {
console.log('✨ 没有需要执行的迁移');
} else {
console.log(`🎉 成功执行了 ${count} 个迁移`);
}
} catch (err) {
console.error('❌ 迁移过程中发生错误:', err);
process.exit(1);
}
}
migrate();

View File

@@ -0,0 +1 @@
v2.72.7

9
supabase/init_rpc.sql Normal file
View File

@@ -0,0 +1,9 @@
-- 这是一个初始化脚本,用于在 Supabase 中启用 SQL 执行功能。
-- 请复制以下内容并在 Supabase Dashboard 的 SQL 编辑器中运行一次。
-- 运行后,scripts/migrate.js 脚本才能通过 RPC 调用执行迁移。
create or replace function exec_sql(sql text) returns void as $$
begin
execute sql;
end;
$$ language plpgsql security definer;

View File

@@ -1,7 +1,7 @@
-- Create tasks table
CREATE TABLE IF NOT EXISTS tasks (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
kr_id UUID NOT NULL REFERENCES krs(id) ON DELETE CASCADE,
title TEXT NOT NULL,
description TEXT, -- 思路
@@ -18,7 +18,7 @@ CREATE TABLE IF NOT EXISTS tasks (
-- Create task_steps table
CREATE TABLE IF NOT EXISTS task_steps (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
content TEXT NOT NULL,
note TEXT,