chore: Update deploy script

This commit is contained in:
sunny
2025-03-04 16:38:15 +08:00
parent 9b7ed0b031
commit 5173cbf9d3
12 changed files with 805 additions and 45 deletions

View File

@@ -1,3 +1,11 @@
AUTH_GITHUB_ID = ""
AUTH_GITHUB_SECRET = ""
AUTH_SECRET = ""
CLOUDFLARE_API_TOKEN = ""
CLOUDFLARE_ACCOUNT_ID = ""
DATABASE_NAME = ""
DATABASE_ID = ""
KV_NAMESPACE_ID = ""
PROJECT_URL = ""

4
.gitignore vendored
View File

@@ -47,3 +47,7 @@ wrangler.cleanup.toml
public/workbox-*.js
public/sw.js
wrangler.json
wrangler.cleanup.json
wrangler.email.json

View File

@@ -12,10 +12,10 @@
"db:migrate-remote": "bun run scripts/migrate.ts remote",
"webhook-test-server": "bun run scripts/webhook-test-server.ts",
"generate-test-data": "wrangler dev scripts/generate-test-data.ts",
"dev:cleanup": "wrangler dev --config wrangler.cleanup.toml --test-scheduled",
"dev:cleanup": "wrangler dev --config wrangler.cleanup.json --test-scheduled",
"test:cleanup": "curl http://localhost:8787/__scheduled",
"deploy:email": "wrangler deploy --config wrangler.email.toml",
"deploy:cleanup": "wrangler deploy --config wrangler.cleanup.toml",
"deploy:email": "wrangler deploy --config wrangler.email.json",
"deploy:cleanup": "wrangler deploy --config wrangler.cleanup.json",
"deploy:pages": "npm run build:pages && wrangler pages deploy .vercel/output/static --branch main"
},
"type": "module",
@@ -60,6 +60,8 @@
"@types/react": "^18",
"@types/react-dom": "^18",
"bun": "^1.1.39",
"cloudflare": "^4.1.0",
"dotenv": "^16.4.7",
"drizzle-kit": "^0.28.1",
"eslint": "^8",
"eslint-config-next": "15.0.3",

199
pnpm-lock.yaml generated
View File

@@ -123,6 +123,12 @@ importers:
bun:
specifier: ^1.1.39
version: 1.1.39
cloudflare:
specifier: ^4.1.0
version: 4.1.0
dotenv:
specifier: ^16.4.7
version: 16.4.7
drizzle-kit:
specifier: ^0.28.1
version: 0.28.1
@@ -2226,12 +2232,18 @@ packages:
'@types/next-pwa@5.6.9':
resolution: {integrity: sha512-KcymH+MtFYB5KVKIOH1DMqd0wUb8VLCxzHtsaRQQ7S8sGOaTH24Lo2vGZf6/0Ok9e+xWCKhqsSt6cgDJTk91Iw==}
'@types/node-fetch@2.6.12':
resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==}
'@types/node-forge@1.3.11':
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
'@types/node@16.18.11':
resolution: {integrity: sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==}
'@types/node@18.19.78':
resolution: {integrity: sha512-m1ilZCTwKLkk9rruBJXFeYN0Bc5SbjirwYX/Td3MqPfioYbgun3IvK/m8dQxMCnrPGZPg1kvXjp3SIekCN/ynw==}
'@types/node@20.12.14':
resolution: {integrity: sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==}
@@ -2429,6 +2441,10 @@ packages:
abbrev@1.1.1:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
acorn-import-attributes@1.9.5:
resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==}
peerDependencies:
@@ -2452,6 +2468,10 @@ packages:
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
engines: {node: '>= 6.0.0'}
agentkeepalive@4.6.0:
resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==}
engines: {node: '>= 8.0.0'}
ajv-keywords@3.5.2:
resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
peerDependencies:
@@ -2587,6 +2607,9 @@ packages:
async@3.2.6:
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
at-least-node@1.0.0:
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
engines: {node: '>= 4.0.0'}
@@ -2686,6 +2709,10 @@ packages:
resolution: {integrity: sha512-CCKAP2tkPau7D3GE8+V8R6sQubA9R5foIzGp+85EXCVSCivuxBNAWqcpn72PKYiIcqoViv/kcUDpaEIMBVi1lQ==}
engines: {node: '>= 0.4'}
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
call-bind@1.0.8:
resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
engines: {node: '>= 0.4'}
@@ -2750,6 +2777,9 @@ packages:
client-only@0.0.1:
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
cloudflare@4.1.0:
resolution: {integrity: sha512-TySwSEGGQhuVHFVjKRUHkzZum0MSGJkgfVjep+KBJxuxScEvjoTckQFbxlYThPp5kLm8IUi4C7oJeVr5e9etVw==}
clsx@2.1.1:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
@@ -2775,6 +2805,10 @@ packages:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
engines: {node: '>=12.5.0'}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
commander@11.1.0:
resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==}
engines: {node: '>=16'}
@@ -2913,6 +2947,10 @@ packages:
resolution: {integrity: sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==}
engines: {node: '>=6'}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
delegates@1.0.0:
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
@@ -2949,6 +2987,10 @@ packages:
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
engines: {node: '>=6.0.0'}
dotenv@16.4.7:
resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==}
engines: {node: '>=12'}
drizzle-kit@0.28.1:
resolution: {integrity: sha512-JimOV+ystXTWMgZkLHYHf2w3oS28hxiH1FR0dkmJLc7GHzdGJoJAQtQS5DRppnabsRZwE2U1F6CuezVBgmsBBQ==}
hasBin: true
@@ -3049,6 +3091,10 @@ packages:
resolution: {integrity: sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==}
engines: {node: '>= 0.4'}
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
@@ -3111,10 +3157,18 @@ packages:
resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
engines: {node: '>= 0.4'}
es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
es-set-tostringtag@2.0.3:
resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==}
engines: {node: '>= 0.4'}
es-set-tostringtag@2.1.0:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
es-shim-unscopables@1.0.2:
resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
@@ -3532,6 +3586,10 @@ packages:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'}
event-target-shim@5.0.1:
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
engines: {node: '>=6'}
events-intercept@2.0.0:
resolution: {integrity: sha512-blk1va0zol9QOrdZt0rFXo5KMkNPVSp92Eju/Qz8THwKWKRKeE0T8Br/1aW6+Edkyq9xHYgYxn2QtOnUKPUp+Q==}
@@ -3613,6 +3671,17 @@ packages:
resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
engines: {node: '>=14'}
form-data-encoder@1.7.2:
resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==}
form-data@4.0.2:
resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==}
engines: {node: '>= 6'}
formdata-node@4.4.1:
resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==}
engines: {node: '>= 12.20'}
fs-extra@11.1.0:
resolution: {integrity: sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==}
engines: {node: '>=14.14'}
@@ -3666,6 +3735,10 @@ packages:
resolution: {integrity: sha512-Y4+pKa7XeRUPWFNvOOYHkRYrfzW07oraURSvjDmRVOJ748OrVmeXtpE4+GCEHncjCjkTxPNRt8kEbxDhsn6VTg==}
engines: {node: '>= 0.4'}
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
get-nonce@1.0.1:
resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
engines: {node: '>=6'}
@@ -3673,6 +3746,10 @@ packages:
get-own-enumerable-property-symbols@3.0.2:
resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==}
get-proto@1.0.1:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
get-source@2.0.12:
resolution: {integrity: sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==}
@@ -3780,6 +3857,9 @@ packages:
resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==}
engines: {node: '>=8.12.0'}
humanize-ms@1.2.1:
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
iconv-lite@0.4.24:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'}
@@ -4142,6 +4222,10 @@ packages:
make-error@1.3.6:
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
merge-stream@2.0.0:
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
@@ -4322,6 +4406,10 @@ packages:
sass:
optional: true
node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
node-fetch@2.6.7:
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
engines: {node: 4.x || >=6.0.0}
@@ -5394,6 +5482,10 @@ packages:
resolution: {integrity: sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==}
engines: {node: '>=10.13.0'}
web-streams-polyfill@4.0.0-beta.3:
resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
engines: {node: '>= 14'}
web-vitals@0.2.4:
resolution: {integrity: sha512-6BjspCO9VriYy12z356nL6JBS0GYeEcA457YyRzD+dD6XYCQ75NKhcOHUMHentOE7OcVCIXXDvOm0jKFfQG2Gg==}
@@ -7566,12 +7658,21 @@ snapshots:
- sass
- supports-color
'@types/node-fetch@2.6.12':
dependencies:
'@types/node': 20.17.9
form-data: 4.0.2
'@types/node-forge@1.3.11':
dependencies:
'@types/node': 20.17.9
'@types/node@16.18.11': {}
'@types/node@18.19.78':
dependencies:
undici-types: 5.26.5
'@types/node@20.12.14':
dependencies:
undici-types: 5.26.5
@@ -7914,6 +8015,10 @@ snapshots:
abbrev@1.1.1: {}
abort-controller@3.0.0:
dependencies:
event-target-shim: 5.0.1
acorn-import-attributes@1.9.5(acorn@8.14.0):
dependencies:
acorn: 8.14.0
@@ -7934,6 +8039,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
agentkeepalive@4.6.0:
dependencies:
humanize-ms: 1.2.1
ajv-keywords@3.5.2(ajv@6.12.6):
dependencies:
ajv: 6.12.6
@@ -8090,6 +8199,8 @@ snapshots:
async@3.2.6: {}
asynckit@0.4.0: {}
at-least-node@1.0.0: {}
available-typed-arrays@1.0.7:
@@ -8201,6 +8312,11 @@ snapshots:
es-errors: 1.3.0
function-bind: 1.1.2
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
call-bind@1.0.8:
dependencies:
call-bind-apply-helpers: 1.0.0
@@ -8267,6 +8383,18 @@ snapshots:
client-only@0.0.1: {}
cloudflare@4.1.0:
dependencies:
'@types/node': 18.19.78
'@types/node-fetch': 2.6.12
abort-controller: 3.0.0
agentkeepalive: 4.6.0
form-data-encoder: 1.7.2
formdata-node: 4.4.1
node-fetch: 2.7.0
transitivePeerDependencies:
- encoding
clsx@2.1.1: {}
code-block-writer@10.1.1: {}
@@ -8291,6 +8419,10 @@ snapshots:
color-string: 1.9.1
optional: true
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
commander@11.1.0: {}
commander@2.20.3: {}
@@ -8365,7 +8497,7 @@ snapshots:
debug@4.1.1:
dependencies:
ms: 2.1.1
ms: 2.1.3
debug@4.4.0:
dependencies:
@@ -8399,6 +8531,8 @@ snapshots:
pify: 4.0.1
rimraf: 2.7.1
delayed-stream@1.0.0: {}
delegates@1.0.0: {}
depd@1.1.2: {}
@@ -8425,6 +8559,8 @@ snapshots:
dependencies:
esutils: 2.0.3
dotenv@16.4.7: {}
drizzle-kit@0.28.1:
dependencies:
'@drizzle-team/brocli': 0.10.2
@@ -8447,6 +8583,12 @@ snapshots:
es-errors: 1.3.0
gopd: 1.2.0
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
es-errors: 1.3.0
gopd: 1.2.0
eastasianwidth@0.2.0: {}
edge-runtime@2.5.9:
@@ -8565,12 +8707,23 @@ snapshots:
dependencies:
es-errors: 1.3.0
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
es-set-tostringtag@2.0.3:
dependencies:
get-intrinsic: 1.2.5
has-tostringtag: 1.0.2
hasown: 2.0.2
es-set-tostringtag@2.1.0:
dependencies:
es-errors: 1.3.0
get-intrinsic: 1.3.0
has-tostringtag: 1.0.2
hasown: 2.0.2
es-shim-unscopables@1.0.2:
dependencies:
hasown: 2.0.2
@@ -9050,6 +9203,8 @@ snapshots:
etag@1.8.1: {}
event-target-shim@5.0.1: {}
events-intercept@2.0.0: {}
events@3.3.0: {}
@@ -9148,6 +9303,20 @@ snapshots:
cross-spawn: 7.0.6
signal-exit: 4.1.0
form-data-encoder@1.7.2: {}
form-data@4.0.2:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
es-set-tostringtag: 2.1.0
mime-types: 2.1.35
formdata-node@4.4.1:
dependencies:
node-domexception: 1.0.0
web-streams-polyfill: 4.0.0-beta.3
fs-extra@11.1.0:
dependencies:
graceful-fs: 4.2.11
@@ -9218,10 +9387,28 @@ snapshots:
has-symbols: 1.1.0
hasown: 2.0.2
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
function-bind: 1.1.2
get-proto: 1.0.1
gopd: 1.2.0
has-symbols: 1.1.0
hasown: 2.0.2
math-intrinsics: 1.1.0
get-nonce@1.0.1: {}
get-own-enumerable-property-symbols@3.0.2: {}
get-proto@1.0.1:
dependencies:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
get-source@2.0.12:
dependencies:
data-uri-to-buffer: 2.0.2
@@ -9349,6 +9536,10 @@ snapshots:
human-signals@1.1.1: {}
humanize-ms@1.2.1:
dependencies:
ms: 2.1.3
iconv-lite@0.4.24:
dependencies:
safer-buffer: 2.1.2
@@ -9684,6 +9875,8 @@ snapshots:
make-error@1.3.6: {}
math-intrinsics@1.1.0: {}
merge-stream@2.0.0: {}
merge2@1.4.1: {}
@@ -9872,6 +10065,8 @@ snapshots:
- '@babel/core'
- babel-plugin-macros
node-domexception@1.0.0: {}
node-fetch@2.6.7:
dependencies:
whatwg-url: 5.0.0
@@ -10982,6 +11177,8 @@ snapshots:
glob-to-regexp: 0.4.1
graceful-fs: 4.2.11
web-streams-polyfill@4.0.0-beta.3: {}
web-vitals@0.2.4: {}
webidl-conversions@3.0.1: {}

View File

@@ -0,0 +1,112 @@
import Cloudflare from "cloudflare";
import "dotenv/config";
const CF_ACCOUNT_ID = process.env.CLOUDFLARE_ACCOUNT_ID!;
const CF_API_TOKEN = process.env.CLOUDFLARE_API_TOKEN;
const PROJECT_URL = process.env.PROJECT_URL;
const PROJECT_NAME = process.env.PROJECT_NAME || "moemail";
const DB_NAME = process.env.DATABASE_NAME || "moemail-db";
const KV_NAMESPACE_NAME = process.env.KV_NAME || "moemail-kv";
const client = new Cloudflare({
apiKey: CF_API_TOKEN,
});
export const getPages = async () => {
try {
const projectInfo = await client.pages.projects.get(PROJECT_NAME, {
account_id: CF_ACCOUNT_ID,
});
return projectInfo;
} catch (error) {
throw error;
}
};
export const createPages = async () => {
try {
console.log(`🆕 Creating new Cloudflare Pages project: "${PROJECT_NAME}"`);
const project = await client.pages.projects.create({
account_id: CF_ACCOUNT_ID,
name: PROJECT_NAME,
production_branch: "main",
});
if (PROJECT_URL) {
console.log("🔗 Setting pages domain...");
await client.pages.projects.domains.create(PROJECT_NAME, {
account_id: CF_ACCOUNT_ID,
name: PROJECT_URL?.split("://")[1],
});
console.log("✅ Pages domain set successfully");
}
console.log("✅ Project created successfully");
return project;
} catch (error) {
throw error;
}
};
export const getDatabase = async () => {
try {
const database = await client.d1.database.get(DB_NAME, {
account_id: CF_ACCOUNT_ID,
});
return database;
} catch (error) {
throw error;
}
};
export const createDatabase = async () => {
try {
console.log(`🆕 Creating new D1 database: "${DB_NAME}"`);
const database = await client.d1.database.create({
account_id: CF_ACCOUNT_ID,
name: DB_NAME,
});
console.log("✅ Database created successfully");
return database;
} catch (error) {
throw error;
}
};
export const getKVNamespace = async (namespaceId: string) => {
if (!namespaceId) {
throw new Error("KV namespace ID is required");
}
try {
const kvNamespace = await client.kv.namespaces.get(namespaceId, {
account_id: CF_ACCOUNT_ID,
});
return kvNamespace;
} catch (error) {
throw error;
}
};
export const createKVNamespace = async () => {
try {
console.log(`🆕 Creating new KV namespace: "${KV_NAMESPACE_NAME}"`);
const kvNamespace = await client.kv.namespaces.create({
account_id: CF_ACCOUNT_ID,
title: KV_NAMESPACE_NAME,
});
console.log("✅ KV namespace created successfully");
return kvNamespace;
} catch (error) {
throw error;
}
};

422
scripts/deploy/index.ts Normal file
View File

@@ -0,0 +1,422 @@
import { NotFoundError } from "cloudflare";
import "dotenv/config";
import { execSync } from "node:child_process";
import { readFileSync, writeFileSync, existsSync } from "node:fs";
import { resolve } from "node:path";
import {
createDatabase,
createKVNamespace,
createPages,
getDatabase,
getKVNamespace,
getPages,
} from "./cloudflare";
const PROJECT_NAME = process.env.PROJECT_NAME || "moemail";
const DATABASE_NAME = process.env.DATABASE_NAME || "moemail-db";
const KV_NAMESPACE_NAME = process.env.KV_NAME || "moemail-kv";
const PROJECT_URL = process.env.PROJECT_URL;
const DATABASE_ID = process.env.DATABASE_ID || "";
const KV_NAMESPACE_ID = process.env.KV_NAMESPACE_ID || "";
/**
* 验证必要的环境变量
*/
const validateEnvironment = () => {
const requiredEnvVars = ["CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_TOKEN"];
const missing = requiredEnvVars.filter((varName) => !process.env[varName]);
if (missing.length > 0) {
throw new Error(
`Missing required environment variables: ${missing.join(", ")}`
);
}
};
/**
* 处理JSON配置文件
*/
const setupConfigFile = (examplePath: string, targetPath: string) => {
try {
// 如果目标文件已存在,则跳过
if (existsSync(targetPath)) {
console.log(`✨ Configuration ${targetPath} already exists.`);
return;
}
if (!existsSync(examplePath)) {
console.log(`⚠️ Example file ${examplePath} does not exist, skipping...`);
return;
}
const configContent = readFileSync(examplePath, "utf-8");
const json = JSON.parse(configContent);
// 处理数据库配置
if (json.d1_databases && json.d1_databases.length > 0) {
json.d1_databases[0].database_name = DATABASE_NAME;
if (DATABASE_ID) {
json.d1_databases[0].database_id = DATABASE_ID;
}
}
// 处理KV配置
if (json.kv_namespaces && json.kv_namespaces.length > 0 && KV_NAMESPACE_ID) {
json.kv_namespaces[0].id = KV_NAMESPACE_ID;
}
// 写入配置文件
writeFileSync(targetPath, JSON.stringify(json, null, 2));
console.log(`✅ Configuration ${targetPath} setup successfully.`);
} catch (error) {
console.error(`❌ Failed to setup ${targetPath}:`, error);
throw error;
}
};
/**
* 设置所有Wrangler配置文件
*/
const setupWranglerConfigs = () => {
console.log("🔧 Setting up Wrangler configuration files...");
const configs = [
{ example: "wrangler.example.json", target: "wrangler.json" },
{ example: "wrangler.email.example.json", target: "wrangler.email.json" },
{ example: "wrangler.cleanup.example.json", target: "wrangler.cleanup.json" },
];
// 处理每个配置文件
for (const config of configs) {
setupConfigFile(
resolve(config.example),
resolve(config.target)
);
}
};
/**
* 更新数据库ID到所有配置文件
*/
const updateDatabaseConfig = (dbId: string) => {
console.log(`📝 Updating database ID (${dbId}) in configurations...`);
// 更新环境变量
updateEnvVar("DATABASE_ID", dbId);
// 更新所有配置文件
const configFiles = [
"wrangler.json",
"wrangler.email.json",
"wrangler.cleanup.json",
];
for (const filename of configFiles) {
const configPath = resolve(filename);
if (!existsSync(configPath)) continue;
try {
const json = JSON.parse(readFileSync(configPath, "utf-8"));
if (json.d1_databases && json.d1_databases.length > 0) {
json.d1_databases[0].database_id = dbId;
}
writeFileSync(configPath, JSON.stringify(json, null, 2));
console.log(`✅ Updated database ID in ${filename}`);
} catch (error) {
console.error(`❌ Failed to update ${filename}:`, error);
}
}
};
/**
* 更新KV命名空间ID到所有配置文件
*/
const updateKVConfig = (namespaceId: string) => {
console.log(`📝 Updating KV namespace ID (${namespaceId}) in configurations...`);
// 更新环境变量
updateEnvVar("KV_NAMESPACE_ID", namespaceId);
// KV命名空间只在主wrangler.json中使用
const wranglerPath = resolve("wrangler.json");
if (existsSync(wranglerPath)) {
try {
const json = JSON.parse(readFileSync(wranglerPath, "utf-8"));
if (json.kv_namespaces && json.kv_namespaces.length > 0) {
json.kv_namespaces[0].id = namespaceId;
}
writeFileSync(wranglerPath, JSON.stringify(json, null, 2));
console.log(`✅ Updated KV namespace ID in wrangler.json`);
} catch (error) {
console.error(`❌ Failed to update wrangler.json:`, error);
}
}
};
/**
* 检查并创建数据库
*/
const checkAndCreateDatabase = async () => {
console.log(`🔍 Checking if database "${DATABASE_NAME}" exists...`);
try {
const database = await getDatabase();
if (!database || !database.uuid) {
throw new Error('Database object is missing a valid UUID');
}
updateDatabaseConfig(database.uuid);
console.log(`✅ Database "${DATABASE_NAME}" already exists (ID: ${database.uuid})`);
} catch (error) {
if (error instanceof NotFoundError) {
console.log(`⚠️ Database not found, creating new database...`);
try {
const database = await createDatabase();
if (!database || !database.uuid) {
throw new Error('Database object is missing a valid UUID');
}
updateDatabaseConfig(database.uuid);
console.log(`✅ Database "${DATABASE_NAME}" created successfully (ID: ${database.uuid})`);
} catch (createError) {
console.error(`❌ Failed to create database:`, createError);
throw createError;
}
} else {
console.error(`❌ An error occurred while checking the database:`, error);
throw error;
}
}
};
/**
* 迁移数据库
*/
const migrateDatabase = () => {
console.log("📝 Migrating remote database...");
try {
execSync("pnpm run db:migrate-remote", { stdio: "inherit" });
console.log("✅ Database migration completed successfully");
} catch (error) {
console.error("❌ Database migration failed:", error);
throw error;
}
};
/**
* 检查并创建KV命名空间
*/
const checkAndCreateKVNamespace = async () => {
console.log(`🔍 Checking if KV namespace "${KV_NAMESPACE_NAME}" exists...`);
try {
if (!KV_NAMESPACE_ID) {
console.log("⚠️ KV_NAMESPACE_ID is not set, creating a new KV namespace...");
const namespace = await createKVNamespace();
updateKVConfig(namespace.id);
console.log(`✅ KV namespace "${KV_NAMESPACE_NAME}" created successfully (ID: ${namespace.id})`);
return;
}
const namespace = await getKVNamespace(KV_NAMESPACE_ID);
console.log(`✅ KV namespace "${KV_NAMESPACE_NAME}" already exists (ID: ${namespace.id})`);
} catch (error) {
if (error instanceof NotFoundError || (error instanceof Error && error.message?.includes("required"))) {
console.log(`⚠️ KV namespace not found or invalid, creating new KV namespace...`);
try {
const namespace = await createKVNamespace();
updateKVConfig(namespace.id);
console.log(`✅ KV namespace "${KV_NAMESPACE_NAME}" created successfully (ID: ${namespace.id})`);
} catch (createError) {
console.error(`❌ Failed to create KV namespace:`, createError);
throw createError;
}
} else {
console.error(`❌ An error occurred while checking the KV namespace:`, error);
throw error;
}
}
};
/**
* 检查并创建Pages项目
*/
const checkAndCreatePages = async () => {
console.log(`🔍 Checking if project "${PROJECT_NAME}" exists...`);
try {
await getPages();
console.log("✅ Project already exists, proceeding with update...");
} catch (error) {
if (error instanceof NotFoundError) {
console.log("⚠️ Project not found, creating new project...");
const pages = await createPages();
if (!PROJECT_URL && pages.subdomain) {
console.log("⚠️ PROJECT_URL is empty, using pages default domain...");
console.log("📝 Updating environment variables...");
// 更新环境变量为默认的Pages域名
const appUrl = `https://${pages.subdomain}`;
updateEnvVar("PROJECT_URL", appUrl);
}
} else {
console.error(`❌ An error occurred while checking the project:`, error);
throw error;
}
}
};
/**
* 推送Pages密钥
*/
const pushPagesSecret = () => {
console.log("🔐 Pushing environment secrets to Pages...");
try {
// 确保.env文件存在
if (!existsSync(resolve('.env'))) {
setupEnvFile();
}
execSync(`pnpm dlx wrangler pages secret bulk .env`, { stdio: "inherit" });
console.log("✅ Secrets pushed successfully");
} catch (error) {
console.error("❌ Failed to push secrets:", error);
throw error;
}
};
/**
* 部署Pages应用
*/
const deployPages = () => {
console.log("🚧 Deploying to Cloudflare Pages...");
try {
execSync("pnpm run build:pages && pnpm dlx wrangler pages deploy .vercel/output/static --branch main", { stdio: "inherit" });
console.log("✅ Pages deployment completed successfully");
} catch (error) {
console.error("❌ Pages deployment failed:", error);
throw error;
}
};
/**
* 部署Email Worker
*/
const deployEmailWorker = () => {
console.log("🚧 Deploying Email Worker...");
try {
execSync("pnpm dlx wrangler deploy --config wrangler.email.json", { stdio: "inherit" });
console.log("✅ Email Worker deployed successfully");
} catch (error) {
console.error("❌ Email Worker deployment failed:", error);
// 继续执行而不中断
}
};
/**
* 部署Cleanup Worker
*/
const deployCleanupWorker = () => {
console.log("🚧 Deploying Cleanup Worker...");
try {
execSync("pnpm dlx wrangler deploy --config wrangler.cleanup.json", { stdio: "inherit" });
console.log("✅ Cleanup Worker deployed successfully");
} catch (error) {
console.error("❌ Cleanup Worker deployment failed:", error);
// 继续执行而不中断
}
};
/**
* 创建或更新环境变量文件
*/
const setupEnvFile = () => {
console.log("📄 Setting up environment file...");
const envFilePath = resolve(".env");
const envExamplePath = resolve(".env.example");
// 如果.env文件不存在则从.env.example复制创建
if (!existsSync(envFilePath) && existsSync(envExamplePath)) {
console.log("⚠️ .env file does not exist, creating from example...");
// 从示例文件复制
let envContent = readFileSync(envExamplePath, "utf-8");
// 填充当前的环境变量
const envVarMatches = envContent.match(/^([A-Z_]+)\s*=\s*".*?"/gm);
if (envVarMatches) {
for (const match of envVarMatches) {
const varName = match.split("=")[0].trim();
if (process.env[varName]) {
const regex = new RegExp(`${varName}\\s*=\\s*".*?"`, "g");
envContent = envContent.replace(regex, `${varName} = "${process.env[varName]}"`);
}
}
}
writeFileSync(envFilePath, envContent);
console.log("✅ .env file created from example");
} else if (existsSync(envFilePath)) {
console.log("✨ .env file already exists");
} else {
console.error("❌ .env.example file not found!");
throw new Error(".env.example file not found");
}
};
/**
* 更新环境变量
*/
const updateEnvVar = (name: string, value: string) => {
// 首先更新进程环境变量
process.env[name] = value;
// 然后尝试更新.env文件
const envFilePath = resolve(".env");
if (!existsSync(envFilePath)) {
setupEnvFile();
}
let envContent = readFileSync(envFilePath, "utf-8");
const regex = new RegExp(`^${name}\\s*=\\s*".*?"`, "m");
if (envContent.match(regex)) {
envContent = envContent.replace(regex, `${name} = "${value}"`);
} else {
envContent += `\n${name} = "${value}"`;
}
writeFileSync(envFilePath, envContent);
console.log(`✅ Updated ${name} in .env file`);
};
/**
* 主函数
*/
const main = async () => {
try {
console.log("🚀 Starting deployment process...");
validateEnvironment();
setupEnvFile();
setupWranglerConfigs();
await checkAndCreateDatabase();
migrateDatabase();
await checkAndCreateKVNamespace();
await checkAndCreatePages();
pushPagesSecret();
deployPages();
deployEmailWorker();
deployCleanupWorker();
console.log("🎉 Deployment completed successfully");
} catch (error) {
console.error("❌ Deployment failed:", error);
process.exit(1);
}
};
main();

View File

@@ -0,0 +1,18 @@
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "cleanup-worker",
"main": "workers/cleanup.ts",
"compatibility_date": "2024-03-20",
"compatibility_flags": ["nodejs_compat"],
"triggers": {
"crons": ["0 * * * *"]
},
"d1_databases": [
{
"binding": "DB",
"migrations_dir": "drizzle",
"database_name": "${DATABASE_NAME}",
"database_id": "${DATABASE_ID}"
}
]
}

View File

@@ -1,14 +0,0 @@
name = "cleanup-worker"
main = "workers/cleanup.ts"
compatibility_date = "2024-03-20"
compatibility_flags = ["nodejs_compat"]
# 每 1 小时运行一次
[triggers]
crons = ["0 * * * *"]
[[d1_databases]]
binding = "DB"
migrations_dir = "drizzle"
database_name = ""
database_id = ""

View File

@@ -0,0 +1,15 @@
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "email-receiver-worker",
"compatibility_date": "2024-03-20",
"compatibility_flags": ["nodejs_compat"],
"main": "workers/email-receiver.ts",
"d1_databases": [
{
"binding": "DB",
"migrations_dir": "drizzle",
"database_name": "${DATABASE_NAME}",
"database_id": "${DATABASE_ID}"
}
]
}

View File

@@ -1,11 +0,0 @@
name = "email-receiver-worker"
compatibility_date = "2024-03-20"
compatibility_flags = ["nodejs_compat"]
main = "workers/email-receiver.ts"
[[d1_databases]]
binding = "DB"
migrations_dir = "drizzle"
database_name = ""
database_id = ""

21
wrangler.example.json Normal file
View File

@@ -0,0 +1,21 @@
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "moemail",
"compatibility_date": "2024-03-20",
"compatibility_flags": ["nodejs_compat"],
"pages_build_output_dir": ".vercel/output/static",
"d1_databases": [
{
"binding": "DB",
"database_name": "${DATABASE_NAME}",
"database_id": "${DATABASE_ID}",
"migrations_dir": "drizzle"
}
],
"kv_namespaces": [
{
"binding": "SITE_CONFIG",
"id": "${KV_NAMESPACE_ID}"
}
]
}

View File

@@ -1,14 +0,0 @@
name = "moemail"
compatibility_date = "2024-03-20"
compatibility_flags = ["nodejs_compat"]
pages_build_output_dir = ".vercel/output/static"
[[d1_databases]]
binding = "DB"
migrations_dir = "drizzle"
database_name = ""
database_id = ""
[[kv_namespaces]]
binding = "SITE_CONFIG"
id = ""