Compare commits

...

224 Commits

Author SHA1 Message Date
Alex Yang
7930671a51 v0.7.0 2023-07-27 21:27:37 -07:00
Alex Yang
3acd546337 v0.7.0-beta.1 2023-07-18 13:02:18 +08:00
Alex Yang
3bcf30348a build: fix nx inputs
(cherry picked from commit 41edacfc81)
2023-07-18 13:00:33 +08:00
JimmFly
74909d4fd5 fix: temporarily handle all page scroll bar styles (#3269) 2023-07-17 16:12:53 +08:00
Camol
691eb3cb2d feat(i18n): support i18n in app version (#3263) 2023-07-17 16:12:51 +08:00
Peng Xiao
6ddc56632e fix: disable updater button when app updating (#3268)
(cherry picked from commit 81bad608bc)
2023-07-17 16:12:45 +08:00
Alex Yang
5c183c8353 fix(plugin-infra): react as peer dependency (#3260)
(cherry picked from commit eeed398155)
2023-07-17 15:49:01 +08:00
xiaodong zuo
5e62fec49c chore: update blocksuite version (#3261)
(cherry picked from commit f173c8b183)
2023-07-17 15:49:00 +08:00
Alex Yang
8f590ff360 fix: first workspace not found (#3258)
(cherry picked from commit 071d582250)
2023-07-17 13:32:16 +08:00
JimmFly
24bb109ec3 chore: upadete onboarding video and changlog link (#3255)
(cherry picked from commit e8f8bd21cf)
2023-07-17 13:32:16 +08:00
Alex Yang
efdf6c2b81 refactor: use useCallback (#3254)
(cherry picked from commit c0749fbb9f)
2023-07-17 13:32:16 +08:00
Si Yang
b40051e4a4 docs: update building-desktop-client-app.md (#3248) 2023-07-17 11:24:10 +08:00
Alex Yang
02506bcda4 fix(plugin-infra): dependencies (#3252) 2023-07-17 11:24:10 +08:00
Alex Yang
58dc0f2b2f chore: bump version (#3250)
Co-authored-by: Alex Yang <himself65@Alexs-MacBook-Pro.local>
(cherry picked from commit 202e9b8fe3)
2023-07-16 23:30:59 +08:00
angle
5a648baff4 fix: pwa icon (#3246)
(cherry picked from commit ce23817c11)
2023-07-16 21:44:49 +08:00
Alex Yang
5ec3279910 fix: create first workspace logic (#3241)
(cherry picked from commit c49cf1c53c)
2023-07-16 21:44:49 +08:00
Alex Yang
e29db64e7f fix: migration logic (#3238)
(cherry picked from commit 1bc427e7a6)
2023-07-16 21:44:49 +08:00
Alex Yang
f8ec1ff6d7 fix: remove hello-world page (#3234)
(cherry picked from commit ea592eb150)
2023-07-16 21:44:49 +08:00
Alex Yang
e1a62c1f03 refactor: simplify code (#3231)
(cherry picked from commit 5864f8cb9a)
2023-07-16 21:44:49 +08:00
Alex Yang
27fa5654af revert: use stable react (#3228) 2023-07-14 14:03:43 +08:00
Peng Xiao
0a7852b1eb fix: internal build updater (#3229) 2023-07-14 14:03:43 +08:00
Alex Yang
0ca9f2cc6d fix(plugin-infra): dependencies (#3226) 2023-07-14 11:30:55 +08:00
Alex Yang
4750e2535f chore: bump version (#3223) 2023-07-14 00:46:42 +08:00
Alex Yang
9ff483cd4e refactor(plugin-infra): workspace loading (#3222) 2023-07-14 00:46:28 +08:00
3720
c38dd1cc5e fix: can't go back to all pages by click All Pages button (#3219) 2023-07-14 00:46:28 +08:00
Alex Yang
735d91a003 refactor: mode atom (#3221) 2023-07-14 00:46:28 +08:00
Alex Yang
02946a0f4f test: add workspace ref in e2e (#3217) 2023-07-14 00:46:28 +08:00
Alex Yang
22aa7ff25f refactor(web): move current atoms into plugin infra (#3220) 2023-07-14 00:46:27 +08:00
Alex Yang
912cd7fb8b refactor: move test utils to package (#3206) 2023-07-14 00:46:24 +08:00
3720
559d9bb9a8 fix: collections should be unique for workspaces (#3213) 2023-07-13 16:52:07 +08:00
Alex Yang
944f4ee895 fix: fullscreen settting not working (#3215)
Co-authored-by: QiShaoXuan <qishaoxuan777@gmail.com>
2023-07-13 16:52:00 +08:00
xiaodong zuo
94d518e09a chore: update blocksuite version (#3209)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-07-13 16:52:00 +08:00
Alex Yang
b9dff62f1f fix: page id could be invalid (#3204) 2023-07-13 16:52:00 +08:00
Alex Yang
1bc8d6a0b8 fix: block hub might not work (#3199) 2023-07-13 02:16:55 +08:00
Alex Yang
04ade02254 chore: bump version (#3197) 2023-07-13 02:09:10 +08:00
Alex Yang
655b6d5399 chore: bump version (#3196) 2023-07-13 02:09:10 +08:00
Peng Xiao
9df1eac0b8 fix: iconUrl for windows build (#3194) 2023-07-13 02:09:10 +08:00
Alex Yang
e58b21c599 fix: first page id conflict (#3192) 2023-07-13 02:09:10 +08:00
Alex Yang
c1e53b90a2 fix: unexpected jump 404 page (#3190) 2023-07-13 02:09:10 +08:00
JimmFly
1fecb22627 style: update delete button style (#3180) 2023-07-13 02:09:10 +08:00
Peng Xiao
770bde8088 fix: add default fonts (#3185)
(cherry picked from commit bd42380f8a)
2023-07-12 17:21:21 +08:00
xiaodong zuo
bb7ed6869e fix: enhancing the security of image proxy (#3176)
(cherry picked from commit 30dee18835)
2023-07-12 17:21:21 +08:00
Alex Yang
ac55ae467c chore: bump version (#3179)
(cherry picked from commit e51c98c1dd)
2023-07-12 17:20:58 +08:00
Alex Yang
626e9f96e9 feat: display app version in setting panel (#3170)
(cherry picked from commit bbb1387469)
2023-07-12 17:20:58 +08:00
xiaodong zuo
0efcaa1b6b fix: the image lost after exporting (#3150)
Co-authored-by: Alex Yang <himself65@outlook.com>
(cherry picked from commit 4f88774999)
2023-07-12 17:20:57 +08:00
Alex Yang
8a1f022c49 feat: add suspense to workspace settings (#3167)
Co-authored-by: Qi <474021214@qq.com>
(cherry picked from commit 3968deb6d4)
2023-07-12 17:20:57 +08:00
Alex Yang
7faa909ab1 fix: jump to index page after deletion (#3169)
(cherry picked from commit 37c8465af8)
2023-07-12 17:20:57 +08:00
Peng Xiao
c9a7744270 fix: settings style update (#3161)
(cherry picked from commit d88a21d24a)
2023-07-12 17:20:56 +08:00
3720
2c9c4c2cd3 fix: some typo and i18n (#3155)
(cherry picked from commit 6ad2d106bc)
2023-07-12 17:20:56 +08:00
Alex Yang
b0bb925ef6 refactor: remove unused code (#3149)
(cherry picked from commit 8c1fcee135)
2023-07-12 17:20:56 +08:00
Peng Xiao
272835cfba fix: updater not working (#3144)
(cherry picked from commit 0514da9759)
2023-07-12 17:20:55 +08:00
JimmFly
0c66780fda style: modify the style of community item (#3143)
(cherry picked from commit b2fed03f30)
2023-07-12 17:20:55 +08:00
Alex Yang
74e6fd8d4a fix: remove workspace not working (#3140)
(cherry picked from commit ddb2931f38)
2023-07-11 12:58:01 +08:00
Alex Yang
6815110727 chore: bump version (#3138)
(cherry picked from commit acf17ebace)
2023-07-11 12:58:01 +08:00
Alex Yang
7bda0f87e6 revert: restrict node version
(cherry picked from commit 01de2ae714)
2023-07-11 12:57:57 +08:00
Qi
38521dc8b2 fix: font style setting only control editor's font (#3117)
Co-authored-by: Alex Yang <himself65@outlook.com>
(cherry picked from commit cfa18d1bc3)
2023-07-11 12:57:57 +08:00
Alex Yang
1effd9b22e chore: bump version (#3131)
(cherry picked from commit 127c63601e)
2023-07-11 12:57:57 +08:00
LongYinan
d7a7c6aa50 fix: add semver into server dependencies
(cherry picked from commit f079b0b49a)
2023-07-11 12:57:56 +08:00
Alex Yang
4de1baf2eb refactor: follow correct react rules (#3119)
(cherry picked from commit 6caf934d47)
2023-07-11 12:57:55 +08:00
Qi
d66f7da3c0 feat: modify setting modal entry in quick search modal (#3089)
(cherry picked from commit 2f910fbad0)
2023-07-11 12:57:54 +08:00
Peng Xiao
9b948613dc fix: add DB migration to add workspace (#3115)
(cherry picked from commit dac4e390aa)
2023-07-11 12:57:54 +08:00
JimmFly
77200ed8fc style: change switch tip color (#3123)
(cherry picked from commit 812e0e9c9a)
2023-07-11 12:57:52 +08:00
Alex Yang
d42c53c3da chore: restrict node version (#3120)
(cherry picked from commit 05291a8a36)
2023-07-11 12:57:52 +08:00
JimmFly
28316861e3 test: fix incorrect day suffix (#3121)
(cherry picked from commit 8bcc4d6a57)
2023-07-11 12:57:51 +08:00
danielchim
9bc42c7eca fix: page mode shortcut (#3097)
(cherry picked from commit e06d5e1c8d)
2023-07-11 12:57:51 +08:00
Alex Yang
994a5d9b54 feat: improve error log message (#3112)
(cherry picked from commit 1c8895f23f)
2023-07-11 12:57:50 +08:00
Alex Yang
0ddbe89d88 refactor(hooks): reduce null types (#3111)
(cherry picked from commit 8b5d997322)
2023-07-11 12:57:50 +08:00
Peng Xiao
fdffac6911 fix: disable move db by default (#3105)
Co-authored-by: Alex Yang <himself65@outlook.com>
(cherry picked from commit 33644a68b2)
2023-07-11 12:57:50 +08:00
mon-jai
72466d0768 fix: sidebar noise background on Windows (#3107)
(cherry picked from commit bc85ad5b65)
2023-07-11 12:57:50 +08:00
Alex Yang
4a982b3bcf fix: init workspace before loaded (#3104)
(cherry picked from commit 3c5ccd7231)
2023-07-11 12:57:44 +08:00
Alex Yang
348c56ba30 chore: remove unused code (#3102)
(cherry picked from commit da140b0b85)
2023-07-11 12:57:43 +08:00
Alex Yang
83d92af07d test: fix flaky (#3100)
(cherry picked from commit c4d53d59b5)
2023-07-11 12:57:43 +08:00
boomlion8
b4397eb64f fix: color of UI in dark mode (#3081)
Co-authored-by: boomlion8 <201116201@manit.ac.in>
Co-authored-by: Alex Yang <himself65@outlook.com>
(cherry picked from commit a48726d088)
2023-07-11 12:57:43 +08:00
Alex Yang
2ae7bea5b1 feat: improve workspace hook (#3099)
(cherry picked from commit b49306607b)
2023-07-11 12:57:42 +08:00
Alex Yang
1908d1a213 refactor: lazy load workspaces (#3091)
(cherry picked from commit 283f0cd263)
2023-07-11 12:57:33 +08:00
JimmFly
e6530f5017 chore: add new item for share component (#3084)
(cherry picked from commit 66152401be)
2023-07-11 12:57:33 +08:00
Qi
9ef492f5f8 feat: add font style setting (#3092)
(cherry picked from commit b12412a3c1)
2023-07-11 12:57:32 +08:00
Peng Xiao
a41d9c0ac2 fix: a possible issue on electron flaky test (#3094)
(cherry picked from commit ce1e8d868c)
2023-07-11 12:57:32 +08:00
Alex Yang
d4dd352ebe perf: reduce unused provider connection (#3090)
(cherry picked from commit 3294043180)
2023-07-11 12:57:31 +08:00
Alex Yang
c23d31f912 ci: fix nx.yml (#3086)
(cherry picked from commit 152fbaabda)
2023-07-11 12:57:31 +08:00
JimmFly
94b5563a0e style: adjust settings style (#3083)
(cherry picked from commit 5756bdf8d7)
2023-07-11 12:57:31 +08:00
Alex Yang
446d2d3370 chore: bump version (#3078)
(cherry picked from commit 80ee33fd3e)
2023-07-11 12:57:30 +08:00
Alex Yang
c8492d2014 test: image preview e2e (#3080)
Co-authored-by: danielchim <kahungchim@gmail.com>
(cherry picked from commit 955d80e2c1)
2023-07-11 12:57:30 +08:00
Alex Yang
1a223a99d1 build: fix nx inputs (#3079)
(cherry picked from commit 67fe7f04da)
2023-07-11 12:57:29 +08:00
Alex Yang
b8a2438679 test: upgrade playwright (#3077)
(cherry picked from commit 6395521f09)
2023-07-11 12:57:29 +08:00
Alex Yang
f38978e7c9 fix: cleanup workspace when switch setting panel (#3072)
(cherry picked from commit 822078e640)
2023-07-11 12:57:28 +08:00
Alex Yang
44dbe39001 refactor: block-hub in tool wrapper (#3073)
(cherry picked from commit fafd93f7dc)
2023-07-11 12:57:28 +08:00
Peng Xiao
424580971e fix: workspace storage settings issues (#3055)
(cherry picked from commit 00ce086e79)
2023-07-11 12:57:27 +08:00
Alex Yang
6d552ce85e fix(web): setting panel refresh (#3070)
(cherry picked from commit 28653d6892)
2023-07-11 12:57:27 +08:00
Alex Yang
031dd6d4ea fix(web): fetch hello-world from local (#3062)
(cherry picked from commit e30c67482f)
2023-07-11 12:57:27 +08:00
Pratik Kumar
d2503d46eb fix(component): new page button in all page (#3053)
(cherry picked from commit bda28e0404)
2023-07-11 12:57:26 +08:00
Alex Yang
1d701b9a8e fix(component): image preview fallback (#3058)
(cherry picked from commit ce63364299)
2023-07-11 12:57:26 +08:00
JimmFly
29c3744c53 chore: update communities link and icon (#3052)
(cherry picked from commit f468dff6aa)
2023-07-11 12:57:25 +08:00
Peng Xiao
794f19637d fix: menu item click area (#3051)
(cherry picked from commit fab03006e8)
2023-07-11 12:57:25 +08:00
JimmFly
822c01c360 fix: date-picker hidden in update collection (#3045)
(cherry picked from commit 8a565b8633)
2023-07-11 12:57:24 +08:00
Pratik Kumar
76da19bac6 fix(component): all page list UI padding (#3046)
Co-authored-by: Alex Yang <himself65@outlook.com>
(cherry picked from commit 95c2e20cb5)
2023-07-11 12:57:13 +08:00
JimmFly
c53638c08d chore: temporary fix sync script error (#3044)
(cherry picked from commit 2e0f410978)
2023-07-11 12:57:12 +08:00
Alex Yang
aa31043871 chore: bump version (#3041)
(cherry picked from commit fa1cd87348)
2023-07-11 12:57:11 +08:00
Alex Yang
e2d6d5548f fix: workspace name should change in the setting panel (#3039)
(cherry picked from commit e95d28e136)
2023-07-11 12:57:11 +08:00
Qi
8f1bfa46a9 fix: a series of setting issues (#3032)
(cherry picked from commit 87ba71e77e)
2023-07-11 12:57:10 +08:00
Peng Xiao
0a607e0450 fix: delete workspace in settings (#3030)
(cherry picked from commit dec0c0d3d1)
2023-07-11 12:57:10 +08:00
Peng Xiao
e41abe4cfe fix: updater issues (#3027)
(cherry picked from commit 776172bc88)
2023-07-11 12:57:09 +08:00
Alex Yang
5a846755a8 build: remove legacy cloud config (#3024)
(cherry picked from commit 70ac31b907)
2023-07-05 16:05:52 +08:00
Alex Yang
e99891f22d chore: bump version (#3023)
(cherry picked from commit cff9fd1ead)
2023-07-05 16:05:52 +08:00
Alex Yang
eaf7965e79 docs: update README.md
(cherry picked from commit 319febb00d)
2023-07-05 16:05:52 +08:00
3720
1d2b4c6057 fix: tags does not exist (#3020)
(cherry picked from commit 72fa2da2d3)
2023-07-05 16:05:52 +08:00
Alex Yang
e609ab201f feat: update server login feature (#3004)
Co-authored-by: LongYinan <lynweklm@gmail.com>
(cherry picked from commit 3084c427f1)
2023-07-05 16:05:52 +08:00
3720
43e996355a fix: flaky tests (#3019)
(cherry picked from commit 9cd1f013f8)
2023-07-05 16:05:51 +08:00
Alex Yang
d41c7fb899 chore: bump version (#3016) 2023-07-05 02:25:39 +08:00
Peng Xiao
093851ec0c fix: sqlite provider import sub doc db file (#2991)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-07-05 02:25:39 +08:00
Alex Yang
c5f5e606b0 chore: update pre-commit (#3017) 2023-07-05 02:25:38 +08:00
JimmFly
b45c8b582e chore: update setting text (#3000) 2023-07-05 02:25:38 +08:00
Alex Yang
d94ad8caac refactor: simplify code (#3015) 2023-07-05 02:25:38 +08:00
Alex Yang
8dfc95d002 ci: check macOS arm64 bundle output (#3012) 2023-07-05 02:25:38 +08:00
Alex Yang
466b1bb173 feat(electron): move preload to infra (#3011) 2023-07-05 02:25:38 +08:00
Alex Yang
1a93429ffa chore: bump nx (#3014) 2023-07-05 02:24:15 +08:00
Fangdun Tsai
e5f150f093 chore(electron): renaming clipboard api (#3005) 2023-07-04 21:52:58 +08:00
Alex Yang
e6d709a8a0 fix: bookmark block output missing (#3010) 2023-07-04 21:52:58 +08:00
Qi
708e361264 feat: modify setting modal (#3008) 2023-07-04 21:52:58 +08:00
3720
34ad5cdaef feat: add tags support (#2988)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-07-04 16:16:34 +08:00
Alex Yang
65c6479ee4 refactor: input component (#2999) 2023-07-04 16:16:33 +08:00
Alex Yang
394d535906 chore: bump version (#2998) 2023-07-04 16:16:33 +08:00
ShortCipher5
70cf3176b7 docs: update README.md (#2997) 2023-07-04 16:16:33 +08:00
Alex Yang
c11fe97b47 test: use static server (#2996) 2023-07-04 16:16:33 +08:00
Alex Yang
d5926fce8c chore: add circular check (#2995) 2023-07-04 16:16:32 +08:00
Alex Yang
9ba6539b5b build: enable next server (#2992) 2023-07-04 16:16:32 +08:00
danielchim
8ae091b8c3 feat: page mode shortcut (#2985) 2023-07-04 01:17:56 +08:00
Alex Yang
d04585f630 chore: bump version (#2989) 2023-07-04 01:17:44 +08:00
Alex Yang
dedb4ba833 refactor: remove legacy cloud (#2987) 2023-07-04 01:17:43 +08:00
xiaodong zuo
1887a36ca5 fix: dark mode export PDF leaves margin and notification (#2978) 2023-07-04 01:17:43 +08:00
LongYinan
4787fef74a ci: reduce yarn cache (#2983) 2023-07-04 01:17:43 +08:00
Peng Xiao
5be4873d36 fix: potential updater issue (#2973) 2023-07-04 01:17:42 +08:00
Alex Yang
0e342999fd feat: remove old setting page by default (#2980) 2023-07-04 01:17:42 +08:00
JimmFly
f16f02b6b0 chore: add import to sidebar (#2981) 2023-07-04 01:17:42 +08:00
Peng Xiao
3a3053fb49 fix: some potential tests issue (#2982) 2023-07-04 01:17:42 +08:00
Peng Xiao
425439ec42 fix: potential race condition on app load when migration (#2977)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-07-04 01:17:42 +08:00
Alex Yang
2a4a2db6f6 fix(web): hydration issue (#2974) 2023-07-04 01:17:42 +08:00
JimmFly
562013e3bd style: adjust icon button hover color (#2976) 2023-07-04 01:17:41 +08:00
Alex Yang
1f7c1ef91e chore: add affine-cloud build config (#2971) 2023-07-04 01:17:41 +08:00
Alex Yang
1a80c95ae3 build: update build config (#2967) 2023-07-04 01:17:41 +08:00
Alex Yang
bbb97d06da chore: update 'lint-staged' rules (#2969) 2023-07-04 01:17:41 +08:00
JimmFly
20ce8c0cd5 style: adjust copilot chat style (#2915) 2023-07-04 01:17:41 +08:00
JimmFly
940bf15ac4 i18n: update translation resources (#2968) 2023-07-04 01:17:41 +08:00
Qi
5fe8fae6ae feat: modify preloading data (#2947) 2023-07-03 12:07:12 +08:00
Alex Yang
826281453f chore: bump version 2023-07-03 12:07:09 +08:00
Alex Yang
979ef24c39 chore: bump version (#2960) 2023-07-03 12:07:06 +08:00
Alex Yang
fad07b2cab chore: replace 'eslint-plugin-import' (#2957) 2023-07-03 12:07:06 +08:00
Alex Yang
e147487d9c ci: update labeler.yml 2023-07-03 12:07:06 +08:00
Alex Yang
1e15ae5766 ci: use yarn run test 2023-07-03 12:07:06 +08:00
Alex Yang
5d5110126e fix: remove export script 2023-07-03 12:07:02 +08:00
Alex Yang
b467ab1d76 build: update nx.json 2023-07-03 12:06:58 +08:00
Alex Yang
635ec0a401 chore: improve ci build speed (#2953) 2023-07-03 12:06:58 +08:00
Alex Yang
578f92e376 build: update nx.json 2023-07-03 12:06:57 +08:00
Alex Yang
00604bd4b0 build: fix nx.json (#2951) 2023-07-03 12:06:57 +08:00
Alex Yang
8f09df655c chore: bump version (#2950) 2023-07-03 12:06:56 +08:00
Alex Yang
fbc196e828 build: skip type check in next.js build (#2952) 2023-07-03 12:06:56 +08:00
Alex Yang
d120f3ada8 build: enhance nx build (#2948) 2023-07-03 12:06:56 +08:00
3720
d24009f494 chore: adjust icon and style (#2949) 2023-07-03 12:06:55 +08:00
Alex Yang
721e5e4dd9 docs: update react badge 2023-07-03 12:06:55 +08:00
Alex Yang
612fbb4f97 v0.7.0-beta.0 2023-06-30 17:11:46 +08:00
Alex Yang
bba27fb02e ci: fix build desktop 2023-06-30 17:06:44 +08:00
Alex Yang
92e5b3d937 ci: update paths-ignore 2023-06-30 16:47:07 +08:00
Alex Yang
014566fcc9 ci: chmod 777 on output directory 2023-06-30 16:40:47 +08:00
Alex Yang
3fbeefe329 chore: bump blocksuite to '0.0.0-20230630081054-55a25248-nightly' 2023-06-30 16:17:43 +08:00
regischen
ce09a7c09f fix(web): migrate connector (#2941)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-06-30 16:17:34 +08:00
Alex Yang
4757a95528 build(electron): use nx (#2942) 2023-06-30 16:17:34 +08:00
Peng Xiao
5e4cd0f81d fix: disable sqlite blob storage (#2943) 2023-06-30 16:17:34 +08:00
Alex Yang
536bf01dd9 fix(cli): run dev-web crash 2023-06-30 16:17:33 +08:00
Alex Yang
99a60c37d0 ci: cancel previous build (#2794) 2023-06-30 16:17:33 +08:00
Alex Yang
bfe6f0b4dd refactor(cli): use typescript (#2938) 2023-06-30 16:17:33 +08:00
Alex Yang
aac0d24aed ci: build infra code before build layers 2023-06-30 16:17:33 +08:00
3720
c60648ce9b feat: support for view management (#2892) 2023-06-30 16:17:32 +08:00
Fangdun Tsai
48e33e2d73 feat: expose clipboard apis (#2932) 2023-06-30 16:17:32 +08:00
Alex Yang
3236a91d0b chore: tag deprecated files (#2936) 2023-06-30 16:17:24 +08:00
Alex Yang
d790ead446 feat: special ip address 'localhost' (#2935) 2023-06-30 16:17:24 +08:00
Alex Yang
12ed9db207 fix(web): disable notification center (#2934) 2023-06-30 12:03:16 +08:00
Alex Yang
5a4bb70869 chore: bump blocksuite to 0.0.0-20230629103121-76e6587d-nightly (#2931) 2023-06-30 10:19:07 +08:00
DarkSky
de52651c7f ci: make helm release only on bump version (#2928) 2023-06-30 10:19:06 +08:00
Ikko Eltociear Ashimine
ce45c15d13 refactor: fix typo in notification-center/index.tsx (#2929) 2023-06-30 10:17:27 +08:00
Hyden Liu
e65cae121e fix: z-index on app sidebar (#2761)
Co-authored-by: Himself65 <himself65@outlook.com>
2023-06-30 10:17:27 +08:00
xiaodong zuo
51b6f5a105 feat: export page as file (#2923) 2023-06-30 10:17:26 +08:00
Alex Yang
bfbb7e9e6b fix(electron): window only ui (#2926) 2023-06-30 10:17:26 +08:00
LongYinan
03dea53b30 build: affine Node.js server charts (#2895) 2023-06-30 10:17:25 +08:00
Qi
0bdecc776d feat: add and modify test case for new settings modal (#2925) 2023-06-30 10:17:23 +08:00
Alex Yang
b4389b7380 docs: set nodejs version to 18.16.1 2023-06-30 10:17:23 +08:00
Alex Yang
d796fc2ab0 v0.7.0-canary.24 2023-06-30 10:17:22 +08:00
JimmFly
a8dc00b406 chore: update changelog link and remove obsolete changelog components (#2918) 2023-06-30 10:17:18 +08:00
Alex Yang
76039d8d3e chore: bump blocksuite to 0.0.0-20230629084521-542de4e8-nightly (#2921) 2023-06-30 10:17:16 +08:00
Alex Yang
b6b5b58889 ci: allow codecov upload failure (#2922) 2023-06-30 10:17:15 +08:00
Qi
6f5f0ab986 fix: a serise of ui issues of new setting (#2920)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-06-30 10:17:13 +08:00
Alex Yang
f7b07f4216 refactor: rootWorkspacesMetadataAtom loading logic (#2882) 2023-06-30 10:17:11 +08:00
DarkSky
df7f782e05 fix: incorrect formatting (#2917) 2023-06-30 10:17:11 +08:00
JimmFly
3fb30a6cce chore: update translation (#2916)
Co-authored-by: zuozijian3720 <zuozijian1994@gmail.com>
2023-06-30 10:17:09 +08:00
Alex Yang
2c5fa4f008 docs: add apps/README.md 2023-06-30 10:17:08 +08:00
Alex Yang
4433a72b96 ci: split migration test 2023-06-30 10:17:08 +08:00
Alex Yang
1853dd9895 test: migration test in real world (#2885) 2023-06-30 10:17:07 +08:00
Alex Yang
d98586773c ci: add prettier format check (#2908) 2023-06-30 10:17:05 +08:00
liuyi
bf30185c65 feat(storage): binding jwst storage to node (#2808) 2023-06-30 10:17:04 +08:00
Alex Yang
09e38e4a91 build: disable sqlite provider in canary 2023-06-30 10:17:04 +08:00
Peng Xiao
97f41ab620 fix: updater color updates (#2913) 2023-06-30 10:17:03 +08:00
DarkSky
0cbeff6f61 feat: add deployment guide & fix pod label (#2912) 2023-06-30 10:17:03 +08:00
Alex Yang
f3ed31b4b7 ci: fix output variable 2023-06-30 10:17:01 +08:00
Alex Yang
020f86bcc1 v0.7.0-canary.23 2023-06-30 10:17:00 +08:00
Alex Yang
997cf3bd92 ci: use production environment 2023-06-30 10:16:59 +08:00
Alex Yang
af399adf14 ci: fix package version output 2023-06-30 10:16:59 +08:00
Alex Yang
255e53673e v0.7.0-canary.22 2023-06-30 10:16:57 +08:00
Alex Yang
3ba1b3fc61 ci: fix tag version 2023-06-30 10:16:57 +08:00
Alex Yang
135eb07f9e v0.7.0-canary.21 2023-06-30 10:16:57 +08:00
Alex Yang
3362069ae8 ci: automatically build canary release (#2911) 2023-06-30 10:16:56 +08:00
Alex Yang
fe7400abe1 ci: add codeql check to merge group (#2909) 2023-06-30 10:16:56 +08:00
Qi
4cca8a16ab feat: migrate workspace setting with new design to setting modal (#2900)
Co-authored-by: Alex Yang <himself65@outlook.com>
2023-06-30 10:16:52 +08:00
Alex Yang
5a3e5a1565 chore: bump typescript version (#2906) 2023-06-30 10:16:52 +08:00
Alex Yang
87063d20ce chore: codesandbox setup (#2907) 2023-06-30 10:16:52 +08:00
DarkSky
7b6728e8a9 docs: improve helm ci & document (#2902) 2023-06-30 10:16:50 +08:00
Alex Yang
1bff46d5f0 refactor(storybook): move to apps folder (#2901) 2023-06-30 10:16:48 +08:00
Alex Yang
81bac496b4 feat: add rule 'sonarjs/no-identical-functions' (#2905) 2023-06-30 10:16:48 +08:00
Alex Yang
b31aa44d40 chore: update pre-commit hook (#2904) 2023-06-30 10:16:46 +08:00
Alex Yang
bff8ca6178 refactor: environment setup (#2898)
Co-authored-by: Simon He <57086651+Simon-He95@users.noreply.github.com>
2023-06-30 10:16:45 +08:00
Alex Yang
be33cfd174 fix(web): bypass adapter list error (#2903) 2023-06-30 10:16:44 +08:00
Alex Yang
0eb11d14ad test(server): watch mode (#2893) 2023-06-30 10:16:44 +08:00
Alex Yang
b0d8c8e14f ci: enable merge group (#2899) 2023-06-30 10:16:44 +08:00
Alex Yang
72378abe56 feat: update migration test page (#2871) 2023-06-30 10:16:44 +08:00
Alex Yang
fc36aac0fb build: unify build flags (#2891) 2023-06-30 10:16:44 +08:00
Alex Yang
e7f4d82881 build: fix i18n output (#2896) 2023-06-30 10:16:44 +08:00
666 changed files with 22273 additions and 16098 deletions

20
.codesandbox/task.json Normal file
View File

@@ -0,0 +1,20 @@
{
"$schema": "https://codesandbox.io/schemas/tasks.json",
"setupTasks": [
{
"name": "Install Dependencies",
"command": "yarn install"
}
],
"tasks": {
"start-web": {
"name": "Start Web",
"command": "yarn nx dev @affine/web --port 8080",
"runAtStart": true,
"preview": {
"port": 8080
}
}
}
}

View File

@@ -9,6 +9,7 @@
"server",
"web",
"docs",
"storybook",
"component",
"workspace",
"env",
@@ -21,7 +22,9 @@
"templates",
"y-indexeddb",
"debug",
"theme"
"storage",
"infra",
"plugin-infra"
]
]
}

View File

@@ -8,3 +8,4 @@ _next
lib
.eslintrc.js
packages/i18n/src/i18n-generated.ts
e2e-dist-*

View File

@@ -21,6 +21,11 @@ const createPattern = packageName => [
message: 'Do not import package itself',
allowTypeImports: false,
},
{
group: ['@blocksuite/store'],
message: "Import from '@blocksuite/global/utils'",
importNames: ['assertExists', 'assertEquals'],
},
];
const allPackages = [
@@ -41,6 +46,7 @@ const allPackages = [
'apps/web',
'apps/server',
'apps/electron',
'apps/storybook',
'plugins/copilot',
'plugins/bookmark-block',
];
@@ -64,6 +70,7 @@ const config = {
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:@typescript-eslint/recommended',
'prettier',
],
parser: '@typescript-eslint/parser',
parserOptions: {
@@ -81,7 +88,7 @@ const config = {
'@typescript-eslint',
'simple-import-sort',
'sonarjs',
'import',
'i',
'unused-imports',
'unicorn',
],
@@ -132,6 +139,11 @@ const config = {
message: "Don't import from src",
allowTypeImports: false,
},
{
group: ['@blocksuite/store'],
message: "Import from '@blocksuite/global/utils'",
importNames: ['assertExists', 'assertEquals'],
},
],
},
],
@@ -157,6 +169,7 @@ const config = {
'sonarjs/no-duplicated-branches': 'error',
'sonarjs/no-collection-size-mischeck': 'error',
'sonarjs/no-useless-catch': 'error',
'sonarjs/no-identical-functions': 'error',
},
overrides: [
{
@@ -201,6 +214,7 @@ const config = {
'scripts/**/*',
'**/benchmark/**/*',
'**/__debug__/**/*',
'**/e2e/**/*',
],
rules: {
'@typescript-eslint/no-non-null-assertion': 0,

View File

@@ -27,11 +27,11 @@ runs:
.cargo-cache
target/${{ inputs.target }}
key: stable-${{ inputs.target }}-cargo-cache
- name: Build
if: ${{ inputs.target != 'x86_64-unknown-linux-gnu' && inputs.target != 'aarch64-unknown-linux-gnu' }}
shell: bash
run: yarn nx build @affine/native --target ${{ inputs.target }}
run: |
yarn nx build @affine/native --target ${{ inputs.target }}
env:
NX_CLOUD_ACCESS_TOKEN: ${{ inputs.nx_token }}
@@ -41,11 +41,12 @@ runs:
with:
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
options: --user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build -e NX_CLOUD_ACCESS_TOKEN=${{ inputs.nx_token }}
run: >-
export CC=x86_64-unknown-linux-gnu-gcc &&
export CC_x86_64_unknown_linux_gnu=x86_64-unknown-linux-gnu-gcc &&
yarn nx build @affine/native --target ${{ inputs.target }} &&
run: |
export CC=x86_64-unknown-linux-gnu-gcc
export CC_x86_64_unknown_linux_gnu=x86_64-unknown-linux-gnu-gcc
yarn nx build @affine/native --target ${{ inputs.target }}
chmod -R 777 node_modules/.cache
chmod -R 777 target
- name: Build
if: ${{ inputs.target == 'aarch64-unknown-linux-gnu' }}
@@ -53,6 +54,7 @@ runs:
with:
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
options: --user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build -e NX_CLOUD_ACCESS_TOKEN=${{ inputs.nx_token }}
run: >-
run: |
yarn nx build @affine/native --target ${{ inputs.target }}
chmod -R 777 node_modules/.cache
chmod -R 777 target

View File

@@ -13,10 +13,18 @@ inputs:
description: 'Run the install step for Playwright.'
required: false
default: 'false'
electron-install:
description: 'Download the Electron binary'
required: false
default: 'true'
npm-token:
description: 'The NPM token to use for private packages.'
required: false
default: ''
hard-link-nm:
description: 'set nmMode to hardlinks-local in .yarnrc.yml'
required: false
default: 'true'
runs:
using: 'composite'
@@ -29,33 +37,10 @@ runs:
scope: '@toeverything'
cache: 'yarn'
- name: Expose yarn config as "$GITHUB_OUTPUT"
id: yarn-config
- name: Set nmMode
if: ${{ inputs.hard-link-nm == 'true' }}
shell: bash
run: |
echo "CACHE_FOLDER=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT
- name: Restore yarn cache
uses: actions/cache@v3
id: yarn-download-cache
with:
path: ${{ steps.yarn-config.outputs.CACHE_FOLDER }}
key: yarn-download-cache-${{ hashFiles('yarn.lock') }}
restore-keys: |
yarn-download-cache-
- name: Restore node_modules cache
uses: actions/cache@v3
with:
path: '**/node_modules'
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
- name: Restore yarn install state
id: yarn-install-state-cache
uses: actions/cache@v3
with:
path: .yarn/ci-cache/
key: ${{ runner.os }}-yarn-install-state-cache-${{ hashFiles('yarn.lock', '.yarnrc.yml') }}
run: yarn config set nmMode hardlinks-local
- name: yarn install
if: ${{ inputs.package-install == 'true' }}
@@ -64,9 +49,9 @@ runs:
run: yarn install ${{ inputs.extra-flags }}
env:
NODE_AUTH_TOKEN: ${{ inputs.npm-token }}
YARN_ENABLE_GLOBAL_CACHE: 'false'
YARN_INSTALL_STATE_PATH: .yarn/ci-cache/install-state.gz
HUSKY: '0'
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
ELECTRON_SKIP_BINARY_DOWNLOAD: '1'
- name: yarn install (try again)
if: ${{ steps.install.outcome == 'failure' }}
@@ -74,9 +59,9 @@ runs:
run: yarn install ${{ inputs.extra-flags }}
env:
NODE_AUTH_TOKEN: ${{ inputs.npm-token }}
YARN_ENABLE_GLOBAL_CACHE: 'false'
YARN_INSTALL_STATE_PATH: .yarn/ci-cache/install-state.gz
HUSKY: '0'
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
ELECTRON_SKIP_BINARY_DOWNLOAD: '1'
- name: Get installed Playwright version
id: playwright-version
@@ -113,3 +98,30 @@ runs:
shell: bash
if: inputs.playwright-install == 'true' && steps.playwright-cache.outputs.cache-hit != 'true'
run: yarn playwright install --with-deps
- name: Get installed Electron version
id: electron-version
if: ${{ inputs.electron-install == 'true' }}
shell: bash
run: |
echo "version=$(yarn why --json electron | grep -h 'workspace:.' | jq --raw-output '.children[].locator' | sed -e 's/@playwright\/test@.*://' | head -n 1)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
id: electron-cache
if: ${{ inputs.electron-install == 'true' }}
with:
path: 'node_modules/.cache/electron'
key: '${{ runner.os }}-electron-${{ steps.electron-version.outputs.version }}'
restore-keys: |
${{ runner.os }}-electron-
- name: Install Electron binary
shell: bash
if: inputs.electron-install == 'true'
run: node apps/electron/node_modules/electron/install.js
env:
ELECTRON_OVERRIDE_DIST_PATH: ./node_modules/.cache/electron
- name: Build Infra
shell: bash
run: yarn run build:infra

31
.github/actions/setup-rust/action.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: 'AFFiNE Rust setup'
description: 'Rust setup, including cache configuration'
inputs:
target:
description: 'Cargo target'
required: true
toolchain:
description: 'Rustup toolchain'
required: false
default: 'stable'
runs:
using: 'composite'
steps:
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ inputs.toolchain }}
targets: ${{ inputs.target }}
- name: Cache cargo
uses: actions/cache@v3
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: cargo-cache-${{ runner.os }}-${{ inputs.toolchain }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
cargo-cache-${{ runner.os }}-${{ inputs.toolchain }}-

View File

@@ -3,7 +3,7 @@ server {
root /app/dist;
location / {
try_files $uri $uri/index.html $uri.html =404;;
try_files $uri $uri/index.html $uri.html =404;
}
error_page 404 /404.html;

1
.github/helm/affine-cloud/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
charts/

View File

@@ -20,4 +20,4 @@
.project
.idea/
*.tmproj
.vscode/
.vscode/

View File

@@ -3,8 +3,8 @@ name: affine-cloud
description: A Helm chart for AFFiNE Cloud
type: application
version: 0.6.0
appVersion: '0.6.0'
version: 0.6.1
appVersion: '0.6.1'
dependencies:
- name: postgresql

30
.github/helm/affine-cloud/readme.md vendored Normal file
View File

@@ -0,0 +1,30 @@
# Helm Chart Configuration
The following table lists the configurable parameters of this Helm chart and their default values.
## AFFiNE Cloud Server parameters
| Parameter | Description | Default |
| ------------------------------ | -------------------------------------------------- | ------------------ |
| `affineCloud.tag` | The Docker tag of the AffineCloud image to be used | `'nightly-latest'` |
| `affineCloud.resources.cpu` | The CPU resources allocated for AffineCloud | `'250m'` |
| `affineCloud.resources.memory` | The memory resources allocated for AffineCloud | `'0.5Gi'` |
| `affineCloud.signKey` | The key used to sign the JWT tokens | `'c2VjcmV0'` |
| `affineCloud.service.type` | The type of the Kubernetes service | `'ClusterIP'` |
| `affineCloud.service.port` | The port of the Kubernetes service | `'http'` |
| `affineCloud.mail.account` | The email account used to send emails | `''` |
| `affineCloud.mail.password` | The password of the email account | `''` |
## PostgreSQL parameters
| Parameter | Description | Default |
| -------------------------------------------- | ------------------------------------------------------------------------------------- | ------------ |
| `postgresql.auth.username` | Username for the PostgreSQL database | `'affine'` |
| `postgresql.auth.password` | Password for the PostgreSQL database. Please change this for production environments. | `'password'` |
| `postgresql.auth.database` | The name of the default database that will be created on image startup | `'affine'` |
| `postgresql.primary.resources.limits.cpu` | The CPU resources allocated for the PostgreSQL primary node | `'500m'` |
| `postgresql.primary.resources.limits.memory` | The memory resources allocated for the PostgreSQL primary node | `'0.5Gi'` |
For more postgres parameters, please refer to: https://artifacthub.io/packages/helm/bitnami/postgresql
Please note that for the `postgresql.auth.password`, you should provide your own password for production environments. The default value is provided only for demonstration purposes.

View File

@@ -0,0 +1,51 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "affine-cloud.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "affine-cloud.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "affine-cloud.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "affine-cloud.labels" -}}
helm.sh/chart: {{ include "affine-cloud.chart" . }}
{{ include "affine-cloud.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "affine-cloud.selectorLabels" -}}
app.kubernetes.io/name: {{ include "affine-cloud.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

View File

@@ -1,12 +1,14 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: affine-cloud
name: "{{ include "affine-cloud.fullname" . }}"
labels:
{{- include "affine-cloud.labels" . | nindent 4 }}
spec:
replicas: 1
selector:
matchLabels:
app: affine-cloud
{{- include "affine-cloud.selectorLabels" . | nindent 6 }}
strategy:
type: RollingUpdate
rollingUpdate:
@@ -14,7 +16,7 @@ spec:
template:
metadata:
labels:
app: affine-cloud
{{- include "affine-cloud.selectorLabels" . | nindent 8 }}
spec:
restartPolicy: Always
containers:
@@ -28,9 +30,9 @@ spec:
- name: PG_DATABASE
value: "{{ .Values.postgresql.auth.database }}"
- name: PG_HOST
value: "{{ .Release.Name }}-postgresql"
value: "{{ .Values.postgresql.fullnameOverride | default (printf "%s-postgresql" .Release.Name) }}"
- name: DATABASE_URL
value: "{{ default "postgresql://$(PG_USER):$(PG_PASS)@$(PG_HOST)/$(PG_DATABASE)" .Values.affineCloud.databaseUrl }}"
value: "{{ .Values.affineCloud.databaseUrl | default "postgresql://$(PG_USER):$(PG_PASS)@$(PG_HOST)/$(PG_DATABASE)" }}"
envFrom:
- secretRef:
name: affine-cloud-secret

View File

@@ -4,7 +4,6 @@ metadata:
name: affine-cloud-secret
type: Opaque
data:
# only for demo, please modify it at prod env
SIGN_KEY: TUFtdFdzQTJhdGJuem01TA==
# MAIL_ACCOUNT: XXXX
# MAIL_PASSWORD: XXXX
SIGN_KEY: "{{ .Values.affineCloud.signKey }}"
MAIL_ACCOUNT: "{{ .Values.affineCloud.mail.account }}"
MAIL_PASSWORD: "{{ .Values.affineCloud.mail.password }}"

View File

@@ -1,13 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: affine-cloud
name: "{{ include "affine-cloud.fullname" . }}"
labels:
{{- include "affine-cloud.labels" . | nindent 4 }}
spec:
type: ClusterIP
selector:
app: affine-cloud
type: "{{ .Values.affineCloud.service.type }}"
ports:
- name: affine-cloud
- name: http
protocol: TCP
port: 3000
port: {{ .Values.affineCloud.service.port }}
targetPort: 3000
selector:
{{- include "affine-cloud.selectorLabels" . | nindent 4 }}

View File

@@ -1,13 +1,22 @@
affineCloud:
tag: 'nightly-latest'
tag: 'canary-5e0d5e0cc65ea46f326fdde12658bfac59b38c9f-0949'
# databaseUrl: 'postgresql://affine:password@affine-cloud-postgresql:5432/affine'
signKey: TUFtdFdzQTJhdGJuem01TA==
mail:
account: ''
password: ''
service:
type: ClusterIP
port: 80
resources:
cpu: '250m'
memory: 0.5Gi
postgresql:
fullnameOverride: tcp-postgresql
auth:
# only for demo, please modify it at prod env
username: affine
password: XJYMLnuBJS27a2du
password: password
database: affine
primary:
initdb:

23
.github/helm/affine/.helmignore vendored Normal file
View File

@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

6
.github/helm/affine/Chart.yaml vendored Normal file
View File

@@ -0,0 +1,6 @@
apiVersion: v2
name: affine
description: AFFiNE cloud chart
type: application
version: 0.0.0
appVersion: '0.7.0-canary.18'

View File

@@ -0,0 +1,6 @@
apiVersion: v2
name: graphql
description: AFFiNE GraphQL server
type: application
version: 0.0.0
appVersion: '0.7.0-canary.18'

View File

@@ -0,0 +1,16 @@
1. Get the application URL by running these commands:
{{- if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "graphql.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "graphql.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "graphql.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "graphql.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}

View File

@@ -0,0 +1,132 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "graphql.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "graphql.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "graphql.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "graphql.labels" -}}
helm.sh/chart: {{ include "graphql.chart" . }}
{{ include "graphql.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "graphql.selectorLabels" -}}
app.kubernetes.io/name: {{ include "graphql.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "graphql.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "graphql.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{- define "jwt.key" -}}
{{- $secret := lookup "v1" "Secret" .Release.Namespace .Values.app.jwt.secretName -}}
{{- if and $secret $secret.data.private -}}
{{/*
Reusing existing secret data
*/}}
key: {{ $secret.data.private }}
{{- else -}}
{{/*
Generate new data
*/}}
key: {{ genPrivateKey "ecdsa" | b64enc }}
{{- end -}}
{{- end -}}
{{- define "objectStorage.r2" -}}
{{- $secret := lookup "v1" "Secret" .Release.Namespace .Values.app.objectStorage.r2.secretName -}}
{{- if $secret -}}
{{/*
Reusing existing secret data
*/}}
accountId: {{ $secret.data.accountId }}
accessKeyId: {{ $secret.data.accessKeyId }}
secretAccessKey: {{ $secret.data.secretAccessKey }}
bucket: {{ $secret.data.bucket }}
{{- else -}}
{{/*
Generate new data
*/}}
accountId: {{ .Values.app.objectStorage.r2.accountId | b64enc }}
accessKeyId: {{ .Values.app.objectStorage.r2.accessKeyId | b64enc }}
secretAccessKey: {{ .Values.app.objectStorage.r2.secretAccessKey | b64enc }}
bucket: {{ .Values.app.objectStorage.r2.bucket | b64enc }}
{{- end -}}
{{- end -}}
{{- define "objectStorage.oauth.google" -}}
{{- $secret := lookup "v1" "Secret" .Release.Namespace .Values.app.oauth.google.secretName -}}
{{- if $secret -}}
{{/*
Reusing existing secret data
*/}}
clientId: {{ $secret.data.clientId }}
clientSecret: {{ $secret.data.clientSecret }}
{{- else -}}
{{/*
Generate new data
*/}}
clientId: "{{ .Values.app.oauth.google.clientId | b64enc }}"
clientSecret: "{{ .Values.app.oauth.google.clientSecret | b64enc }}"
{{- end -}}
{{- end -}}
{{- define "objectStorage.oauth.github" -}}
{{- $secret := lookup "v1" "Secret" .Release.Namespace .Values.app.oauth.github.secretName -}}
{{- if $secret -}}
{{/*
Reusing existing secret data
*/}}
clientId: {{ $secret.data.clientId }}
clientSecret: {{ $secret.data.clientSecret }}
{{- else -}}
{{/*
Generate new data
*/}}
clientId: "{{ .Values.app.oauth.github.clientId | b64enc }}"
clientSecret: "{{ .Values.app.oauth.github.clientSecret | b64enc }}"
{{- end -}}
{{- end -}}

View File

@@ -0,0 +1,126 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "graphql.fullname" . }}
labels:
{{- include "graphql.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "graphql.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "graphql.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "graphql.serviceAccountName" . }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: AUTH_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.app.jwt.secretName }}"
key: key
- name: NODE_ENV
value: "{{ .Values.env }}"
- name: DATABSE_PASSWORD
valueFrom:
secretKeyRef:
name: pg-postgresql
key: postgres-password
- name: DATABASE_URL
value: postgres://{{ .Values.database.user }}:$(DATABSE_PASSWORD)@{{ .Values.database.url }}:{{ .Values.database.port }}/{{ .Values.database.name }}
- name: AFFINE_SERVER_PORT
value: "{{ .Values.service.port }}"
- name: AFFINE_SERVER_SUB_PATH
value: "{{ .Values.app.path }}"
- name: AFFINE_SERVER_HOST
value: "{{ .Values.app.host }}"
- name: ENABLE_R2_OBJECT_STORAGE
value: "{{ .Values.app.objectStorage.r2.enabled }}"
{{ if .Values.app.objectStorage.r2.enabled }}
- name: R2_OBJECT_STORAGE_ACCOUNT_ID
valueFrom:
secretKeyRef:
name: "{{ .Values.app.objectStorage.r2.secretName }}"
key: accountId
- name: R2_OBJECT_STORAGE_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: "{{ .Values.app.objectStorage.r2.secretName }}"
key: accessKeyId
- name: R2_OBJECT_STORAGE_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.app.objectStorage.r2.secretName }}"
key: secretAccessKey
- name: R2_OBJECT_STORAGE_BUCKET
valueFrom:
secretKeyRef:
name: "{{ .Values.app.objectStorage.r2.secretName }}"
key: bucket
{{ end }}
{{ if .Values.app.oauth.google.enabled }}
- name: OAUTH_GOOGLE_CLIENT_ID
valueFrom:
secretKeyRef:
name: "{{ .Values.app.oauth.google.secretName }}"
key: clientId
- name: OAUTH_GOOGLE_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: "{{ .Values.app.oauth.google.secretName }}"
key: clientSecret
{{ end }}
{{ if .Values.app.oauth.github.enabled }}
- name: OAUTH_GITHUB_CLIENT_ID
valueFrom:
secretKeyRef:
name: "{{ .Values.app.oauth.github.secretName }}"
key: clientId
- name: OAUTH_GITHUB_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: "{{ .Values.app.oauth.github.secretName }}"
key: clientSecret
{{ end }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,7 @@
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.jwt.secretName }}"
type: Opaque
data:
{{- ( include "jwt.key" . ) | indent 2 -}}

View File

@@ -0,0 +1,34 @@
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "graphql.fullname" . }}-database-migration
labels:
{{- include "graphql.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "-1"
"helm.sh/hook-delete-policy": before-hook-creation
spec:
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
command: ["yarn", "prisma", "migrate", "deploy"]
env:
- name: NODE_ENV
value: "{{ .Values.env }}"
- name: DATABSE_PASSWORD
valueFrom:
secretKeyRef:
name: pg-postgresql
key: postgres-password
- name: DATABASE_URL
value: postgres://{{ .Values.database.user }}:$(DATABSE_PASSWORD)@{{ .Values.database.url }}:{{ .Values.database.port }}/{{ .Values.database.name }}
resources:
requests:
cpu: '100m'
memory: '200Mi'
restartPolicy: Never
backoffLimit: 1

View File

@@ -0,0 +1,10 @@
{{- if .Values.app.oauth.github.enabled -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.oauth.github.secretName }}"
type: Opaque
data:
{{- ( include "objectStorage.oauth.github" . ) | indent 2 -}}
{{- end }}

View File

@@ -0,0 +1,10 @@
{{- if .Values.app.oauth.google.enabled -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.oauth.google.secretName }}"
type: Opaque
data:
{{- ( include "objectStorage.oauth.google" . ) | indent 2 -}}
{{- end }}

View File

@@ -0,0 +1,9 @@
{{- if .Values.app.objectStorage.r2.enabled -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.objectStorage.r2.secretName }}"
type: Opaque
data:
{{- ( include "objectStorage.r2" . ) | indent 2 -}}
{{- end }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "graphql.fullname" . }}
labels:
{{- include "graphql.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "graphql.selectorLabels" . | nindent 4 }}

View File

@@ -0,0 +1,12 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "graphql.serviceAccountName" . }}
labels:
{{- include "graphql.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "graphql.fullname" . }}-test-connection"
labels:
{{- include "graphql.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "graphql.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

View File

@@ -0,0 +1,69 @@
replicaCount: 1
image:
repository: ghcr.io/toeverything/affine-graphql
pullPolicy: IfNotPresent
tag: ''
imagePullSecrets: []
nameOverride: ''
fullnameOverride: ''
# map to NODE_ENV environment variable
env: 'production'
database:
user: 'postgres'
url: 'pg-postgresql'
port: '5432'
name: 'affine'
app:
# AFFINE_SERVER_SUB_PATH
path: ''
# AFFINE_SERVER_HOST
host: '0.0.0.0'
jwt:
secretName: jwt-private-key
# base64 encoded ecdsa private key
privateKey: ''
objectStorage:
r2:
enabled: false
secretName: r2
accountId: ''
accessKeyId: ''
secretAccessKey: ''
bucket: ''
oauth:
google:
enabled: false
secretName: oauth-google
clientId: ''
clientSecret: ''
github:
enabled: false
secretName: oauth-github
clientId: ''
clientSecret: ''
serviceAccount:
create: true
annotations: {}
name: 'affine-graphql'
podAnnotations: {}
podSecurityContext:
fsGroup: 2000
resources:
limits:
cpu: '2000m'
memory: 4Gi
requests:
cpu: '1000m'
memory: 2Gi
probe:
initialDelaySeconds: 20
nodeSelector: {}
tolerations: []
affinity: {}

View File

@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@@ -0,0 +1,6 @@
apiVersion: v2
name: web
description: A Helm chart for Kubernetes
type: application
version: 0.0.0
appVersion: "0.7.0-canary.18"

View File

@@ -0,0 +1,16 @@
1. Get the application URL by running these commands:
{{- if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "web.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "web.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "web.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "web.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}

View File

@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "web.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "web.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "web.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "web.labels" -}}
helm.sh/chart: {{ include "web.chart" . }}
{{ include "web.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "web.selectorLabels" -}}
app.kubernetes.io/name: {{ include "web.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "web.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "web.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,57 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "web.fullname" . }}
labels:
{{- include "web.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "web.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "web.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "web.serviceAccountName" . }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "web.fullname" . }}
labels:
{{- include "web.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "web.selectorLabels" . | nindent 4 }}

View File

@@ -0,0 +1,12 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "web.serviceAccountName" . }}
labels:
{{- include "web.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "web.fullname" . }}-test-connection"
labels:
{{- include "web.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "web.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

View File

@@ -0,0 +1,37 @@
replicaCount: 1
image:
repository: ghcr.io/toeverything/affine-front
pullPolicy: IfNotPresent
tag: ""
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
annotations: {}
name: "affine-web"
podAnnotations: {}
podSecurityContext:
fsGroup: 2000
resources:
limits:
cpu: '500m'
memory: 2Gi
requests:
cpu: '500m'
memory: 2Gi
nodeSelector: {}
tolerations: []
affinity: {}
probe:
initialDelaySeconds: 1

View File

@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "affine.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "affine.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "affine.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "affine.labels" -}}
helm.sh/chart: {{ include "affine.chart" . }}
{{ include "affine.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "affine.selectorLabels" -}}
app.kubernetes.io/name: {{ include "affine.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "affine.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "affine.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,64 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "affine.fullname" . -}}
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "affine.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
- host: "{{ .Values.ingress.host }}"
http:
paths:
- path: /graphql
pathType: Prefix
backend:
service:
name: affine-graphql
port:
number: {{ .Values.graphql.service.port }}
- path: /api
pathType: Prefix
backend:
service:
name: affine-graphql
port:
number: {{ .Values.graphql.service.port }}
- path: /
pathType: Prefix
backend:
service:
name: affine-web
port:
number: {{ .Values.web.service.port }}
{{- end }}

17
.github/helm/affine/values.yaml vendored Normal file
View File

@@ -0,0 +1,17 @@
ingress:
enabled: false
className: ''
annotations:
kubernetes.io/ingress.class: nginx
host: affine.pro
tls: []
graphql:
service:
type: ClusterIP
port: 3000
web:
service:
type: ClusterIP
port: 8080

60
.github/helm/deployment_guide.md vendored Normal file
View File

@@ -0,0 +1,60 @@
# Cluster Deployment Guide
This document provides a step-by-step guide for developers on how to deploy services in a Kubernetes cluster. The following content assumes that the reader already has a basic understanding of Kubernetes concepts and operations.
### 1. Configure Service Mesh (Optional)
In the Kubernetes cluster, we optionally use Service Mesh (like Istio and Anthos Service Mesh) to manage the network interactions of microservices. If Service Mesh is already deployed on your cluster or do not need to use the service network, you can skip this step. In this step, we assume that you are using Google Kubernetes Engine (GKE) and have already installed Anthos Service Mesh on your cluster, if you wish to use another Ingress Controller, please refer to the relevant documentation.
To configure your kubectl context to interact with your Kubernetes cluster using the gcloud tool, you need to execute the following commands:
```sh
export CLUSTER_NAME=your_cluster_name
export REGION=your_cluster_region
export PROJECT=your_project_id
gcloud container clusters get-credentials $CLUSTER_NAME --region $REGION --project $PROJECT
```
In this command, you should replace `CLUSTER_NAME`, `REGION` and `PROJECT` with the actual name, region and project id of your Kubernetes cluster. This command retrieves the access credentials for your Kubernetes cluster and automatically configures kubectl to use these credentials.
Now, to inject Service Mesh for a specific Namespace, first, set the environment variable `NAMESPACE` that should correspond to your target Kubernetes Namespace. In this example, we use `prod` as the target Namespace:
```sh
export NAMESPACE=prod
```
Then, we label the Namespace which will enable Istio to automatically inject the sidecar container for all new Pods under this Namespace:
```sh
kubectl label namespace $NAMESPACE istio-injection- istio.io/rev=asm-managed --overwrite
```
Finally, we trigger the Kubernetes Deployment restart mechanism to allow existing Pods to also obtain sidecar container injection:
```sh
kubectl rollout restart deployment -n $NAMESPACE
```
### 2. Deploying the Application
Next, we will deploy our application in the Kubernetes cluster through Helm. First, set relevant environment variables:
```sh
export NAMESPACE=prod
export RELEASE=affine-cloud-prod
export PATH=.github/helm/affine-cloud
```
- `NAMESPACE` should be consistent with the first step, indicating your target Kubernetes Namespace.
- `RELEASE` is the name of your Helm release.
- `PATH` is the location of your Helm chart in your file system.
Finally, use the `helm upgrade --install` command to deploy or upgrade your application:
```sh
helm upgrade --namespace $NAMESPACE --create-namespace --install $RELEASE $PATH
```
This command creates (if it doesn't already exist) and deploys your Helm chart in the specified Namespace. If the release already exists, it will be upgraded.
The above are the complete steps for deploying an application in a Kubernetes cluster. Make sure all prerequisites are met before deploying, and also ensure that you have the correct permissions for operations in Kubernetes.

16
.github/labeler.yml vendored
View File

@@ -8,14 +8,20 @@ test:
- '**/tests/**/*'
- '**/__tests__/**/*'
plugin:copilot:
- 'plugins/copilot/**/*'
mod:dev:
- 'scripts/**/*'
- 'packages/cli/**/*'
- 'packages/debug/**/*'
mod:plugin:
- 'plugins/**/*'
plugin:bookmark-block:
- 'plugins/bookmark-block/**/*'
plugin:copilot:
- 'plugins/copilot/**/*'
mod:plugin-infra:
- 'packages/plugin-infra/**/*'
@@ -29,6 +35,10 @@ mod:hooks: 'packages/hooks/**/*'
mod:component: 'packages/component/**/*'
mod:storage: 'packages/storage/**/*'
mod:native: 'packages/native/**/*'
mod:store:
- 'packages/jotai/**/*'
- '**/atoms/**/*'

View File

@@ -13,6 +13,7 @@ on:
- '!.github/actions/build-rust/action.yml'
- '!.github/actions/setup-node/action.yml'
pull_request:
merge_group:
branches:
- master
- v[0-9]+.[0-9]+.x-staging
@@ -26,6 +27,7 @@ on:
env:
DEBUG: napi:*
BUILD_TYPE: canary
APP_NAME: affine
COVERAGE: true
MACOSX_DEPLOYMENT_TARGET: '10.13'
@@ -41,12 +43,27 @@ jobs:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Run checks
with:
electron-install: false
- name: Run i18n codegen
run: yarn i18n-codegen gen
- name: Run Type Check
run: yarn typecheck
- name: Run ESLint
run: yarn lint:eslint --max-warnings=0
- name: Run Prettier
# Set nmMode in `actions/setup-node` will modify the .yarnrc.yml
run: |
yarn i18n-codegen gen
yarn typecheck
yarn lint --max-warnings=0
yarn circular
git checkout .yarnrc.yml
yarn lint:prettier
- name: Run circular
run: yarn circular
- name: Upload server dist
uses: actions/upload-artifact@v3
with:
name: server-dist
path: ./apps/server/dist
if-no-files-found: error
build-docs:
name: Build Docs
@@ -57,6 +74,8 @@ jobs:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
- run: yarn nx build @affine/docs
env:
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
@@ -70,6 +89,8 @@ jobs:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
- run: yarn nx build @affine/storybook
env:
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
@@ -77,22 +98,13 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: storybook
path: ./packages/storybook/storybook-static
path: ./apps/storybook/storybook-static
if-no-files-found: error
build-web:
name: Build @affine/web
runs-on: ubuntu-latest
environment: development
env:
API_SERVER_PROFILE: local
ENABLE_DEBUG_PAGE: 1
ENABLE_PLUGIN: true
ENABLE_ALL_PAGE_FILTER: true
ENABLE_LEGACY_PROVIDER: true
ENABLE_PRELOADING: false
ENABLE_NEW_SETTING_MODAL: false
ENABLE_SQLITE_PROVIDER: false
steps:
- uses: actions/checkout@v3
@@ -101,34 +113,6 @@ jobs:
- name: Build Web
run: yarn nx build @affine/web
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: next-js
path: ./apps/web/.next
if-no-files-found: error
build-web-desktop:
name: Build @affine/web (Desktop)
runs-on: ubuntu-latest
environment: development
env:
API_SERVER_PROFILE: affine
ENABLE_DEBUG_PAGE: 1
ENABLE_PLUGIN: true
ENABLE_ALL_PAGE_FILTER: true
ENABLE_LEGACY_PROVIDER: false
ENABLE_PRELOADING: false
ENABLE_NEW_SETTING_MODAL: false
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Export static resources
run: yarn nx export @affine/web
- name: Upload static resources artifact
uses: actions/upload-artifact@v3
with:
name: next-js-static
@@ -174,10 +158,24 @@ jobs:
working-directory: apps/server
env:
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
target: 'x86_64-unknown-linux-gnu'
- name: Build Storage
run: yarn build:storage
- name: Run server tests
run: yarn nx test:coverage @affine/server
run: yarn test:coverage
working-directory: apps/server
env:
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
- name: Upload storage.node
uses: actions/upload-artifact@v3
with:
name: storage.node
path: ./packages/storage/storage.node
if-no-files-found: error
- name: Upload server test coverage results
uses: codecov/codecov-action@v3
with:
@@ -185,7 +183,7 @@ jobs:
files: ./apps/server/.coverage/lcov.info
flags: server-test
name: affine
fail_ci_if_error: true
fail_ci_if_error: false
storybook-test:
name: Storybook Test
@@ -198,13 +196,14 @@ jobs:
uses: ./.github/actions/setup-node
with:
playwright-install: true
electron-install: false
- name: Download storybook artifact
uses: actions/download-artifact@v3
with:
name: storybook
path: ./packages/storybook/storybook-static
path: ./apps/storybook/storybook-static
- name: Run storybook tests
working-directory: ./packages/storybook
working-directory: ./apps/storybook
run: |
yarn exec concurrently -k -s first -n "SB,TEST" -c "magenta,blue" "yarn exec serve ./storybook-static -l 6006" "yarn exec wait-on tcp:6006 && yarn test"
@@ -214,18 +213,9 @@ jobs:
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
shard: [1, 2, 3, 4, 5]
environment: development
needs: [build-web, build-storybook]
services:
octobase:
image: ghcr.io/toeverything/cloud-self-hosted:nightly-latest
ports:
- 3000:3000
env:
SIGN_KEY: 'test123'
RUST_LOG: 'debug'
JWST_DEV: '1'
needs: build-web
steps:
- uses: actions/checkout@v3
@@ -233,24 +223,16 @@ jobs:
uses: ./.github/actions/setup-node
with:
playwright-install: true
electron-install: false
- name: Download artifact
uses: actions/download-artifact@v3
with:
name: next-js
path: ./apps/web/.next
- name: Download storybook artifact
uses: actions/download-artifact@v3
with:
name: storybook
path: ./packages/storybook/storybook-static
- name: Wait for Octobase Ready
run: |
node ./scripts/wait-3000-healthz.mjs
name: next-js-static
path: ./apps/web/out
- name: Run playwright tests
run: yarn e2e --forbid-only --shard=${{ matrix.shard }}/${{ strategy.job-total }}
working-directory: tests/affine-local
env:
COVERAGE: true
@@ -264,7 +246,7 @@ jobs:
files: ./.coverage/lcov.info
flags: e2etest
name: affine
fail_ci_if_error: true
fail_ci_if_error: false
- name: Upload test results
if: ${{ failure() }}
@@ -278,6 +260,7 @@ jobs:
name: E2E Migration Test
runs-on: ubuntu-latest
environment: development
needs: [build-web]
steps:
- uses: actions/checkout@v3
@@ -285,6 +268,13 @@ jobs:
uses: ./.github/actions/setup-node
with:
playwright-install: true
electron-install: false
- name: Download next static
uses: actions/download-artifact@v3
with:
name: next-js-static
path: ./apps/web/out
- name: Unzip
run: yarn unzip
@@ -294,6 +284,18 @@ jobs:
run: yarn e2e --forbid-only
working-directory: ./tests/affine-legacy/0.7.0-canary.18
- name: Run vitest
run: yarn test
working-directory: ./tests/affine-legacy/0.7.0-canary.18
- name: Upload test results
if: ${{ failure() }}
uses: actions/upload-artifact@v3
with:
name: test-results-e2e-migration
path: ./tests/affine-legacy/0.7.0-canary.18/test-results
if-no-files-found: ignore
desktop-test:
name: Desktop Test
runs-on: ${{ matrix.spec.os }}
@@ -331,13 +333,14 @@ jobs:
target: x86_64-pc-windows-msvc,
test: true,
}
needs: [build-web-desktop]
needs: [build-web]
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
playwright-install: true
hard-link-nm: false
- name: Build AFFiNE native
uses: ./.github/actions/build-rust
with:
@@ -349,13 +352,18 @@ jobs:
run: yarn nx test @affine/monorepo
env:
NATIVE_TEST: 'true'
- name: Build layers
run: yarn workspace @affine/electron build
- name: Download static resource artifact
uses: actions/download-artifact@v3
with:
name: next-js-static
path: ./apps/electron/resources/web-static
path: apps/electron/resources/web-static
- name: Build Plugins
run: yarn run build:plugins
- name: Build Desktop Layers
run: yarn workspace @affine/electron build
- name: Run desktop tests
if: ${{ matrix.spec.test && matrix.spec.os == 'ubuntu-latest' }}
@@ -369,6 +377,17 @@ jobs:
env:
COVERAGE: true
- name: Make bundle
if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }}
run: yarn workspace @affine/electron make --platform=darwin --arch=arm64
- name: Bundle output check
if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }}
run: |
./scripts/unzip-macos-arm64.sh
yarn ts-node-esm ./scripts/macos-arm64-output-check.mts
working-directory: apps/electron
- name: Collect code coverage report
if: ${{ matrix.spec.test }}
run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov
@@ -381,7 +400,7 @@ jobs:
files: ./.coverage/lcov.info
flags: e2etest-${{ matrix.spec.os }}-${{ matrix.spec.arch }}
name: affine
fail_ci_if_error: true
fail_ci_if_error: false
- name: Upload test results
if: ${{ failure() }}
@@ -395,20 +414,12 @@ jobs:
name: Unit Test
runs-on: ubuntu-latest
environment: development
services:
octobase:
image: ghcr.io/toeverything/cloud-self-hosted:nightly-latest
ports:
- 3000:3000
env:
SIGN_KEY: 'test123'
RUST_LOG: 'debug'
JWST_DEV: '1'
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
- name: Unit Test
run: yarn nx test:coverage @affine/monorepo
@@ -420,13 +431,15 @@ jobs:
files: ./.coverage/store/lcov.info
flags: unittest
name: affine
fail_ci_if_error: true
fail_ci_if_error: false
build-docker:
if: github.ref == 'refs/heads/master'
name: Build Docker
needs:
- build-web-desktop
- lint
- desktop-test
- server-test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -435,6 +448,16 @@ jobs:
with:
name: next-js-static
path: ./apps/web/out
- name: Download server dist
uses: actions/download-artifact@v3
with:
name: server-dist
path: ./apps/server/dist
- name: Download storage.node
uses: actions/download-artifact@v3
with:
name: storage.node
path: ./apps/server
- name: Setup Git short hash
run: |
echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
@@ -460,14 +483,21 @@ jobs:
file: .github/deployment/front/Dockerfile
tags: ghcr.io/toeverything/affine-front:${{ env.GIT_SHORT_HASH }},ghcr.io/toeverything/affine-front:latest
# setup node without cache configuration
# Prisma cache is not compatible with docker build cache
- name: Setup Node.js
uses: ./.github/actions/setup-node
uses: actions/setup-node@v3
with:
package-install: false
node-version-file: '.nvmrc'
registry-url: https://npm.pkg.github.com
scope: '@toeverything'
- name: Install Node.js dependencies
run: yarn workspaces focus @affine/server --production
- name: Generate Prisma client
run: yarn workspace @affine/server prisma generate
- name: Build graphql Dockerfile
uses: docker/build-push-action@v4
with:

18
.github/workflows/cancel.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: Cancel
on:
pull_request_target:
types:
- edited
- synchronize
jobs:
cancel:
name: 'Cancel Previous Runs'
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- uses: styfle/cancel-workflow-action@0.11.0
with:
# See https://api.github.com/repos/toeverything/AFFiNE/actions/workflows
workflow_id: 44038251, 61883931
access_token: ${{ github.token }}

View File

@@ -15,6 +15,7 @@ on:
push:
branches: [master]
pull_request:
merge_group:
# The branches below must be a subset of the branches above
branches: [master]

View File

@@ -3,8 +3,8 @@ name: Release Charts
on:
push:
branches: [master]
pull_request:
branches: [master]
paths:
- '.github/helm/**/Chart.yml'
jobs:
release:
@@ -39,19 +39,24 @@ jobs:
- name: Package charts
working-directory: .helm-chart-repo
run: |
set -ex
git config --local user.name "$GITHUB_ACTOR"
git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com"
mkdir -p .cr-index
owner=$(cut -d '/' -f 1 <<< '${{ github.repository }}')
repo=helm-charts
git_hash=$(git rev-parse HEAD)
echo $git_hash
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm dependencies build ../.github/helm/affine
helm dependencies build ../.github/helm/affine-cloud
cr package ../.github/helm/affine
cr package ../.github/helm/affine-cloud
- name: Publish charts
working-directory: .helm-chart-repo
run: |
set -ex
git config --local user.name "$GITHUB_ACTOR"
git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com"
owner=$(cut -d '/' -f 1 <<< '${{ github.repository }}')
repo=helm-charts
git_hash=$(git rev-parse HEAD)
cr upload --commit "$git_hash" \
--git-repo "$repo" --owner "$owner" \
--token '${{ secrets.HELM_RELEASER_TOKEN }}' \

View File

@@ -11,6 +11,8 @@ on:
- README.md
- .github/**
- '!.github/workflows/nightly-build.yml'
- '!.github/actions/build-rust/action.yml'
- '!.github/actions/setup-rust/action.yml'
- '!.github/actions/setup-node/action.yml'
permissions:
@@ -32,7 +34,7 @@ jobs:
runs-on: ubuntu-latest
environment: production
outputs:
version: 0.0.0-${{ steps.version.outputs.version }}
version: 0.0.0-internal.${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v3
- uses: toeverything/set-build-version@latest
@@ -58,10 +60,6 @@ jobs:
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
NEXT_PUBLIC_SENTRY_DSN: ${{ secrets.NEXT_PUBLIC_SENTRY_DSN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
API_SERVER_PROFILE: prod
ENABLE_TEST_PROPERTIES: false
ENABLE_BOOKMARK_OPERATION: true
ENABLE_SQLITE_PROVIDER: false
RELEASE_VERSION: ${{ needs.set-build-version.outputs.version }}
- name: Upload Artifact (web-static)
@@ -125,7 +123,10 @@ jobs:
name: before-make-web-static
path: apps/electron/resources/web-static
- name: Build layers
- name: Build Plugins
run: yarn run build:plugins
- name: Build Desktop Layers
run: yarn workspace @affine/electron build
- name: Signing By Apple Developer ID

54
.github/workflows/nx.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: NX
on:
push:
branches:
- master
- v[0-9]+.[0-9]+.x-staging
- v[0-9]+.[0-9]+.x
paths-ignore:
- README.md
- .github/**
- '!.github/workflows/nx.yml'
- '!.github/actions/build-rust/action.yml'
- '!.github/actions/setup-node/action.yml'
pull_request:
merge_group:
branches:
- master
- v[0-9]+.[0-9]+.x-staging
- v[0-9]+.[0-9]+.x
paths-ignore:
- README.md
- .github/**
- '!.github/workflows/nx.yml'
- '!.github/actions/build-rust/action.yml'
- '!.github/actions/setup-node/action.yml'
jobs:
main:
name: Nx Cloud - Main Job
uses: nrwl/ci/.github/workflows/nx-cloud-main.yml@v0.13.0
with:
runs-on: macos-latest
main-branch-name: master
number-of-agents: 5
init-commands: |
yarn exec nx-cloud start-ci-run --stop-agents-after="build" --agent-count=5
environment-variables: |
BUILD_TYPE=canary
# parallel-commands: |
# yarn exec nx-cloud record -- yarn exec nx format:check
parallel-commands-on-agents: |
yarn exec nx affected --target=build --parallel=5
timeout: 60
agents:
name: Nx Cloud - Agents
uses: nrwl/ci/.github/workflows/nx-cloud-agents.yml@v0.13.0
with:
runs-on: macos-latest
number-of-agents: 5
environment-variables: |
BUILD_TYPE=canary
timeout: 60

View File

@@ -1,6 +1,9 @@
name: Release Desktop App
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+-canary.[0-9]+'
workflow_dispatch:
inputs:
version:
@@ -28,14 +31,8 @@ permissions:
contents: write
security-events: write
concurrency:
# The concurrency group contains the workflow name and the branch name for
# pull requests or the commit hash for any other events.
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
cancel-in-progress: true
env:
BUILD_TYPE: ${{ github.event.inputs.build-type }}
BUILD_TYPE: ${{ github.event.inputs.build-type || (github.ref_type == 'tag' && contains(github.ref, 'canary') && 'canary') }}
DEBUG: napi:*
APP_NAME: affine
MACOSX_DEPLOYMENT_TARGET: '10.13'
@@ -43,22 +40,31 @@ env:
jobs:
before-make:
runs-on: ubuntu-latest
environment: ${{ github.ref_name == 'master' && 'production' || 'development' }}
environment: production
outputs:
RELEASE_VERSION: ${{ steps.get-canary-version.outputs.RELEASE_VERSION }}
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Get canary version
id: get-canary-version
if: ${{ github.ref_type == 'tag' }}
run: |
TAG_VERSION=${GITHUB_REF#refs/tags/v}
PACKAGE_VERSION=$(node -p "require('./apps/electron/package.json').version")
if [ "$TAG_VERSION" != "$PACKAGE_VERSION" ]; then
echo "Tag version ($TAG_VERSION) does not match package.json version ($PACKAGE_VERSION)"
exit 1
fi
echo "RELEASE_VERSION=$(node -p "require('./apps/electron/package.json').version")" >> $GITHUB_OUTPUT
- name: generate-assets
run: yarn workspace @affine/electron generate-assets
env:
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
API_SERVER_PROFILE: prod
ENABLE_TEST_PROPERTIES: false
ENABLE_BOOKMARK_OPERATION: true
ENABLE_SQLITE_PROVIDER: false
RELEASE_VERSION: ${{ github.event.inputs.version }}
RELEASE_VERSION: ${{ github.event.inputs.version || steps.get-canary-version.outputs.RELEASE_VERSION }}
- name: Upload Artifact (web-static)
uses: actions/upload-artifact@v3
@@ -67,7 +73,7 @@ jobs:
path: apps/electron/resources/web-static
make-distribution:
environment: ${{ github.ref_name == 'master' && 'production' || 'development' }}
environment: production
strategy:
# all combinations: macos-latest x64, macos-latest arm64, windows-latest x64, ubuntu-latest x64
matrix:
@@ -117,7 +123,10 @@ jobs:
name: before-make-web-static
path: apps/electron/resources/web-static
- name: Build layers
- name: Build Plugins
run: yarn run build:plugins
- name: Build Desktop Layers
run: yarn workspace @affine/electron build
- name: Signing By Apple Developer ID
@@ -158,7 +167,7 @@ jobs:
path: builds
release:
needs: make-distribution
needs: [before-make, make-distribution]
runs-on: ubuntu-latest
steps:
@@ -191,16 +200,16 @@ jobs:
cp ./apps/electron/scripts/generate-yml.js .
node generate-yml.js
env:
RELEASE_VERSION: ${{ github.event.inputs.version }}
RELEASE_VERSION: ${{ github.event.inputs.version || needs.before-make.outputs.RELEASE_VERSION }}
- name: Create Release Draft
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
with:
name: Desktop APP ${{ github.event.inputs.version }}
body: 'TODO: Add release notes here'
draft: ${{ github.event.inputs.is-draft }}
prerelease: ${{ github.event.inputs.is-pre-release }}
name: ${{ github.event.inputs.version || needs.before-make.outputs.RELEASE_VERSION }}
body: ''
draft: ${{ github.event.inputs.is-draft || true }}
prerelease: ${{ github.event.inputs.is-pre-release || needs.before-make.outputs.version }}
files: |
./VERSION
./*.zip

22
.github/workflows/workers.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: Deploy Cloudflare Worker
on:
push:
branches:
- master
paths:
- packages/workers/**
jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy
environment: production
steps:
- uses: actions/checkout@v2
- name: Publish
uses: cloudflare/wrangler-action@2.0.0
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
workingDirectory: 'packages/workers'

7
.gitignore vendored
View File

@@ -60,9 +60,10 @@ out/
storybook-static
i18n-generated.ts
/test-results/
/playwright-report/
/playwright/.cache/
test-results
playwright-report
playwright/.cache
download
# Cache
.eslintcache

View File

@@ -2,7 +2,22 @@
. "$(dirname -- "$0")/_/husky.sh"
# check lockfile is up to date
yarn install
yarn install --mode=skip-build --inline-builds --immutable
# build infra code
yarn -T run build:infra
# generate prisma client type
yarn workspace @affine/server prisma generate
# generate i18n
yarn i18n-codegen gen
# lint staged files
yarn exec lint-staged
# type check
yarn typecheck
# circular dependency check
yarn circular

1
.npmrc
View File

@@ -1,2 +1,3 @@
shell-emulator=true
electron_mirror="https://cdn.npmmirror.com/binaries/electron/"
engine-strict=true

View File

@@ -1,5 +1,14 @@
pnpm-lock.yaml
yarn.lock
target
lib
test-results
packages/i18n/src/i18n-generated.ts
packages/graphql/src/graphql/index.ts
.next
out
dist
.yarn
tests/affine-legacy/0.7.0-canary.18/static
.github/helm
_next
storybook-static

View File

@@ -2,13 +2,13 @@
<h1 style="border-bottom: none">
<b><a href="https://affine.pro">AFFiNE.PRO</a></b><br />
The Next-Gen Collaborative Knowledge Base
Write, Draw and Plan All at Once
<br>
</h1>
<p>
AFFiNE is a next-gen knowledge base that brings planning, sorting and creating all together.<br />
Privacy first, open-source, customizable and ready to use - a free replacement for Notion & Miro. <br />
One hyper-fused platform for wildly creative minds. <br />
A privacy-focussed, local-first, open-source, and ready-to-use alternative for Notion & Miro.
</p>
</div>
@@ -60,7 +60,7 @@ See https://github.com/all-?/all-contributors/issues/361#issuecomment-637166066
<br />
<div align="center">
<em>See docs, canvas and tables are hyper merged with AFFiNE - just like the word affine (əˈɪn | a-fine).</em>
<em>Docs, canvas and tables are hyper-merged with AFFiNE - just like the word affine (əˈɪn | a-fine).</em>
</div>
<br />
@@ -123,6 +123,8 @@ If you have questions, you are welcome to contact us. One of the best places to
## Plugins
> Plugins are a way to extend the functionality of AFFiNE.
>
> (Currently, plugins are under heavy development, and the SDK is not yet available.)
| Name | |
| ------------------------------------------------ | ----------------------------------------- |
@@ -135,7 +137,7 @@ We would also like to give thanks to open-source projects that make AFFiNE possi
- [BlockSuite](https://github.com/toeverything/BlockSuite) - 💠 BlockSuite is the open-source collaborative editor project behind AFFiNE.
- [OctoBase](https://github.com/toeverything/OctoBase) - 🐙 OctoBase is the open-source database behind AFFiNE, local-first, yet collaborative. A light-weight, scalable, data engine written in Rust.
- [Yjs](https://github.com/yjs/yjs) & [Yrs](https://github.com/y-crdt/y-crdt) - Fundamental support of CRDTs for our implementation on state management and data sync.
- [Yjs](https://github.com/yjs/yjs) - Fundamental support of CRDTs for our implementation on state management and data sync.
- [Electron](https://github.com/electron/electron) - Build cross-platform desktop apps with JavaScript, HTML, and CSS.
- [React](https://github.com/facebook/react) - View layer support and web GUI framework.
- [Rust](https://github.com/rust-lang/rust) - High performance language that extends the ability and availability of our real-time backend, OctoBase.
@@ -156,7 +158,8 @@ We would like to express our gratitude to all the individuals who have already c
## Self-Host
Get started with Docker and deploy your own feature-rich, restriction-free deployment of AFFiNE - check the [latest packages].
Get started with Docker and deploy your own feature-rich, restriction-free deployment of AFFiNE.
We are working hard to get this updated to the latest version, you can keep an eye on the [latest packages].
## Hiring
@@ -197,7 +200,7 @@ See [LICENSE] for details.
[rust-version-icon]: https://img.shields.io/badge/Rust-1.70.0-dea584
[stars-icon]: https://img.shields.io/github/stars/toeverything/AFFiNE.svg?style=flat&logo=github&colorB=red&label=stars
[codecov]: https://codecov.io/gh/toeverything/affine/branch/master/graphs/badge.svg?branch=master
[node-version-icon]: https://img.shields.io/badge/node-%3E=18.16.0-success
[node-version-icon]: https://img.shields.io/badge/node-%3E=18.16.1-success
[typescript-version-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/affine/dev/typescript
[react-version-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/affine/dev/react?color=rgb%2897%2C%20218%2C%20251%29
[react-version-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/AFFiNE/react?filename=apps%2Fweb%2Fpackage.json&color=rgb(97%2C228%2C251)
[blocksuite-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/AFFiNE/@blocksuite/store?color=6880ff&filename=apps%2Fweb%2Fpackage.json&label=blocksuite

25
apps/README.md Normal file
View File

@@ -0,0 +1,25 @@
# Apps structure
> This is the structure of the `apps` directory.
## docs
AFFiNE Developer Documentation using [waku](https://github.com/dai-shi/waku).
## electron
> `web` needs to be built before electron.
AFFiNE Desktop (macOS, Linux and Windows Distribution) using [Electron](https://www.electronjs.org/).
## server
Server using [Nest.js](https://nestjs.com/).
## storybook
Storybook using [Storybook](https://storybook.js.org/).
## web
AFFiNE Core Application using [React.js](https://reactjs.org/).

View File

@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />

View File

@@ -1,6 +1,6 @@
{
"name": "@affine/docs",
"version": "0.7.0-canary.20",
"version": "0.7.0",
"type": "module",
"private": true,
"scripts": {
@@ -10,26 +10,26 @@
},
"dependencies": {
"@affine/component": "workspace:*",
"@blocksuite/block-std": "0.0.0-20230627165830-836e6fd1-nightly",
"@blocksuite/blocks": "0.0.0-20230627165830-836e6fd1-nightly",
"@blocksuite/editor": "0.0.0-20230627165830-836e6fd1-nightly",
"@blocksuite/global": "0.0.0-20230627165830-836e6fd1-nightly",
"@blocksuite/lit": "0.0.0-20230627165830-836e6fd1-nightly",
"@blocksuite/store": "0.0.0-20230627165830-836e6fd1-nightly",
"@blocksuite/block-std": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/blocks": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/editor": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/global": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/lit": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/store": "0.0.0-20230717055529-79180930-nightly",
"express": "^4.18.2",
"jotai": "^2.2.1",
"react": "18.3.0-canary-8ec962d82-20230623",
"react-dom": "18.3.0-canary-8ec962d82-20230623",
"react-server-dom-webpack": "18.3.0-canary-8ec962d82-20230623",
"jotai": "^2.2.2",
"react": "18.3.0-canary-1fdacbefd-20230630",
"react-dom": "18.3.0-canary-1fdacbefd-20230630",
"react-server-dom-webpack": "18.3.0-canary-1fdacbefd-20230630",
"waku": "0.12.1"
},
"devDependencies": {
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
"@vanilla-extract/css": "^1.11.1",
"@vanilla-extract/css": "^1.12.0",
"@vanilla-extract/vite-plugin": "^3.8.2",
"autoprefixer": "^10.4.14",
"tailwindcss": "^3.3.2",
"typescript": "^5.1.3"
"typescript": "^5.1.6"
}
}

View File

@@ -13,3 +13,5 @@ resources/web-static
!.yarn/sdks
!.yarn/versions
dev.json
zip-out

View File

@@ -140,3 +140,44 @@ test('affine onboarding button', async ({ page }) => {
expect(await onboardingModal.isVisible()).toEqual(false);
});
test('windows only check', async ({ page }) => {
const windowOnlyUI = page.locator('[data-platform-target=win32]');
if (process.platform === 'win32') {
await expect(windowOnlyUI).toBeVisible();
} else {
await expect(windowOnlyUI).not.toBeVisible();
}
});
test('delete workspace', async ({ page }) => {
await page.getByTestId('current-workspace').click();
await page.getByTestId('add-or-new-workspace').click();
await page.getByTestId('new-workspace').click();
await page.getByTestId('create-workspace-default-location-button').click();
await page.getByTestId('create-workspace-input').type('Delete Me');
await page.getByTestId('create-workspace-create-button').click();
await page.getByTestId('create-workspace-continue-button').click();
await page.getByTestId('slider-bar-workspace-setting-button').click();
await page.getByTestId('current-workspace-label').click();
expect(await page.getByTestId('workspace-name-input').inputValue()).toBe(
'Delete Me'
);
const contentElement = await page.getByTestId('setting-modal-content');
const boundingBox = await contentElement.boundingBox();
if (!boundingBox) {
throw new Error('boundingBox is null');
}
await page.mouse.move(
boundingBox.x + boundingBox.width / 2,
boundingBox.y + boundingBox.height / 2
);
await page.mouse.wheel(0, 500);
await page.getByTestId('delete-workspace-button').click();
await page.getByTestId('delete-workspace-input').type('Delete Me');
await page.getByTestId('delete-workspace-confirm-button').click();
await page.waitForTimeout(1000);
expect(await page.getByTestId('workspace-name').textContent()).toBe(
'Demo Workspace'
);
});

View File

@@ -16,6 +16,8 @@ function generateUUID() {
return crypto.randomUUID();
}
type RoutePath = 'setting';
export const test = base.extend<{
page: Page;
electronApp: ElectronApplication;
@@ -24,9 +26,8 @@ export const test = base.extend<{
appData: string;
sessionData: string;
};
workspace: {
// get current workspace
current: () => Promise<any>; // todo: type
router: {
goto: (path: RoutePath) => Promise<void>;
};
}>({
page: async ({ electronApp }, use) => {
@@ -41,10 +42,6 @@ export const test = base.extend<{
});
});
}
const logFilePath = await page.evaluate(async () => {
// @ts-expect-error
return window.apis?.debug.logFilePath();
});
// wat for blocksuite to be loaded
await page.waitForSelector('v-line');
if (enableCoverage) {
@@ -71,10 +68,6 @@ export const test = base.extend<{
);
}
await page.close();
if (logFilePath) {
const logs = await fs.readFile(logFilePath, 'utf-8');
console.log(logs);
}
},
electronApp: async ({}, use) => {
// a random id to avoid conflicts between tests
@@ -124,14 +117,4 @@ export const test = base.extend<{
});
await use(appInfo);
},
workspace: async ({ page }, use) => {
await use({
current: async () => {
return await page.evaluate(async () => {
// @ts-expect-error
return globalThis.currentWorkspace;
});
},
});
},
});

View File

@@ -5,7 +5,7 @@ import fs from 'fs-extra';
import { test } from './fixture';
test.skip('check workspace has a DB file', async ({ appInfo, workspace }) => {
test('check workspace has a DB file', async ({ appInfo, workspace }) => {
const w = await workspace.current();
const dbPath = path.join(
appInfo.sessionData,
@@ -19,9 +19,11 @@ test.skip('check workspace has a DB file', async ({ appInfo, workspace }) => {
test.skip('move workspace db file', async ({ page, appInfo, workspace }) => {
const w = await workspace.current();
const settingButton = page.getByTestId('slider-bar-workspace-setting-button');
// goto settings
await settingButton.click();
await page.getByTestId('slider-bar-workspace-setting-button').click();
await expect(page.getByTestId('setting-modal')).toBeVisible();
// goto workspace setting
await page.getByTestId('workspace-list-item').click();
const tmpPath = path.join(appInfo.sessionData, w.id + '-tmp-dir');
@@ -42,21 +44,26 @@ test.skip('move workspace db file', async ({ page, appInfo, workspace }) => {
expect(files.some(f => f.endsWith('.affine'))).toBe(true);
});
test.skip('export then add', async ({ page, appInfo, workspace }) => {
test('export then add', async ({ page, appInfo, workspace }) => {
const w = await workspace.current();
const settingButton = page.getByTestId('slider-bar-workspace-setting-button');
// goto settings
await settingButton.click();
await page.getByTestId('slider-bar-workspace-setting-button').click();
await expect(page.getByTestId('setting-modal')).toBeVisible();
const originalId = w.id;
const newWorkspaceName = 'new-test-name';
// goto workspace setting
await page.getByTestId('workspace-list-item').click();
await page.waitForTimeout(500);
// change workspace name
await page.getByTestId('workspace-name-input').fill(newWorkspaceName);
await page.getByTestId('save-workspace-name').click();
await page.waitForSelector('text="Update workspace name success"');
await page.click('[data-tab-key="export"]');
await page.waitForTimeout(500);
const tmpPath = path.join(appInfo.sessionData, w.id + '-tmp.db');
@@ -73,10 +80,11 @@ test.skip('export then add', async ({ page, appInfo, workspace }) => {
expect(await fs.exists(tmpPath)).toBe(true);
await page.getByTestId('modal-close-button').click();
// add workspace
// we are reusing the same db file so that we don't need to maintain one
// in the codebase
await page.getByTestId('current-workspace').click();
await page.getByTestId('add-or-new-workspace').click();

View File

@@ -26,6 +26,8 @@ const arch =
? process.argv[process.argv.indexOf('--arch') + 1]
: process.arch;
const windowsIconUrl = `https://cdn.affine.pro/app-icons/icon_${buildType}.ico`;
/**
* @type {import('@electron-forge/shared-types').ForgeConfig}
*/
@@ -95,6 +97,7 @@ module.exports = {
config: {
name: 'AFFiNE',
setupIcon: icoPath,
iconUrl: windowsIconUrl,
loadingGif: './resources/icons/affine_installing.gif',
},
},

View File

@@ -1,7 +1,7 @@
{
"name": "@affine/electron",
"private": true,
"version": "0.7.0-canary.20",
"version": "0.7.0",
"author": "affine",
"repository": {
"url": "https://github.com/toeverything/AFFiNE",
@@ -11,28 +11,25 @@
"homepage": "https://github.com/toeverything/AFFiNE",
"scripts": {
"dev": "yarn cross-env DEV_SERVER_URL=http://localhost:8080 node scripts/dev.mjs",
"watch": "yarn cross-env DEV_SERVER_URL=http://localhost:8080 node scripts/dev.mjs --watch",
"prod": "yarn node scripts/dev.mjs",
"build": "zx scripts/build-layers.mjs",
"dev:prod": "yarn node scripts/dev.mjs",
"build": "NODE_ENV=production zx scripts/build-layers.mjs",
"generate-assets": "zx scripts/generate-assets.mjs",
"package": "electron-forge package",
"make": "electron-forge make",
"test": "DEBUG=pw:browser playwright test"
"test": "DEBUG=pw:browser yarn -T run playwright test -c ./playwright.config.ts"
},
"config": {
"forge": "./forge.config.js"
},
"main": "./dist/main.js",
"exports": {
"./scripts/plugins/build-plugins.mjs": "./scripts/plugins/build-plugins.mjs"
},
"devDependencies": {
"@affine-test/kit": "workspace:*",
"@affine/env": "workspace:*",
"@affine/native": "workspace:*",
"@blocksuite/blocks": "0.0.0-20230627165830-836e6fd1-nightly",
"@blocksuite/editor": "0.0.0-20230627165830-836e6fd1-nightly",
"@blocksuite/lit": "0.0.0-20230627165830-836e6fd1-nightly",
"@blocksuite/store": "0.0.0-20230627165830-836e6fd1-nightly",
"@blocksuite/blocks": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/editor": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/lit": "0.0.0-20230717055529-79180930-nightly",
"@blocksuite/store": "0.0.0-20230717055529-79180930-nightly",
"@electron-forge/cli": "^6.2.1",
"@electron-forge/core": "^6.2.1",
"@electron-forge/core-utils": "^6.2.1",
@@ -50,10 +47,9 @@
"electron-log": "^5.0.0-beta.24",
"electron-squirrel-startup": "1.0.0",
"electron-window-state": "^5.0.3",
"esbuild": "^0.18.9",
"esbuild": "^0.18.11",
"fs-extra": "^11.1.1",
"jotai": "^2.2.1",
"playwright": "=1.33.0",
"jotai": "^2.2.2",
"ts-node": "^10.9.1",
"undici": "^5.22.1",
"uuid": "^9.0.0",
@@ -63,7 +59,7 @@
"dependencies": {
"@toeverything/plugin-infra": "workspace:*",
"async-call-rpc": "^6.3.1",
"electron-updater": "^5.3.0",
"electron-updater": "^6.0.0",
"link-preview-js": "^3.0.4",
"lodash-es": "^4.17.21",
"nanoid": "^4.0.2",
@@ -85,7 +81,6 @@
"hoistingLimits": "workspaces"
},
"peerDependencies": {
"playwright": "*",
"ts-node": "*"
}
}

View File

@@ -11,7 +11,7 @@ import type { PlaywrightTestConfig } from '@playwright/test';
* See https://playwright.dev/docs/test-configuration.
*/
const config: PlaywrightTestConfig = {
testDir: './tests',
testDir: './e2e',
testIgnore: '**/lib/**',
fullyParallel: true,
timeout: process.env.CI ? 50_000 : 30_000,

View File

@@ -0,0 +1,24 @@
{
"name": "@affine/electron",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"root": "apps/electron",
"sourceRoot": "apps/electron/src",
"targets": {
"build": {
"executor": "nx:run-script",
"dependsOn": [
{
"projects": ["@affine/bookmark-block"],
"target": "build",
"params": "ignore"
},
"^build"
],
"options": {
"script": "build"
},
"outputs": ["{projectRoot}/dist"]
}
}
}

View File

@@ -1,12 +1,9 @@
#!/usr/bin/env zx
import 'zx/globals';
import { resolve } from 'node:path';
import { spawnSync } from 'child_process';
import * as esbuild from 'esbuild';
import { config, rootDir } from './common.mjs';
import { config } from './common.mjs';
const NODE_ENV =
process.env.NODE_ENV === 'development' ? 'development' : 'production';
@@ -18,20 +15,10 @@ if (process.platform === 'win32') {
async function buildLayers() {
const common = config();
console.log('Build plugin infra');
spawnSync('yarn', ['build'], {
stdio: 'inherit',
cwd: resolve(rootDir, './packages/plugin-infra'),
});
console.log('Build plugins');
await import('./plugins/build-plugins.mjs');
await esbuild.build(common.workers);
await esbuild.build({
...common.layers,
define: {
...common.define,
'process.env.NODE_ENV': `"${NODE_ENV}"`,
'process.env.BUILD_TYPE': `"${process.env.BUILD_TYPE || 'stable'}"`,
},

View File

@@ -15,16 +15,9 @@ const DEV_SERVER_URL = process.env.DEV_SERVER_URL;
/** @type 'production' | 'development'' */
const mode = (process.env.NODE_ENV = process.env.NODE_ENV || 'development');
// List of env that will be replaced by esbuild
const ENV_MACROS = ['AFFINE_GOOGLE_CLIENT_ID', 'AFFINE_GOOGLE_CLIENT_SECRET'];
/** @return {{layers: import('esbuild').BuildOptions, workers: import('esbuild').BuildOptions}} */
export const config = () => {
const define = Object.fromEntries([
...ENV_MACROS.map(key => [
'process.env.' + key,
JSON.stringify(process.env[key] ?? ''),
]),
['process.env.NODE_ENV', `"${mode}"`],
['process.env.USE_WORKER', '"true"'],
]);
@@ -45,7 +38,12 @@ export const config = () => {
bundle: true,
target: `node${NODE_MAJOR_VERSION}`,
platform: 'node',
external: ['electron', 'electron-updater', '@toeverything/plugin-infra'],
external: [
'electron',
'electron-updater',
'@toeverything/plugin-infra',
'yjs',
],
define: define,
format: 'cjs',
loader: {

View File

@@ -1,13 +1,12 @@
/* eslint-disable no-async-promise-executor */
import { spawn } from 'node:child_process';
import { readFileSync } from 'node:fs';
import path, { resolve } from 'node:path';
import path from 'node:path';
import electronPath from 'electron';
import * as esbuild from 'esbuild';
import which from 'which';
import { config, electronDir, rootDir } from './common.mjs';
import { config, electronDir } from './common.mjs';
// this means we don't spawn electron windows, mainly for testing
const watchMode = process.argv.includes('--watch');
@@ -68,14 +67,6 @@ function spawnOrReloadElectron() {
}
const common = config();
const yarnPath = which.sync('yarn');
async function watchPlugins() {
spawn(yarnPath, ['dev'], {
stdio: 'inherit',
cwd: resolve(rootDir, './packages/plugin-infra'),
});
await import('./plugins/dev-plugins.mjs');
}
async function watchLayers() {
return new Promise(async resolve => {
@@ -134,7 +125,6 @@ async function watchWorkers() {
}
async function main() {
await watchPlugins();
await watchLayers();
await watchWorkers();

View File

@@ -43,7 +43,6 @@ cd(repoRootDir);
if (!process.env.SKIP_WEB_BUILD) {
process.env.ENABLE_LEGACY_PROVIDER = 'false';
await $`yarn nx build @affine/web`;
await $`yarn nx export @affine/web`;
// step 1.5: amend sourceMappingURL to allow debugging in devtools
await glob('**/*.{js,css}', { cwd: affineWebOutDir }).then(files => {

View File

@@ -0,0 +1,44 @@
import { fileURLToPath } from 'node:url';
import { readdir } from 'node:fs/promises';
const outputRoot = fileURLToPath(
new URL(
'../zip-out/AFFiNE-canary.app/Contents/Resources/app',
import.meta.url
)
);
const outputList = [
[
'dist',
[
'main.js',
'helper.js',
'preload.js',
'affine.darwin-arm64.node',
'plugins',
'workers',
],
],
['dist/plugins', ['bookmark-block']],
['dist/plugins/bookmark-block', ['index.mjs']],
['dist/workers', ['plugin.worker.js']],
[
'node_modules/@toeverything/plugin-infra/dist',
['manager.js', 'manager.cjs'],
],
['node_modules/@blocksuite/global/dist', ['utils.js']],
['node_modules/jotai', ['vanilla.js']],
] as [entry: string, expected: string[]][];
await Promise.all(
outputList.map(async ([entry, output]) => {
const files = await readdir(`${outputRoot}/${entry}`);
output.forEach(file => {
if (!files.includes(file)) {
throw new Error(`File ${entry}/${file} not found`);
}
});
})
);
console.log('Output check passed');

View File

@@ -0,0 +1,16 @@
#!/bin/bash
# Set the directory
dir="./out/canary/make/zip/darwin/arm64"
# Get the first file
file=$(ls -1 $dir | head -n 1)
# Check if file exists and is a zip file
if [ -f "$dir/$file" ] && [ ${file: -4} == ".zip" ]
then
# Unzip the file
unzip "$dir/$file" -d "zip-out"
else
echo "No zip file found"
fi

View File

@@ -1,10 +1,11 @@
import path from 'node:path';
import { setTimeout } from 'node:timers/promises';
import fs from 'fs-extra';
import { v4 } from 'uuid';
import { afterEach, beforeEach, expect, test, vi } from 'vitest';
import { removeWithRetry } from '../../../../tests/utils';
const tmpDir = path.join(__dirname, 'tmp');
const appDataPath = path.join(tmpDir, 'app-data');
@@ -44,11 +45,7 @@ beforeEach(() => {
afterEach(async () => {
existProcess();
// wait for the db to be closed on Windows
if (process.platform === 'win32') {
await setTimeout(200);
}
await fs.remove(tmpDir);
await removeWithRetry(tmpDir);
vi.useRealTimers();
});

View File

@@ -0,0 +1,69 @@
import path from 'node:path';
import { SqliteConnection } from '@affine/native';
import { afterEach, describe, expect, it, vi } from 'vitest';
import * as Y from 'yjs';
import { removeWithRetry } from '../../../../tests/utils';
import { copyToTemp, migrateToSubdocAndReplaceDatabase } from '../migration';
const tmpDir = path.join(__dirname, 'tmp');
const testDBFilePath = path.resolve(__dirname, 'old-db.affine');
const appDataPath = path.join(tmpDir, 'app-data');
vi.mock('../../main-rpc', () => ({
mainRPC: {
getPath: async () => appDataPath,
},
}));
afterEach(async () => {
await removeWithRetry(tmpDir);
});
describe('migrateToSubdocAndReplaceDatabase', () => {
it('should migrate and replace the database', async () => {
const copiedDbFilePath = await copyToTemp(testDBFilePath);
await migrateToSubdocAndReplaceDatabase(copiedDbFilePath);
const db = new SqliteConnection(copiedDbFilePath);
await db.connect();
// check if db has two rows, one for root doc and one for subdoc
const rows = await db.getAllUpdates();
expect(rows.length).toBe(2);
const rootUpdate = rows.find(row => row.docId === undefined)!.data;
const subdocUpdate = rows.find(row => row.docId !== undefined)!.data;
expect(rootUpdate).toBeDefined();
expect(subdocUpdate).toBeDefined();
// apply updates
const rootDoc = new Y.Doc();
Y.applyUpdate(rootDoc, rootUpdate);
// check if root doc has one subdoc
expect(rootDoc.subdocs.size).toBe(1);
// populates subdoc
Y.applyUpdate(rootDoc.subdocs.values().next().value, subdocUpdate);
// check if root doc's meta is correct
const meta = rootDoc.getMap('meta').toJSON();
expect(meta.workspaceVersion).toBe(1);
expect(meta.name).toBe('hiw');
expect(meta.pages.length).toBe(1);
const pageMeta = meta.pages[0];
expect(pageMeta.title).toBe('Welcome to AFFiNEd');
// get the subdoc through id
const subDoc = rootDoc
.getMap('spaces')
.get(`space:${pageMeta.id}`) as Y.Doc;
expect(subDoc).toEqual(rootDoc.subdocs.values().next().value);
await db.close();
});
});

Binary file not shown.

View File

@@ -5,6 +5,7 @@ import { v4 } from 'uuid';
import { afterEach, expect, test, vi } from 'vitest';
import * as Y from 'yjs';
import { removeWithRetry } from '../../../../tests/utils';
import { dbSubjects } from '../subjects';
const tmpDir = path.join(__dirname, 'tmp');
@@ -17,7 +18,7 @@ vi.doMock('../../main-rpc', () => ({
}));
afterEach(async () => {
await fs.remove(tmpDir);
await removeWithRetry(tmpDir);
});
let testYDoc: Y.Doc;

View File

@@ -119,6 +119,8 @@ export abstract class BaseSQLiteAdapter {
`[SQLiteAdapter][${this.role}] addUpdateToSQLite`,
'length:',
updates.length,
'docids',
updates.map(u => u.docId),
performance.now() - start,
'ms'
);

View File

@@ -0,0 +1,55 @@
import { resolve } from 'node:path';
import { migrateToSubdoc } from '@affine/env/blocksuite';
import { SqliteConnection } from '@affine/native';
import fs from 'fs-extra';
import { nanoid } from 'nanoid';
import * as Y from 'yjs';
import { mainRPC } from '../main-rpc';
export const migrateToSubdocAndReplaceDatabase = async (path: string) => {
const db = new SqliteConnection(path);
await db.connect();
const rows = await db.getAllUpdates();
const originalDoc = new Y.Doc();
// 1. apply all updates to the root doc
rows.forEach(row => {
Y.applyUpdate(originalDoc, row.data);
});
// 2. migrate using migrateToSubdoc
const migratedDoc = migrateToSubdoc(originalDoc);
// 3. replace db rows with the migrated doc
await replaceRows(db, migratedDoc, true);
// 4. close db
await db.close();
};
export const copyToTemp = async (path: string) => {
const tmpDirPath = resolve(await mainRPC.getPath('sessionData'), 'tmp');
const tmpFilePath = resolve(tmpDirPath, nanoid());
await fs.ensureDir(tmpDirPath);
await fs.copyFile(path, tmpFilePath);
return tmpFilePath;
};
async function replaceRows(
db: SqliteConnection,
doc: Y.Doc,
isRoot: boolean
): Promise<void> {
const migratedUpdates = Y.encodeStateAsUpdate(doc);
const docId = isRoot ? undefined : doc.guid;
const rows = [{ data: migratedUpdates, docId: docId }];
await db.replaceUpdates(docId, rows);
await Promise.all(
[...doc.subdocs].map(async subdoc => {
await replaceRows(db, subdoc, false);
})
);
}

View File

@@ -115,19 +115,43 @@ export class SecondaryWorkspaceSQLiteDB extends BaseSQLiteAdapter {
}
setupListener(docId?: string) {
logger.debug(
'SecondaryWorkspaceSQLiteDB:setupListener',
this.workspaceId,
docId
);
const doc = this.getDoc(docId);
if (!doc) {
const upstreamDoc = this.upstream.getDoc(docId);
if (!doc || !upstreamDoc) {
logger.warn(
'[SecondaryWorkspaceSQLiteDB] setupListener: doc not found',
docId
);
return;
}
const onUpstreamUpdate = (update: Uint8Array, origin: YOrigin) => {
if (origin === 'renderer') {
logger.debug(
'SecondaryWorkspaceSQLiteDB:onUpstreamUpdate',
origin,
this.workspaceId,
docId,
update.length
);
if (origin === 'renderer' || origin === 'self') {
// update to upstream yDoc should be replicated to self yDoc
this.applyUpdate(update, 'upstream', docId);
}
};
const onSelfUpdate = async (update: Uint8Array, origin: YOrigin) => {
logger.debug(
'SecondaryWorkspaceSQLiteDB:onSelfUpdate',
origin,
this.workspaceId,
docId,
update.length
);
// for self update from upstream, we need to push it to external DB
if (origin === 'upstream') {
await this.addUpdateToUpdateQueue({
@@ -147,15 +171,19 @@ export class SecondaryWorkspaceSQLiteDB extends BaseSQLiteAdapter {
});
};
doc.subdocs.forEach(subdoc => {
this.setupListener(subdoc.guid);
});
// listen to upstream update
this.upstream.yDoc.on('update', onUpstreamUpdate);
this.yDoc.on('update', onSelfUpdate);
this.yDoc.on('subdocs', onSubdocs);
doc.on('update', onSelfUpdate);
doc.on('subdocs', onSubdocs);
this.unsubscribers.add(() => {
this.upstream.yDoc.off('update', onUpstreamUpdate);
this.yDoc.off('update', onSelfUpdate);
this.yDoc.off('subdocs', onSubdocs);
doc.off('update', onSelfUpdate);
doc.off('subdocs', onSubdocs);
});
}
@@ -188,7 +216,10 @@ export class SecondaryWorkspaceSQLiteDB extends BaseSQLiteAdapter {
if (doc) {
Y.applyUpdate(this.yDoc, data, origin);
} else {
logger.warn('applyUpdate: doc not found', docId);
logger.warn(
'[SecondaryWorkspaceSQLiteDB] applyUpdate: doc not found',
docId
);
}
};

View File

@@ -18,7 +18,10 @@ export class WorkspaceSQLiteDB extends BaseSQLiteAdapter {
update$ = new Subject<void>();
constructor(public override path: string, public workspaceId: string) {
constructor(
public override path: string,
public workspaceId: string
) {
super(path);
}
@@ -49,9 +52,21 @@ export class WorkspaceSQLiteDB extends BaseSQLiteAdapter {
};
setupListener(docId?: string) {
logger.debug(
'WorkspaceSQLiteDB:setupListener',
this.workspaceId,
docId,
this.getWorkspaceName()
);
const doc = this.getDoc(docId);
if (doc) {
const onUpdate = async (update: Uint8Array, origin: YOrigin) => {
logger.debug(
'WorkspaceSQLiteDB:onUpdate',
this.workspaceId,
docId,
update.length
);
const insertRows = [{ data: update, docId }];
if (origin === 'renderer') {
await this.addUpdateToSQLite(insertRows);
@@ -65,7 +80,11 @@ export class WorkspaceSQLiteDB extends BaseSQLiteAdapter {
logger.debug('external update', this.workspaceId);
}
};
doc.subdocs.forEach(subdoc => {
this.setupListener(subdoc.guid);
});
const onSubdocs = ({ added }: { added: Set<Y.Doc> }) => {
logger.info('onSubdocs', this.workspaceId, docId, added);
added.forEach(subdoc => {
this.setupListener(subdoc.guid);
});
@@ -132,7 +151,7 @@ export class WorkspaceSQLiteDB extends BaseSQLiteAdapter {
if (doc) {
Y.applyUpdate(doc, data, origin);
} else {
logger.warn('applyUpdate: doc not found', docId);
logger.warn('[WorkspaceSQLiteDB] applyUpdate: doc not found', docId);
}
};

View File

@@ -1,9 +1,11 @@
import path from 'node:path';
import { ValidationResult } from '@affine/native';
import fs from 'fs-extra';
import { nanoid } from 'nanoid';
import { ensureSQLiteDB } from '../db/ensure-db';
import { copyToTemp, migrateToSubdocAndReplaceDatabase } from '../db/migration';
import type { WorkspaceSQLiteDB } from '../db/workspace-db-adapter';
import { logger } from '../logger';
import { mainRPC } from '../main-rpc';
@@ -55,6 +57,7 @@ const ErrorMessages = [
'DB_FILE_ALREADY_LOADED',
'DB_FILE_PATH_INVALID',
'DB_FILE_INVALID',
'DB_FILE_MIGRATION_FAILED',
'FILE_ALREADY_EXISTS',
'UNKNOWN_ERROR',
] as const;
@@ -191,27 +194,42 @@ export async function loadDBFile(): Promise<LoadDBFileResult> {
],
message: 'Load Workspace from a AFFiNE file',
}));
const filePath = ret.filePaths?.[0];
if (ret.canceled || !filePath) {
let originalPath = ret.filePaths?.[0];
if (ret.canceled || !originalPath) {
logger.info('loadDBFile canceled');
return { canceled: true };
}
// the imported file should not be in app data dir
if (filePath.startsWith(await getWorkspacesBasePath())) {
if (originalPath.startsWith(await getWorkspacesBasePath())) {
logger.warn('loadDBFile: db file in app data dir');
return { error: 'DB_FILE_PATH_INVALID' };
}
if (await dbFileAlreadyLoaded(filePath)) {
if (await dbFileAlreadyLoaded(originalPath)) {
logger.warn('loadDBFile: db file already loaded');
return { error: 'DB_FILE_ALREADY_LOADED' };
}
const { SqliteConnection } = await import('@affine/native');
if (!(await SqliteConnection.validate(filePath))) {
// TODO: report invalid db file error?
const validationResult = await SqliteConnection.validate(originalPath);
if (validationResult === ValidationResult.MissingDocIdColumn) {
try {
const tmpDBPath = await copyToTemp(originalPath);
await migrateToSubdocAndReplaceDatabase(tmpDBPath);
originalPath = tmpDBPath;
} catch (error) {
logger.warn(`loadDBFile, migration failed: ${originalPath}`, error);
return { error: 'DB_FILE_MIGRATION_FAILED' };
}
}
if (
validationResult !== ValidationResult.MissingDocIdColumn &&
validationResult !== ValidationResult.Valid
) {
return { error: 'DB_FILE_INVALID' }; // invalid db file
}
@@ -220,14 +238,12 @@ export async function loadDBFile(): Promise<LoadDBFileResult> {
const internalFilePath = await getWorkspaceDBPath(workspaceId);
await fs.ensureDir(await getWorkspacesBasePath());
await fs.copy(filePath, internalFilePath);
logger.info(`loadDBFile, copy: ${filePath} -> ${internalFilePath}`);
await fs.copy(originalPath, internalFilePath);
logger.info(`loadDBFile, copy: ${originalPath} -> ${internalFilePath}`);
await storeWorkspaceMeta(workspaceId, {
id: workspaceId,
mainDBPath: internalFilePath,
secondaryDBPath: filePath,
});
return { workspaceId };

View File

@@ -1,3 +1,4 @@
import type { RendererToHelper } from '@toeverything/infra/preload/electron';
import { AsyncCall } from 'async-call-rpc';
import { events, handlers } from './exposed';
@@ -30,7 +31,7 @@ function setupRendererConnection(rendererPort: Electron.MessagePortMain) {
});
}
);
const rpc = AsyncCall<PeersAPIs.RendererToHelper>(
const rpc = AsyncCall<RendererToHelper>(
Object.fromEntries(flattenedHandlers),
{
channel: {

View File

@@ -1,12 +1,16 @@
import type {
HelperToMain,
MainToHelper,
} from '@toeverything/infra/preload/electron';
import { AsyncCall } from 'async-call-rpc';
import { getExposedMeta } from './exposed';
const helperToMainServer: PeersAPIs.HelperToMain = {
const helperToMainServer: HelperToMain = {
getMeta: () => getExposedMeta(),
};
export const mainRPC = AsyncCall<PeersAPIs.MainToHelper>(helperToMainServer, {
export const mainRPC = AsyncCall<MainToHelper>(helperToMainServer, {
strict: {
unknownMessage: false,
},

View File

@@ -4,6 +4,8 @@ import fs from 'fs-extra';
import { v4 } from 'uuid';
import { afterEach, describe, expect, test, vi } from 'vitest';
import { removeWithRetry } from '../../../../tests/utils';
const tmpDir = path.join(__dirname, 'tmp');
const appDataPath = path.join(tmpDir, 'app-data');
@@ -20,7 +22,7 @@ vi.doMock('../../main-rpc', () => ({
}));
afterEach(async () => {
await fs.remove(tmpDir);
await removeWithRetry(tmpDir);
});
describe('list workspaces', () => {

View File

@@ -1,10 +1,10 @@
import assert from 'node:assert';
import path from 'node:path';
import { setTimeout } from 'node:timers/promises';
import fs from 'fs-extra';
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
import { removeWithRetry } from '../../../tests/utils';
import type { MainIPCHandlerMap } from '../exposed';
const registeredHandlers = new Map<
@@ -21,7 +21,7 @@ type WithoutFirstParameter<T> = T extends (_: any, ...args: infer P) => infer R
// however this is too hard to be typed correctly
async function dispatch<
T extends keyof MainIPCHandlerMap,
F extends keyof MainIPCHandlerMap[T]
F extends keyof MainIPCHandlerMap[T],
>(
namespace: T,
functionName: F,
@@ -121,11 +121,7 @@ beforeEach(async () => {
afterEach(async () => {
// reset registered handlers
registeredHandlers.get('before-quit')?.forEach(fn => fn());
// wait for the db to be closed on Windows
if (process.platform === 'win32') {
await setTimeout(200);
}
await fs.remove(SESSION_DATA_PATH);
await removeWithRetry(SESSION_DATA_PATH);
});
describe('UI handlers', () => {

View File

@@ -1,7 +1,7 @@
import { app, Menu } from 'electron';
import { revealLogFile } from '../logger';
import { checkForUpdatesAndNotify } from '../updater';
import { checkForUpdates } from '../updater';
import { isMacOS } from '../utils';
import { applicationMenuSubjects } from './subject';
@@ -125,7 +125,7 @@ export function createApplicationMenu() {
{
label: 'Check for Updates',
click: async () => {
await checkForUpdatesAndNotify(true);
await checkForUpdates(true);
},
},
],

View File

@@ -0,0 +1,9 @@
import { clipboard, type IpcMainInvokeEvent, nativeImage } from 'electron';
import type { NamespaceHandlers } from '../type';
export const clipboardHandlers = {
copyAsImageFromString: async (_: IpcMainInvokeEvent, dataURL: string) => {
clipboard.writeImage(nativeImage.createFromDataURL(dataURL));
},
} satisfies NamespaceHandlers;

View File

@@ -1,4 +1,4 @@
import { BrowserWindow, dialog, shell } from 'electron';
import { BrowserWindow, dialog } from 'electron';
import fs from 'fs-extra';
import { logger } from '../logger';
@@ -39,6 +39,13 @@ export async function savePDFFileAs(
await BrowserWindow.getFocusedWindow()
?.webContents.printToPDF({
margins: {
marginType: 'custom',
top: 0,
bottom: 0,
left: 0,
right: 0,
},
pageSize: 'A4',
printBackground: true,
landscape: false,
@@ -49,8 +56,6 @@ export async function savePDFFileAs(
logger.log(`Wrote PDF successfully to ${filePath}`);
});
});
await shell.openPath(filePath);
return { filePath };
} catch (err) {
logger.error('savePDFFileAs', err);

View File

@@ -1,4 +1,5 @@
import type {
ClipboardHandlerManager,
DebugHandlerManager,
ExportHandlerManager,
UIHandlerManager,
@@ -7,6 +8,7 @@ import type {
} from '@toeverything/infra';
import { ipcMain } from 'electron';
import { clipboardHandlers } from './clipboard';
import { exportHandlers } from './export';
import { getLogFilePath, logger, revealLogFile } from './logger';
import { uiHandlers } from './ui';
@@ -26,6 +28,10 @@ type AllHandlers = {
Electron.IpcMainInvokeEvent,
DebugHandlerManager
>;
clipboard: UnwrapManagerHandlerToServerSide<
Electron.IpcMainInvokeEvent,
ClipboardHandlerManager
>;
export: UnwrapManagerHandlerToServerSide<
Electron.IpcMainInvokeEvent,
ExportHandlerManager
@@ -44,6 +50,7 @@ type AllHandlers = {
export const allHandlers = {
debug: debugHandlers,
ui: uiHandlers,
clipboard: clipboardHandlers,
export: exportHandlers,
updater: updaterHandlers,
} satisfies AllHandlers;

View File

@@ -1,5 +1,9 @@
import path from 'node:path';
import type {
HelperToMain,
MainToHelper,
} from '@toeverything/infra/preload/electron';
import { type _AsyncVersionOf, AsyncCall } from 'async-call-rpc';
import {
app,
@@ -36,7 +40,7 @@ class HelperProcessManager {
#process: UtilityProcess;
// a rpc server for the main process -> helper process
rpc?: _AsyncVersionOf<PeersAPIs.HelperToMain>;
rpc?: _AsyncVersionOf<HelperToMain>;
static instance = new HelperProcessManager();
@@ -86,13 +90,13 @@ class HelperProcessManager {
]);
const appMethods = pickAndBind(app, ['getPath']);
const mainToHelperServer: PeersAPIs.MainToHelper = {
const mainToHelperServer: MainToHelper = {
...dialogMethods,
...shellMethods,
...appMethods,
};
this.rpc = AsyncCall<PeersAPIs.HelperToMain>(mainToHelperServer, {
this.rpc = AsyncCall<HelperToMain>(mainToHelperServer, {
strict: {
// the channel is shared for other purposes as well so that we do not want to
// restrict to only JSONRPC messages

View File

@@ -1,5 +1,5 @@
import { app } from 'electron';
import type { AppUpdater } from 'electron-updater';
import { autoUpdater } from 'electron-updater';
import { z } from 'zod';
import { logger } from '../logger';
@@ -20,56 +20,55 @@ export const buildType = ReleaseTypeSchema.parse(envBuildType);
const mode = process.env.NODE_ENV;
const isDev = mode === 'development';
let _autoUpdater: AppUpdater | null = null;
export const quitAndInstall = async () => {
_autoUpdater?.quitAndInstall();
autoUpdater.quitAndInstall();
};
let lastCheckTime = 0;
export const checkForUpdatesAndNotify = async (force = true) => {
if (!_autoUpdater) {
return void 0;
}
export const checkForUpdates = async (force = true) => {
// check every 30 minutes (1800 seconds) at most
if (force || lastCheckTime + 1000 * 1800 < Date.now()) {
lastCheckTime = Date.now();
return await _autoUpdater.checkForUpdatesAndNotify();
return await autoUpdater.checkForUpdates();
}
return void 0;
};
export const registerUpdater = async () => {
// so we wrap it in a function
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { autoUpdater } = require('electron-updater');
_autoUpdater = autoUpdater;
// skip auto update in dev mode
if (!_autoUpdater || isDev) {
if (isDev) {
return;
}
// TODO: support auto update on windows and linux
const allowAutoUpdate = isMacOS();
_autoUpdater.autoDownload = false;
_autoUpdater.allowPrerelease = buildType !== 'stable';
_autoUpdater.autoInstallOnAppQuit = false;
_autoUpdater.autoRunAppAfterInstall = true;
_autoUpdater.setFeedURL({
autoUpdater.logger = logger;
autoUpdater.autoDownload = false;
autoUpdater.allowPrerelease = buildType !== 'stable';
autoUpdater.autoInstallOnAppQuit = false;
autoUpdater.autoRunAppAfterInstall = true;
const feedUrl: Parameters<typeof autoUpdater.setFeedURL>[0] = {
channel: buildType,
provider: 'github',
repo: buildType !== 'internal' ? 'AFFiNE' : 'AFFiNE-Releases',
owner: 'toeverything',
releaseType: buildType === 'stable' ? 'release' : 'prerelease',
});
};
logger.debug('auto-updater feed config', feedUrl);
autoUpdater.setFeedURL(feedUrl);
// register events for checkForUpdatesAndNotify
_autoUpdater.on('update-available', info => {
autoUpdater.on('checking-for-update', () => {
logger.info('Checking for update');
});
autoUpdater.on('update-available', info => {
logger.info('Update available', info);
if (allowAutoUpdate) {
_autoUpdater?.downloadUpdate().catch(e => {
autoUpdater?.downloadUpdate().catch(e => {
logger.error('Failed to download update', e);
});
logger.info('Update available, downloading...', info);
@@ -79,11 +78,14 @@ export const registerUpdater = async () => {
allowAutoUpdate,
});
});
_autoUpdater.on('download-progress', e => {
autoUpdater.on('update-not-available', info => {
logger.info('Update not available', info);
});
autoUpdater.on('download-progress', e => {
logger.info(`Download progress: ${e.percent}`);
updaterSubjects.downloadProgress.next(e.percent);
});
_autoUpdater.on('update-downloaded', e => {
autoUpdater.on('update-downloaded', e => {
updaterSubjects.updateReady.next({
version: e.version,
allowAutoUpdate,
@@ -92,12 +94,12 @@ export const registerUpdater = async () => {
// updaterSubjects.clientDownloadProgress.next(100);
logger.info('Update downloaded, ready to install');
});
_autoUpdater.on('error', e => {
autoUpdater.on('error', e => {
logger.error('Error while updating client', e);
});
_autoUpdater.forceDevUpdateConfig = isDev;
autoUpdater.forceDevUpdateConfig = isDev;
app.on('activate', async () => {
await checkForUpdatesAndNotify(false);
await checkForUpdates(false);
});
};

View File

@@ -1,7 +1,7 @@
import { app } from 'electron';
import type { NamespaceHandlers } from '../type';
import { checkForUpdatesAndNotify, quitAndInstall } from './electron-updater';
import { checkForUpdates, quitAndInstall } from './electron-updater';
export const updaterHandlers = {
currentVersion: async () => {
@@ -11,7 +11,14 @@ export const updaterHandlers = {
return quitAndInstall();
},
checkForUpdatesAndNotify: async () => {
return checkForUpdatesAndNotify(true);
const res = await checkForUpdates(true);
if (res) {
const { updateInfo } = res;
return {
updateInfo,
};
}
return null;
},
} satisfies NamespaceHandlers;

View File

@@ -1,12 +1,15 @@
import { contextBridge, ipcRenderer } from 'electron';
(async () => {
const { appInfo, getAffineAPIs } = await import('./affine-apis');
const { apis, events } = getAffineAPIs();
const { appInfo, getElectronAPIs } = await import(
'@toeverything/infra/preload/electron'
);
const { apis, events } = getElectronAPIs();
contextBridge.exposeInMainWorld('appInfo', appInfo);
contextBridge.exposeInMainWorld('apis', apis);
contextBridge.exposeInMainWorld('events', events);
contextBridge.exposeInMainWorld('platform', process.platform);
// Credit to microsoft/vscode
const globals = {

Some files were not shown because too many files have changed in this diff Show More