Compare commits
756 Commits
codeql2
...
fix-ios-is
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d8adc6168 | ||
|
|
ec208960e8 | ||
|
|
b9505158b4 | ||
|
|
ad0c3421f0 | ||
|
|
916c00344b | ||
|
|
459cdee17e | ||
|
|
bb26a64dbb | ||
|
|
29a3fa532a | ||
|
|
738b8f9012 | ||
|
|
c95272288e | ||
|
|
919febd166 | ||
|
|
10ccc20b53 | ||
|
|
d9ca64da54 | ||
|
|
ce00ec97d1 | ||
|
|
2b9cd37c6c | ||
|
|
f8f14eb6f3 | ||
|
|
645fc863aa | ||
|
|
c53f030b24 | ||
|
|
45d74f9ba0 | ||
|
|
87870919ca | ||
|
|
ce2fdde474 | ||
|
|
6e2f30c6ed | ||
|
|
5c8040008a | ||
|
|
639e25d679 | ||
|
|
f7e5ef96d2 | ||
|
|
745f5487e9 | ||
|
|
0e7f3adf53 | ||
|
|
342d2b1fc4 | ||
|
|
15279685f7 | ||
|
|
12aa959f50 | ||
|
|
9478946c7a | ||
|
|
8560bbf28b | ||
|
|
df7afe1b64 | ||
|
|
df52b60d61 | ||
|
|
65b051f0eb | ||
|
|
7678084061 | ||
|
|
022d33d06f | ||
|
|
4d157bf8dc | ||
|
|
9fcbe4e8c5 | ||
|
|
5aeb92eb4f | ||
|
|
00dfa629b5 | ||
|
|
3ca471b6a2 | ||
|
|
a525589186 | ||
|
|
59ed10398d | ||
|
|
25a86e31df | ||
|
|
7d6743a81a | ||
|
|
6616f62da5 | ||
|
|
a3cbc05e12 | ||
|
|
97095a627a | ||
|
|
910d257c56 | ||
|
|
0c0a008b28 | ||
|
|
9879458353 | ||
|
|
d44f1f3b4b | ||
|
|
c5d387a7e5 | ||
|
|
a6aacd5c55 | ||
|
|
57e7485564 | ||
|
|
42a38a6f47 | ||
|
|
34bb9c2127 | ||
|
|
6442b5e4aa | ||
|
|
dde5a55446 | ||
|
|
13e615a798 | ||
|
|
9c81961b0b | ||
|
|
c1a35e2d75 | ||
|
|
13415c75c2 | ||
|
|
300557a0e6 | ||
|
|
fcbb97010c | ||
|
|
6be46b16b2 | ||
|
|
35b2356a31 | ||
|
|
53ef756723 | ||
|
|
0f0b743a10 | ||
|
|
3f7dafb65c | ||
|
|
9df791b5ff | ||
|
|
dea40d9757 | ||
|
|
dd12a589d6 | ||
|
|
af6e5ba31e | ||
|
|
2b57b2080b | ||
|
|
154c85a0f7 | ||
|
|
3f465d4594 | ||
|
|
94e883f4c3 | ||
|
|
38622101f1 | ||
|
|
0eb64c0084 | ||
|
|
409f5b1791 | ||
|
|
14398a9c4f | ||
|
|
d1cdf6e216 | ||
|
|
65da25a626 | ||
|
|
ce8b019e93 | ||
|
|
67d7fe016d | ||
|
|
47583b5a32 | ||
|
|
03c9a6aaae | ||
|
|
4dcf9b093b | ||
|
|
5ba5ebf63d | ||
|
|
115bea2792 | ||
|
|
b0495a8a42 | ||
|
|
faabd371f5 | ||
|
|
f0be6de0b3 | ||
|
|
b338c6d28d | ||
|
|
07e9a7c007 | ||
|
|
928bb3f8bc | ||
|
|
b9d62f6af2 | ||
|
|
f7ac38953b | ||
|
|
6441c0aa31 | ||
|
|
16479eb6cf | ||
|
|
69472c21c2 | ||
|
|
c270688e8f | ||
|
|
00c86c7082 | ||
|
|
e95e9f9fda | ||
|
|
1588c2f47b | ||
|
|
53850c96db | ||
|
|
ae2cb15055 | ||
|
|
8bf1e096c0 | ||
|
|
0052dc88f0 | ||
|
|
d67d62df45 | ||
|
|
5d45de6bc4 | ||
|
|
cf5bc51e94 | ||
|
|
9a7d24ea4e | ||
|
|
649f28ff8d | ||
|
|
bc5a81d146 | ||
|
|
7dce35bde4 | ||
|
|
f30ebc32ec | ||
|
|
027bc20975 | ||
|
|
3b1cddb9ce | ||
|
|
bd22aaaa86 | ||
|
|
e0e42d2eed | ||
|
|
616210f1bf | ||
|
|
ff2e7f6cc7 | ||
|
|
d1ce037f7d | ||
|
|
91f87f4b7b | ||
|
|
61657b9f9a | ||
|
|
476d032642 | ||
|
|
7538e570c5 | ||
|
|
66fcf4b79b | ||
|
|
21371b1815 | ||
|
|
a53c13d6ed | ||
|
|
1a0c6e72b2 | ||
|
|
ba7c8b79b1 | ||
|
|
d7b504eed0 | ||
|
|
a1df10eb09 | ||
|
|
92be409d4f | ||
|
|
665c7c6bf1 | ||
|
|
6c2ff7ee08 | ||
|
|
295a1bf402 | ||
|
|
3e6f558b08 | ||
|
|
aad5a59e82 | ||
|
|
36d02480b2 | ||
|
|
99454ac57b | ||
|
|
e2915f878e | ||
|
|
710a813e9b | ||
|
|
8bdb818995 | ||
|
|
20466c3800 | ||
|
|
faf6c2d062 | ||
|
|
a760a3c341 | ||
|
|
94e6d2f215 | ||
|
|
a6f1c0f63d | ||
|
|
c653996cbb | ||
|
|
da44fef89d | ||
|
|
4dc2c5e3df | ||
|
|
1797c2ae20 | ||
|
|
3b5da01c0a | ||
|
|
0f1bdce002 | ||
|
|
7c8f3e826f | ||
|
|
f21d63bb55 | ||
|
|
f223bb3d3f | ||
|
|
51001d07b6 | ||
|
|
a9eedd3c7a | ||
|
|
b0aa08fe4e | ||
|
|
8d45d24d55 | ||
|
|
8c1b9f81b9 | ||
|
|
71fad1c22b | ||
|
|
292266c597 | ||
|
|
54e589a6a0 | ||
|
|
fb3f425c27 | ||
|
|
1aaa30c6e9 | ||
|
|
8611410b21 | ||
|
|
40fa7a69c0 | ||
|
|
5eca30e513 | ||
|
|
4b78493782 | ||
|
|
2ce44b734f | ||
|
|
85d8f8c3ae | ||
|
|
3f16291137 | ||
|
|
a5958d5653 | ||
|
|
fdbdf8207a | ||
|
|
630e5489ec | ||
|
|
36943bb786 | ||
|
|
e1bbb0a10f | ||
|
|
27da540846 | ||
|
|
7d7f6ed04a | ||
|
|
ff01bc342d | ||
|
|
cd8b40b569 | ||
|
|
31c742f7a8 | ||
|
|
d6a7a2c21f | ||
|
|
499ecab691 | ||
|
|
df06540f1b | ||
|
|
a32b213ca5 | ||
|
|
6120f992a4 | ||
|
|
389a551a69 | ||
|
|
8ddbdc0e1e | ||
|
|
302c6a90c0 | ||
|
|
18e597d8a3 | ||
|
|
81d717ccff | ||
|
|
2e979c7323 | ||
|
|
4dfd15d6dd | ||
|
|
5b9bf3ff43 | ||
|
|
d2f7485098 | ||
|
|
f8fee1fba7 | ||
|
|
19249ca00f | ||
|
|
01e5700340 | ||
|
|
ff2f7660a6 | ||
|
|
2bc05e2b4a | ||
|
|
137c6447b7 | ||
|
|
ebc8f0c917 | ||
|
|
5a8d10b5b4 | ||
|
|
875815fb62 | ||
|
|
cdf526e130 | ||
|
|
b685032b34 | ||
|
|
a171f9cb00 | ||
|
|
c452f05ec2 | ||
|
|
93d91f80f2 | ||
|
|
7b764c8427 | ||
|
|
016289c8cb | ||
|
|
93a9575389 | ||
|
|
9e265adf14 | ||
|
|
eb08a0ed14 | ||
|
|
c533f37983 | ||
|
|
ca4f8385e4 | ||
|
|
3eb9aa74ed | ||
|
|
637b51464c | ||
|
|
fd9585a66e | ||
|
|
49ecbcb0c9 | ||
|
|
1132bdd66a | ||
|
|
c7d6ed9ea3 | ||
|
|
782528f169 | ||
|
|
104c78275f | ||
|
|
d9d88f7175 | ||
|
|
bf7e24cf11 | ||
|
|
c8aba01db3 | ||
|
|
a896c7e46e | ||
|
|
8018ec14a2 | ||
|
|
9c3208c860 | ||
|
|
e1063964cf | ||
|
|
38568738cc | ||
|
|
15b8358b14 | ||
|
|
2173cb2610 | ||
|
|
87b925d622 | ||
|
|
885b06cc26 | ||
|
|
adb6a5f41e | ||
|
|
3b815e22e3 | ||
|
|
4d4a5c0e64 | ||
|
|
0e89293974 | ||
|
|
c306911b3a | ||
|
|
4f276f0095 | ||
|
|
81fc97c7e9 | ||
|
|
785c5a59c6 | ||
|
|
25ecfaa883 | ||
|
|
38e2c019fa | ||
|
|
15878a4ac5 | ||
|
|
9802536ded | ||
|
|
2c7f92a4d7 | ||
|
|
c653841037 | ||
|
|
ec314c14ea | ||
|
|
c03e60ac0b | ||
|
|
cbf2343143 | ||
|
|
9d9b3ac543 | ||
|
|
591b35a70b | ||
|
|
f0c7b881d3 | ||
|
|
3fd5515db1 | ||
|
|
f32401afd6 | ||
|
|
1b9d91f1e8 | ||
|
|
1f039d707c | ||
|
|
6671d877ad | ||
|
|
2867c95494 | ||
|
|
aa55cec060 | ||
|
|
dfb6c4cd9e | ||
|
|
a9082f66e8 | ||
|
|
bf39b0fbfb | ||
|
|
e347f2179a | ||
|
|
d4f155b6bc | ||
|
|
da001834f5 | ||
|
|
f54352dd82 | ||
|
|
0fba0fae73 | ||
|
|
406ec88515 | ||
|
|
b97957d166 | ||
|
|
655ad6b9e0 | ||
|
|
f5ce42fc2d | ||
|
|
709cdf260d | ||
|
|
5c583028e0 | ||
|
|
c70008d1be | ||
|
|
13fa716fe8 | ||
|
|
c3af5b428f | ||
|
|
40e2f28e94 | ||
|
|
2964f2e079 | ||
|
|
e1a5291123 | ||
|
|
ef41f35209 | ||
|
|
2f64b202c1 | ||
|
|
2500c739ae | ||
|
|
63a9a6135b | ||
|
|
417005c6e9 | ||
|
|
cd1739c901 | ||
|
|
709917eb8f | ||
|
|
3ba70122d5 | ||
|
|
5ff025543e | ||
|
|
896d5bad12 | ||
|
|
e9dbaa3c28 | ||
|
|
d352d03071 | ||
|
|
ebefe775bb | ||
|
|
0852a961cc | ||
|
|
46f06f4c0e | ||
|
|
afb39e4aba | ||
|
|
2c6a90f82b | ||
|
|
e35f732e48 | ||
|
|
ec8b17dee2 | ||
|
|
947bc1a233 | ||
|
|
7050caa2f3 | ||
|
|
c4fd1a0a54 | ||
|
|
4de5f5c490 | ||
|
|
b3f336c959 | ||
|
|
010784c2b2 | ||
|
|
306f654617 | ||
|
|
60d0563487 | ||
|
|
777210ec42 | ||
|
|
8649522b5b | ||
|
|
71ebde06f4 | ||
|
|
d98eb5b46f | ||
|
|
6a2a8b74c8 | ||
|
|
43d5d3d719 | ||
|
|
5527f184b7 | ||
|
|
7dd5cf8b6e | ||
|
|
aec697f5b9 | ||
|
|
aa2588dd89 | ||
|
|
ed886e1794 | ||
|
|
452709dec7 | ||
|
|
a5cac35cfd | ||
|
|
3ee8485ef0 | ||
|
|
673f61be17 | ||
|
|
db86247510 | ||
|
|
090f6eef71 | ||
|
|
214d18616f | ||
|
|
3b126291a6 | ||
|
|
55a230e127 | ||
|
|
2a107ece7f | ||
|
|
7a3ef93a18 | ||
|
|
6255c9baad | ||
|
|
c322a963ab | ||
|
|
b1e8cb5a07 | ||
|
|
a391089efc | ||
|
|
1894bbe4f7 | ||
|
|
07dba90679 | ||
|
|
ca5ea315d6 | ||
|
|
646fe9c67f | ||
|
|
6a123a2399 | ||
|
|
39aa9f0941 | ||
|
|
625a4dcfae | ||
|
|
7971681d02 | ||
|
|
3dea241d7a | ||
|
|
e5ce6532f5 | ||
|
|
aa910ca3f0 | ||
|
|
c2d237a99a | ||
|
|
a371bdaedd | ||
|
|
dbbd77a8eb | ||
|
|
c28de7c079 | ||
|
|
05f1068e01 | ||
|
|
7103ec9877 | ||
|
|
9cd7a25343 | ||
|
|
2d028d18e5 | ||
|
|
0164eca206 | ||
|
|
f227c9e97e | ||
|
|
aecedfd082 | ||
|
|
e0f180bf04 | ||
|
|
5d0c435a33 | ||
|
|
daa7e7b56a | ||
|
|
655f319083 | ||
|
|
fcfe5682da | ||
|
|
e1140ac436 | ||
|
|
1529f5d478 | ||
|
|
4870dc8d45 | ||
|
|
a25e5dcfcd | ||
|
|
828e23b5c6 | ||
|
|
1921312445 | ||
|
|
0b9a884364 | ||
|
|
da4211f0b0 | ||
|
|
b21827cb32 | ||
|
|
4424a8a21d | ||
|
|
eb030f9ed6 | ||
|
|
333372d61c | ||
|
|
48a92f3e55 | ||
|
|
ddc767e53e | ||
|
|
432425ea59 | ||
|
|
6075fd3ef8 | ||
|
|
f099a46f83 | ||
|
|
fe54ef66c6 | ||
|
|
4eb0e930f6 | ||
|
|
fae925aa25 | ||
|
|
764a3d2fde | ||
|
|
b5a51f1304 | ||
|
|
140aee749b | ||
|
|
4113dd1873 | ||
|
|
0e0d3780d3 | ||
|
|
38ff01aedc | ||
|
|
cdf687ad80 | ||
|
|
a399fc7f80 | ||
|
|
c54a48e70b | ||
|
|
884b6f12ae | ||
|
|
5cae0febc9 | ||
|
|
0e898db710 | ||
|
|
40d54d60d4 | ||
|
|
269e026381 | ||
|
|
8245f2f6af | ||
|
|
8c07e8b1a8 | ||
|
|
e94b0845a2 | ||
|
|
4acc85bd12 | ||
|
|
ffa534d5eb | ||
|
|
fccf0f1e39 | ||
|
|
a5d80d1f02 | ||
|
|
803a73afb6 | ||
|
|
1eb8049d04 | ||
|
|
f9ed0c487f | ||
|
|
fa7d33351f | ||
|
|
e3084760b8 | ||
|
|
8e5addad5c | ||
|
|
6e741018e5 | ||
|
|
98c7c78421 | ||
|
|
16c588138c | ||
|
|
1373863af5 | ||
|
|
75315ea2c5 | ||
|
|
9f6fb8a387 | ||
|
|
b84d3d5806 | ||
|
|
5c2c1bbfcd | ||
|
|
54e84858b5 | ||
|
|
833d0789d7 | ||
|
|
1a974f3dd8 | ||
|
|
146173883f | ||
|
|
ebb02a5723 | ||
|
|
c96f7fed18 | ||
|
|
861eff3cd2 | ||
|
|
b66c0d17d0 | ||
|
|
0e748050f3 | ||
|
|
ae3524b79f | ||
|
|
0ce58b592a | ||
|
|
578346840e | ||
|
|
56bcb46d6c | ||
|
|
91405c48e0 | ||
|
|
b40dff621a | ||
|
|
7d4409e2b4 | ||
|
|
64a385b835 | ||
|
|
ee2573d128 | ||
|
|
d082e7c44d | ||
|
|
cd65850308 | ||
|
|
b91ae7e9b1 | ||
|
|
9baab1bf08 | ||
|
|
fdd4d8b926 | ||
|
|
c26c42d67f | ||
|
|
e1553becbc | ||
|
|
2e845ab0c0 | ||
|
|
79062a5476 | ||
|
|
840495b0e6 | ||
|
|
dd8fdbc7e3 | ||
|
|
a18c2aadf6 | ||
|
|
c7b653f073 | ||
|
|
8201df66c3 | ||
|
|
30e9baf0df | ||
|
|
a92881645b | ||
|
|
9522545152 | ||
|
|
33a09874c1 | ||
|
|
933755b81d | ||
|
|
8a1352d149 | ||
|
|
b47b7d7ea8 | ||
|
|
27cc4b7a5d | ||
|
|
249cc7f276 | ||
|
|
7ee9e92f7e | ||
|
|
588e80a237 | ||
|
|
e3d2d355de | ||
|
|
e8e00691c0 | ||
|
|
216be571e2 | ||
|
|
0176e82b3c | ||
|
|
30d3297002 | ||
|
|
6aa94a55ab | ||
|
|
a747ca42ad | ||
|
|
cd7a056bf1 | ||
|
|
baab774ede | ||
|
|
877c974108 | ||
|
|
d8033762d7 | ||
|
|
47a265016c | ||
|
|
ef78c68ac1 | ||
|
|
bc3f261076 | ||
|
|
4fa8a66c5f | ||
|
|
5885afc4f8 | ||
|
|
f3d21c50ab | ||
|
|
30729db47a | ||
|
|
0ff690a344 | ||
|
|
bec138c4f0 | ||
|
|
2976810fdf | ||
|
|
ae70ff2901 | ||
|
|
ee2642825f | ||
|
|
9cf98df425 | ||
|
|
1229904188 | ||
|
|
9152181a00 | ||
|
|
4843cb8789 | ||
|
|
6d3a489dff | ||
|
|
ea8e98fa0d | ||
|
|
614385da75 | ||
|
|
f6ef3ba7c5 | ||
|
|
22e8a137ef | ||
|
|
69734cc922 | ||
|
|
a9fe05d64a | ||
|
|
8bb3bd9409 | ||
|
|
5219065b8e | ||
|
|
cb8497229d | ||
|
|
25b8920d20 | ||
|
|
9203db88ab | ||
|
|
36378e9c23 | ||
|
|
9c33e77755 | ||
|
|
88cb4c742f | ||
|
|
475cce8253 | ||
|
|
a86c1738d1 | ||
|
|
96a4d02c80 | ||
|
|
bb6df783ab | ||
|
|
26cca5c2f8 | ||
|
|
7e3dd7d624 | ||
|
|
db9a53f923 | ||
|
|
92ae4786f0 | ||
|
|
b35cf14d32 | ||
|
|
14374b55d2 | ||
|
|
5a919018c5 | ||
|
|
6ac73d3f25 | ||
|
|
510fe3902e | ||
|
|
2bc23594ad | ||
|
|
06e00f3066 | ||
|
|
9b3d409695 | ||
|
|
f7f5737abf | ||
|
|
9160d63ad4 | ||
|
|
458f135ee1 | ||
|
|
b6fc104357 | ||
|
|
8e116bf62d | ||
|
|
f1d697a83f | ||
|
|
69a7a57f41 | ||
|
|
24de1559a5 | ||
|
|
ec29abfcaf | ||
|
|
eac97db665 | ||
|
|
d8386328e7 | ||
|
|
d28f321aa2 | ||
|
|
e691c076a1 | ||
|
|
ad842e0e80 | ||
|
|
dcf4109c5b | ||
|
|
05287c135e | ||
|
|
6ff8ec21cf | ||
|
|
7b6e22aa04 | ||
|
|
ee56914285 | ||
|
|
a2e9cd3c43 | ||
|
|
359f29a264 | ||
|
|
576b15fec0 | ||
|
|
42434290da | ||
|
|
62c6189dfd | ||
|
|
21c9ebbca3 | ||
|
|
658d4687f9 | ||
|
|
3775453db8 | ||
|
|
edcaf8e639 | ||
|
|
3aa658a64e | ||
|
|
58fc66ad1c | ||
|
|
f68f87645f | ||
|
|
25f99da172 | ||
|
|
5da6faa972 | ||
|
|
02b25138ef | ||
|
|
21644f5ad8 | ||
|
|
d3adc1629c | ||
|
|
7c60c57c60 | ||
|
|
7006a790dc | ||
|
|
2575b649a0 | ||
|
|
8399391aaa | ||
|
|
dfbec20016 | ||
|
|
17ac777e9b | ||
|
|
01edc0e6e0 | ||
|
|
d6b58a5e66 | ||
|
|
517ef9515c | ||
|
|
7d94861db9 | ||
|
|
cb1e4fa583 | ||
|
|
dd5fced6c4 | ||
|
|
01a4d91167 | ||
|
|
2e2f0fdbb5 | ||
|
|
3ca1a72c6a | ||
|
|
9905199055 | ||
|
|
5970ff917f | ||
|
|
d197c91995 | ||
|
|
6e1ee6df12 | ||
|
|
b44d6e60bd | ||
|
|
1d0e49d5b6 | ||
|
|
ced7b2aa2c | ||
|
|
6aa473c316 | ||
|
|
aebe36b9e8 | ||
|
|
71c92766d3 | ||
|
|
a3f5f7645a | ||
|
|
7e8514e7be | ||
|
|
7715789d0f | ||
|
|
a93fed448f | ||
|
|
10738a7af0 | ||
|
|
2c00c55e5d | ||
|
|
80ba02851f | ||
|
|
5fd3190a2d | ||
|
|
117ec317de | ||
|
|
f4f2836bdb | ||
|
|
e194a2feb8 | ||
|
|
93e9ec867c | ||
|
|
1fe625a9b4 | ||
|
|
1c1ef56e00 | ||
|
|
838fe16845 | ||
|
|
7f3c45f85a | ||
|
|
9a4f6721e2 | ||
|
|
a4ffc03e55 | ||
|
|
c4c98bda31 | ||
|
|
63aa2fd307 | ||
|
|
99d0b1786d | ||
|
|
843fed5ffd | ||
|
|
9b8112b478 | ||
|
|
2433d40918 | ||
|
|
2c1f473bbe | ||
|
|
7605a4d835 | ||
|
|
a1e2fddd5c | ||
|
|
74b770a937 | ||
|
|
bd8724c1e2 | ||
|
|
e508159255 | ||
|
|
4649921b1d | ||
|
|
1c58ac3704 | ||
|
|
4676b4cd25 | ||
|
|
15f36651d8 | ||
|
|
7b11ef9b40 | ||
|
|
3b90f085b1 | ||
|
|
36bf445370 | ||
|
|
8936ce928f | ||
|
|
28aec8852b | ||
|
|
5d1224e438 | ||
|
|
a0d02a843e | ||
|
|
08f22983e7 | ||
|
|
07289667c0 | ||
|
|
f49375dce4 | ||
|
|
9910cafe78 | ||
|
|
9996a1579b | ||
|
|
fd913ad1fa | ||
|
|
412a873c47 | ||
|
|
22046f4cfb | ||
|
|
030debe9d9 | ||
|
|
9db1d548a0 | ||
|
|
da167642b7 | ||
|
|
971053c90d | ||
|
|
65d9220df6 | ||
|
|
264ebb1d4c | ||
|
|
3509d3594f | ||
|
|
2a268accff | ||
|
|
37966880fd | ||
|
|
6699c92082 | ||
|
|
9ca1c5c14b | ||
|
|
af1a5f7361 | ||
|
|
c349a3b869 | ||
|
|
8d2edf91a1 | ||
|
|
39b686a13b | ||
|
|
139965d6ca | ||
|
|
e79e692735 | ||
|
|
797c56585b | ||
|
|
e38391ade0 | ||
|
|
f344715381 | ||
|
|
1e95b8cf9e | ||
|
|
8ffaf0748e | ||
|
|
88357a3aeb | ||
|
|
dfe025ab8e | ||
|
|
97a66168c0 | ||
|
|
a9983e1fe0 | ||
|
|
793ce0afd8 | ||
|
|
4d7cc26983 | ||
|
|
c72ce9b446 | ||
|
|
f3c628ba76 | ||
|
|
9a66e26b00 | ||
|
|
8b7f2f102f | ||
|
|
56d8c3f50f | ||
|
|
61f5c66444 | ||
|
|
a2f7b1a780 | ||
|
|
35b2d12e18 | ||
|
|
5dcd32050a | ||
|
|
1099d67bf1 | ||
|
|
69625ae832 | ||
|
|
a1c7c4a310 | ||
|
|
a3bea3b7da | ||
|
|
a3043c1f6d | ||
|
|
d0da7858ec | ||
|
|
7bba09c16d | ||
|
|
78813d53b1 | ||
|
|
35eac02545 | ||
|
|
a5b0d39adf | ||
|
|
f80d1b32b7 | ||
|
|
7598a16b75 | ||
|
|
b83b54eee1 | ||
|
|
eb2621f72a | ||
|
|
44980d21a9 | ||
|
|
e1d2e1357b | ||
|
|
f80c7d03e2 | ||
|
|
4ca6ee358b | ||
|
|
9dad06222d | ||
|
|
37ef6be4c3 | ||
|
|
f0a4fad878 | ||
|
|
06ae035e11 | ||
|
|
91b5177bdb | ||
|
|
30bd427985 | ||
|
|
a92762ff47 | ||
|
|
0a5c98aba0 | ||
|
|
6f041bf693 | ||
|
|
23c9dc304a | ||
|
|
97377fe8bd | ||
|
|
36cf16ce90 | ||
|
|
ab3ef63097 | ||
|
|
7f8549124f | ||
|
|
43b1cb904d | ||
|
|
762a3ca626 | ||
|
|
91f0d00ba2 | ||
|
|
41f42f4427 | ||
|
|
98181bfe6c | ||
|
|
a8ab4aaf2e | ||
|
|
78dca7a2bf | ||
|
|
844ea40c3a | ||
|
|
7a6dedf452 | ||
|
|
b641b37308 | ||
|
|
8c1f8bfb42 | ||
|
|
1f1563401d | ||
|
|
9fd585ee07 | ||
|
|
609dcabf77 | ||
|
|
80d338c998 | ||
|
|
306784c31b | ||
|
|
bcd68e0f19 | ||
|
|
5764148753 | ||
|
|
e7edfe3ba1 | ||
|
|
da6f54eede | ||
|
|
ade5c3d80e | ||
|
|
cc2600cfba | ||
|
|
9b191ef3e4 | ||
|
|
c450c35baf | ||
|
|
b35b82f4ee | ||
|
|
ea52624ab2 | ||
|
|
0484bccfd1 | ||
|
|
d094f63faa | ||
|
|
dc6bc61442 | ||
|
|
5918c42cf9 | ||
|
|
c34a08561e | ||
|
|
7213c726b4 | ||
|
|
f650ac4e76 | ||
|
|
2ff1be2c4a | ||
|
|
61ac306ef3 | ||
|
|
022569e404 | ||
|
|
ebf22df7b6 | ||
|
|
adcc596875 | ||
|
|
1bcdf06b43 | ||
|
|
3be78f0312 | ||
|
|
5633499834 | ||
|
|
88847a153b | ||
|
|
3e7c3a45c3 | ||
|
|
1af1a92fec | ||
|
|
7b38923b7d | ||
|
|
38500e1d79 | ||
|
|
2e82fc3ead | ||
|
|
e88ae4aa3d | ||
|
|
5f55c922dc | ||
|
|
fc3c044e00 |
61
.cursor/rules/build-and-deployment.mdc
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs:
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
# Build & Deployment Best Practices
|
||||||
|
|
||||||
|
## Build Process
|
||||||
|
|
||||||
|
### Running Builds
|
||||||
|
- Use `pnpm build` from project root for full build
|
||||||
|
- Monitor for React hooks warnings and fix them immediately
|
||||||
|
- Ensure all TypeScript errors are resolved before deployment
|
||||||
|
|
||||||
|
### Common Build Issues & Fixes
|
||||||
|
|
||||||
|
#### React Hooks Warnings
|
||||||
|
- Capture ref values in variables within useEffect cleanup
|
||||||
|
- Avoid accessing `.current` directly in cleanup functions
|
||||||
|
- Pattern for fixing ref cleanup warnings:
|
||||||
|
```typescript
|
||||||
|
useEffect(() => {
|
||||||
|
const currentRef = myRef.current;
|
||||||
|
return () => {
|
||||||
|
if (currentRef) {
|
||||||
|
currentRef.cleanup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Test Failures During Build
|
||||||
|
- Ensure all test mocks include required constants like `SESSION_MAX_AGE`
|
||||||
|
- Mock Next.js navigation hooks properly: `useParams`, `useRouter`, `useSearchParams`
|
||||||
|
- Remove unused imports and constants from test files
|
||||||
|
- Use literal values instead of imported constants when the constant isn't actually needed
|
||||||
|
|
||||||
|
### Test Execution
|
||||||
|
- Run `pnpm test` to execute all tests
|
||||||
|
- Use `pnpm test -- --run filename.test.tsx` for specific test files
|
||||||
|
- Fix test failures before merging code
|
||||||
|
- Ensure 100% test coverage for new components
|
||||||
|
|
||||||
|
### Performance Monitoring
|
||||||
|
- Monitor build times and optimize if necessary
|
||||||
|
- Watch for memory usage during builds
|
||||||
|
- Use proper caching strategies for faster rebuilds
|
||||||
|
|
||||||
|
### Deployment Checklist
|
||||||
|
1. All tests passing
|
||||||
|
2. Build completes without warnings
|
||||||
|
3. TypeScript compilation successful
|
||||||
|
4. No linter errors
|
||||||
|
5. Database migrations applied (if any)
|
||||||
|
6. Environment variables configured
|
||||||
|
|
||||||
|
### EKS Deployment Considerations
|
||||||
|
- Ensure latest code is deployed to all pods
|
||||||
|
- Monitor AWS RDS Performance Insights for database issues
|
||||||
|
- Verify environment-specific configurations
|
||||||
|
- Check pod health and resource usage
|
||||||
41
.cursor/rules/database-performance.mdc
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs:
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
# Database Performance & Prisma Best Practices
|
||||||
|
|
||||||
|
## Critical Performance Rules
|
||||||
|
|
||||||
|
### Response Count Queries
|
||||||
|
- **NEVER** use `skip`/`offset` with `prisma.response.count()` - this causes expensive subqueries with OFFSET
|
||||||
|
- Always use only `where` clauses for count operations: `prisma.response.count({ where: { ... } })`
|
||||||
|
- For pagination, separate count queries from data queries
|
||||||
|
- Reference: [apps/web/lib/response/service.ts](mdc:apps/web/lib/response/service.ts) line 654-686
|
||||||
|
|
||||||
|
### Prisma Query Optimization
|
||||||
|
- Use proper indexes defined in [packages/database/schema.prisma](mdc:packages/database/schema.prisma)
|
||||||
|
- Leverage existing indexes: `@@index([surveyId, createdAt])`, `@@index([createdAt])`
|
||||||
|
- Use cursor-based pagination for large datasets instead of offset-based
|
||||||
|
- Cache frequently accessed data using React Cache and custom cache tags
|
||||||
|
|
||||||
|
### Date Range Filtering
|
||||||
|
- When filtering by `createdAt`, always use indexed queries
|
||||||
|
- Combine with `surveyId` for optimal performance: `{ surveyId, createdAt: { gte: start, lt: end } }`
|
||||||
|
- Avoid complex WHERE clauses that can't utilize indexes
|
||||||
|
|
||||||
|
### Count vs Data Separation
|
||||||
|
- Always separate count queries from data fetching queries
|
||||||
|
- Use `Promise.all()` to run count and data queries in parallel
|
||||||
|
- Example pattern from [apps/web/modules/api/v2/management/responses/lib/response.ts](mdc:apps/web/modules/api/v2/management/responses/lib/response.ts):
|
||||||
|
```typescript
|
||||||
|
const [responses, totalCount] = await Promise.all([
|
||||||
|
prisma.response.findMany(query),
|
||||||
|
prisma.response.count({ where: whereClause }),
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Monitoring & Debugging
|
||||||
|
- Monitor AWS RDS Performance Insights for problematic queries
|
||||||
|
- Look for queries with OFFSET in count operations - these indicate performance issues
|
||||||
|
- Use proper error handling with `DatabaseError` for Prisma exceptions
|
||||||
334
.cursor/rules/formbricks-architecture.mdc
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs:
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
# Formbricks Architecture & Patterns
|
||||||
|
|
||||||
|
## Monorepo Structure
|
||||||
|
|
||||||
|
### Apps Directory
|
||||||
|
- `apps/web/` - Main Next.js web application
|
||||||
|
- `packages/` - Shared packages and utilities
|
||||||
|
|
||||||
|
### Key Directories in Web App
|
||||||
|
```
|
||||||
|
apps/web/
|
||||||
|
├── app/ # Next.js 13+ app directory
|
||||||
|
│ ├── (app)/ # Main application routes
|
||||||
|
│ ├── (auth)/ # Authentication routes
|
||||||
|
│ ├── api/ # API routes
|
||||||
|
│ └── share/ # Public sharing routes
|
||||||
|
├── components/ # Shared components
|
||||||
|
├── lib/ # Utility functions and services
|
||||||
|
└── modules/ # Feature-specific modules
|
||||||
|
```
|
||||||
|
|
||||||
|
## Routing Patterns
|
||||||
|
|
||||||
|
### App Router Structure
|
||||||
|
The application uses Next.js 13+ app router with route groups:
|
||||||
|
|
||||||
|
```
|
||||||
|
(app)/environments/[environmentId]/
|
||||||
|
├── surveys/[surveyId]/
|
||||||
|
│ ├── (analysis)/ # Analysis views
|
||||||
|
│ │ ├── responses/ # Response management
|
||||||
|
│ │ ├── summary/ # Survey summary
|
||||||
|
│ │ └── hooks/ # Analysis-specific hooks
|
||||||
|
│ ├── edit/ # Survey editing
|
||||||
|
│ └── settings/ # Survey settings
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dynamic Routes
|
||||||
|
- `[environmentId]` - Environment-specific routes
|
||||||
|
- `[surveyId]` - Survey-specific routes
|
||||||
|
- `[sharingKey]` - Public sharing routes
|
||||||
|
|
||||||
|
## Service Layer Pattern
|
||||||
|
|
||||||
|
### Service Organization
|
||||||
|
Services are organized by domain in `apps/web/lib/`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Example: Response service
|
||||||
|
// apps/web/lib/response/service.ts
|
||||||
|
export const getResponseCountAction = async ({
|
||||||
|
surveyId,
|
||||||
|
filterCriteria,
|
||||||
|
}: {
|
||||||
|
surveyId: string;
|
||||||
|
filterCriteria: any;
|
||||||
|
}) => {
|
||||||
|
// Service implementation
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Action Pattern
|
||||||
|
Server actions follow a consistent pattern:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Action wrapper for service calls
|
||||||
|
export const getResponseCountAction = async (params) => {
|
||||||
|
try {
|
||||||
|
const result = await responseService.getCount(params);
|
||||||
|
return { data: result };
|
||||||
|
} catch (error) {
|
||||||
|
return { error: error.message };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Context Patterns
|
||||||
|
|
||||||
|
### Provider Structure
|
||||||
|
Context providers follow a consistent pattern:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Provider component
|
||||||
|
export const ResponseFilterProvider = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
const [selectedFilter, setSelectedFilter] = useState(defaultFilter);
|
||||||
|
|
||||||
|
const value = {
|
||||||
|
selectedFilter,
|
||||||
|
setSelectedFilter,
|
||||||
|
// ... other state and methods
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ResponseFilterContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</ResponseFilterContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hook for consuming context
|
||||||
|
export const useResponseFilter = () => {
|
||||||
|
const context = useContext(ResponseFilterContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useResponseFilter must be used within ResponseFilterProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Context Composition
|
||||||
|
Multiple contexts are often composed together:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Layout component with multiple providers
|
||||||
|
export default function AnalysisLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<ResponseFilterProvider>
|
||||||
|
<ResponseCountProvider>
|
||||||
|
{children}
|
||||||
|
</ResponseCountProvider>
|
||||||
|
</ResponseFilterProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Patterns
|
||||||
|
|
||||||
|
### Page Components
|
||||||
|
Page components are located in the app directory and follow this pattern:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/page.tsx
|
||||||
|
export default function ResponsesPage() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<ResponsesTable />
|
||||||
|
<ResponsesPagination />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Component Organization
|
||||||
|
- **Pages** - Route components in app directory
|
||||||
|
- **Components** - Reusable UI components
|
||||||
|
- **Modules** - Feature-specific components and logic
|
||||||
|
|
||||||
|
### Shared Components
|
||||||
|
Common components are in `apps/web/components/`:
|
||||||
|
- UI components (buttons, inputs, modals)
|
||||||
|
- Layout components (headers, sidebars)
|
||||||
|
- Data display components (tables, charts)
|
||||||
|
|
||||||
|
## Hook Patterns
|
||||||
|
|
||||||
|
### Custom Hook Structure
|
||||||
|
Custom hooks follow consistent patterns:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export const useResponseCount = ({
|
||||||
|
survey,
|
||||||
|
initialCount
|
||||||
|
}: {
|
||||||
|
survey: TSurvey;
|
||||||
|
initialCount?: number;
|
||||||
|
}) => {
|
||||||
|
const [responseCount, setResponseCount] = useState(initialCount ?? 0);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
// Hook logic...
|
||||||
|
|
||||||
|
return {
|
||||||
|
responseCount,
|
||||||
|
isLoading,
|
||||||
|
refetch,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hook Dependencies
|
||||||
|
- Use context hooks for shared state
|
||||||
|
- Implement proper cleanup with AbortController
|
||||||
|
- Optimize dependency arrays to prevent unnecessary re-renders
|
||||||
|
|
||||||
|
## Data Fetching Patterns
|
||||||
|
|
||||||
|
### Server Actions
|
||||||
|
The app uses Next.js server actions for data fetching:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Server action
|
||||||
|
export async function getResponsesAction(params: GetResponsesParams) {
|
||||||
|
const responses = await getResponses(params);
|
||||||
|
return { data: responses };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client usage
|
||||||
|
const { data } = await getResponsesAction(params);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
Consistent error handling across the application:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
try {
|
||||||
|
const result = await apiCall();
|
||||||
|
return { data: result };
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Operation failed:", error);
|
||||||
|
return { error: error.message };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Type Safety
|
||||||
|
|
||||||
|
### Type Organization
|
||||||
|
Types are organized in packages:
|
||||||
|
- `@formbricks/types` - Shared type definitions
|
||||||
|
- Local types in component/hook files
|
||||||
|
|
||||||
|
### Common Types
|
||||||
|
```typescript
|
||||||
|
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||||
|
import { TResponse } from "@formbricks/types/responses";
|
||||||
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
|
```
|
||||||
|
|
||||||
|
## State Management
|
||||||
|
|
||||||
|
### Local State
|
||||||
|
- Use `useState` for component-specific state
|
||||||
|
- Use `useReducer` for complex state logic
|
||||||
|
- Use refs for mutable values that don't trigger re-renders
|
||||||
|
|
||||||
|
### Global State
|
||||||
|
- React Context for feature-specific shared state
|
||||||
|
- URL state for filters and pagination
|
||||||
|
- Server state through server actions
|
||||||
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
### Code Splitting
|
||||||
|
- Dynamic imports for heavy components
|
||||||
|
- Route-based code splitting with app router
|
||||||
|
- Lazy loading for non-critical features
|
||||||
|
|
||||||
|
### Caching Strategy
|
||||||
|
- Server-side caching for database queries
|
||||||
|
- Client-side caching with React Query (where applicable)
|
||||||
|
- Static generation for public pages
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
### Test Organization
|
||||||
|
```
|
||||||
|
component/
|
||||||
|
├── Component.tsx
|
||||||
|
├── Component.test.tsx
|
||||||
|
└── hooks/
|
||||||
|
├── useHook.ts
|
||||||
|
└── useHook.test.tsx
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Patterns
|
||||||
|
- Unit tests for utilities and services
|
||||||
|
- Integration tests for components with context
|
||||||
|
- Hook tests with proper mocking
|
||||||
|
|
||||||
|
## Build & Deployment
|
||||||
|
|
||||||
|
### Build Process
|
||||||
|
- TypeScript compilation
|
||||||
|
- Next.js build optimization
|
||||||
|
- Asset optimization and bundling
|
||||||
|
|
||||||
|
### Environment Configuration
|
||||||
|
- Environment-specific configurations
|
||||||
|
- Feature flags for gradual rollouts
|
||||||
|
- Database connection management
|
||||||
|
|
||||||
|
## Security Patterns
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
- Session-based authentication
|
||||||
|
- Environment-based access control
|
||||||
|
- API route protection
|
||||||
|
|
||||||
|
### Data Validation
|
||||||
|
- Input validation on both client and server
|
||||||
|
- Type-safe API contracts
|
||||||
|
- Sanitization of user inputs
|
||||||
|
|
||||||
|
## Monitoring & Observability
|
||||||
|
|
||||||
|
### Error Tracking
|
||||||
|
- Client-side error boundaries
|
||||||
|
- Server-side error logging
|
||||||
|
- Performance monitoring
|
||||||
|
|
||||||
|
### Analytics
|
||||||
|
- User interaction tracking
|
||||||
|
- Performance metrics
|
||||||
|
- Database query monitoring
|
||||||
|
|
||||||
|
## Best Practices Summary
|
||||||
|
|
||||||
|
### Code Organization
|
||||||
|
- ✅ Follow the established directory structure
|
||||||
|
- ✅ Use consistent naming conventions
|
||||||
|
- ✅ Separate concerns (UI, logic, data)
|
||||||
|
- ✅ Keep components focused and small
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- ✅ Implement proper loading states
|
||||||
|
- ✅ Use AbortController for async operations
|
||||||
|
- ✅ Optimize database queries
|
||||||
|
- ✅ Implement proper caching strategies
|
||||||
|
|
||||||
|
### Type Safety
|
||||||
|
- ✅ Use TypeScript throughout
|
||||||
|
- ✅ Define proper interfaces for props
|
||||||
|
- ✅ Use type guards for runtime validation
|
||||||
|
- ✅ Leverage shared type packages
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
- ✅ Write tests for critical functionality
|
||||||
|
- ✅ Mock external dependencies properly
|
||||||
|
- ✅ Test error scenarios and edge cases
|
||||||
|
- ✅ Maintain good test coverage
|
||||||
5
.cursor/rules/performance-optimization.mdc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs:
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
52
.cursor/rules/react-context-patterns.mdc
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs:
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
# React Context & Provider Patterns
|
||||||
|
|
||||||
|
## Context Provider Best Practices
|
||||||
|
|
||||||
|
### Provider Implementation
|
||||||
|
- Use TypeScript interfaces for provider props with optional `initialCount` for testing
|
||||||
|
- Implement proper cleanup in `useEffect` to avoid React hooks warnings
|
||||||
|
- Reference: [apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/ResponseCountProvider.tsx](mdc:apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/ResponseCountProvider.tsx)
|
||||||
|
|
||||||
|
### Cleanup Pattern for Refs
|
||||||
|
```typescript
|
||||||
|
useEffect(() => {
|
||||||
|
const currentPendingRequests = pendingRequests.current;
|
||||||
|
const currentAbortController = abortController.current;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (currentAbortController) {
|
||||||
|
currentAbortController.abort();
|
||||||
|
}
|
||||||
|
currentPendingRequests.clear();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing Context Providers
|
||||||
|
- Always wrap components using context in the provider during tests
|
||||||
|
- Use `initialCount` prop for predictable test scenarios
|
||||||
|
- Mock context dependencies like `useParams`, `useResponseFilter`
|
||||||
|
- Example from [apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SurveyAnalysisCTA.test.tsx](mdc:apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SurveyAnalysisCTA.test.tsx):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
render(
|
||||||
|
<ResponseCountProvider survey={dummySurvey} initialCount={5}>
|
||||||
|
<ComponentUnderTest />
|
||||||
|
</ResponseCountProvider>
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Required Mocks for Context Testing
|
||||||
|
- Mock `next/navigation` with `useParams` returning environment and survey IDs
|
||||||
|
- Mock response filter context and actions
|
||||||
|
- Mock API actions that the provider depends on
|
||||||
|
|
||||||
|
### Context Hook Usage
|
||||||
|
- Create custom hooks like `useResponseCountContext()` for consuming context
|
||||||
|
- Provide meaningful error messages when context is used outside provider
|
||||||
|
- Use context for shared state that multiple components need to access
|
||||||
5
.cursor/rules/react-context-providers.mdc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs:
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
282
.cursor/rules/testing-patterns.mdc
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs:
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
# Testing Patterns & Best Practices
|
||||||
|
|
||||||
|
## Test File Naming & Environment
|
||||||
|
|
||||||
|
### File Extensions
|
||||||
|
- Use `.test.tsx` for React component/hook tests (runs in jsdom environment)
|
||||||
|
- Use `.test.ts` for utility/service tests (runs in Node environment)
|
||||||
|
- The vitest config uses `environmentMatchGlobs` to automatically set jsdom for `.tsx` files
|
||||||
|
|
||||||
|
### Test Structure
|
||||||
|
```typescript
|
||||||
|
// Import the mocked functions first
|
||||||
|
import { useHook } from "@/path/to/hook";
|
||||||
|
import { serviceFunction } from "@/path/to/service";
|
||||||
|
import { renderHook, waitFor } from "@testing-library/react";
|
||||||
|
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||||
|
|
||||||
|
// Mock dependencies
|
||||||
|
vi.mock("@/path/to/hook", () => ({
|
||||||
|
useHook: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("ComponentName", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
// Setup default mocks
|
||||||
|
});
|
||||||
|
|
||||||
|
test("descriptive test name", async () => {
|
||||||
|
// Test implementation
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## React Hook Testing
|
||||||
|
|
||||||
|
### Context Mocking
|
||||||
|
When testing hooks that use React Context:
|
||||||
|
```typescript
|
||||||
|
vi.mocked(useResponseFilter).mockReturnValue({
|
||||||
|
selectedFilter: {
|
||||||
|
filter: [],
|
||||||
|
onlyComplete: false,
|
||||||
|
},
|
||||||
|
setSelectedFilter: vi.fn(),
|
||||||
|
selectedOptions: {
|
||||||
|
questionOptions: [],
|
||||||
|
questionFilterOptions: [],
|
||||||
|
},
|
||||||
|
setSelectedOptions: vi.fn(),
|
||||||
|
dateRange: { from: new Date(), to: new Date() },
|
||||||
|
setDateRange: vi.fn(),
|
||||||
|
resetState: vi.fn(),
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing Async Hooks
|
||||||
|
- Always use `waitFor` for async operations
|
||||||
|
- Test both loading and completed states
|
||||||
|
- Verify API calls with correct parameters
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
test("fetches data on mount", async () => {
|
||||||
|
const { result } = renderHook(() => useHook());
|
||||||
|
|
||||||
|
expect(result.current.isLoading).toBe(true);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current.isLoading).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.data).toBe(expectedData);
|
||||||
|
expect(vi.mocked(apiCall)).toHaveBeenCalledWith(expectedParams);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing Hook Dependencies
|
||||||
|
To test useEffect dependencies, ensure mocks return different values:
|
||||||
|
```typescript
|
||||||
|
// First render
|
||||||
|
mockGetFormattedFilters.mockReturnValue(mockFilters);
|
||||||
|
|
||||||
|
// Change dependency and trigger re-render
|
||||||
|
const newMockFilters = { ...mockFilters, finished: true };
|
||||||
|
mockGetFormattedFilters.mockReturnValue(newMockFilters);
|
||||||
|
rerender();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Testing
|
||||||
|
|
||||||
|
### Race Condition Testing
|
||||||
|
Test AbortController implementation:
|
||||||
|
```typescript
|
||||||
|
test("cancels previous request when new request is made", async () => {
|
||||||
|
let resolveFirst: (value: any) => void;
|
||||||
|
let resolveSecond: (value: any) => void;
|
||||||
|
|
||||||
|
const firstPromise = new Promise((resolve) => {
|
||||||
|
resolveFirst = resolve;
|
||||||
|
});
|
||||||
|
const secondPromise = new Promise((resolve) => {
|
||||||
|
resolveSecond = resolve;
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.mocked(apiCall)
|
||||||
|
.mockReturnValueOnce(firstPromise as any)
|
||||||
|
.mockReturnValueOnce(secondPromise as any);
|
||||||
|
|
||||||
|
const { result } = renderHook(() => useHook());
|
||||||
|
|
||||||
|
// Trigger second request
|
||||||
|
result.current.refetch();
|
||||||
|
|
||||||
|
// Resolve in order - first should be cancelled
|
||||||
|
resolveFirst!({ data: 100 });
|
||||||
|
resolveSecond!({ data: 200 });
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current.isLoading).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should have result from second request
|
||||||
|
expect(result.current.data).toBe(200);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cleanup Testing
|
||||||
|
```typescript
|
||||||
|
test("cleans up on unmount", () => {
|
||||||
|
const abortSpy = vi.spyOn(AbortController.prototype, "abort");
|
||||||
|
|
||||||
|
const { unmount } = renderHook(() => useHook());
|
||||||
|
unmount();
|
||||||
|
|
||||||
|
expect(abortSpy).toHaveBeenCalled();
|
||||||
|
abortSpy.mockRestore();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling Testing
|
||||||
|
|
||||||
|
### API Error Testing
|
||||||
|
```typescript
|
||||||
|
test("handles API errors gracefully", async () => {
|
||||||
|
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||||
|
vi.mocked(apiCall).mockRejectedValue(new Error("API Error"));
|
||||||
|
|
||||||
|
const { result } = renderHook(() => useHook());
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current.isLoading).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(consoleSpy).toHaveBeenCalledWith("Error message:", expect.any(Error));
|
||||||
|
expect(result.current.data).toBe(fallbackValue);
|
||||||
|
|
||||||
|
consoleSpy.mockRestore();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cancelled Request Testing
|
||||||
|
```typescript
|
||||||
|
test("does not update state for cancelled requests", async () => {
|
||||||
|
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||||
|
|
||||||
|
let rejectFirst: (error: any) => void;
|
||||||
|
const firstPromise = new Promise((_, reject) => {
|
||||||
|
rejectFirst = reject;
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.mocked(apiCall)
|
||||||
|
.mockReturnValueOnce(firstPromise as any)
|
||||||
|
.mockResolvedValueOnce({ data: 42 });
|
||||||
|
|
||||||
|
const { result } = renderHook(() => useHook());
|
||||||
|
result.current.refetch();
|
||||||
|
|
||||||
|
const abortError = new Error("Request cancelled");
|
||||||
|
rejectFirst!(abortError);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current.isLoading).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should not log error for cancelled request
|
||||||
|
expect(consoleSpy).not.toHaveBeenCalled();
|
||||||
|
consoleSpy.mockRestore();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Type Safety in Tests
|
||||||
|
|
||||||
|
### Mock Type Assertions
|
||||||
|
Use type assertions for edge cases:
|
||||||
|
```typescript
|
||||||
|
vi.mocked(apiCall).mockResolvedValue({
|
||||||
|
data: null as any, // For testing null handling
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.mocked(apiCall).mockResolvedValue({
|
||||||
|
data: undefined as any, // For testing undefined handling
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Proper Mock Typing
|
||||||
|
Ensure mocks match the actual interface:
|
||||||
|
```typescript
|
||||||
|
const mockSurvey: TSurvey = {
|
||||||
|
id: "survey-123",
|
||||||
|
name: "Test Survey",
|
||||||
|
// ... other required properties
|
||||||
|
} as unknown as TSurvey; // Use when partial mocking is needed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Test Patterns
|
||||||
|
|
||||||
|
### Testing State Changes
|
||||||
|
```typescript
|
||||||
|
test("updates state correctly", async () => {
|
||||||
|
const { result } = renderHook(() => useHook());
|
||||||
|
|
||||||
|
// Initial state
|
||||||
|
expect(result.current.value).toBe(initialValue);
|
||||||
|
|
||||||
|
// Trigger change
|
||||||
|
result.current.updateValue(newValue);
|
||||||
|
|
||||||
|
// Verify change
|
||||||
|
expect(result.current.value).toBe(newValue);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing Multiple Scenarios
|
||||||
|
```typescript
|
||||||
|
test("handles different modes", async () => {
|
||||||
|
// Test regular mode
|
||||||
|
vi.mocked(useParams).mockReturnValue({ surveyId: "123" });
|
||||||
|
const { rerender } = renderHook(() => useHook());
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(vi.mocked(regularApi)).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test sharing mode
|
||||||
|
vi.mocked(useParams).mockReturnValue({
|
||||||
|
surveyId: "123",
|
||||||
|
sharingKey: "share-123"
|
||||||
|
});
|
||||||
|
rerender();
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(vi.mocked(sharingApi)).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Organization
|
||||||
|
|
||||||
|
### Comprehensive Test Coverage
|
||||||
|
For hooks, ensure you test:
|
||||||
|
- ✅ Initialization (with/without initial values)
|
||||||
|
- ✅ Data fetching (success/error cases)
|
||||||
|
- ✅ State updates and refetching
|
||||||
|
- ✅ Dependency changes triggering effects
|
||||||
|
- ✅ Manual actions (refetch, reset)
|
||||||
|
- ✅ Race condition prevention
|
||||||
|
- ✅ Cleanup on unmount
|
||||||
|
- ✅ Mode switching (if applicable)
|
||||||
|
- ✅ Edge cases (null/undefined data)
|
||||||
|
|
||||||
|
### Test Naming
|
||||||
|
Use descriptive test names that explain the scenario:
|
||||||
|
- ✅ "initializes with initial count"
|
||||||
|
- ✅ "fetches response count on mount for regular survey"
|
||||||
|
- ✅ "cancels previous request when new request is made"
|
||||||
|
- ❌ "test hook"
|
||||||
|
- ❌ "it works"
|
||||||
6
.cursor/rules/testing.mdc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
description: Whenever the user asks to write or update a test file for .tsx or .ts files.
|
||||||
|
globs:
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
Use the rules in this file when writing tests [copilot-instructions.md](mdc:.github/copilot-instructions.md)
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
|
|
||||||
ARG VARIANT=20
|
|
||||||
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}
|
|
||||||
|
|
||||||
# [Optional] Uncomment this section to install additional OS packages.
|
|
||||||
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
|
||||||
# && apt-get -y install --no-install-recommends <your-package-list-here>
|
|
||||||
|
|
||||||
# [Optional] Uncomment if you want to install an additional version of node using nvm
|
|
||||||
# ARG EXTRA_NODE_VERSION=10
|
|
||||||
# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"
|
|
||||||
|
|
||||||
# [Optional] Uncomment if you want to install more global node modules
|
|
||||||
# RUN su node -c "npm install -g <your-package-list-here>"
|
|
||||||
|
|
||||||
RUN su node -c "npm install -g pnpm"
|
|
||||||
@@ -1,28 +1,6 @@
|
|||||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
|
||||||
// https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/javascript-node-postgres
|
|
||||||
// Update the VARIANT arg in docker-compose.yml to pick a Node.js version
|
|
||||||
{
|
{
|
||||||
// Configure tool-specific properties.
|
"features": {},
|
||||||
"customizations": {
|
"image": "mcr.microsoft.com/devcontainers/universal:2",
|
||||||
// Configure properties specific to VS Code.
|
"postAttachCommand": "pnpm go",
|
||||||
"vscode": {
|
"postCreateCommand": "cp .env.example .env && sed -i '/^ENCRYPTION_KEY=/c\\ENCRYPTION_KEY='$(openssl rand -hex 32) .env && sed -i '/^NEXTAUTH_SECRET=/c\\NEXTAUTH_SECRET='$(openssl rand -hex 32) .env && sed -i '/^CRON_SECRET=/c\\CRON_SECRET='$(openssl rand -hex 32) .env && pnpm install && pnpm db:migrate:dev"
|
||||||
// Add the IDs of extensions you want installed when the container is created.
|
|
||||||
"extensions": ["dbaeumer.vscode-eslint"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"dockerComposeFile": "docker-compose.yml",
|
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
|
||||||
// This can be used to network with other containers or with the host.
|
|
||||||
"forwardPorts": [3000, 5432, 8025],
|
|
||||||
|
|
||||||
"name": "Node.js & PostgreSQL",
|
|
||||||
"postAttachCommand": "pnpm dev --filter=@formbricks/web... --filter=@formbricks/demo...",
|
|
||||||
|
|
||||||
// Use 'postCreateCommand' to run commands after the container is created.
|
|
||||||
"postCreateCommand": "cp .env.example .env && sed -i '/^ENCRYPTION_KEY=/c\\ENCRYPTION_KEY='$(openssl rand -hex 32) .env && sed -i '/^NEXTAUTH_SECRET=/c\\NEXTAUTH_SECRET='$(openssl rand -hex 32) .env && sed -i '/^CRON_SECRET=/c\\CRON_SECRET='$(openssl rand -hex 32) .env && pnpm install && pnpm db:migrate:dev",
|
|
||||||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
|
||||||
"remoteUser": "node",
|
|
||||||
"service": "app",
|
|
||||||
"workspaceFolder": "/workspace"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
version: "3.8"
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
args:
|
|
||||||
# Update 'VARIANT' to pick an LTS version of Node.js: 20, 18, 16, 14.
|
|
||||||
# Append -bullseye or -buster to pin to an OS version.
|
|
||||||
# Use -bullseye variants on local arm64/Apple Silicon.
|
|
||||||
VARIANT: "20"
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- ..:/workspace:cached
|
|
||||||
|
|
||||||
# Overrides default command so things don't shut down after the process ends.
|
|
||||||
command: sleep infinity
|
|
||||||
|
|
||||||
# Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function.
|
|
||||||
network_mode: service:db
|
|
||||||
# Uncomment the next line to use a non-root user for all processes.
|
|
||||||
# user: node
|
|
||||||
|
|
||||||
# Use "forwardPorts" in **devcontainer.json** to forward an app port locally.
|
|
||||||
# (Adding the "ports" property to this file will not forward from a Codespace.)
|
|
||||||
|
|
||||||
db:
|
|
||||||
image: pgvector/pgvector:pg17
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
|
||||||
- postgres-data:/var/lib/postgresql/data
|
|
||||||
environment:
|
|
||||||
POSTGRES_PASSWORD: postgres
|
|
||||||
POSTGRES_USER: postgres
|
|
||||||
POSTGRES_DB: formbricks
|
|
||||||
# Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally.
|
|
||||||
# (Adding the "ports" property to this file will not forward from a Codespace.)
|
|
||||||
|
|
||||||
mailhog:
|
|
||||||
image: mailhog/mailhog
|
|
||||||
network_mode: service:app
|
|
||||||
logging:
|
|
||||||
driver:
|
|
||||||
"none" # disable saving logs
|
|
||||||
# ports:
|
|
||||||
# - 8025:8025 # web ui
|
|
||||||
# 1025:1025 # smtp server
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
postgres-data: null
|
|
||||||
@@ -1,39 +1,56 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
# **/node_modules
|
**/node_modules
|
||||||
.pnp
|
.pnp
|
||||||
.pnp.js
|
.pnp.js
|
||||||
.pnpm-store/
|
.pnpm-store/
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
coverage
|
**/coverage
|
||||||
|
|
||||||
# next.js
|
# next.js
|
||||||
**/.next
|
**/.next/
|
||||||
**/out
|
**/out/
|
||||||
**/build
|
**/build
|
||||||
|
|
||||||
# node
|
# node
|
||||||
**/dist
|
**/dist/
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
**/.DS_Store
|
||||||
*.pem
|
*.pem
|
||||||
|
Zone.Identifier
|
||||||
|
|
||||||
# debug
|
# debug
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
|
||||||
# turbo
|
# local env files
|
||||||
.turbo
|
**/.env
|
||||||
|
**/.env.local
|
||||||
|
**/.env.development.local
|
||||||
|
**/.env.test.local
|
||||||
|
**/.env.production.local
|
||||||
|
!packages/database/.env
|
||||||
|
!apps/web/.env
|
||||||
|
|
||||||
# nixos stuff
|
# build tools
|
||||||
|
.turbo
|
||||||
|
**/*vite.config.*.timestamp-*
|
||||||
|
|
||||||
|
# environment specific
|
||||||
.direnv
|
.direnv
|
||||||
|
|
||||||
.vscode
|
# Playwright
|
||||||
.github
|
/test-results/
|
||||||
**/.turbo
|
/playwright-report/
|
||||||
|
/blob-report/
|
||||||
|
/playwright/.cache/
|
||||||
|
|
||||||
.env
|
# project specific
|
||||||
|
packages/lib/uploads
|
||||||
|
apps/web/public/js
|
||||||
|
packages/database/migrations
|
||||||
|
branch.json
|
||||||
72
.env.example
@@ -25,6 +25,9 @@ NEXTAUTH_SECRET=
|
|||||||
# You can use: `openssl rand -hex 32` to generate a secure one
|
# You can use: `openssl rand -hex 32` to generate a secure one
|
||||||
CRON_SECRET=
|
CRON_SECRET=
|
||||||
|
|
||||||
|
# Set the minimum log level(debug, info, warn, error, fatal)
|
||||||
|
LOG_LEVEL=info
|
||||||
|
|
||||||
##############
|
##############
|
||||||
# DATABASE #
|
# DATABASE #
|
||||||
##############
|
##############
|
||||||
@@ -39,6 +42,7 @@ DATABASE_URL='postgresql://postgres:postgres@localhost:5432/formbricks?schema=pu
|
|||||||
# See optional configurations below if you want to disable these features.
|
# See optional configurations below if you want to disable these features.
|
||||||
|
|
||||||
MAIL_FROM=noreply@example.com
|
MAIL_FROM=noreply@example.com
|
||||||
|
MAIL_FROM_NAME=Formbricks
|
||||||
SMTP_HOST=localhost
|
SMTP_HOST=localhost
|
||||||
SMTP_PORT=1025
|
SMTP_PORT=1025
|
||||||
# Enable SMTP_SECURE_ENABLED for TLS (port 465)
|
# Enable SMTP_SECURE_ENABLED for TLS (port 465)
|
||||||
@@ -46,6 +50,9 @@ SMTP_SECURE_ENABLED=0
|
|||||||
SMTP_USER=smtpUser
|
SMTP_USER=smtpUser
|
||||||
SMTP_PASSWORD=smtpPassword
|
SMTP_PASSWORD=smtpPassword
|
||||||
|
|
||||||
|
# If set to 0, the server will not require SMTP_USER and SMTP_PASSWORD(default is 1)
|
||||||
|
# SMTP_AUTHENTICATED=
|
||||||
|
|
||||||
# If set to 0, the server will accept connections without requiring authorization from the list of supplied CAs (default is 1).
|
# If set to 0, the server will accept connections without requiring authorization from the list of supplied CAs (default is 1).
|
||||||
# SMTP_REJECT_UNAUTHORIZED_TLS=0
|
# SMTP_REJECT_UNAUTHORIZED_TLS=0
|
||||||
|
|
||||||
@@ -73,6 +80,9 @@ S3_ENDPOINT_URL=
|
|||||||
# Force path style for S3 compatible storage (0 for disabled, 1 for enabled)
|
# Force path style for S3 compatible storage (0 for disabled, 1 for enabled)
|
||||||
S3_FORCE_PATH_STYLE=0
|
S3_FORCE_PATH_STYLE=0
|
||||||
|
|
||||||
|
# Set this URL to add a custom domain to your survey links(default is WEBAPP_URL)
|
||||||
|
# SURVEY_URL=https://survey.example.com
|
||||||
|
|
||||||
#####################
|
#####################
|
||||||
# Disable Features #
|
# Disable Features #
|
||||||
#####################
|
#####################
|
||||||
@@ -83,16 +93,15 @@ EMAIL_VERIFICATION_DISABLED=1
|
|||||||
# Password Reset. If you enable Password Reset functionality you have to setup SMTP-Settings, too.
|
# Password Reset. If you enable Password Reset functionality you have to setup SMTP-Settings, too.
|
||||||
PASSWORD_RESET_DISABLED=1
|
PASSWORD_RESET_DISABLED=1
|
||||||
|
|
||||||
# Signup. Disable the ability for new users to create an account.
|
|
||||||
# Note: This variable is only available to the SaaS setup of Formbricks Cloud. Signup is disable by default for self-hosting.
|
|
||||||
# SIGNUP_DISABLED=1
|
|
||||||
|
|
||||||
# Email login. Disable the ability for users to login with email.
|
# Email login. Disable the ability for users to login with email.
|
||||||
# EMAIL_AUTH_DISABLED=1
|
# EMAIL_AUTH_DISABLED=1
|
||||||
|
|
||||||
# Organization Invite. Disable the ability for invited users to create an account.
|
# Organization Invite. Disable the ability for invited users to create an account.
|
||||||
# INVITE_DISABLED=1
|
# INVITE_DISABLED=1
|
||||||
|
|
||||||
|
# Docker cron jobs. Disable the supercronic cron jobs in the Docker image (useful for cluster setups).
|
||||||
|
# DOCKER_CRON_ENABLED=1
|
||||||
|
|
||||||
##########
|
##########
|
||||||
# Other #
|
# Other #
|
||||||
##########
|
##########
|
||||||
@@ -101,6 +110,15 @@ PASSWORD_RESET_DISABLED=1
|
|||||||
PRIVACY_URL=
|
PRIVACY_URL=
|
||||||
TERMS_URL=
|
TERMS_URL=
|
||||||
IMPRINT_URL=
|
IMPRINT_URL=
|
||||||
|
IMPRINT_ADDRESS=
|
||||||
|
|
||||||
|
# Configure Turnstile in signup flow
|
||||||
|
# TURNSTILE_SITE_KEY=
|
||||||
|
# TURNSTILE_SECRET_KEY=
|
||||||
|
|
||||||
|
# Google reCAPTCHA v3 keys
|
||||||
|
RECAPTCHA_SITE_KEY=
|
||||||
|
RECAPTCHA_SECRET_KEY=
|
||||||
|
|
||||||
# Configure Github Login
|
# Configure Github Login
|
||||||
GITHUB_ID=
|
GITHUB_ID=
|
||||||
@@ -122,6 +140,9 @@ AZUREAD_TENANT_ID=
|
|||||||
# OIDC_DISPLAY_NAME=
|
# OIDC_DISPLAY_NAME=
|
||||||
# OIDC_SIGNING_ALGORITHM=
|
# OIDC_SIGNING_ALGORITHM=
|
||||||
|
|
||||||
|
# Configure SAML SSO
|
||||||
|
# SAML_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/formbricks-saml
|
||||||
|
|
||||||
# Configure this when you want to ship JS & CSS files from a complete URL instead of the current domain
|
# Configure this when you want to ship JS & CSS files from a complete URL instead of the current domain
|
||||||
# ASSET_PREFIX_URL=
|
# ASSET_PREFIX_URL=
|
||||||
|
|
||||||
@@ -133,11 +154,6 @@ NOTION_OAUTH_CLIENT_SECRET=
|
|||||||
STRIPE_SECRET_KEY=
|
STRIPE_SECRET_KEY=
|
||||||
STRIPE_WEBHOOK_SECRET=
|
STRIPE_WEBHOOK_SECRET=
|
||||||
|
|
||||||
# Configure Formbricks usage within Formbricks
|
|
||||||
NEXT_PUBLIC_FORMBRICKS_API_HOST=
|
|
||||||
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=
|
|
||||||
NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID=
|
|
||||||
|
|
||||||
# Oauth credentials for Google sheet integration
|
# Oauth credentials for Google sheet integration
|
||||||
GOOGLE_SHEETS_CLIENT_ID=
|
GOOGLE_SHEETS_CLIENT_ID=
|
||||||
GOOGLE_SHEETS_CLIENT_SECRET=
|
GOOGLE_SHEETS_CLIENT_SECRET=
|
||||||
@@ -156,12 +172,12 @@ ENTERPRISE_LICENSE_KEY=
|
|||||||
# Automatically assign new users to a specific organization and role within that organization
|
# Automatically assign new users to a specific organization and role within that organization
|
||||||
# Insert an existing organization id or generate a valid CUID for a new one at https://www.getuniqueid.com/cuid (e.g. cjld2cjxh0000qzrmn831i7rn)
|
# Insert an existing organization id or generate a valid CUID for a new one at https://www.getuniqueid.com/cuid (e.g. cjld2cjxh0000qzrmn831i7rn)
|
||||||
# (Role Management is an Enterprise feature)
|
# (Role Management is an Enterprise feature)
|
||||||
# DEFAULT_ORGANIZATION_ID=
|
# AUTH_SSO_DEFAULT_TEAM_ID=
|
||||||
# DEFAULT_ORGANIZATION_ROLE=admin
|
# AUTH_SKIP_INVITE_FOR_SSO=
|
||||||
|
|
||||||
# Send new users to customer.io
|
# Send new users to Brevo
|
||||||
# CUSTOMER_IO_API_KEY=
|
# BREVO_API_KEY=
|
||||||
# CUSTOMER_IO_SITE_ID=
|
# BREVO_LIST_ID=
|
||||||
|
|
||||||
# Ignore Rate Limiting across the Formbricks app
|
# Ignore Rate Limiting across the Formbricks app
|
||||||
# RATE_LIMITING_DISABLED=1
|
# RATE_LIMITING_DISABLED=1
|
||||||
@@ -173,16 +189,30 @@ ENTERPRISE_LICENSE_KEY=
|
|||||||
UNSPLASH_ACCESS_KEY=
|
UNSPLASH_ACCESS_KEY=
|
||||||
|
|
||||||
# The below is used for Next Caching (uses In-Memory from Next Cache if not provided)
|
# The below is used for Next Caching (uses In-Memory from Next Cache if not provided)
|
||||||
|
# You can also add more configuration to Redis using the redis.conf file in the root directory
|
||||||
# REDIS_URL=redis://localhost:6379
|
# REDIS_URL=redis://localhost:6379
|
||||||
|
|
||||||
# The below is used for Rate Limiting (uses In-Memory LRU Cache if not provided) (You can use a service like Webdis for this)
|
# The below is used for Rate Limiting (uses In-Memory LRU Cache if not provided) (You can use a service like Webdis for this)
|
||||||
# REDIS_HTTP_URL:
|
# REDIS_HTTP_URL:
|
||||||
|
|
||||||
# Disable custom cache handler if necessary (e.g. if deployed on Vercel)
|
# The below is used for Rate Limiting for management API
|
||||||
# CUSTOM_CACHE_DISABLED=1
|
UNKEY_ROOT_KEY=
|
||||||
|
|
||||||
# Azure AI settings
|
# INTERCOM_APP_ID=
|
||||||
# AI_AZURE_RESSOURCE_NAME=
|
# INTERCOM_SECRET_KEY=
|
||||||
# AI_AZURE_API_KEY=
|
|
||||||
# AI_AZURE_EMBEDDINGS_DEPLOYMENT_ID=
|
# Enable Prometheus metrics
|
||||||
# AI_AZURE_LLM_DEPLOYMENT_ID=
|
# PROMETHEUS_ENABLED=
|
||||||
|
# PROMETHEUS_EXPORTER_PORT=
|
||||||
|
|
||||||
|
# The SENTRY_DSN is used for error tracking and performance monitoring with Sentry.
|
||||||
|
# SENTRY_DSN=
|
||||||
|
# The SENTRY_AUTH_TOKEN variable is picked up by the Sentry Build Plugin.
|
||||||
|
# It's used automatically by Sentry during the build for authentication when uploading source maps.
|
||||||
|
# SENTRY_AUTH_TOKEN=
|
||||||
|
|
||||||
|
# Configure the minimum role for user management from UI(owner, manager, disabled)
|
||||||
|
# USER_MANAGEMENT_MINIMUM_ROLE="manager"
|
||||||
|
|
||||||
|
# Configure the maximum age for the session in seconds. Default is 86400 (24 hours)
|
||||||
|
# SESSION_MAX_AGE=86400
|
||||||
|
|||||||
11
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
name: Bug report
|
name: Bug report
|
||||||
description: "Found a bug? Please fill out the sections below. \U0001F44D"
|
description: "Found a bug? Please fill out the sections below. \U0001F44D"
|
||||||
labels:
|
type: bug
|
||||||
- bug
|
labels: ["bug"]
|
||||||
body:
|
body:
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: issue-summary
|
id: issue-summary
|
||||||
@@ -10,6 +10,13 @@ body:
|
|||||||
description: A summary of the issue. This needs to be a clear detailed-rich summary.
|
description: A summary of the issue. This needs to be a clear detailed-rich summary.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: issue-expected-behavior
|
||||||
|
attributes:
|
||||||
|
label: Expected Behavior
|
||||||
|
description: A clear and concise description of what you expected to happen.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: other-information
|
id: other-information
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
5
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,7 +1,6 @@
|
|||||||
name: Feature request
|
name: Feature request
|
||||||
description: "Suggest an idea for this project \U0001F680"
|
description: "Suggest an idea for this project \U0001F680"
|
||||||
labels:
|
type: feature
|
||||||
- enhancement
|
|
||||||
body:
|
body:
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: problem-description
|
id: problem-description
|
||||||
@@ -30,4 +29,4 @@ body:
|
|||||||
### Additional resources 🤓
|
### Additional resources 🤓
|
||||||
|
|
||||||
- Check out our [Contributor Docs](https://formbricks.com/docs/developer-docs/contributing/get-started)
|
- Check out our [Contributor Docs](https://formbricks.com/docs/developer-docs/contributing/get-started)
|
||||||
- Anything unclear? [Ask in Discord](https://formbricks.com/discord)
|
- Anything unclear? [Ask in Github Discussions](https://github.com/formbricks/formbricks/discussions)
|
||||||
|
|||||||
11
.github/ISSUE_TEMPLATE/task.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
name: Task (internal)
|
||||||
|
description: "Template for creating a task. Used by the Formbricks Team only \U0001f4e5"
|
||||||
|
type: task
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: task-summary
|
||||||
|
attributes:
|
||||||
|
label: Task description
|
||||||
|
description: A clear detailed-rich description of the task.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
17
.github/actions/cache-build-web/action.yml
vendored
@@ -8,6 +8,14 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
default: "0"
|
default: "0"
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
turbo_token:
|
||||||
|
description: "Turborepo token"
|
||||||
|
required: false
|
||||||
|
turbo_team:
|
||||||
|
description: "Turborepo team"
|
||||||
|
required: false
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
steps:
|
steps:
|
||||||
@@ -41,7 +49,7 @@ runs:
|
|||||||
if: steps.cache-build.outputs.cache-hit != 'true'
|
if: steps.cache-build.outputs.cache-hit != 'true'
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||||
if: steps.cache-build.outputs.cache-hit != 'true'
|
if: steps.cache-build.outputs.cache-hit != 'true'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
@@ -57,14 +65,13 @@ runs:
|
|||||||
run: |
|
run: |
|
||||||
RANDOM_KEY=$(openssl rand -hex 32)
|
RANDOM_KEY=$(openssl rand -hex 32)
|
||||||
sed -i "s/ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${RANDOM_KEY}/" .env
|
sed -i "s/ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${RANDOM_KEY}/" .env
|
||||||
sed -i "s/CRON_SECRET=.*/CRON_SECRET=${RANDOM_KEY}/" .env
|
|
||||||
sed -i "s/NEXTAUTH_SECRET=.*/NEXTAUTH_SECRET=${RANDOM_KEY}/" .env
|
|
||||||
sed -i "s/ENTERPRISE_LICENSE_KEY=.*/ENTERPRISE_LICENSE_KEY=${RANDOM_KEY}/" .env
|
|
||||||
echo "E2E_TESTING=${{ inputs.e2e_testing_mode }}" >> .env
|
echo "E2E_TESTING=${{ inputs.e2e_testing_mode }}" >> .env
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- run: |
|
- run: |
|
||||||
pnpm build --filter=@formbricks/web...
|
pnpm build --filter=@formbricks/web...
|
||||||
|
|
||||||
if: steps.cache-build.outputs.cache-hit != 'true'
|
if: steps.cache-build.outputs.cache-hit != 'true'
|
||||||
shell: bash
|
shell: bash
|
||||||
|
env:
|
||||||
|
TURBO_TOKEN: ${{ inputs.turbo_token }}
|
||||||
|
TURBO_TEAM: ${{ inputs.turbo_team }}
|
||||||
|
|||||||
32
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Testing Instructions
|
||||||
|
|
||||||
|
When generating test files inside the "/app/web" path, follow these rules:
|
||||||
|
|
||||||
|
- You are an experienced senior software engineer
|
||||||
|
- Use vitest
|
||||||
|
- Ensure 100% code coverage
|
||||||
|
- Add as few comments as possible
|
||||||
|
- The test file should be located in the same folder as the original file
|
||||||
|
- Use the `test` function instead of `it`
|
||||||
|
- Follow the same test pattern used for other files in the package where the file is located
|
||||||
|
- All imports should be at the top of the file, not inside individual tests
|
||||||
|
- For mocking inside "test" blocks use "vi.mocked"
|
||||||
|
- If the file is located in the "packages/survey" path, use "@testing-library/preact" instead of "@testing-library/react"
|
||||||
|
- Don't mock functions that are already mocked in the "apps/web/vitestSetup.ts" file
|
||||||
|
- When using "screen.getByText" check for the tolgee string if it is being used in the file.
|
||||||
|
- The types for mocked variables can be found in the "packages/types" path. Be sure that every imported type exists before using it. Don't create types that are not already in the codebase.
|
||||||
|
- When mocking data check if the properties added are part of the type of the object being mocked. Only specify known properties, don't use properties that are not part of the type.
|
||||||
|
|
||||||
|
If it's a test for a ".tsx" file, follow these extra instructions:
|
||||||
|
|
||||||
|
- Add this code inside the "describe" block and before any test:
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
|
- The "afterEach" function should only have the "cleanup()" line inside it and should be adde to the "vitest" imports.
|
||||||
|
- For click events, import userEvent from "@testing-library/user-event"
|
||||||
|
- Mock other components that can make the text more complex and but at the same time mocking it wouldn't make the test flaky. It's ok to leave basic and simple components.
|
||||||
|
- You don't need to mock @tolgee/react
|
||||||
|
- Use "import "@testing-library/jest-dom/vitest";"
|
||||||
10
.github/workflows/apply-issue-labels-to-pr.yml
vendored
@@ -5,6 +5,9 @@ on:
|
|||||||
types:
|
types:
|
||||||
- opened
|
- opened
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
label_on_pr:
|
label_on_pr:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -15,8 +18,13 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Apply labels from linked issue to PR
|
- name: Apply labels from linked issue to PR
|
||||||
uses: actions/github-script@v5
|
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
script: |
|
script: |
|
||||||
|
|||||||
28
.github/workflows/build-docs.yml
vendored
@@ -1,28 +0,0 @@
|
|||||||
name: Build Docs
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build Docs
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 30
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: ./.github/actions/dangerous-git-checkout
|
|
||||||
|
|
||||||
- name: Setup Node.js 20.x
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 20.x
|
|
||||||
|
|
||||||
- name: Install pnpm
|
|
||||||
uses: pnpm/action-setup@v4
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- run: |
|
|
||||||
pnpm build --filter=@formbricks/docs...
|
|
||||||
shell: bash
|
|
||||||
13
.github/workflows/build-web.yml
vendored
@@ -1,6 +1,10 @@
|
|||||||
name: Build Web
|
name: Build Web
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build Formbricks-web
|
name: Build Formbricks-web
|
||||||
@@ -8,7 +12,12 @@ jobs:
|
|||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: ./.github/actions/dangerous-git-checkout
|
- uses: ./.github/actions/dangerous-git-checkout
|
||||||
|
|
||||||
- name: Build & Cache Web Binaries
|
- name: Build & Cache Web Binaries
|
||||||
@@ -16,3 +25,5 @@ jobs:
|
|||||||
id: cache-build-web
|
id: cache-build-web
|
||||||
with:
|
with:
|
||||||
e2e_testing_mode: "0"
|
e2e_testing_mode: "0"
|
||||||
|
turbo_token: ${{ secrets.TURBO_TOKEN }}
|
||||||
|
turbo_team: ${{ vars.TURBO_TEAM }}
|
||||||
|
|||||||
18
.github/workflows/chromatic.yml
vendored
@@ -10,20 +10,30 @@ jobs:
|
|||||||
chromatic:
|
chromatic:
|
||||||
name: Run Chromatic
|
name: Run Chromatic
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
id-token: write
|
||||||
|
actions: read
|
||||||
steps:
|
steps:
|
||||||
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||||
- name: Run Chromatic
|
- name: Run Chromatic
|
||||||
uses: chromaui/action@latest
|
uses: chromaui/action@c93e0bc3a63aa176e14a75b61a31847cbfdd341c # latest
|
||||||
with:
|
with:
|
||||||
# ⚠️ Make sure to configure a `CHROMATIC_PROJECT_TOKEN` repository secret
|
# ⚠️ Make sure to configure a `CHROMATIC_PROJECT_TOKEN` repository secret
|
||||||
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||||
|
|||||||
92
.github/workflows/codeql.yml
vendored
@@ -1,92 +0,0 @@
|
|||||||
# For most projects, this workflow file will not need changing; you simply need
|
|
||||||
# to commit it to your repository.
|
|
||||||
#
|
|
||||||
# You may wish to alter this file to override the set of languages analyzed,
|
|
||||||
# or to provide custom queries or build logic.
|
|
||||||
#
|
|
||||||
# ******** NOTE ********
|
|
||||||
# We have attempted to detect the languages in your repository. Please check
|
|
||||||
# the `language` matrix defined below to confirm you have the correct set of
|
|
||||||
# supported CodeQL languages.
|
|
||||||
#
|
|
||||||
name: "CodeQL Advanced"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "main" ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ "main" ]
|
|
||||||
schedule:
|
|
||||||
- cron: '17 1 * * 1'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze (${{ matrix.language }})
|
|
||||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
|
||||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
|
||||||
# - https://gh.io/supported-runners-and-hardware-resources
|
|
||||||
# - https://gh.io/using-larger-runners (GitHub.com only)
|
|
||||||
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
|
||||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
|
||||||
permissions:
|
|
||||||
# required for all workflows
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
# required to fetch internal or private CodeQL packs
|
|
||||||
packages: read
|
|
||||||
|
|
||||||
# only required for workflows in private repositories
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- language: javascript-typescript
|
|
||||||
build-mode: none
|
|
||||||
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
|
|
||||||
# Use `c-cpp` to analyze code written in C, C++ or both
|
|
||||||
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
|
||||||
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
|
||||||
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
|
||||||
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
|
||||||
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
|
||||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v3
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
build-mode: ${{ matrix.build-mode }}
|
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
||||||
# By default, queries listed here will override any specified in a config file.
|
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
||||||
|
|
||||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
|
||||||
# queries: security-extended,security-and-quality
|
|
||||||
|
|
||||||
# If the analyze step fails for one of the languages you are analyzing with
|
|
||||||
# "We were unable to automatically build your code", modify the matrix above
|
|
||||||
# to set the build mode to "manual" for that language. Then modify this step
|
|
||||||
# to build your code.
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
|
||||||
- if: matrix.build-mode == 'manual'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo 'If you are using a "manual" build mode for one or more of the' \
|
|
||||||
'languages you are analyzing, replace this with the commands to build' \
|
|
||||||
'your code, for example:'
|
|
||||||
echo ' make bootstrap'
|
|
||||||
echo ' make release'
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v3
|
|
||||||
with:
|
|
||||||
category: "/language:${{matrix.language}}"
|
|
||||||
24
.github/workflows/cron-surveyStatusUpdate.yml
vendored
@@ -1,24 +0,0 @@
|
|||||||
name: Cron - Survey status update
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
# "Scheduled workflows run on the latest commit on the default or base branch."
|
|
||||||
# — https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule
|
|
||||||
# schedule:
|
|
||||||
# Runs “At 00:00.” (see https://crontab.guru)
|
|
||||||
# - cron: "0 0 * * *"
|
|
||||||
jobs:
|
|
||||||
cron-weeklySummary:
|
|
||||||
env:
|
|
||||||
APP_URL: ${{ secrets.APP_URL }}
|
|
||||||
CRON_SECRET: ${{ secrets.CRON_SECRET }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: cURL request
|
|
||||||
if: ${{ env.APP_URL && env.CRON_SECRET }}
|
|
||||||
run: |
|
|
||||||
curl ${{ env.APP_URL }}/api/cron/survey-status \
|
|
||||||
-X POST \
|
|
||||||
-H 'content-type: application/json' \
|
|
||||||
-H 'x-api-key: ${{ env.CRON_SECRET }}' \
|
|
||||||
--fail
|
|
||||||
24
.github/workflows/cron-weeklySummary.yml
vendored
@@ -1,24 +0,0 @@
|
|||||||
name: Cron - Weekly summary
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
# "Scheduled workflows run on the latest commit on the default or base branch."
|
|
||||||
# — https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule
|
|
||||||
schedule:
|
|
||||||
# Runs “At 08:00 on Monday.” (see https://crontab.guru)
|
|
||||||
- cron: "0 8 * * 1"
|
|
||||||
jobs:
|
|
||||||
cron-weeklySummary:
|
|
||||||
env:
|
|
||||||
APP_URL: ${{ secrets.APP_URL }}
|
|
||||||
CRON_SECRET: ${{ secrets.CRON_SECRET }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: cURL request
|
|
||||||
if: ${{ env.APP_URL && env.CRON_SECRET }}
|
|
||||||
run: |
|
|
||||||
curl ${{ env.APP_URL }}/api/cron/weekly-summary \
|
|
||||||
-X POST \
|
|
||||||
-H 'content-type: application/json' \
|
|
||||||
-H 'x-api-key: ${{ env.CRON_SECRET }}' \
|
|
||||||
--fail
|
|
||||||
27
.github/workflows/dependency-review.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Dependency Review Action
|
||||||
|
#
|
||||||
|
# This Action will scan dependency manifest files that change as part of a Pull Request,
|
||||||
|
# surfacing known-vulnerable versions of the packages declared or updated in the PR.
|
||||||
|
# Once installed, if the workflow run is marked as required,
|
||||||
|
# PRs introducing known-vulnerable packages will be blocked from merging.
|
||||||
|
#
|
||||||
|
# Source repository: https://github.com/actions/dependency-review-action
|
||||||
|
name: 'Dependency Review'
|
||||||
|
on: [pull_request]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
dependency-review:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: 'Checkout Repository'
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
- name: 'Dependency Review'
|
||||||
|
uses: actions/dependency-review-action@38ecb5b593bf0eb19e335c03f97670f792489a8b # v4.7.0
|
||||||
102
.github/workflows/deploy-formbricks-cloud.yml
vendored
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
name: Formbricks Cloud Deployment
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
VERSION:
|
||||||
|
description: 'The version of the Docker image to release, full image tag if image tag is v0.0.0 enter v0.0.0.'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
REPOSITORY:
|
||||||
|
description: 'The repository to use for the Docker image'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: 'ghcr.io/formbricks/formbricks'
|
||||||
|
ENVIRONMENT:
|
||||||
|
description: 'The environment to deploy to'
|
||||||
|
required: true
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- stage
|
||||||
|
- prod
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
VERSION:
|
||||||
|
description: 'The version of the Docker image to release'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
REPOSITORY:
|
||||||
|
description: 'The repository to use for the Docker image'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: 'ghcr.io/formbricks/formbricks'
|
||||||
|
ENVIRONMENT:
|
||||||
|
description: 'The environment to deploy to'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
helmfile-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4.2.2
|
||||||
|
|
||||||
|
- name: Tailscale
|
||||||
|
uses: tailscale/github-action@v3
|
||||||
|
with:
|
||||||
|
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
||||||
|
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
||||||
|
tags: tag:github
|
||||||
|
|
||||||
|
- name: Configure AWS Credentials
|
||||||
|
uses: aws-actions/configure-aws-credentials@f24d7193d98baebaeacc7e2227925dd47cc267f5 # v4.2.0
|
||||||
|
with:
|
||||||
|
role-to-assume: ${{ secrets.AWS_ASSUME_ROLE_ARN }}
|
||||||
|
aws-region: "eu-central-1"
|
||||||
|
|
||||||
|
- name: Setup Cluster Access
|
||||||
|
run: |
|
||||||
|
aws eks update-kubeconfig --name formbricks-prod-eks --region eu-central-1
|
||||||
|
env:
|
||||||
|
AWS_REGION: eu-central-1
|
||||||
|
|
||||||
|
- uses: helmfile/helmfile-action@v2
|
||||||
|
name: Deploy Formbricks Cloud Prod
|
||||||
|
if: inputs.ENVIRONMENT == 'prod'
|
||||||
|
env:
|
||||||
|
VERSION: ${{ inputs.VERSION }}
|
||||||
|
REPOSITORY: ${{ inputs.REPOSITORY }}
|
||||||
|
FORMBRICKS_S3_BUCKET: ${{ secrets.FORMBRICKS_S3_BUCKET }}
|
||||||
|
FORMBRICKS_INGRESS_CERT_ARN: ${{ secrets.FORMBRICKS_INGRESS_CERT_ARN }}
|
||||||
|
FORMBRICKS_ROLE_ARN: ${{ secrets.FORMBRICKS_ROLE_ARN }}
|
||||||
|
with:
|
||||||
|
helmfile-version: 'v1.0.0'
|
||||||
|
helm-plugins: >
|
||||||
|
https://github.com/databus23/helm-diff,
|
||||||
|
https://github.com/jkroepke/helm-secrets
|
||||||
|
helmfile-args: apply -l environment=prod
|
||||||
|
helmfile-auto-init: "false"
|
||||||
|
helmfile-workdirectory: infra/formbricks-cloud-helm
|
||||||
|
|
||||||
|
- uses: helmfile/helmfile-action@v2
|
||||||
|
name: Deploy Formbricks Cloud Stage
|
||||||
|
if: inputs.ENVIRONMENT == 'stage'
|
||||||
|
env:
|
||||||
|
VERSION: ${{ inputs.VERSION }}
|
||||||
|
REPOSITORY: ${{ inputs.REPOSITORY }}
|
||||||
|
FORMBRICKS_INGRESS_CERT_ARN: ${{ secrets.STAGE_FORMBRICKS_INGRESS_CERT_ARN }}
|
||||||
|
FORMBRICKS_ROLE_ARN: ${{ secrets.STAGE_FORMBRICKS_ROLE_ARN }}
|
||||||
|
with:
|
||||||
|
helmfile-version: 'v1.0.0'
|
||||||
|
helm-plugins: >
|
||||||
|
https://github.com/databus23/helm-diff,
|
||||||
|
https://github.com/jkroepke/helm-secrets
|
||||||
|
helmfile-args: apply -l environment=stage
|
||||||
|
helmfile-auto-init: "false"
|
||||||
|
helmfile-workdirectory: infra/formbricks-cloud-helm
|
||||||
|
|
||||||
167
.github/workflows/docker-build-validation.yml
vendored
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
name: Docker Build Validation
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
merge_group:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
env:
|
||||||
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
|
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate-docker-build:
|
||||||
|
name: Validate Docker Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# Add PostgreSQL service container
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: pgvector/pgvector:pg17
|
||||||
|
env:
|
||||||
|
POSTGRES_USER: test
|
||||||
|
POSTGRES_PASSWORD: test
|
||||||
|
POSTGRES_DB: formbricks
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
# Health check to ensure PostgreSQL is ready before using it
|
||||||
|
options: >-
|
||||||
|
--health-cmd pg_isready
|
||||||
|
--health-interval 10s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 5
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4.2.2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Build Docker Image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./apps/web/Dockerfile
|
||||||
|
push: false
|
||||||
|
load: true
|
||||||
|
tags: formbricks-test:${{ github.sha }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
secrets: |
|
||||||
|
database_url=${{ secrets.DUMMY_DATABASE_URL }}
|
||||||
|
encryption_key=${{ secrets.DUMMY_ENCRYPTION_KEY }}
|
||||||
|
|
||||||
|
- name: Verify PostgreSQL Connection
|
||||||
|
run: |
|
||||||
|
echo "Verifying PostgreSQL connection..."
|
||||||
|
# Install PostgreSQL client to test connection
|
||||||
|
sudo apt-get update && sudo apt-get install -y postgresql-client
|
||||||
|
|
||||||
|
# Test connection using psql
|
||||||
|
PGPASSWORD=test psql -h localhost -U test -d formbricks -c "\dt" || echo "Failed to connect to PostgreSQL"
|
||||||
|
|
||||||
|
# Show network configuration
|
||||||
|
echo "Network configuration:"
|
||||||
|
ip addr show
|
||||||
|
netstat -tulpn | grep 5432 || echo "No process listening on port 5432"
|
||||||
|
|
||||||
|
- name: Test Docker Image with Health Check
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "🧪 Testing if the Docker image starts correctly..."
|
||||||
|
|
||||||
|
# Add extra docker run args to support host.docker.internal on Linux
|
||||||
|
DOCKER_RUN_ARGS="--add-host=host.docker.internal:host-gateway"
|
||||||
|
|
||||||
|
# Start the container with host.docker.internal pointing to the host
|
||||||
|
docker run --name formbricks-test \
|
||||||
|
$DOCKER_RUN_ARGS \
|
||||||
|
-p 3000:3000 \
|
||||||
|
-e DATABASE_URL="postgresql://test:test@host.docker.internal:5432/formbricks" \
|
||||||
|
-e ENCRYPTION_KEY="${{ secrets.DUMMY_ENCRYPTION_KEY }}" \
|
||||||
|
-d formbricks-test:${{ github.sha }}
|
||||||
|
|
||||||
|
# Give it more time to start up
|
||||||
|
echo "Waiting 45 seconds for application to start..."
|
||||||
|
sleep 45
|
||||||
|
|
||||||
|
# Check if the container is running
|
||||||
|
if [ "$(docker inspect -f '{{.State.Running}}' formbricks-test)" != "true" ]; then
|
||||||
|
echo "❌ Container failed to start properly!"
|
||||||
|
docker logs formbricks-test
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "✅ Container started successfully!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try connecting to PostgreSQL from inside the container
|
||||||
|
echo "Testing PostgreSQL connection from inside container..."
|
||||||
|
docker exec formbricks-test sh -c 'apt-get update && apt-get install -y postgresql-client && PGPASSWORD=test psql -h host.docker.internal -U test -d formbricks -c "\dt" || echo "Failed to connect to PostgreSQL from container"'
|
||||||
|
|
||||||
|
# Try to access the health endpoint
|
||||||
|
echo "🏥 Testing /health endpoint..."
|
||||||
|
MAX_RETRIES=10
|
||||||
|
RETRY_COUNT=0
|
||||||
|
HEALTH_CHECK_SUCCESS=false
|
||||||
|
|
||||||
|
set +e # Disable exit on error to allow for retries
|
||||||
|
|
||||||
|
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
|
||||||
|
RETRY_COUNT=$((RETRY_COUNT + 1))
|
||||||
|
echo "Attempt $RETRY_COUNT of $MAX_RETRIES..."
|
||||||
|
|
||||||
|
# Show container logs before each attempt to help debugging
|
||||||
|
if [ $RETRY_COUNT -gt 1 ]; then
|
||||||
|
echo "📋 Current container logs:"
|
||||||
|
docker logs --tail 20 formbricks-test
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get detailed curl output for debugging
|
||||||
|
HTTP_OUTPUT=$(curl -v -s -m 30 http://localhost:3000/health 2>&1)
|
||||||
|
CURL_EXIT_CODE=$?
|
||||||
|
|
||||||
|
echo "Curl exit code: $CURL_EXIT_CODE"
|
||||||
|
echo "Curl output: $HTTP_OUTPUT"
|
||||||
|
|
||||||
|
if [ $CURL_EXIT_CODE -eq 0 ]; then
|
||||||
|
STATUS_CODE=$(echo "$HTTP_OUTPUT" | grep -oP "HTTP/\d(\.\d)? \K\d+")
|
||||||
|
echo "Status code detected: $STATUS_CODE"
|
||||||
|
|
||||||
|
if [ "$STATUS_CODE" = "200" ]; then
|
||||||
|
echo "✅ Health check successful!"
|
||||||
|
HEALTH_CHECK_SUCCESS=true
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo "❌ Health check returned non-200 status code: $STATUS_CODE"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "❌ Curl command failed with exit code: $CURL_EXIT_CODE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Waiting 15 seconds before next attempt..."
|
||||||
|
sleep 15
|
||||||
|
done
|
||||||
|
|
||||||
|
# Show full container logs for debugging
|
||||||
|
echo "📋 Full container logs:"
|
||||||
|
docker logs formbricks-test
|
||||||
|
|
||||||
|
# Clean up the container
|
||||||
|
echo "🧹 Cleaning up..."
|
||||||
|
docker rm -f formbricks-test
|
||||||
|
|
||||||
|
# Exit with failure if health check did not succeed
|
||||||
|
if [ "$HEALTH_CHECK_SUCCESS" != "true" ]; then
|
||||||
|
echo "❌ Health check failed after $MAX_RETRIES attempts"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✨ Docker validation complete - all checks passed!"
|
||||||
94
.github/workflows/e2e.yml
vendored
@@ -1,9 +1,31 @@
|
|||||||
name: E2E Tests
|
name: E2E Tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
|
secrets:
|
||||||
|
AZURE_CLIENT_ID:
|
||||||
|
required: false
|
||||||
|
AZURE_TENANT_ID:
|
||||||
|
required: false
|
||||||
|
AZURE_SUBSCRIPTION_ID:
|
||||||
|
required: false
|
||||||
|
PLAYWRIGHT_SERVICE_URL:
|
||||||
|
required: false
|
||||||
|
ENTERPRISE_LICENSE_KEY:
|
||||||
|
required: true
|
||||||
|
# Add other secrets if necessary
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
TELEMETRY_DISABLED: 1
|
TELEMETRY_DISABLED: 1
|
||||||
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
|
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: read
|
||||||
|
actions: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Run E2E Tests
|
name: Run E2E Tests
|
||||||
@@ -24,16 +46,23 @@ jobs:
|
|||||||
--health-timeout=5s
|
--health-timeout=5s
|
||||||
--health-retries=5
|
--health-retries=5
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: allow
|
||||||
|
allowed-endpoints: |
|
||||||
|
ee.formbricks.com:443
|
||||||
|
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: ./.github/actions/dangerous-git-checkout
|
- uses: ./.github/actions/dangerous-git-checkout
|
||||||
|
|
||||||
- name: Setup Node.js 20.x
|
- name: Setup Node.js 22.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 22.x
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||||
@@ -49,7 +78,7 @@ jobs:
|
|||||||
sed -i "s/ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${RANDOM_KEY}/" .env
|
sed -i "s/ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${RANDOM_KEY}/" .env
|
||||||
sed -i "s/CRON_SECRET=.*/CRON_SECRET=${RANDOM_KEY}/" .env
|
sed -i "s/CRON_SECRET=.*/CRON_SECRET=${RANDOM_KEY}/" .env
|
||||||
sed -i "s/NEXTAUTH_SECRET=.*/NEXTAUTH_SECRET=${RANDOM_KEY}/" .env
|
sed -i "s/NEXTAUTH_SECRET=.*/NEXTAUTH_SECRET=${RANDOM_KEY}/" .env
|
||||||
sed -i "s/ENTERPRISE_LICENSE_KEY=.*/ENTERPRISE_LICENSE_KEY=${RANDOM_KEY}/" .env
|
sed -i "s/ENTERPRISE_LICENSE_KEY=.*/ENTERPRISE_LICENSE_KEY=${{ secrets.ENTERPRISE_LICENSE_KEY }}/" .env
|
||||||
echo "" >> .env
|
echo "" >> .env
|
||||||
echo "E2E_TESTING=1" >> .env
|
echo "E2E_TESTING=1" >> .env
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -60,11 +89,22 @@ jobs:
|
|||||||
|
|
||||||
- name: Apply Prisma Migrations
|
- name: Apply Prisma Migrations
|
||||||
run: |
|
run: |
|
||||||
pnpm prisma migrate deploy
|
# pnpm prisma migrate deploy
|
||||||
|
pnpm db:migrate:dev
|
||||||
|
|
||||||
|
- name: Check for Enterprise License
|
||||||
|
run: |
|
||||||
|
LICENSE_KEY=$(grep '^ENTERPRISE_LICENSE_KEY=' .env | cut -d'=' -f2-)
|
||||||
|
if [ -z "$LICENSE_KEY" ]; then
|
||||||
|
echo "::error::ENTERPRISE_LICENSE_KEY in .env is empty. Please check your secret configuration."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "License key length: ${#LICENSE_KEY}"
|
||||||
|
|
||||||
- name: Run App
|
- name: Run App
|
||||||
run: |
|
run: |
|
||||||
NODE_ENV=test pnpm start --filter=@formbricks/web &
|
echo "Starting app with enterprise license..."
|
||||||
|
NODE_ENV=test pnpm start --filter=@formbricks/web | tee app.log 2>&1 &
|
||||||
sleep 10 # Optional: gives some buffer for the app to start
|
sleep 10 # Optional: gives some buffer for the app to start
|
||||||
for attempt in {1..10}; do
|
for attempt in {1..10}; do
|
||||||
if [ $(curl -o /dev/null -s -w "%{http_code}" http://localhost:3000/health) -eq 200 ]; then
|
if [ $(curl -o /dev/null -s -w "%{http_code}" http://localhost:3000/health) -eq 200 ]; then
|
||||||
@@ -82,13 +122,47 @@ jobs:
|
|||||||
- name: Install Playwright
|
- name: Install Playwright
|
||||||
run: pnpm exec playwright install --with-deps
|
run: pnpm exec playwright install --with-deps
|
||||||
|
|
||||||
- name: Run E2E Tests
|
- name: Set Azure Secret Variables
|
||||||
|
run: |
|
||||||
|
if [[ -n "${{ secrets.AZURE_CLIENT_ID }}" && -n "${{ secrets.AZURE_TENANT_ID }}" && -n "${{ secrets.AZURE_SUBSCRIPTION_ID }}" ]]; then
|
||||||
|
echo "AZURE_ENABLED=true" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "AZURE_ENABLED=false" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Azure login
|
||||||
|
if: env.AZURE_ENABLED == 'true'
|
||||||
|
uses: azure/login@a65d910e8af852a8061c627c456678983e180302 # v2.2.0
|
||||||
|
with:
|
||||||
|
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||||
|
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||||
|
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||||
|
|
||||||
|
- name: Run E2E Tests (Azure)
|
||||||
|
if: env.AZURE_ENABLED == 'true'
|
||||||
|
env:
|
||||||
|
PLAYWRIGHT_SERVICE_URL: ${{ secrets.PLAYWRIGHT_SERVICE_URL }}
|
||||||
|
run: |
|
||||||
|
pnpm test-e2e:azure
|
||||||
|
|
||||||
|
- name: Run E2E Tests (Local)
|
||||||
|
if: env.AZURE_ENABLED == 'false'
|
||||||
run: |
|
run: |
|
||||||
pnpm test:e2e
|
pnpm test:e2e
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: playwright-report
|
name: playwright-report
|
||||||
path: playwright-report/
|
path: playwright-report/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: app-logs
|
||||||
|
path: app.log
|
||||||
|
|
||||||
|
- name: Output App Logs
|
||||||
|
if: failure()
|
||||||
|
run: cat app.log
|
||||||
|
|||||||
34
.github/workflows/formbricks-release.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
name: Build, release & deploy Formbricks images
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker-build:
|
||||||
|
name: Build & release stable docker image
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
uses: ./.github/workflows/release-docker-github.yml
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
helm-chart-release:
|
||||||
|
name: Release Helm Chart
|
||||||
|
uses: ./.github/workflows/release-helm-chart.yml
|
||||||
|
secrets: inherit
|
||||||
|
needs:
|
||||||
|
- docker-build
|
||||||
|
with:
|
||||||
|
VERSION: ${{ needs.docker-build.outputs.VERSION }}
|
||||||
|
|
||||||
|
deploy-formbricks-cloud:
|
||||||
|
name: Deploy Helm Chart to Formbricks Cloud
|
||||||
|
secrets: inherit
|
||||||
|
uses: ./.github/workflows/deploy-formbricks-cloud.yml
|
||||||
|
needs:
|
||||||
|
- docker-build
|
||||||
|
- helm-chart-release
|
||||||
|
with:
|
||||||
|
VERSION: v${{ needs.docker-build.outputs.VERSION }}
|
||||||
|
ENVIRONMENT: "prod"
|
||||||
19
.github/workflows/labeler.yml
vendored
@@ -1,19 +0,0 @@
|
|||||||
name: "Pull Request Labeler"
|
|
||||||
on:
|
|
||||||
- pull_request_target
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
jobs:
|
|
||||||
labeler:
|
|
||||||
name: Pull Request Labeler
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/labeler@v4
|
|
||||||
with:
|
|
||||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
# https://github.com/actions/labeler/issues/442#issuecomment-1297359481
|
|
||||||
sync-labels: ""
|
|
||||||
15
.github/workflows/lint.yml
vendored
@@ -1,6 +1,10 @@
|
|||||||
name: Lint
|
name: Lint
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Linters
|
name: Linters
|
||||||
@@ -8,16 +12,21 @@ jobs:
|
|||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||||
- uses: ./.github/actions/dangerous-git-checkout
|
- uses: ./.github/actions/dangerous-git-checkout
|
||||||
|
|
||||||
- name: Setup Node.js 20.x
|
- name: Setup Node.js 20.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 20.x
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||||
|
|||||||
50
.github/workflows/pr.yml
vendored
@@ -1,5 +1,13 @@
|
|||||||
name: PR Update
|
name: PR Update
|
||||||
|
|
||||||
|
# Update permissions to include all necessary ones
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: read
|
||||||
|
actions: read
|
||||||
|
checks: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
@@ -12,64 +20,40 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
changes:
|
|
||||||
name: Detect changes
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
pull-requests: read
|
|
||||||
outputs:
|
|
||||||
has-files-requiring-all-checks: ${{ steps.filter.outputs.has-files-requiring-all-checks }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: ./.github/actions/dangerous-git-checkout
|
|
||||||
- uses: dorny/paths-filter@v2
|
|
||||||
id: filter
|
|
||||||
with:
|
|
||||||
filters: |
|
|
||||||
has-files-requiring-all-checks:
|
|
||||||
- "!(**.md|.github/CODEOWNERS)"
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: Run Unit Tests
|
name: Run Unit Tests
|
||||||
needs: [changes]
|
|
||||||
if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
|
|
||||||
uses: ./.github/workflows/test.yml
|
uses: ./.github/workflows/test.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
name: Run Linters
|
name: Run Linters
|
||||||
needs: [changes]
|
|
||||||
if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
|
|
||||||
uses: ./.github/workflows/lint.yml
|
uses: ./.github/workflows/lint.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build Formbricks-web
|
name: Build Formbricks-web
|
||||||
needs: [changes]
|
|
||||||
if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
|
|
||||||
uses: ./.github/workflows/build-web.yml
|
uses: ./.github/workflows/build-web.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
docs:
|
|
||||||
name: Build Docs
|
|
||||||
needs: [changes]
|
|
||||||
if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
|
|
||||||
uses: ./.github/workflows/build-docs.yml
|
|
||||||
secrets: inherit
|
|
||||||
|
|
||||||
e2e-test:
|
e2e-test:
|
||||||
name: Run E2E Tests
|
name: Run E2E Tests
|
||||||
needs: [changes]
|
|
||||||
if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
|
|
||||||
uses: ./.github/workflows/e2e.yml
|
uses: ./.github/workflows/e2e.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
required:
|
required:
|
||||||
name: PR Check Summary
|
name: PR Check Summary
|
||||||
needs: [lint, test, build, e2e-test, docs]
|
needs: [lint, test, build, e2e-test]
|
||||||
if: always()
|
if: always()
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
checks: write
|
||||||
|
statuses: write
|
||||||
steps:
|
steps:
|
||||||
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
- name: fail if conditional jobs failed
|
- name: fail if conditional jobs failed
|
||||||
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'skipped') || contains(needs.*.result, 'cancelled')
|
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'skipped') || contains(needs.*.result, 'cancelled')
|
||||||
run: exit 1
|
run: exit 1
|
||||||
|
|||||||
46
.github/workflows/release-changesets.yml
vendored
@@ -1,46 +0,0 @@
|
|||||||
name: Release Changesets
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
#push:
|
|
||||||
# branches:
|
|
||||||
# - main
|
|
||||||
|
|
||||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
|
|
||||||
env:
|
|
||||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
||||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
name: Release
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 15
|
|
||||||
env:
|
|
||||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
||||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout Repo
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Setup Node.js 18.x
|
|
||||||
uses: actions/setup-node@v2
|
|
||||||
with:
|
|
||||||
node-version: 18.x
|
|
||||||
|
|
||||||
- name: Install pnpm
|
|
||||||
uses: pnpm/action-setup@v2.2.4
|
|
||||||
|
|
||||||
- name: Install Dependencies
|
|
||||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
|
||||||
|
|
||||||
- name: Create Release Pull Request or Publish to npm
|
|
||||||
id: changesets
|
|
||||||
uses: changesets/action@v1
|
|
||||||
with:
|
|
||||||
# This expects you to have a script called release which does a build for your packages and calls changeset publish
|
|
||||||
publish: pnpm release
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
name: Docker for Data Migrations
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v*"
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: ghcr.io
|
|
||||||
IMAGE_NAME: formbricks/data-migrations
|
|
||||||
DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/formbricks?schema=public"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-push:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Install cosign
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
uses: sigstore/cosign-installer@v3.5.0
|
|
||||||
|
|
||||||
- name: Log in to GitHub Container Registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Extract Docker metadata
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=tag
|
|
||||||
type=raw,value=${{ github.ref_name }}
|
|
||||||
type=raw,value=latest
|
|
||||||
|
|
||||||
- name: Build and push Docker image
|
|
||||||
uses: docker/build-push-action@v3
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./packages/database/Dockerfile
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
build-args: |
|
|
||||||
DATABASE_URL=${{ env.DATABASE_URL }}
|
|
||||||
|
|
||||||
- name: Sign the published Docker image
|
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
|
||||||
run: |
|
|
||||||
cosign sign --yes ghcr.io/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
|
|
||||||
cosign sign --yes ghcr.io/${{ env.IMAGE_NAME }}:latest
|
|
||||||
@@ -15,7 +15,9 @@ env:
|
|||||||
IMAGE_NAME: ${{ github.repository }}-experimental
|
IMAGE_NAME: ${{ github.repository }}-experimental
|
||||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||||
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/formbricks?schema=public"
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -28,23 +30,28 @@ jobs:
|
|||||||
id-token: write
|
id-token: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Set up Depot CLI
|
- name: Set up Depot CLI
|
||||||
uses: depot/setup-action@v1
|
uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # v1.6.0
|
||||||
|
|
||||||
# Install the cosign tool except on PR
|
# Install the cosign tool except on PR
|
||||||
# https://github.com/sigstore/cosign-installer
|
# https://github.com/sigstore/cosign-installer
|
||||||
- name: Install cosign
|
- name: Install cosign
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: sigstore/cosign-installer@v3.5.0
|
uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2
|
||||||
|
|
||||||
# Login against a Docker registry except on PR
|
# Login against a Docker registry except on PR
|
||||||
# https://github.com/docker/login-action
|
# https://github.com/docker/login-action
|
||||||
- name: Log into registry ${{ env.REGISTRY }}
|
- name: Log into registry ${{ env.REGISTRY }}
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: docker/login-action@v3 # v3.0.0
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -54,7 +61,7 @@ jobs:
|
|||||||
# https://github.com/docker/metadata-action
|
# https://github.com/docker/metadata-action
|
||||||
- name: Extract Docker metadata
|
- name: Extract Docker metadata
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5 # v5.0.0
|
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||||
with:
|
with:
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
|
||||||
@@ -62,7 +69,7 @@ jobs:
|
|||||||
# https://github.com/docker/build-push-action
|
# https://github.com/docker/build-push-action
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
id: build-and-push
|
id: build-and-push
|
||||||
uses: depot/build-push-action@v1
|
uses: depot/build-push-action@636daae76684e38c301daa0c5eca1c095b24e780 # v1.14.0
|
||||||
with:
|
with:
|
||||||
project: tw0fqmsx3c
|
project: tw0fqmsx3c
|
||||||
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
|
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
|
||||||
@@ -72,8 +79,9 @@ jobs:
|
|||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
cache-from: type=gha
|
secrets: |
|
||||||
cache-to: type=gha,mode=max
|
database_url=${{ secrets.DUMMY_DATABASE_URL }}
|
||||||
|
encryption_key=${{ secrets.DUMMY_ENCRYPTION_KEY }}
|
||||||
|
|
||||||
# Sign the resulting Docker image digest except on PRs.
|
# Sign the resulting Docker image digest except on PRs.
|
||||||
# This will only write to the public Rekor transparency log when the Docker
|
# This will only write to the public Rekor transparency log when the Docker
|
||||||
|
|||||||
50
.github/workflows/release-docker-github.yml
vendored
@@ -6,10 +6,11 @@ name: Docker Release to Github
|
|||||||
# documentation.
|
# documentation.
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_call:
|
||||||
push:
|
outputs:
|
||||||
tags:
|
VERSION:
|
||||||
- "v*"
|
description: release version
|
||||||
|
value: ${{ jobs.build.outputs.VERSION }}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# Use docker.io for Docker Hub if empty
|
# Use docker.io for Docker Hub if empty
|
||||||
@@ -18,7 +19,6 @@ env:
|
|||||||
IMAGE_NAME: ${{ github.repository }}
|
IMAGE_NAME: ${{ github.repository }}
|
||||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||||
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/formbricks?schema=public"
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -26,28 +26,49 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
id-token: write
|
||||||
# This is used to complete the identity challenge
|
# This is used to complete the identity challenge
|
||||||
# with sigstore/fulcio when running outside of PRs.
|
# with sigstore/fulcio when running outside of PRs.
|
||||||
id-token: write
|
|
||||||
|
outputs:
|
||||||
|
VERSION: ${{ steps.extract_release_tag.outputs.VERSION }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
|
- name: Get Release Tag
|
||||||
|
id: extract_release_tag
|
||||||
|
run: |
|
||||||
|
TAG=${{ github.ref }}
|
||||||
|
TAG=${TAG#refs/tags/v}
|
||||||
|
echo "RELEASE_TAG=$TAG" >> $GITHUB_ENV
|
||||||
|
echo "VERSION=$TAG" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Update package.json version
|
||||||
|
run: |
|
||||||
|
sed -i "s/\"version\": \"0.0.0\"/\"version\": \"${{ env.RELEASE_TAG }}\"/" ./apps/web/package.json
|
||||||
|
cat ./apps/web/package.json | grep version
|
||||||
|
|
||||||
- name: Set up Depot CLI
|
- name: Set up Depot CLI
|
||||||
uses: depot/setup-action@v1
|
uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # v1.6.0
|
||||||
|
|
||||||
# Install the cosign tool except on PR
|
# Install the cosign tool except on PR
|
||||||
# https://github.com/sigstore/cosign-installer
|
# https://github.com/sigstore/cosign-installer
|
||||||
- name: Install cosign
|
- name: Install cosign
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: sigstore/cosign-installer@v3.5.0
|
uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2
|
||||||
|
|
||||||
# Login against a Docker registry except on PR
|
# Login against a Docker registry except on PR
|
||||||
# https://github.com/docker/login-action
|
# https://github.com/docker/login-action
|
||||||
- name: Log into registry ${{ env.REGISTRY }}
|
- name: Log into registry ${{ env.REGISTRY }}
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: docker/login-action@v3 # v3.0.0
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -57,7 +78,7 @@ jobs:
|
|||||||
# https://github.com/docker/metadata-action
|
# https://github.com/docker/metadata-action
|
||||||
- name: Extract Docker metadata
|
- name: Extract Docker metadata
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5 # v5.0.0
|
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||||
with:
|
with:
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
|
||||||
@@ -65,7 +86,7 @@ jobs:
|
|||||||
# https://github.com/docker/build-push-action
|
# https://github.com/docker/build-push-action
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
id: build-and-push
|
id: build-and-push
|
||||||
uses: depot/build-push-action@v1
|
uses: depot/build-push-action@636daae76684e38c301daa0c5eca1c095b24e780 # v1.14.0
|
||||||
with:
|
with:
|
||||||
project: tw0fqmsx3c
|
project: tw0fqmsx3c
|
||||||
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
|
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
|
||||||
@@ -75,8 +96,9 @@ jobs:
|
|||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
cache-from: type=gha
|
secrets: |
|
||||||
cache-to: type=gha,mode=max
|
database_url=${{ secrets.DUMMY_DATABASE_URL }}
|
||||||
|
encryption_key=${{ secrets.DUMMY_ENCRYPTION_KEY }}
|
||||||
|
|
||||||
# Sign the resulting Docker image digest except on PRs.
|
# Sign the resulting Docker image digest except on PRs.
|
||||||
# This will only write to the public Rekor transparency log when the Docker
|
# This will only write to the public Rekor transparency log when the Docker
|
||||||
|
|||||||
44
.github/workflows/release-docker.yml
vendored
@@ -1,44 +0,0 @@
|
|||||||
name: Release on Dockerhub
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v*"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release-image-on-dockerhub:
|
|
||||||
name: Release on Dockerhub
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
||||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
|
||||||
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/formbricks?schema=public"
|
|
||||||
steps:
|
|
||||||
- name: Checkout Repo
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Log in to Docker Hub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
|
|
||||||
- name: Get Release Tag
|
|
||||||
id: extract_release_tag
|
|
||||||
run: |
|
|
||||||
TAG=${{ github.ref }}
|
|
||||||
TAG=${TAG#refs/tags/v}
|
|
||||||
echo "RELEASE_TAG=$TAG" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Build and push Docker image
|
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./apps/web/Dockerfile
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
${{ secrets.DOCKER_USERNAME }}/formbricks:${{ env.RELEASE_TAG }}
|
|
||||||
${{ secrets.DOCKER_USERNAME }}/formbricks:latest
|
|
||||||
54
.github/workflows/release-helm-chart.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
name: Publish Helm Chart
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
VERSION:
|
||||||
|
description: "The version of the Helm chart to release"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
|
- name: Extract release version
|
||||||
|
run: echo "VERSION=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set up Helm
|
||||||
|
uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78 # v3.5
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
|
||||||
|
- name: Log in to GitHub Container Registry
|
||||||
|
run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io --username ${{ github.actor }} --password-stdin
|
||||||
|
|
||||||
|
- name: Install YQ
|
||||||
|
uses: dcarbone/install-yq-action@4075b4dca348d74bd83f2bf82d30f25d7c54539b # v1.3.1
|
||||||
|
|
||||||
|
- name: Update Chart.yaml with new version
|
||||||
|
run: |
|
||||||
|
yq -i ".version = \"${{ inputs.VERSION }}\"" helm-chart/Chart.yaml
|
||||||
|
yq -i ".appVersion = \"v${{ inputs.VERSION }}\"" helm-chart/Chart.yaml
|
||||||
|
|
||||||
|
- name: Package Helm chart
|
||||||
|
run: |
|
||||||
|
helm package ./helm-chart
|
||||||
|
|
||||||
|
- name: Push Helm chart to GitHub Container Registry
|
||||||
|
run: |
|
||||||
|
helm push formbricks-${{ inputs.VERSION }}.tgz oci://ghcr.io/formbricks/helm-charts
|
||||||
81
.github/workflows/scorecard.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# This workflow uses actions that are not certified by GitHub. They are provided
|
||||||
|
# by a third-party and are governed by separate terms of service, privacy
|
||||||
|
# policy, and support documentation.
|
||||||
|
|
||||||
|
name: Scorecard supply-chain security
|
||||||
|
on:
|
||||||
|
# For Branch-Protection check. Only the default branch is supported. See
|
||||||
|
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
|
||||||
|
branch_protection_rule:
|
||||||
|
# To guarantee Maintained check is occasionally updated. See
|
||||||
|
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
|
||||||
|
schedule:
|
||||||
|
- cron: "17 17 * * 6"
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
# Declare default permissions as read only.
|
||||||
|
permissions: read-all
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analysis:
|
||||||
|
name: Scorecard analysis
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
# Needed to upload the results to code-scanning dashboard.
|
||||||
|
security-events: write
|
||||||
|
# Needed to publish results and get a badge (see publish_results below).
|
||||||
|
id-token: write
|
||||||
|
# Add this permission
|
||||||
|
actions: write # Required for artifact upload
|
||||||
|
# Uncomment the permissions below if installing in a private repository.
|
||||||
|
# contents: read
|
||||||
|
# actions: read
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: "Checkout code"
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: "Run analysis"
|
||||||
|
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
|
||||||
|
with:
|
||||||
|
results_file: results.sarif
|
||||||
|
results_format: sarif
|
||||||
|
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
|
||||||
|
# - you want to enable the Branch-Protection check on a *public* repository, or
|
||||||
|
# - you are installing Scorecard on a *private* repository
|
||||||
|
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
|
||||||
|
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
|
||||||
|
|
||||||
|
# Public repositories:
|
||||||
|
# - Publish results to OpenSSF REST API for easy access by consumers
|
||||||
|
# - Allows the repository to include the Scorecard badge.
|
||||||
|
# - See https://github.com/ossf/scorecard-action#publishing-results.
|
||||||
|
# For private repositories:
|
||||||
|
# - `publish_results` will always be set to `false`, regardless
|
||||||
|
# of the value entered here.
|
||||||
|
publish_results: true
|
||||||
|
|
||||||
|
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||||
|
# format to the repository Actions tab.
|
||||||
|
- name: "Upload artifact"
|
||||||
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
|
with:
|
||||||
|
name: sarif
|
||||||
|
path: results.sarif
|
||||||
|
retention-days: 5
|
||||||
|
|
||||||
|
# Upload the results to GitHub's code scanning dashboard (optional).
|
||||||
|
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
|
||||||
|
- name: "Upload to code-scanning"
|
||||||
|
uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
|
||||||
|
with:
|
||||||
|
sarif_file: results.sarif
|
||||||
11
.github/workflows/semantic-pull-requests.yml
vendored
@@ -16,7 +16,12 @@ jobs:
|
|||||||
name: PR title
|
name: PR title
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: amannn/action-semantic-pull-request@v5
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 # v5.5.3
|
||||||
id: lint_pr_title
|
id: lint_pr_title
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -35,7 +40,7 @@ jobs:
|
|||||||
revert
|
revert
|
||||||
ossgg
|
ossgg
|
||||||
|
|
||||||
- uses: marocchino/sticky-pull-request-comment@v2
|
- uses: marocchino/sticky-pull-request-comment@67d0dec7b07ed060a405f9b2a64b8ab319fdd7db # v2.9.2
|
||||||
# When the previous steps fails, the workflow would stop. By adding this
|
# When the previous steps fails, the workflow would stop. By adding this
|
||||||
# condition you can continue the execution with the populated error message.
|
# condition you can continue the execution with the populated error message.
|
||||||
if: always() && (steps.lint_pr_title.outputs.error_message != null)
|
if: always() && (steps.lint_pr_title.outputs.error_message != null)
|
||||||
@@ -54,7 +59,7 @@ jobs:
|
|||||||
|
|
||||||
# Delete a previous comment when the issue has been resolved
|
# Delete a previous comment when the issue has been resolved
|
||||||
- if: ${{ steps.lint_pr_title.outputs.error_message == null }}
|
- if: ${{ steps.lint_pr_title.outputs.error_message == null }}
|
||||||
uses: marocchino/sticky-pull-request-comment@v2
|
uses: marocchino/sticky-pull-request-comment@67d0dec7b07ed060a405f9b2a64b8ab319fdd7db # v2.9.2
|
||||||
with:
|
with:
|
||||||
header: pr-title-lint-error
|
header: pr-title-lint-error
|
||||||
message: |
|
message: |
|
||||||
|
|||||||
54
.github/workflows/sonarqube.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
name: SonarQube
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
merge_group:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
jobs:
|
||||||
|
sonarqube:
|
||||||
|
name: SonarQube
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||||
|
|
||||||
|
- name: Setup Node.js 22.x
|
||||||
|
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
|
||||||
|
with:
|
||||||
|
node-version: 22.x
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||||
|
|
||||||
|
- name: create .env
|
||||||
|
run: cp .env.example .env
|
||||||
|
|
||||||
|
- name: Generate Random ENCRYPTION_KEY, CRON_SECRET & NEXTAUTH_SECRET and fill in .env
|
||||||
|
run: |
|
||||||
|
RANDOM_KEY=$(openssl rand -hex 32)
|
||||||
|
sed -i "s/ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${RANDOM_KEY}/" .env
|
||||||
|
sed -i "s/CRON_SECRET=.*/CRON_SECRET=${RANDOM_KEY}/" .env
|
||||||
|
sed -i "s/NEXTAUTH_SECRET=.*/NEXTAUTH_SECRET=${RANDOM_KEY}/" .env
|
||||||
|
|
||||||
|
- name: Run tests with coverage
|
||||||
|
run: |
|
||||||
|
pnpm test:coverage
|
||||||
|
- name: SonarQube Scan
|
||||||
|
uses: SonarSource/sonarqube-scan-action@2500896589ef8f7247069a56136f8dc177c27ccf
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||||
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
84
.github/workflows/terraform-plan-and-apply.yml
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
name: "Terraform"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
# TODO: enable it back when migration is completed.
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- "infra/terraform/**"
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- "infra/terraform/**"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
terraform:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
|
- name: Tailscale
|
||||||
|
uses: tailscale/github-action@v3
|
||||||
|
with:
|
||||||
|
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
||||||
|
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
||||||
|
tags: tag:github
|
||||||
|
|
||||||
|
- name: Configure AWS Credentials
|
||||||
|
uses: aws-actions/configure-aws-credentials@f24d7193d98baebaeacc7e2227925dd47cc267f5 # v4.2.0
|
||||||
|
with:
|
||||||
|
role-to-assume: ${{ secrets.AWS_ASSUME_ROLE_ARN }}
|
||||||
|
aws-region: "eu-central-1"
|
||||||
|
|
||||||
|
- name: Setup Terraform
|
||||||
|
uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2
|
||||||
|
|
||||||
|
- name: Terraform Format
|
||||||
|
id: fmt
|
||||||
|
run: terraform fmt -check -recursive
|
||||||
|
continue-on-error: true
|
||||||
|
working-directory: infra/terraform
|
||||||
|
|
||||||
|
- name: Terraform Init
|
||||||
|
id: init
|
||||||
|
run: terraform init
|
||||||
|
working-directory: infra/terraform
|
||||||
|
|
||||||
|
- name: Terraform Validate
|
||||||
|
id: validate
|
||||||
|
run: terraform validate
|
||||||
|
working-directory: infra/terraform
|
||||||
|
|
||||||
|
- name: Terraform Plan
|
||||||
|
id: plan
|
||||||
|
run: terraform plan -out .planfile
|
||||||
|
working-directory: infra/terraform
|
||||||
|
|
||||||
|
- name: Post PR comment
|
||||||
|
uses: borchero/terraform-plan-comment@434458316f8f24dd073cd2561c436cce41dc8f34 # v2.4.1
|
||||||
|
if: always() && github.ref != 'refs/heads/main' && (steps.plan.outcome == 'success' || steps.plan.outcome == 'failure')
|
||||||
|
with:
|
||||||
|
token: ${{ github.token }}
|
||||||
|
planfile: .planfile
|
||||||
|
working-directory: "infra/terraform"
|
||||||
|
|
||||||
|
- name: Terraform Apply
|
||||||
|
id: apply
|
||||||
|
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||||
|
run: terraform apply .planfile
|
||||||
|
working-directory: "infra/terraform"
|
||||||
16
.github/workflows/test.yml
vendored
@@ -1,23 +1,33 @@
|
|||||||
name: Tests
|
name: Tests
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Unit Tests
|
name: Unit Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: ./.github/actions/dangerous-git-checkout
|
- uses: ./.github/actions/dangerous-git-checkout
|
||||||
|
|
||||||
- name: Setup Node.js 20.x
|
- name: Setup Node.js 20.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 20.x
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||||
|
|||||||
51
.github/workflows/tolgee-missing-key-check.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
name: Check Missing Translations
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
pull_request_target:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-missing-translations:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.base.ref }}
|
||||||
|
|
||||||
|
- name: Checkout PR
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
|
||||||
|
- name: Install Tolgee CLI
|
||||||
|
run: npm install -g @tolgee/cli
|
||||||
|
|
||||||
|
- name: Compare Tolgee Keys
|
||||||
|
id: compare
|
||||||
|
run: |
|
||||||
|
tolgee compare --api-key ${{ secrets.TOLGEE_API_KEY }} > compare_output.txt
|
||||||
|
cat compare_output.txt
|
||||||
|
|
||||||
|
- name: Check for Missing Translations
|
||||||
|
run: |
|
||||||
|
if grep -q "new key found" compare_output.txt; then
|
||||||
|
echo "New keys found that may require translations:"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "No new keys found."
|
||||||
|
fi
|
||||||
87
.github/workflows/tolgee.yml
vendored
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
name: Tolgee Tagging on PR Merge
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [closed]
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tag-production-keys:
|
||||||
|
name: Tag Production Keys
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event.pull_request.merged == true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # This ensures we get the full git history
|
||||||
|
|
||||||
|
- name: Get source branch name
|
||||||
|
id: branch-name
|
||||||
|
run: |
|
||||||
|
RAW_BRANCH="${{ github.head_ref }}"
|
||||||
|
SOURCE_BRANCH=$(echo "$RAW_BRANCH" | sed 's/[^a-zA-Z0-9._\/-]//g')
|
||||||
|
|
||||||
|
|
||||||
|
# Safely add to environment variables using GitHub's recommended method
|
||||||
|
# This prevents environment variable injection attacks
|
||||||
|
echo "SOURCE_BRANCH<<EOF" >> $GITHUB_ENV
|
||||||
|
echo "$SOURCE_BRANCH" >> $GITHUB_ENV
|
||||||
|
echo "EOF" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
echo "Detected source branch: $SOURCE_BRANCH"
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||||
|
with:
|
||||||
|
node-version: 18 # Ensure compatibility with your project
|
||||||
|
|
||||||
|
- name: Install Tolgee CLI
|
||||||
|
run: npm install -g @tolgee/cli
|
||||||
|
|
||||||
|
- name: Tag Production Keys
|
||||||
|
run: |
|
||||||
|
npx tolgee tag \
|
||||||
|
--api-key ${{ secrets.TOLGEE_API_KEY }} \
|
||||||
|
--filter-extracted \
|
||||||
|
--filter-tag "draft:${SOURCE_BRANCH}" \
|
||||||
|
--tag production \
|
||||||
|
--untag "draft:${SOURCE_BRANCH}"
|
||||||
|
|
||||||
|
- name: Tag unused production keys as Deprecated
|
||||||
|
run: |
|
||||||
|
npx tolgee tag \
|
||||||
|
--api-key ${{ secrets.TOLGEE_API_KEY }} \
|
||||||
|
--filter-not-extracted --filter-tag production \
|
||||||
|
--tag deprecated --untag production
|
||||||
|
|
||||||
|
- name: Tag unused draft:current-branch keys as Deprecated
|
||||||
|
run: |
|
||||||
|
npx tolgee tag \
|
||||||
|
--api-key ${{ secrets.TOLGEE_API_KEY }} \
|
||||||
|
--filter-not-extracted --filter-tag "draft:${SOURCE_BRANCH}" \
|
||||||
|
--tag deprecated --untag "draft:${SOURCE_BRANCH}"
|
||||||
|
|
||||||
|
- name: Sync with backup
|
||||||
|
run: |
|
||||||
|
npx tolgee sync \
|
||||||
|
--api-key ${{ secrets.TOLGEE_API_KEY }} \
|
||||||
|
--backup ./tolgee-backup \
|
||||||
|
--continue-on-warning \
|
||||||
|
--yes
|
||||||
|
|
||||||
|
- name: Upload backup as artifact
|
||||||
|
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
|
||||||
|
with:
|
||||||
|
name: tolgee-backup-${{ github.sha }}
|
||||||
|
path: ./tolgee-backup
|
||||||
|
retention-days: 90
|
||||||
11
.github/workflows/welcome-new-contributors.yml
vendored
@@ -3,7 +3,7 @@ name: "Welcome new contributors"
|
|||||||
on:
|
on:
|
||||||
issues:
|
issues:
|
||||||
types: opened
|
types: opened
|
||||||
pull_request:
|
pull_request_target:
|
||||||
types: opened
|
types: opened
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -17,11 +17,16 @@ jobs:
|
|||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
if: github.event.action == 'opened'
|
if: github.event.action == 'opened'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/first-interaction@v1
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/first-interaction@3c71ce730280171fd1cfb57c00c774f8998586f7 # v1
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
pr-message: |-
|
pr-message: |-
|
||||||
Thank you so much for making your first Pull Request and taking the time to improve Formbricks! 🚀🙏❤️
|
Thank you so much for making your first Pull Request and taking the time to improve Formbricks! 🚀🙏❤️
|
||||||
Feel free to join the conversation at [Discord](https://formbricks.com/discord)
|
Feel free to join the conversation on [Github Discussions](https://github.com/formbricks/formbricks/discussions) if you need any help or have any questions. 😊
|
||||||
issue-message: |
|
issue-message: |
|
||||||
Thank you for opening your first issue! 🙏❤️ One of our team members will review it and get back to you as soon as it possible. 😊
|
Thank you for opening your first issue! 🙏❤️ One of our team members will review it and get back to you as soon as it possible. 😊
|
||||||
|
|||||||
63
.gitignore
vendored
@@ -1,25 +1,26 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
node_modules
|
**/node_modules
|
||||||
.pnp
|
.pnp
|
||||||
.pnp.js
|
.pnp.js
|
||||||
.pnpm-store/
|
.pnpm-store/
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
coverage
|
**/coverage
|
||||||
|
|
||||||
# next.js
|
# next.js
|
||||||
.next/
|
**/.next/
|
||||||
out/
|
**/out/
|
||||||
build
|
**/build
|
||||||
|
|
||||||
# node
|
# node
|
||||||
dist/
|
**/dist/
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
**/.DS_Store
|
||||||
*.pem
|
*.pem
|
||||||
|
Zone.Identifier
|
||||||
|
|
||||||
# debug
|
# debug
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
@@ -27,36 +28,48 @@ yarn-debug.log*
|
|||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
.env
|
**/.env
|
||||||
.env.local
|
**/.env.local
|
||||||
.env.development.local
|
**/.env.development.local
|
||||||
.env.test.local
|
**/.env.test.local
|
||||||
.env.production.local
|
**/.env.production.local
|
||||||
!packages/database/.env
|
!packages/database/.env
|
||||||
!apps/web/.env
|
!apps/web/.env
|
||||||
|
|
||||||
# Prisma generated files
|
# build tools
|
||||||
packages/database/zod
|
|
||||||
|
|
||||||
# turbo
|
|
||||||
.turbo
|
.turbo
|
||||||
|
**/*vite.config.*.timestamp-*
|
||||||
|
|
||||||
# nixos stuff
|
# environment specific
|
||||||
.direnv
|
.direnv
|
||||||
|
|
||||||
Zone.Identifier
|
|
||||||
|
|
||||||
# Playwright
|
# Playwright
|
||||||
/test-results/
|
/test-results/
|
||||||
/playwright-report/
|
/playwright-report/
|
||||||
/blob-report/
|
/blob-report/
|
||||||
/playwright/.cache/
|
/playwright/.cache/
|
||||||
|
|
||||||
# uploads
|
# project specific
|
||||||
packages/lib/uploads
|
packages/lib/uploads
|
||||||
|
|
||||||
# Vite Timestamps
|
|
||||||
*vite.config.*.timestamp-*
|
|
||||||
|
|
||||||
# js compiled assets
|
|
||||||
apps/web/public/js
|
apps/web/public/js
|
||||||
|
packages/database/migrations
|
||||||
|
branch.json
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# Terraform
|
||||||
|
infra/terraform/.terraform/
|
||||||
|
**/.terraform.lock.hcl
|
||||||
|
**/terraform.tfstate
|
||||||
|
**/terraform.tfstate.*
|
||||||
|
**/crash.log
|
||||||
|
**/override.tf
|
||||||
|
**/override.tf.json
|
||||||
|
**/*.tfvars
|
||||||
|
**/*.tfvars.json
|
||||||
|
**/.terraformrc
|
||||||
|
**/terraform.rc
|
||||||
|
|
||||||
|
# IntelliJ IDEA
|
||||||
|
/.idea/
|
||||||
|
/*.iml
|
||||||
|
packages/ios/FormbricksSDK/FormbricksSDK.xcodeproj/project.xcworkspace/xcuserdata
|
||||||
|
|||||||
6
.gitpod.Dockerfile
vendored
@@ -1,6 +0,0 @@
|
|||||||
FROM gitpod/workspace-full
|
|
||||||
|
|
||||||
# Install custom tools, runtime, etc.
|
|
||||||
RUN brew install yq
|
|
||||||
|
|
||||||
RUN pnpm install turbo --global
|
|
||||||
74
.gitpod.yml
@@ -1,74 +0,0 @@
|
|||||||
tasks:
|
|
||||||
- name: demo
|
|
||||||
init: |
|
|
||||||
gp sync-await init-install &&
|
|
||||||
bash .gitpod/setup-demo.bash
|
|
||||||
command: |
|
|
||||||
cd apps/demo &&
|
|
||||||
cp .env.example .env &&
|
|
||||||
sed -i -r "s#^(NEXT_PUBLIC_FORMBRICKS_API_HOST=).*#\1 $(gp url 3000)#" .env &&
|
|
||||||
gp sync-await init &&
|
|
||||||
turbo --filter "@formbricks/demo" go
|
|
||||||
|
|
||||||
- name: Init Formbricks
|
|
||||||
init: |
|
|
||||||
cp .env.example .env &&
|
|
||||||
bash .gitpod/init.bash &&
|
|
||||||
turbo --filter "@formbricks/js" build &&
|
|
||||||
gp sync-done init-install
|
|
||||||
command: |
|
|
||||||
gp sync-done init &&
|
|
||||||
gp tasks list &&
|
|
||||||
gp ports await 3002 && gp ports await 3000 && gp open apps/demo/.env && gp preview $(gp url 3002) --external
|
|
||||||
|
|
||||||
- name: web
|
|
||||||
init: |
|
|
||||||
gp sync-await init-install &&
|
|
||||||
bash .gitpod/setup-web.bash &&
|
|
||||||
turbo --filter "@formbricks/database" db:down
|
|
||||||
command: |
|
|
||||||
gp sync-await init &&
|
|
||||||
cp .env.example .env &&
|
|
||||||
sed -i -r "s#^(WEBAPP_URL=).*#\1 $(gp url 3000)#" .env &&
|
|
||||||
RANDOM_ENCRYPTION_KEY=$(openssl rand -hex 32)
|
|
||||||
sed -i 's/^ENCRYPTION_KEY=.*/ENCRYPTION_KEY='"$RANDOM_ENCRYPTION_KEY"'/' .env
|
|
||||||
turbo --filter "@formbricks/web" go
|
|
||||||
|
|
||||||
image:
|
|
||||||
file: .gitpod.Dockerfile
|
|
||||||
|
|
||||||
ports:
|
|
||||||
- port: 3000
|
|
||||||
visibility: public
|
|
||||||
onOpen: open-browser
|
|
||||||
- port: 3001
|
|
||||||
visibility: public
|
|
||||||
onOpen: ignore
|
|
||||||
- port: 3002
|
|
||||||
visibility: public
|
|
||||||
onOpen: ignore
|
|
||||||
- port: 5432
|
|
||||||
visibility: public
|
|
||||||
onOpen: ignore
|
|
||||||
- port: 1025
|
|
||||||
visibility: public
|
|
||||||
onOpen: ignore
|
|
||||||
- port: 8025
|
|
||||||
visibility: public
|
|
||||||
onOpen: open-browser
|
|
||||||
|
|
||||||
github:
|
|
||||||
prebuilds:
|
|
||||||
master: true
|
|
||||||
pullRequests: true
|
|
||||||
addComment: true
|
|
||||||
|
|
||||||
vscode:
|
|
||||||
extensions:
|
|
||||||
- "ban.spellright"
|
|
||||||
- "bradlc.vscode-tailwindcss"
|
|
||||||
- "DavidAnson.vscode-markdownlint"
|
|
||||||
- "dbaeumer.vscode-eslint"
|
|
||||||
- "esbenp.prettier-vscode"
|
|
||||||
- "Prisma.prisma"
|
|
||||||
- "yzhang.markdown-all-in-one"
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
images=($(yq eval '.services.*.image' packages/database/docker-compose.yml))
|
images=($(yq eval '.services.*.image' docker-compose.dev.yml))
|
||||||
|
|
||||||
pull_image() {
|
pull_image() {
|
||||||
docker pull "$1"
|
docker pull "$1"
|
||||||
|
|||||||
2
.husky/post-checkout
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
echo "{\"branchName\": \"$(git rev-parse --abbrev-ref HEAD)\"}" > ./branch.json
|
||||||
|
prettier --write ./branch.json
|
||||||
@@ -1 +1,21 @@
|
|||||||
pnpm lint-staged
|
#!/bin/sh
|
||||||
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
# Load environment variables from .env files
|
||||||
|
if [ -f .env ]; then
|
||||||
|
set -a
|
||||||
|
. .env
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
|
||||||
|
pnpm lint-staged
|
||||||
|
|
||||||
|
# Run tolgee-pull if branch.json exists and NEXT_PUBLIC_TOLGEE_API_KEY is not set
|
||||||
|
if [ -f branch.json ]; then
|
||||||
|
if [ -z "$NEXT_PUBLIC_TOLGEE_API_KEY" ]; then
|
||||||
|
echo "Skipping tolgee-pull: NEXT_PUBLIC_TOLGEE_API_KEY is not set"
|
||||||
|
else
|
||||||
|
pnpm run tolgee-pull
|
||||||
|
git add apps/web/locales
|
||||||
|
fi
|
||||||
|
fi
|
||||||
39
.tolgeerc.json
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.tolgee.io/cli-schema.json",
|
||||||
|
"format": "JSON_TOLGEE",
|
||||||
|
"patterns": ["./apps/web/**/*.ts?(x)"],
|
||||||
|
"projectId": 10304,
|
||||||
|
"pull": {
|
||||||
|
"path": "./apps/web/locales"
|
||||||
|
},
|
||||||
|
"push": {
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"language": "en-US",
|
||||||
|
"path": "./apps/web/locales/en-US.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "de-DE",
|
||||||
|
"path": "./apps/web/locales/de-DE.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "fr-FR",
|
||||||
|
"path": "./apps/web/locales/fr-FR.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "pt-BR",
|
||||||
|
"path": "./apps/web/locales/pt-BR.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "zh-Hant-TW",
|
||||||
|
"path": "./apps/web/locales/zh-Hant-TW.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "pt-PT",
|
||||||
|
"path": "./apps/web/locales/pt-PT.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"forceMode": "OVERRIDE"
|
||||||
|
},
|
||||||
|
"strictNamespace": false
|
||||||
|
}
|
||||||
4
.vscode/extensions.json
vendored
@@ -6,6 +6,8 @@
|
|||||||
"dbaeumer.vscode-eslint", // eslint plugin
|
"dbaeumer.vscode-eslint", // eslint plugin
|
||||||
"esbenp.prettier-vscode", // prettier plugin
|
"esbenp.prettier-vscode", // prettier plugin
|
||||||
"Prisma.prisma", // syntax|format|completion for prisma
|
"Prisma.prisma", // syntax|format|completion for prisma
|
||||||
"yzhang.markdown-all-in-one" // nicer markdown support
|
"yzhang.markdown-all-in-one", // nicer markdown support
|
||||||
|
"vitest.explorer", // run tests directly from the code window
|
||||||
|
"sonarsource.sonarlint-vscode" // sonarqube linter for vscode
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
8
.vscode/settings.json
vendored
@@ -1,4 +1,10 @@
|
|||||||
{
|
{
|
||||||
|
"javascript.updateImportsOnFileMove.enabled": "always",
|
||||||
|
"sonarlint.connectedMode.project": {
|
||||||
|
"connectionId": "formbricks",
|
||||||
|
"projectKey": "formbricks_formbricks"
|
||||||
|
},
|
||||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||||
"typescript.tsdk": "node_modules/typescript/lib"
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
|
"typescript.updateImportsOnFileMove.enabled": "always"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,13 +18,13 @@ Ready to dive into the code and make a real impact? Here's your path:
|
|||||||
|
|
||||||
1. **Read our Best Practices**: [It takes 5 minutes](https://formbricks.com/docs/developer-docs/contributing/get-started) but will help you save hours 🤓
|
1. **Read our Best Practices**: [It takes 5 minutes](https://formbricks.com/docs/developer-docs/contributing/get-started) but will help you save hours 🤓
|
||||||
|
|
||||||
1. **Fork the Repository:** Fork our repository or use [Gitpod](https://formbricks.com/docs/developer-docs/contributing/gitpod) or use [Codespaces](https://formbricks.com/docs/developer-docs/contributing/codespaces)
|
1. **Fork the Repository:** Fork our repository or use [Gitpod](https://gitpod.io) or use [Github Codespaces](https://github.com/features/codespaces) to get started instantly.
|
||||||
|
|
||||||
1. **Tweak and Transform:** Work your coding magic and apply your changes.
|
1. **Tweak and Transform:** Work your coding magic and apply your changes.
|
||||||
|
|
||||||
1. **Pull Request Act:** If you're ready to go, craft a new pull request closely following our PR template 🙏
|
1. **Pull Request Act:** If you're ready to go, craft a new pull request closely following our PR template 🙏
|
||||||
|
|
||||||
Would you prefer a chat before you dive into a lot of work? Our [Discord server](https://formbricks.com/discord) is your harbor. Share your thoughts, and we'll meet you there with open arms. We're responsive and friendly, promise!
|
Would you prefer a chat before you dive into a lot of work? [Github Discussions](https://github.com/formbricks/formbricks/discussions) is your harbor. Share your thoughts, and we'll meet you there with open arms. We're responsive and friendly, promise!
|
||||||
|
|
||||||
## 🚀 Aspiring Features
|
## 🚀 Aspiring Features
|
||||||
|
|
||||||
|
|||||||
4
LICENSE
@@ -2,8 +2,8 @@ Copyright (c) 2024 Formbricks GmbH
|
|||||||
|
|
||||||
Portions of this software are licensed as follows:
|
Portions of this software are licensed as follows:
|
||||||
|
|
||||||
- All content that resides under the "packages/ee/", "apps/web/modules/ee" & "apps/web/app/(ee)" directories of this repository, if these directories exist, is licensed under the license defined in "packages/ee/LICENSE".
|
- All content that resides under the "apps/web/modules/ee" directory of this repository, if these directories exist, is licensed under the license defined in "apps/web/modules/ee/LICENSE".
|
||||||
- All content that resides under the "packages/js/", "packages/react-native/" and "packages/api/" directories of this repository, if that directories exist, is licensed under the "MIT" license as defined in the "LICENSE" files of these packages.
|
- All content that resides under the "packages/js/", "packages/android/", "packages/ios/" and "packages/api/" directories of this repository, if that directories exist, is licensed under the "MIT" license as defined in the "LICENSE" files of these packages.
|
||||||
- All third party components incorporated into the Formbricks Software are licensed under the original license provided by the owner of the applicable component.
|
- All third party components incorporated into the Formbricks Software are licensed under the original license provided by the owner of the applicable component.
|
||||||
- Content outside of the above mentioned directories or restrictions above is available under the "AGPLv3" license as defined below.
|
- Content outside of the above mentioned directories or restrictions above is available under the "AGPLv3" license as defined below.
|
||||||
|
|
||||||
|
|||||||
12
README.md
@@ -13,14 +13,14 @@
|
|||||||
<h3 align="center">Formbricks</h3>
|
<h3 align="center">Formbricks</h3>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
Harvest user-insights, build irresistible experiences.
|
The Open Source Qualtrics Alternative
|
||||||
<br />
|
<br />
|
||||||
<a href="https://formbricks.com/">Website</a> | <a href="https://formbricks.com/discord">Join Discord community</a>
|
<a href="https://formbricks.com/">Website</a>
|
||||||
</p>
|
</p>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/formbricks/formbricks/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-AGPL-purple" alt="License"></a> <a href="https://formbricks.com/discord"><img src="https://img.shields.io/discord/979077669410979880?label=Discord&logo=discord&logoColor=%23fff" alt="Join Formbricks Discord"></a> <a href="https://github.com/formbricks/formbricks/stargazers"><img src="https://img.shields.io/github/stars/formbricks/formbricks?logo=github" alt="Github Stars"></a>
|
<a href="https://github.com/formbricks/formbricks/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-AGPL-purple" alt="License"></a> <a href="https://github.com/formbricks/formbricks/stargazers"><img src="https://img.shields.io/github/stars/formbricks/formbricks?logo=github" alt="Github Stars"></a>
|
||||||
<a href="https://news.ycombinator.com/item?id=32303986"><img src="https://img.shields.io/badge/Hacker%20News-122-%23FF6600" alt="Hacker News"></a>
|
<a href="https://news.ycombinator.com/item?id=32303986"><img src="https://img.shields.io/badge/Hacker%20News-122-%23FF6600" alt="Hacker News"></a>
|
||||||
<a href="[https://www.producthunt.com/products/formbricks](https://www.producthunt.com/posts/formbricks)"><img src="https://img.shields.io/badge/Product%20Hunt-455-orange?logo=producthunt&logoColor=%23fff" alt="Product Hunt"></a>
|
<a href="[https://www.producthunt.com/products/formbricks](https://www.producthunt.com/posts/formbricks)"><img src="https://img.shields.io/badge/Product%20Hunt-455-orange?logo=producthunt&logoColor=%23fff" alt="Product Hunt"></a>
|
||||||
<a href="https://github.blog/2023-04-12-github-accelerator-our-first-cohort-and-whats-next/"><img src="https://img.shields.io/badge/2023-blue?logo=github&label=Github%20Accelerator" alt="Github Accelerator"></a>
|
<a href="https://github.blog/2023-04-12-github-accelerator-our-first-cohort-and-whats-next/"><img src="https://img.shields.io/badge/2023-blue?logo=github&label=Github%20Accelerator" alt="Github Accelerator"></a>
|
||||||
@@ -228,14 +228,14 @@ The Formbricks core application is licensed under the [AGPLv3 Open Source Licens
|
|||||||
|
|
||||||
### The Enterprise Edition
|
### The Enterprise Edition
|
||||||
|
|
||||||
Additional to the AGPL licensed Formbricks core, this repository contains code licensed under an Enterprise license. The [code](https://github.com/formbricks/formbricks/tree/main/packages/ee) and [license](https://github.com/formbricks/formbricks/blob/main/packages/ee/LICENSE) for the enterprise functionality can be found in the `/packages/ee` folder of this repository. This additional functionality is not part of the AGPLv3 licensed Formbricks core and is designed to meet the needs of larger teams and enterprises. This advanced functionality is already included in the Docker images, but you need an [Enterprise License Key](https://formbricks.com/docs/self-hosting/enterprise) to unlock it.
|
Additional to the AGPL licensed Formbricks core, this repository contains code licensed under an Enterprise license. The [code](https://github.com/formbricks/formbricks/tree/main/apps/web/modules/ee) and [license](https://github.com/formbricks/formbricks/blob/main/apps/web/modules/ee/LICENSE) for the enterprise functionality can be found in the `/apps/web/modules/ee` folder of this repository. This additional functionality is not part of the AGPLv3 licensed Formbricks core and is designed to meet the needs of larger teams and enterprises. This advanced functionality is already included in the Docker images, but you need an [Enterprise License Key](https://formbricks.com/docs/self-hosting/enterprise) to unlock it.
|
||||||
|
|
||||||
### White-Labeling Formbricks and Other Licensing Needs
|
### White-Labeling Formbricks and Other Licensing Needs
|
||||||
|
|
||||||
We currently do not offer Formbricks white-labeled. Any other needs? [Send us an email](mailto:hola@formbricks.com).
|
We currently do not offer Formbricks white-labeled. That means that we don't sell a license which let's other companies resell Formbricks to third parties under their name nor take parts (like the survey editor) out of Formbricks to add to their own software products. Any other needs? [Send us an email](mailto:hola@formbricks.com).
|
||||||
|
|
||||||
### Why charge for Enterprise Features?
|
### Why charge for Enterprise Features?
|
||||||
|
|
||||||
The Enterprise Edition and White-Label Licenses allow us to fund the development of Formbricks sustainably. It guarantees that the open-source surveying infrastructure we're building will be around for decades to come.
|
The Enterprise Edition allows us to fund the development of Formbricks sustainably. It guarantees that the free and open-source surveying infrastructure we're building will be around for decades to come.
|
||||||
|
|
||||||
<p align="right"><a href="#top">🔼 Back to top</a></p>
|
<p align="right"><a href="#top">🔼 Back to top</a></p>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Security Policy of Formbricks
|
# Security Policy of Formbricks
|
||||||
|
|
||||||
This is Formbrick's security policy. Please reach out to us
|
This is Formbrick's security policy. Please reach out to us
|
||||||
on our Discord or, if privately, via <security@formbricks.com>
|
on Github or, if privately, via <security@formbricks.com>
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ We invite you to report if:
|
|||||||
|
|
||||||
Avoid reporting if:
|
Avoid reporting if:
|
||||||
|
|
||||||
- Assistance is needed to optimize Formbricks for security – please engage on our Discord for this.
|
- Assistance is needed to optimize Formbricks for security – please engage on Github Discussions for this.
|
||||||
- Help is required for applying security-related updates.
|
- Help is required for applying security-related updates.
|
||||||
- The concern is not related to security.
|
- The concern is not related to security.
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
EXPO_PUBLIC_API_HOST=http://192.168.178.20:3000
|
|
||||||
EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=clzr04nkd000bcdl110j0ijyq
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
extends: ["@formbricks/eslint-config/react.js"],
|
|
||||||
parserOptions: {
|
|
||||||
project: "tsconfig.json",
|
|
||||||
tsconfigRootDir: __dirname,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
35
apps/demo-react-native/.gitignore
vendored
@@ -1,35 +0,0 @@
|
|||||||
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
node_modules/
|
|
||||||
|
|
||||||
# Expo
|
|
||||||
.expo/
|
|
||||||
dist/
|
|
||||||
web-build/
|
|
||||||
|
|
||||||
# Native
|
|
||||||
*.orig.*
|
|
||||||
*.jks
|
|
||||||
*.p8
|
|
||||||
*.p12
|
|
||||||
*.key
|
|
||||||
*.mobileprovision
|
|
||||||
|
|
||||||
# Metro
|
|
||||||
.metro-health-check*
|
|
||||||
|
|
||||||
# debug
|
|
||||||
npm-debug.*
|
|
||||||
yarn-debug.*
|
|
||||||
yarn-error.*
|
|
||||||
|
|
||||||
# macOS
|
|
||||||
.DS_Store
|
|
||||||
*.pem
|
|
||||||
|
|
||||||
# local env files
|
|
||||||
.env*.local
|
|
||||||
|
|
||||||
# typescript
|
|
||||||
*.tsbuildinfo
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
{
|
|
||||||
"expo": {
|
|
||||||
"android": {
|
|
||||||
"adaptiveIcon": {
|
|
||||||
"backgroundColor": "#ffffff",
|
|
||||||
"foregroundImage": "./assets/adaptive-icon.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"assetBundlePatterns": ["**/*"],
|
|
||||||
"icon": "./assets/icon.png",
|
|
||||||
"ios": {
|
|
||||||
"infoPlist": {
|
|
||||||
"NSCameraUsageDescription": "Take pictures for certain activities.",
|
|
||||||
"NSMicrophoneUsageDescription": "Need microphone access for recording videos.",
|
|
||||||
"NSPhotoLibraryUsageDescription": "Select pictures for certain activities."
|
|
||||||
},
|
|
||||||
"supportsTablet": true
|
|
||||||
},
|
|
||||||
"jsEngine": "hermes",
|
|
||||||
"name": "react-native-demo",
|
|
||||||
"orientation": "portrait",
|
|
||||||
"slug": "react-native-demo",
|
|
||||||
"splash": {
|
|
||||||
"backgroundColor": "#ffffff",
|
|
||||||
"image": "./assets/splash.png",
|
|
||||||
"resizeMode": "contain"
|
|
||||||
},
|
|
||||||
"userInterfaceStyle": "light",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"web": {
|
|
||||||
"favicon": "./assets/favicon.png"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 46 KiB |
@@ -1,6 +0,0 @@
|
|||||||
module.exports = function babel(api) {
|
|
||||||
api.cache(true);
|
|
||||||
return {
|
|
||||||
presets: ["babel-preset-expo"],
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import { registerRootComponent } from "expo";
|
|
||||||
import { LogBox } from "react-native";
|
|
||||||
import App from "./src/app";
|
|
||||||
|
|
||||||
registerRootComponent(App);
|
|
||||||
|
|
||||||
LogBox.ignoreAllLogs();
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
// Learn more https://docs.expo.io/guides/customizing-metro
|
|
||||||
const path = require("node:path");
|
|
||||||
const { getDefaultConfig } = require("expo/metro-config");
|
|
||||||
|
|
||||||
// Find the workspace root, this can be replaced with `find-yarn-workspace-root`
|
|
||||||
const workspaceRoot = path.resolve(__dirname, "../..");
|
|
||||||
const projectRoot = __dirname;
|
|
||||||
|
|
||||||
const config = getDefaultConfig(projectRoot);
|
|
||||||
|
|
||||||
// 1. Watch all files within the monorepo
|
|
||||||
config.watchFolders = [workspaceRoot];
|
|
||||||
// 2. Let Metro know where to resolve packages, and in what order
|
|
||||||
config.resolver.nodeModulesPaths = [
|
|
||||||
path.resolve(projectRoot, "node_modules"),
|
|
||||||
path.resolve(workspaceRoot, "node_modules"),
|
|
||||||
];
|
|
||||||
// 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths`
|
|
||||||
config.resolver.disableHierarchicalLookup = true;
|
|
||||||
|
|
||||||
module.exports = config;
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@formbricks/demo-react-native",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"main": "./index.js",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "expo start",
|
|
||||||
"android": "expo start --android",
|
|
||||||
"ios": "expo start --ios",
|
|
||||||
"web": "expo start --web",
|
|
||||||
"eject": "expo eject",
|
|
||||||
"clean": "rimraf .turbo node_modules .expo"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@formbricks/js": "workspace:*",
|
|
||||||
"@formbricks/react-native": "workspace:*",
|
|
||||||
"expo": "51.0.26",
|
|
||||||
"expo-status-bar": "1.12.1",
|
|
||||||
"react": "18.3.1",
|
|
||||||
"react-native": "0.74.4",
|
|
||||||
"react-native-webview": "13.8.6"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@babel/core": "7.25.2",
|
|
||||||
"@types/react": "18.3.11",
|
|
||||||
"typescript": "5.3.3"
|
|
||||||
},
|
|
||||||
"private": true
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import { StatusBar } from "expo-status-bar";
|
|
||||||
import { Button, LogBox, StyleSheet, Text, View } from "react-native";
|
|
||||||
import Formbricks, { track } from "@formbricks/react-native";
|
|
||||||
|
|
||||||
LogBox.ignoreAllLogs();
|
|
||||||
|
|
||||||
export default function App(): JSX.Element {
|
|
||||||
if (!process.env.EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID) {
|
|
||||||
throw new Error("EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID is required");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!process.env.EXPO_PUBLIC_API_HOST) {
|
|
||||||
throw new Error("EXPO_PUBLIC_API_HOST is required");
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
environmentId: process.env.EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID,
|
|
||||||
apiHost: process.env.EXPO_PUBLIC_API_HOST,
|
|
||||||
userId: "random-user-id",
|
|
||||||
attributes: {
|
|
||||||
language: "en",
|
|
||||||
testAttr: "attr-test",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={styles.container}>
|
|
||||||
<Text>Formbricks React Native SDK Demo</Text>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
title="Trigger Code Action"
|
|
||||||
onPress={() => {
|
|
||||||
track("code").catch((error: unknown) => {
|
|
||||||
// eslint-disable-next-line no-console -- logging is allowed in demo apps
|
|
||||||
console.error("Error tracking event:", error);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<StatusBar style="auto" />
|
|
||||||
<Formbricks initConfig={config} />
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
backgroundColor: "#fff",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"strict": true
|
|
||||||
},
|
|
||||||
"extends": "expo/tsconfig.base"
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
NEXT_PUBLIC_FORMBRICKS_API_HOST=http://localhost:3000
|
|
||||||
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=YOUR_ENVIRONMENT_ID
|
|
||||||
|
|
||||||
# Copy the environment ID for the URL of your Formbricks App and
|
|
||||||
# paste it above to connect your Formbricks App with the Demo App.
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
extends: ["@formbricks/eslint-config/legacy-next.js"],
|
|
||||||
};
|
|
||||||
36
apps/demo/.gitignore
vendored
@@ -1,36 +0,0 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
|
||||||
/.pnp
|
|
||||||
.pnp.js
|
|
||||||
|
|
||||||
# testing
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# next.js
|
|
||||||
/.next/
|
|
||||||
/out/
|
|
||||||
|
|
||||||
# production
|
|
||||||
/build
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.DS_Store
|
|
||||||
*.pem
|
|
||||||
|
|
||||||
# debug
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
.pnpm-debug.log*
|
|
||||||
|
|
||||||
# local env files
|
|
||||||
.env*.local
|
|
||||||
|
|
||||||
# vercel
|
|
||||||
.vercel
|
|
||||||
|
|
||||||
# typescript
|
|
||||||
*.tsbuildinfo
|
|
||||||
next-env.d.ts
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { Sidebar } from "./Sidebar";
|
|
||||||
|
|
||||||
export const LayoutApp = ({ children }: { children: React.ReactNode }) => {
|
|
||||||
return (
|
|
||||||
<div className="min-h-full">
|
|
||||||
{/* Static sidebar for desktop */}
|
|
||||||
<div className="hidden lg:fixed lg:inset-y-0 lg:flex lg:w-64 lg:flex-col">
|
|
||||||
<Sidebar />
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-1 flex-col lg:pl-64">{children}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import {
|
|
||||||
ClockIcon,
|
|
||||||
CogIcon,
|
|
||||||
CreditCardIcon,
|
|
||||||
FileBarChartIcon,
|
|
||||||
HelpCircleIcon,
|
|
||||||
HomeIcon,
|
|
||||||
ScaleIcon,
|
|
||||||
ShieldCheckIcon,
|
|
||||||
UsersIcon,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { classNames } from "../lib/utils";
|
|
||||||
|
|
||||||
const navigation = [
|
|
||||||
{ name: "Home", href: "#", icon: HomeIcon, current: true },
|
|
||||||
{ name: "History", href: "#", icon: ClockIcon, current: false },
|
|
||||||
{ name: "Balances", href: "#", icon: ScaleIcon, current: false },
|
|
||||||
{ name: "Cards", href: "#", icon: CreditCardIcon, current: false },
|
|
||||||
{ name: "Recipients", href: "#", icon: UsersIcon, current: false },
|
|
||||||
{ name: "Reports", href: "#", icon: FileBarChartIcon, current: false },
|
|
||||||
];
|
|
||||||
const secondaryNavigation = [
|
|
||||||
{ name: "Settings", href: "#", icon: CogIcon },
|
|
||||||
{ name: "Help", href: "#", icon: HelpCircleIcon },
|
|
||||||
{ name: "Privacy", href: "#", icon: ShieldCheckIcon },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const Sidebar = () => {
|
|
||||||
return (
|
|
||||||
<div className="flex flex-grow flex-col overflow-y-auto bg-cyan-700 pb-4 pt-5">
|
|
||||||
<nav
|
|
||||||
className="mt-5 flex flex-1 flex-col divide-y divide-cyan-800 overflow-y-auto"
|
|
||||||
aria-label="Sidebar">
|
|
||||||
<div className="space-y-1 px-2">
|
|
||||||
{navigation.map((item) => (
|
|
||||||
<a
|
|
||||||
key={item.name}
|
|
||||||
href={item.href}
|
|
||||||
className={classNames(
|
|
||||||
item.current ? "bg-cyan-800 text-white" : "text-cyan-100 hover:bg-cyan-600 hover:text-white",
|
|
||||||
"group flex items-center rounded-md px-2 py-2 text-sm font-medium leading-6"
|
|
||||||
)}
|
|
||||||
aria-current={item.current ? "page" : undefined}>
|
|
||||||
<item.icon className="mr-4 h-6 w-6 flex-shrink-0 text-cyan-200" aria-hidden="true" />
|
|
||||||
{item.name}
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="mt-6 pt-6">
|
|
||||||
<div className="space-y-1 px-2">
|
|
||||||
{secondaryNavigation.map((item) => (
|
|
||||||
<a
|
|
||||||
key={item.name}
|
|
||||||
href={item.href}
|
|
||||||
className="group flex items-center rounded-md px-2 py-2 text-sm font-medium leading-6 text-cyan-100 hover:bg-cyan-600 hover:text-white">
|
|
||||||
<item.icon className="mr-4 h-6 w-6 text-cyan-200" aria-hidden="true" />
|
|
||||||
{item.name}
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export const classNames = (...classes: any) => {
|
|
||||||
return classes.filter(Boolean).join(" ");
|
|
||||||
};
|
|
||||||
5
apps/demo/next-env.d.ts
vendored
@@ -1,5 +0,0 @@
|
|||||||
/// <reference types="next" />
|
|
||||||
/// <reference types="next/image-types/global" />
|
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
|
||||||
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
|
||||||
const nextConfig = {
|
|
||||||
images: {
|
|
||||||
remotePatterns: [
|
|
||||||
{
|
|
||||||
protocol: "https",
|
|
||||||
hostname: "tailwindui.com",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
protocol: "https",
|
|
||||||
hostname: "images.unsplash.com",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default nextConfig;
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@formbricks/demo",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"clean": "rimraf .turbo node_modules .next",
|
|
||||||
"dev": "next dev -p 3002 --turbo",
|
|
||||||
"go": "next dev -p 3002 --turbo",
|
|
||||||
"build": "next build",
|
|
||||||
"start": "next start",
|
|
||||||
"lint": "next lint"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@formbricks/js": "workspace:*",
|
|
||||||
"@formbricks/ui": "workspace:*",
|
|
||||||
"lucide-react": "0.452.0",
|
|
||||||
"next": "14.2.15",
|
|
||||||
"react": "18.3.1",
|
|
||||||
"react-dom": "18.3.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@formbricks/eslint-config": "workspace:*",
|
|
||||||
"@formbricks/config-typescript": "workspace:*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import type { AppProps } from "next/app";
|
|
||||||
import Head from "next/head";
|
|
||||||
import "@formbricks/ui/globals.css";
|
|
||||||
|
|
||||||
const App = ({ Component, pageProps }: AppProps) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Head>
|
|
||||||
<title>Demo App</title>
|
|
||||||
</Head>
|
|
||||||
{(!process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID ||
|
|
||||||
!process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST) && (
|
|
||||||
<div className="w-full bg-red-500 p-3 text-center text-sm text-white">
|
|
||||||
Please set Formbricks environment variables in apps/demo/.env
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<Component {...pageProps} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { Head, Html, Main, NextScript } from "next/document";
|
|
||||||
|
|
||||||
const Document = () => {
|
|
||||||
return (
|
|
||||||
<Html lang="en" className="h-full bg-slate-50">
|
|
||||||
<Head />
|
|
||||||
<body className="h-full">
|
|
||||||
<Main />
|
|
||||||
<NextScript />
|
|
||||||
</body>
|
|
||||||
</Html>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Document;
|
|
||||||
@@ -1,239 +0,0 @@
|
|||||||
import Image from "next/image";
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import formbricks from "@formbricks/js";
|
|
||||||
import fbsetup from "../public/fb-setup.png";
|
|
||||||
|
|
||||||
declare const window: any;
|
|
||||||
|
|
||||||
const AppPage = ({}) => {
|
|
||||||
const [darkMode, setDarkMode] = useState(false);
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (darkMode) {
|
|
||||||
document.body.classList.add("dark");
|
|
||||||
} else {
|
|
||||||
document.body.classList.remove("dark");
|
|
||||||
}
|
|
||||||
}, [darkMode]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// enable Formbricks debug mode by adding formbricksDebug=true GET parameter
|
|
||||||
const addFormbricksDebugParam = () => {
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
|
||||||
if (!urlParams.has("formbricksDebug")) {
|
|
||||||
urlParams.set("formbricksDebug", "true");
|
|
||||||
const newUrl = `${window.location.pathname}?${urlParams.toString()}`;
|
|
||||||
window.history.replaceState({}, "", newUrl);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
addFormbricksDebugParam();
|
|
||||||
|
|
||||||
if (process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID && process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST) {
|
|
||||||
const userId = "THIS-IS-A-VERY-LONG-USER-ID-FOR-TESTING";
|
|
||||||
const userInitAttributes = {
|
|
||||||
language: "de",
|
|
||||||
"Init Attribute 1": "eight",
|
|
||||||
"Init Attribute 2": "two",
|
|
||||||
};
|
|
||||||
|
|
||||||
formbricks.init({
|
|
||||||
environmentId: process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID,
|
|
||||||
apiHost: process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST,
|
|
||||||
userId,
|
|
||||||
attributes: userInitAttributes,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect next.js router to Formbricks
|
|
||||||
if (process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID && process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST) {
|
|
||||||
const handleRouteChange = formbricks?.registerRouteChange;
|
|
||||||
router.events.on("routeChangeComplete", handleRouteChange);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
router.events.off("routeChangeComplete", handleRouteChange);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-white px-12 py-6 dark:bg-slate-800">
|
|
||||||
<div className="flex flex-col justify-between md:flex-row">
|
|
||||||
<div className="flex flex-col items-center gap-2 sm:flex-row">
|
|
||||||
<div>
|
|
||||||
<h1 className="text-2xl font-bold text-slate-900 dark:text-white">
|
|
||||||
Formbricks In-product Survey Demo App
|
|
||||||
</h1>
|
|
||||||
<p className="text-slate-700 dark:text-slate-300">
|
|
||||||
This app helps you test your app surveys. You can create and test user actions, create and
|
|
||||||
update user attributes, etc.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
className="mt-2 rounded-lg bg-slate-200 px-6 py-1 dark:bg-slate-700 dark:text-slate-100"
|
|
||||||
onClick={() => setDarkMode(!darkMode)}>
|
|
||||||
{darkMode ? "Toggle Light Mode" : "Toggle Dark Mode"}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="my-4 grid grid-cols-1 gap-6 md:grid-cols-2">
|
|
||||||
<div>
|
|
||||||
<div className="rounded-lg border border-slate-300 bg-slate-100 p-6 dark:border-slate-600 dark:bg-slate-900">
|
|
||||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-white">1. Setup .env</h3>
|
|
||||||
<p className="text-slate-700 dark:text-slate-300">
|
|
||||||
Copy the environment ID of your Formbricks app to the env variable in /apps/demo/.env
|
|
||||||
</p>
|
|
||||||
<Image src={fbsetup} alt="fb setup" className="mt-4 rounded" priority />
|
|
||||||
|
|
||||||
<div className="mt-4 flex-col items-start text-sm text-slate-700 sm:flex sm:items-center sm:text-base dark:text-slate-300">
|
|
||||||
<p className="mb-1 sm:mb-0 sm:mr-2">You're connected with env:</p>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<strong className="w-32 truncate sm:w-auto">
|
|
||||||
{process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID}
|
|
||||||
</strong>
|
|
||||||
<span className="relative ml-2 flex h-3 w-3">
|
|
||||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-500 opacity-75"></span>
|
|
||||||
<span className="relative inline-flex h-3 w-3 rounded-full bg-green-500"></span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-4 rounded-lg border border-slate-300 bg-slate-100 p-6 dark:border-slate-600 dark:bg-slate-900">
|
|
||||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-white">2. Widget Logs</h3>
|
|
||||||
<p className="text-slate-700 dark:text-slate-300">
|
|
||||||
Look at the logs to understand how the widget works.{" "}
|
|
||||||
<strong className="dark:text-white">Open your browser console</strong> to see the logs.
|
|
||||||
</p>
|
|
||||||
{/* <div className="max-h-[40vh] overflow-y-auto py-4">
|
|
||||||
<LogsContainer />
|
|
||||||
</div> */}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="md:grid md:grid-cols-3">
|
|
||||||
<div className="col-span-3 self-start rounded-lg border border-slate-300 bg-slate-100 p-6 dark:border-slate-600 dark:bg-slate-900">
|
|
||||||
<h3 className="text-lg font-semibold dark:text-white">
|
|
||||||
Reset person / pull data from Formbricks app
|
|
||||||
</h3>
|
|
||||||
<p className="text-slate-700 dark:text-slate-300">
|
|
||||||
On formbricks.reset() the local state will <strong>be deleted</strong> and formbricks gets{" "}
|
|
||||||
<strong>reinitialized</strong>.
|
|
||||||
</p>
|
|
||||||
<button
|
|
||||||
className="my-4 rounded-lg bg-slate-500 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
|
|
||||||
onClick={() => {
|
|
||||||
formbricks.reset();
|
|
||||||
}}>
|
|
||||||
Reset
|
|
||||||
</button>
|
|
||||||
<p className="text-xs text-slate-700 dark:text-slate-300">
|
|
||||||
If you made a change in Formbricks app and it does not seem to work, hit 'Reset' and
|
|
||||||
try again.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="p-6">
|
|
||||||
<div>
|
|
||||||
<button className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
|
|
||||||
No-Code Action
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-xs text-slate-700 dark:text-slate-300">
|
|
||||||
This button sends a{" "}
|
|
||||||
<a
|
|
||||||
href="https://formbricks.com/docs/actions/no-code"
|
|
||||||
className="underline dark:text-blue-500"
|
|
||||||
target="_blank">
|
|
||||||
No Code Action
|
|
||||||
</a>{" "}
|
|
||||||
as long as you created it beforehand in the Formbricks App.{" "}
|
|
||||||
<a
|
|
||||||
href="https://formbricks.com/docs/actions/no-code"
|
|
||||||
target="_blank"
|
|
||||||
className="underline dark:text-blue-500">
|
|
||||||
Here are instructions on how to do it.
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="p-6">
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
formbricks.setAttribute("Plan", "Free");
|
|
||||||
}}
|
|
||||||
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
|
|
||||||
Set Plan to 'Free'
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-xs text-slate-700 dark:text-slate-300">
|
|
||||||
This button sets the{" "}
|
|
||||||
<a
|
|
||||||
href="https://formbricks.com/docs/attributes/custom-attributes"
|
|
||||||
target="_blank"
|
|
||||||
className="underline dark:text-blue-500">
|
|
||||||
attribute
|
|
||||||
</a>{" "}
|
|
||||||
'Plan' to 'Free'. If the attribute does not exist, it creates it.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="p-6">
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
formbricks.setAttribute("Plan", "Paid");
|
|
||||||
}}
|
|
||||||
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
|
|
||||||
Set Plan to 'Paid'
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-xs text-slate-700 dark:text-slate-300">
|
|
||||||
This button sets the{" "}
|
|
||||||
<a
|
|
||||||
href="https://formbricks.com/docs/attributes/custom-attributes"
|
|
||||||
target="_blank"
|
|
||||||
className="underline dark:text-blue-500">
|
|
||||||
attribute
|
|
||||||
</a>{" "}
|
|
||||||
'Plan' to 'Paid'. If the attribute does not exist, it creates it.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="p-6">
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
formbricks.setEmail("test@web.com");
|
|
||||||
}}
|
|
||||||
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
|
|
||||||
Set Email
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-xs text-slate-700 dark:text-slate-300">
|
|
||||||
This button sets the{" "}
|
|
||||||
<a
|
|
||||||
href="https://formbricks.com/docs/attributes/identify-users"
|
|
||||||
target="_blank"
|
|
||||||
className="underline dark:text-blue-500">
|
|
||||||
user email
|
|
||||||
</a>{" "}
|
|
||||||
'test@web.com'
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AppPage;
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: {
|
|
||||||
tailwindcss: {},
|
|
||||||
autoprefixer: {},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="31" fill="none"><g opacity=".9"><path fill="url(#a)" d="M13 .4v29.3H7V6.3h-.2L0 10.5V5L7.2.4H13Z"/><path fill="url(#b)" d="M28.8 30.1c-2.2 0-4-.3-5.7-1-1.7-.8-3-1.8-4-3.1a7.7 7.7 0 0 1-1.4-4.6h6.2c0 .8.3 1.4.7 2 .4.5 1 .9 1.7 1.2.7.3 1.6.4 2.5.4 1 0 1.7-.2 2.5-.5.7-.3 1.3-.8 1.7-1.4.4-.6.6-1.2.6-2s-.2-1.5-.7-2.1c-.4-.6-1-1-1.8-1.4-.8-.4-1.8-.5-2.9-.5h-2.7v-4.6h2.7a6 6 0 0 0 2.5-.5 4 4 0 0 0 1.7-1.3c.4-.6.6-1.3.6-2a3.5 3.5 0 0 0-2-3.3 5.6 5.6 0 0 0-4.5 0 4 4 0 0 0-1.7 1.2c-.4.6-.6 1.2-.6 2h-6c0-1.7.6-3.2 1.5-4.5 1-1.3 2.2-2.3 3.8-3C25 .4 26.8 0 28.8 0s3.8.4 5.3 1.1c1.5.7 2.7 1.7 3.6 3a7.2 7.2 0 0 1 1.2 4.2c0 1.6-.5 3-1.5 4a7 7 0 0 1-4 2.2v.2c2.2.3 3.8 1 5 2.2a6.4 6.4 0 0 1 1.6 4.6c0 1.7-.5 3.1-1.4 4.4a9.7 9.7 0 0 1-4 3.1c-1.7.8-3.7 1.1-5.8 1.1Z"/></g><defs><linearGradient id="a" x1="20" x2="20" y1="0" y2="30.1" gradientUnits="userSpaceOnUse"><stop/><stop offset="1" stop-color="#3D3D3D"/></linearGradient><linearGradient id="b" x1="20" x2="20" y1="0" y2="30.1" gradientUnits="userSpaceOnUse"><stop/><stop offset="1" stop-color="#3D3D3D"/></linearGradient></defs></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 629 B |
@@ -1,13 +0,0 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
|
||||||
module.exports = {
|
|
||||||
content: [
|
|
||||||
"./app/**/*.{js,ts,jsx,tsx}",
|
|
||||||
"./pages/**/*.{js,ts,jsx,tsx}",
|
|
||||||
"./components/**/*.{js,ts,jsx,tsx}",
|
|
||||||
],
|
|
||||||
darkMode: "class",
|
|
||||||
theme: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
plugins: [require("@tailwindcss/forms")],
|
|
||||||
};
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"exclude": ["node_modules"],
|
|
||||||
"extends": "@formbricks/config-typescript/nextjs.json",
|
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
NEXT_PUBLIC_FORMBRICKS_COM_API_HOST=http://localhost:3000
|
|
||||||
NEXT_PUBLIC_FORMBRICKS_COM_ENVIRONMENT_ID=
|
|
||||||
NEXT_PUBLIC_FORMBRICKS_COM_DOCS_FEEDBACK_SURVEY_ID=
|
|
||||||
|
|
||||||
# Strapi API Key
|
|
||||||
STRAPI_API_KEY=
|
|
||||||
|
|
||||||
NEXT_PUBLIC_DOCSEARCH_APP_ID=
|
|
||||||
NEXT_PUBLIC_DOCSEARCH_API_KEY=
|
|
||||||
NEXT_PUBLIC_DOCSEARCH_INDEX_NAME=
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
extends: ["@formbricks/eslint-config/legacy-next.js"],
|
|
||||||
};
|
|
||||||
38
apps/docs/.gitignore
vendored
@@ -1,38 +0,0 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
|
||||||
/.pnp
|
|
||||||
.pnp.js
|
|
||||||
|
|
||||||
# testing
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# next.js
|
|
||||||
/.next/
|
|
||||||
/out/
|
|
||||||
|
|
||||||
# production
|
|
||||||
/build
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.DS_Store
|
|
||||||
*.pem
|
|
||||||
|
|
||||||
# debug
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
# local env files
|
|
||||||
.env*.local
|
|
||||||
|
|
||||||
# vercel
|
|
||||||
.vercel
|
|
||||||
|
|
||||||
# typescript
|
|
||||||
*.tsbuildinfo
|
|
||||||
next-env.d.ts
|
|
||||||
|
|
||||||
public/sitemap*.xml
|
|
||||||
public/robots.txt
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
# Tailwind UI License
|
|
||||||
|
|
||||||
## Personal License
|
|
||||||
|
|
||||||
Tailwind Labs Inc. grants you an on-going, non-exclusive license to use the Components and Templates.
|
|
||||||
|
|
||||||
The license grants permission to **one individual** (the Licensee) to access and use the Components and Templates.
|
|
||||||
|
|
||||||
You **can**:
|
|
||||||
|
|
||||||
- Use the Components and Templates to create unlimited End Products.
|
|
||||||
- Modify the Components and Templates to create derivative components and templates. Those components and templates are subject to this license.
|
|
||||||
- Use the Components and Templates to create unlimited End Products for unlimited Clients.
|
|
||||||
- Use the Components and Templates to create End Products where the End Product is sold to End Users.
|
|
||||||
- Use the Components and Templates to create End Products that are open source and freely available to End Users.
|
|
||||||
|
|
||||||
You **cannot**:
|
|
||||||
|
|
||||||
- Use the Components and Templates to create End Products that are designed to allow an End User to build their own End Products using the Components and Templates or derivatives of the Components and Templates.
|
|
||||||
- Re-distribute the Components and Templates or derivatives of the Components and Templates separately from an End Product, neither in code or as design assets.
|
|
||||||
- Share your access to the Components and Templates with any other individuals.
|
|
||||||
- Use the Components and Templates to produce anything that may be deemed by Tailwind Labs Inc, in their sole and absolute discretion, to be competitive or in conflict with the business of Tailwind Labs Inc.
|
|
||||||
|
|
||||||
### Example usage
|
|
||||||
|
|
||||||
Examples of usage **allowed** by the license:
|
|
||||||
|
|
||||||
- Creating a personal website by yourself.
|
|
||||||
- Creating a website or web application for a client that will be owned by that client.
|
|
||||||
- Creating a commercial SaaS application (like an invoicing app for example) where end users have to pay a fee to use the application.
|
|
||||||
- Creating a commercial self-hosted web application that is sold to end users for a one-time fee.
|
|
||||||
- Creating a web application where the primary purpose is clearly not to simply re-distribute the components (like a conference organization app that uses the components for its UI for example) that is free and open source, where the source code is publicly available.
|
|
||||||
|
|
||||||
Examples of usage **not allowed** by the license:
|
|
||||||
|
|
||||||
- Creating a repository of your favorite Tailwind UI components or templates (or derivatives based on Tailwind UI components or templates) and publishing it publicly.
|
|
||||||
- Creating a React or Vue version of Tailwind UI and making it available either for sale or for free.
|
|
||||||
- Create a Figma or Sketch UI kit based on the Tailwind UI component designs.
|
|
||||||
- Creating a "website builder" project where end users can build their own websites using components or templates included with or derived from Tailwind UI.
|
|
||||||
- Creating a theme, template, or project starter kit using the components or templates and making it available either for sale or for free.
|
|
||||||
- Creating an admin panel tool (like [Laravel Nova](https://nova.laravel.com/) or [ActiveAdmin](https://activeadmin.info/)) that is made available either for sale or for free.
|
|
||||||
|
|
||||||
In simple terms, use Tailwind UI for anything you like as long as it doesn't compete with Tailwind UI.
|
|
||||||
|
|
||||||
### Personal License Definitions
|
|
||||||
|
|
||||||
Licensee is the individual who has purchased a Personal License.
|
|
||||||
|
|
||||||
Components and Templates are the source code and design assets made available to the Licensee after purchasing a Tailwind UI license.
|
|
||||||
|
|
||||||
End Product is any artifact produced that incorporates the Components or Templates or derivatives of the Components or Templates.
|
|
||||||
|
|
||||||
End User is a user of an End Product.
|
|
||||||
|
|
||||||
Client is an individual or entity receiving custom professional services directly from the Licensee, produced specifically for that individual or entity. Customers of software-as-a-service products are not considered clients for the purpose of this document.
|
|
||||||
|
|
||||||
## Team License
|
|
||||||
|
|
||||||
Tailwind Labs Inc. grants you an on-going, non-exclusive license to use the Components and Templates.
|
|
||||||
|
|
||||||
The license grants permission for **up to 25 Employees and Contractors of the Licensee** to access and use the Components and Templates.
|
|
||||||
|
|
||||||
You **can**:
|
|
||||||
|
|
||||||
- Use the Components and Templates to create unlimited End Products.
|
|
||||||
- Modify the Components and Templates to create derivative components and templates. Those components and templates are subject to this license.
|
|
||||||
- Use the Components and Templates to create unlimited End Products for unlimited Clients.
|
|
||||||
- Use the Components and Templates to create End Products where the End Product is sold to End Users.
|
|
||||||
- Use the Components and Templates to create End Products that are open source and freely available to End Users.
|
|
||||||
|
|
||||||
You **cannot**:
|
|
||||||
|
|
||||||
- Use the Components or Templates to create End Products that are designed to allow an End User to build their own End Products using the Components or Templates or derivatives of the Components or Templates.
|
|
||||||
- Re-distribute the Components or Templates or derivatives of the Components or Templates separately from an End Product.
|
|
||||||
- Use the Components or Templates to create End Products that are the property of any individual or entity other than the Licensee or Clients of the Licensee.
|
|
||||||
- Use the Components or Templates to produce anything that may be deemed by Tailwind Labs Inc, in their sole and absolute discretion, to be competitive or in conflict with the business of Tailwind Labs Inc.
|
|
||||||
|
|
||||||
### Example usage
|
|
||||||
|
|
||||||
Examples of usage **allowed** by the license:
|
|
||||||
|
|
||||||
- Creating a website for your company.
|
|
||||||
- Creating a website or web application for a client that will be owned by that client.
|
|
||||||
- Creating a commercial SaaS application (like an invoicing app for example) where end users have to pay a fee to use the application.
|
|
||||||
- Creating a commercial self-hosted web application that is sold to end users for a one-time fee.
|
|
||||||
- Creating a web application where the primary purpose is clearly not to simply re-distribute the components or templates (like a conference organization app that uses the components or a template for its UI for example) that is free and open source, where the source code is publicly available.
|
|
||||||
|
|
||||||
Examples of use **not allowed** by the license:
|
|
||||||
|
|
||||||
- Creating a repository of your favorite Tailwind UI components or template (or derivatives based on Tailwind UI components or templates) and publishing it publicly.
|
|
||||||
- Creating a React or Vue version of Tailwind UI and making it available either for sale or for free.
|
|
||||||
- Creating a "website builder" project where end users can build their own websites using components or templates included with or derived from Tailwind UI.
|
|
||||||
- Creating a theme or template using the components or templates and making it available either for sale or for free.
|
|
||||||
- Creating an admin panel tool (like [Laravel Nova](https://nova.laravel.com/) or [ActiveAdmin](https://activeadmin.info/)) that is made available either for sale or for free.
|
|
||||||
- Creating any End Product that is not the sole property of either your company or a client of your company. For example your employees/contractors can't use your company Tailwind UI license to build their own websites or side projects.
|
|
||||||
|
|
||||||
### Team License Definitions
|
|
||||||
|
|
||||||
Licensee is the business entity who has purchased a Team License.
|
|
||||||
|
|
||||||
Components and Templates are the source code and design assets made available to the Licensee after purchasing a Tailwind UI license.
|
|
||||||
|
|
||||||
End Product is any artifact produced that incorporates the Components or Templates or derivatives of the Components or Templates.
|
|
||||||
|
|
||||||
End User is a user of an End Product.
|
|
||||||
|
|
||||||
Employee is a full-time or part-time employee of the Licensee.
|
|
||||||
|
|
||||||
Contractor is an individual or business entity contracted to perform services for the Licensee.
|
|
||||||
|
|
||||||
Client is an individual or entity receiving custom professional services directly from the Licensee, produced specifically for that individual or entity. Customers of software-as-a-service products are not considered clients for the purpose of this document.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
If you are found to be in violation of the license, access to your Tailwind UI account will be terminated, and a refund may be issued at our discretion. When license violation is blatant and malicious (such as intentionally redistributing the Components or Templates through private warez channels), no refund will be issued.
|
|
||||||
|
|
||||||
The copyright of the Components and Templates is owned by Tailwind Labs Inc. You are granted only the permissions described in this license; all other rights are reserved. Tailwind Labs Inc. reserves the right to pursue legal remedies for any unauthorized use of the Components or Templates outside the scope of this license.
|
|
||||||
|
|
||||||
## Liability
|
|
||||||
|
|
||||||
Tailwind Labs Inc.’s liability to you for costs, damages, or other losses arising from your use of the Components or Templates — including third-party claims against you — is limited to a refund of your license fee. Tailwind Labs Inc. may not be held liable for any consequential damages related to your use of the Components or Templates.
|
|
||||||
|
|
||||||
This Agreement is governed by the laws of the Province of Ontario and the applicable laws of Canada. Legal proceedings related to this Agreement may only be brought in the courts of Ontario. You agree to service of process at the e-mail address on your original order.
|
|
||||||
|
|
||||||
## Questions?
|
|
||||||
|
|
||||||
Unsure which license you need, or unsure if your use case is covered by our licenses?
|
|
||||||
|
|
||||||
Email us at [support@tailwindui.com](mailto:support@tailwindui.com) with your questions.
|
|
||||||