mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-22 06:00:51 -06:00
Compare commits
1002 Commits
v2.2.1
...
fix/emove-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74337df278 | ||
|
|
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 | ||
|
|
bee0ab07b4 | ||
|
|
0c37956943 | ||
|
|
74bd40e0ff | ||
|
|
31c3f9730e | ||
|
|
da8e7c1870 | ||
|
|
ac0ec1fbcd | ||
|
|
18be650561 | ||
|
|
5977fa6f80 | ||
|
|
1e2833b742 | ||
|
|
f18b030ebf | ||
|
|
ef454d8140 | ||
|
|
1ea391e45b | ||
|
|
b3e6e8d5d0 | ||
|
|
705f55176f | ||
|
|
297f349b45 | ||
|
|
91b6a9e008 | ||
|
|
189dc52ee9 | ||
|
|
280a9a439b | ||
|
|
bc844bbb1f | ||
|
|
ea2d6de9a7 | ||
|
|
e09ab1dcbe | ||
|
|
e1e04517a9 | ||
|
|
06026b6922 | ||
|
|
19a3faadce | ||
|
|
33543f59f8 | ||
|
|
47826a45aa | ||
|
|
6f043ec16e | ||
|
|
c2703788ae | ||
|
|
ef7df0fc77 | ||
|
|
cdb8199199 | ||
|
|
b0ded570ff | ||
|
|
5c0b29eed4 | ||
|
|
8e16d8daf6 | ||
|
|
deea760a17 | ||
|
|
f56f08e3c1 | ||
|
|
5daeab6554 | ||
|
|
0d11c08be7 | ||
|
|
e7fbdb4d00 | ||
|
|
9538c2e6e3 | ||
|
|
3b5f9adcd1 | ||
|
|
90480317af | ||
|
|
352e905529 | ||
|
|
588768c849 | ||
|
|
2bab855b05 | ||
|
|
7fdc2eec34 | ||
|
|
464455be2b | ||
|
|
0a11c6aed5 | ||
|
|
8566b4c3da | ||
|
|
9465bd15f2 | ||
|
|
37791fc78f | ||
|
|
98c16eb4b8 | ||
|
|
7803de6ee7 | ||
|
|
d9e2267a3a | ||
|
|
9872d17abe | ||
|
|
4dad1226ce | ||
|
|
5a2f8c586a | ||
|
|
eba60d4777 | ||
|
|
e633fc76be | ||
|
|
700068dc9f | ||
|
|
0f2f3b1af8 | ||
|
|
35e4b1f965 | ||
|
|
047c9abe9c | ||
|
|
8f0c55808d | ||
|
|
03df021fbf | ||
|
|
be96885260 | ||
|
|
e75c1b3539 | ||
|
|
49727ddf9e | ||
|
|
94985e0502 | ||
|
|
9bf74407ae | ||
|
|
5620d477e9 | ||
|
|
e5f17aff44 | ||
|
|
9bc5fab0ab | ||
|
|
8ca7c38def | ||
|
|
f92a5eabc9 | ||
|
|
b40ed54e3c | ||
|
|
7fa7591d59 | ||
|
|
81361b3901 | ||
|
|
49f78670d0 | ||
|
|
119a86350d | ||
|
|
58b43b7227 | ||
|
|
618617bf55 | ||
|
|
6ec748f6b7 | ||
|
|
36cb4f6cd2 | ||
|
|
6fa805e7ab | ||
|
|
7dbc0f0f0e | ||
|
|
807d448f68 | ||
|
|
014558964c | ||
|
|
28dc81f51f | ||
|
|
fca2989222 | ||
|
|
0224564da5 | ||
|
|
aeec821b7b | ||
|
|
676145233a | ||
|
|
3be72007fa | ||
|
|
f1b9a82192 | ||
|
|
fe8c1fbc47 | ||
|
|
4c8be95737 | ||
|
|
595fe98417 | ||
|
|
abc4c7f156 | ||
|
|
a6a815c014 | ||
|
|
9cc83bc01a | ||
|
|
9262764691 | ||
|
|
c2d452f196 | ||
|
|
17f4cb81d4 | ||
|
|
cf4eec5134 | ||
|
|
cedb0b27c0 | ||
|
|
75d757944a | ||
|
|
989252dc5b | ||
|
|
f31cc9f9a3 | ||
|
|
c01c00ce2e | ||
|
|
ba0adbfad3 | ||
|
|
2bfea919fe | ||
|
|
82a7b2276d | ||
|
|
ed7662be5d | ||
|
|
7207a4d3e6 | ||
|
|
4c594fafb9 | ||
|
|
02345bc82c | ||
|
|
e8403699e1 | ||
|
|
3a49abda43 | ||
|
|
fb4e4159bc | ||
|
|
31a8cf3b23 | ||
|
|
d8cb1c5fc9 | ||
|
|
683278e252 | ||
|
|
6b04cc789b | ||
|
|
e31e9f4fa1 | ||
|
|
f38a4665e4 | ||
|
|
0cc84c8734 | ||
|
|
80243b8561 | ||
|
|
f90b508251 | ||
|
|
d71a9c9c3d | ||
|
|
934a05a814 | ||
|
|
21e9e9167f | ||
|
|
ea0df287d9 | ||
|
|
74407e7919 | ||
|
|
ea4941fe98 | ||
|
|
1c10679b71 | ||
|
|
0b9d76e243 | ||
|
|
4d225818a6 | ||
|
|
f60ae69ed1 | ||
|
|
e583aa38ba | ||
|
|
d47944ed21 | ||
|
|
a8e1fce3b7 | ||
|
|
706fa8a04e | ||
|
|
11748cbeba | ||
|
|
0f50c9690c | ||
|
|
316e5f15b0 | ||
|
|
18719746ed | ||
|
|
41fb76e4ff | ||
|
|
24d1f23421 | ||
|
|
354ec1b887 | ||
|
|
87c584add8 | ||
|
|
5035e3db9d | ||
|
|
b7f4097508 | ||
|
|
26591d9b9f | ||
|
|
655b67c3ad | ||
|
|
2dbd7111a9 | ||
|
|
06ddee42a9 | ||
|
|
47aa84bf8a | ||
|
|
2f7a59817a | ||
|
|
e7a0228bfa | ||
|
|
2367313ff2 | ||
|
|
68e52954e2 | ||
|
|
59ebde49cf | ||
|
|
6ab2560432 | ||
|
|
861d399025 | ||
|
|
fc3886fafa | ||
|
|
ddf7ad8475 | ||
|
|
ad6d5d6c00 | ||
|
|
315aaac395 | ||
|
|
01ceaa13ec | ||
|
|
25774f6f08 | ||
|
|
9a98772210 | ||
|
|
59a29dd3d6 | ||
|
|
e4fceb2e5e | ||
|
|
0b553447e0 | ||
|
|
6b64367d99 | ||
|
|
fe9746ba67 | ||
|
|
e4009d5951 | ||
|
|
b1ed61c247 | ||
|
|
10255aa102 | ||
|
|
774c6f19a5 | ||
|
|
ebf35ea582 | ||
|
|
f13efc954e | ||
|
|
9ee052a229 | ||
|
|
152fbede90 | ||
|
|
c9b8ffa9ef | ||
|
|
04e16d44a1 | ||
|
|
29131f93c2 | ||
|
|
1e2fe7b066 | ||
|
|
426a0a3847 | ||
|
|
80ef504bef | ||
|
|
6ab83e25d3 | ||
|
|
688dc25990 | ||
|
|
c53b58c64f | ||
|
|
61d18edb5d | ||
|
|
07b5dfe28a | ||
|
|
f55cad0121 | ||
|
|
494299cd89 | ||
|
|
5cc071e5a8 | ||
|
|
0532f2744b | ||
|
|
43ea26a33a | ||
|
|
ec54e40a8b | ||
|
|
4b508f02e3 | ||
|
|
eec7e1b62a | ||
|
|
39e87eb8d3 | ||
|
|
780115ffb8 | ||
|
|
c7c4ba6e49 | ||
|
|
b9a7edf1f5 | ||
|
|
86bf2accc9 | ||
|
|
954c435404 | ||
|
|
538c1bd809 | ||
|
|
e0767881f2 | ||
|
|
7d0cbad326 | ||
|
|
0ce7703ab8 | ||
|
|
4362fdf35a | ||
|
|
4bcca2daf4 | ||
|
|
a1d83ac7b9 | ||
|
|
492729baf3 | ||
|
|
4003d21826 | ||
|
|
b10d398728 | ||
|
|
198df84b89 | ||
|
|
211fc22b2a | ||
|
|
4a0a5c9591 | ||
|
|
8f51afe198 | ||
|
|
40b6642ef0 | ||
|
|
0076cbaf54 | ||
|
|
14e0d57091 | ||
|
|
3e79ec9a61 | ||
|
|
42cd7d3b77 | ||
|
|
6f4c65c178 | ||
|
|
863424ffd7 | ||
|
|
c35cfbd170 | ||
|
|
22425726d1 | ||
|
|
94e8c1da68 | ||
|
|
55c305c569 | ||
|
|
ad028947e0 | ||
|
|
212e0753c8 | ||
|
|
711dc83f5c | ||
|
|
6314eeda0a | ||
|
|
cf783ea480 | ||
|
|
899fbef948 | ||
|
|
4a6d7952a7 | ||
|
|
5443226e27 | ||
|
|
89ffe99dcc | ||
|
|
ede306b88e | ||
|
|
0acc49c57d | ||
|
|
2bbeb040c2 | ||
|
|
5689c36b12 | ||
|
|
f4a367d2de | ||
|
|
afe042ecfc | ||
|
|
c108cd4780 | ||
|
|
d84146fd88 | ||
|
|
30e6316e16 | ||
|
|
6835e585b0 | ||
|
|
49d4f43652 | ||
|
|
70dd9c7724 | ||
|
|
f386e47efa | ||
|
|
ec16159497 | ||
|
|
5ba2959eb4 | ||
|
|
e9c5b00628 | ||
|
|
8e1f43eb8b | ||
|
|
3788293bc0 | ||
|
|
89a2e26f25 | ||
|
|
2c453bd491 | ||
|
|
0aebf234f9 | ||
|
|
3e25ef4b5a | ||
|
|
3dc3edb83e | ||
|
|
4ebe144191 | ||
|
|
49cd06a9b4 | ||
|
|
e0208da0ac | ||
|
|
1f41770060 | ||
|
|
3eff06281c | ||
|
|
5848dfb4f3 | ||
|
|
89f27adce5 | ||
|
|
bbab7fa672 | ||
|
|
fb149796fa | ||
|
|
3939013415 | ||
|
|
7fe18e99c3 | ||
|
|
6ddfd29be8 | ||
|
|
46efad94db | ||
|
|
b7ea073204 | ||
|
|
0d9c90ceeb | ||
|
|
3ba23e1787 | ||
|
|
e365718556 | ||
|
|
d65a49a7e7 | ||
|
|
7960aaf5d5 | ||
|
|
32b3a7d1d0 | ||
|
|
53fb976fb6 | ||
|
|
fffe71aa7e | ||
|
|
75b0a3a407 | ||
|
|
158689672a | ||
|
|
24e43dd1a2 | ||
|
|
b34366aaf7 | ||
|
|
2856c8d125 | ||
|
|
4ac1e1d798 | ||
|
|
b15d23035c | ||
|
|
8b2ea63ccb | ||
|
|
b0c65c76e6 | ||
|
|
c65c1af023 | ||
|
|
f10bd9c0d8 | ||
|
|
a6ac78294b | ||
|
|
04c9ead19d | ||
|
|
3c3798ee98 | ||
|
|
8df722ab02 | ||
|
|
dbe5ca60cd | ||
|
|
ec454dc981 | ||
|
|
0988f2145c | ||
|
|
3416c26bdc | ||
|
|
f1a50b7db3 | ||
|
|
f2fa13ba01 | ||
|
|
93fc3bf39e | ||
|
|
695180a2ef | ||
|
|
aeed138294 | ||
|
|
5d347096cf | ||
|
|
75ade97805 | ||
|
|
864d4b3cb7 | ||
|
|
c1492e3429 | ||
|
|
ae266810c2 | ||
|
|
a1d3fe5e06 | ||
|
|
98886ff074 | ||
|
|
b8b7a374a6 | ||
|
|
9d3647f38a | ||
|
|
5332eec7aa | ||
|
|
6bc933532e | ||
|
|
55053cd2b8 | ||
|
|
2e4317a80c | ||
|
|
5ef61f4621 | ||
|
|
4b47a5030a | ||
|
|
27c2dcbee4 | ||
|
|
e17ab16878 | ||
|
|
8c9fff36c8 | ||
|
|
c37ea3fbb6 | ||
|
|
6d6401d1da | ||
|
|
57f90c8b3b | ||
|
|
53883d290a | ||
|
|
ae0408a6f7 | ||
|
|
742abc9032 | ||
|
|
5652965853 | ||
|
|
da0c811846 | ||
|
|
d60dd5f281 | ||
|
|
ec70c6c613 | ||
|
|
aa40b916ab | ||
|
|
fe5242174b | ||
|
|
026cc29e1f | ||
|
|
6055baa0db | ||
|
|
469e6da29f | ||
|
|
6a7fb2d33d | ||
|
|
271ea89c8d | ||
|
|
f94d7f2b03 | ||
|
|
f084e64aed | ||
|
|
294b817957 | ||
|
|
ed85ed394a | ||
|
|
aa495312db | ||
|
|
8ac9b23de3 | ||
|
|
be7090b29c | ||
|
|
1f9b31a6cd | ||
|
|
e83d27f07c | ||
|
|
816cbd2036 | ||
|
|
ec781969fa | ||
|
|
2fc78c9219 | ||
|
|
7b2470cce6 | ||
|
|
31c3fac7f5 | ||
|
|
9d5a7b7dbd | ||
|
|
614710da69 | ||
|
|
f233066e81 | ||
|
|
32ae38ebb2 | ||
|
|
1459229dde | ||
|
|
b8cd602d7e | ||
|
|
647a05f469 | ||
|
|
7cf9885125 | ||
|
|
da72afcd5f | ||
|
|
9f0cdb7ac1 | ||
|
|
482a85565f | ||
|
|
c563d781d3 | ||
|
|
12a6da6558 | ||
|
|
54accbbeff | ||
|
|
6d0bd4a6ed | ||
|
|
73d403d2f1 | ||
|
|
b8fa581665 | ||
|
|
71f661daa4 | ||
|
|
3927867c81 | ||
|
|
60256533e3 | ||
|
|
6a0cabf249 | ||
|
|
a00c696c1a | ||
|
|
7958ef0d1f | ||
|
|
d935cee4db | ||
|
|
d565160124 | ||
|
|
b4cb27cef6 | ||
|
|
d7c211d98e | ||
|
|
c32a358f43 | ||
|
|
1f4b23b105 | ||
|
|
58df9c6edb | ||
|
|
ce4578a829 | ||
|
|
1885b3ac2e | ||
|
|
205ddc88cb | ||
|
|
9da065e1ec | ||
|
|
a40846f6ed | ||
|
|
c3ff6fadc9 | ||
|
|
601bd5d6e7 | ||
|
|
8731f2afe5 | ||
|
|
323df36a97 | ||
|
|
bcf71b583c | ||
|
|
9268407429 | ||
|
|
1ff87d27ca | ||
|
|
d6e4b7700f | ||
|
|
81a4da6199 | ||
|
|
b8efc442e3 | ||
|
|
e00bdf2f79 | ||
|
|
cea5716c48 | ||
|
|
fc150f8860 | ||
|
|
7fdf8a63c8 | ||
|
|
374f17df63 | ||
|
|
d27da8927e | ||
|
|
785afd4bda | ||
|
|
e28c226308 | ||
|
|
4170e20e21 | ||
|
|
af548aa624 | ||
|
|
13d68bbac0 | ||
|
|
fabea3c813 | ||
|
|
db80e7f7cb | ||
|
|
1ebddbd1de | ||
|
|
6c6061a123 | ||
|
|
0bf38aed9f | ||
|
|
8aedbde36a |
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://unpkg.com/@changesets/config@2.2.0/schema.json",
|
"$schema": "https://unpkg.com/@changesets/config@2.2.0/schema.json",
|
||||||
|
"access": "public",
|
||||||
|
"baseBranch": "main",
|
||||||
"changelog": "@changesets/cli/changelog",
|
"changelog": "@changesets/cli/changelog",
|
||||||
"commit": false,
|
"commit": false,
|
||||||
"fixed": [],
|
"fixed": [],
|
||||||
|
"ignore": ["@formbricks/demo", "@formbricks/web"],
|
||||||
"linked": [],
|
"linked": [],
|
||||||
"access": "public",
|
"updateInternalDependencies": "patch"
|
||||||
"baseBranch": "main",
|
|
||||||
"updateInternalDependencies": "patch",
|
|
||||||
"ignore": ["@formbricks/formbricks-com", "@formbricks/demo", "@formbricks/web"]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,29 +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
|
|
||||||
{
|
{
|
||||||
"name": "Node.js & PostgreSQL",
|
"features": {},
|
||||||
"dockerComposeFile": "docker-compose.yml",
|
"image": "mcr.microsoft.com/devcontainers/universal:2",
|
||||||
"service": "app",
|
"postAttachCommand": "pnpm go",
|
||||||
"workspaceFolder": "/workspace",
|
"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"
|
||||||
|
|
||||||
// Configure tool-specific properties.
|
|
||||||
"customizations": {
|
|
||||||
// Configure properties specific to VS Code.
|
|
||||||
"vscode": {
|
|
||||||
// Add the IDs of extensions you want installed when the container is created.
|
|
||||||
"extensions": ["dbaeumer.vscode-eslint"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 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],
|
|
||||||
|
|
||||||
// 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 && pnpm install && pnpm db:migrate:dev",
|
|
||||||
"postAttachCommand": "pnpm dev --filter=@formbricks/web... --filter=@formbricks/demo...",
|
|
||||||
|
|
||||||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
|
||||||
"remoteUser": "node"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +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: postgres:latest
|
|
||||||
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
|
||||||
96
.env.example
96
.env.example
@@ -8,8 +8,8 @@
|
|||||||
|
|
||||||
WEBAPP_URL=http://localhost:3000
|
WEBAPP_URL=http://localhost:3000
|
||||||
|
|
||||||
# Set this if you want to have a shorter link for surveys
|
# Required for next-auth. Should be the same as WEBAPP_URL
|
||||||
SHORT_URL_BASE=
|
NEXTAUTH_URL=http://localhost:3000
|
||||||
|
|
||||||
# Encryption keys
|
# Encryption keys
|
||||||
# Please set both for now, we will change this in the future
|
# Please set both for now, we will change this in the future
|
||||||
@@ -17,28 +17,23 @@ SHORT_URL_BASE=
|
|||||||
# You can use: `openssl rand -hex 32` to generate one
|
# You can use: `openssl rand -hex 32` to generate one
|
||||||
ENCRYPTION_KEY=
|
ENCRYPTION_KEY=
|
||||||
|
|
||||||
|
# @see: https://next-auth.js.org/configuration/options#nextauth_secret
|
||||||
|
# You can use: `openssl rand -hex 32` to generate a secure one
|
||||||
|
NEXTAUTH_SECRET=
|
||||||
|
|
||||||
|
# API Secret for running cron jobs. (mandatory)
|
||||||
|
# You can use: `openssl rand -hex 32` to generate a secure one
|
||||||
|
CRON_SECRET=
|
||||||
|
|
||||||
|
# Set the minimum log level(debug, info, warn, error, fatal)
|
||||||
|
LOG_LEVEL=info
|
||||||
|
|
||||||
##############
|
##############
|
||||||
# DATABASE #
|
# DATABASE #
|
||||||
##############
|
##############
|
||||||
|
|
||||||
DATABASE_URL='postgresql://postgres:postgres@localhost:5432/formbricks?schema=public'
|
DATABASE_URL='postgresql://postgres:postgres@localhost:5432/formbricks?schema=public'
|
||||||
|
|
||||||
###############
|
|
||||||
# NEXT AUTH #
|
|
||||||
###############
|
|
||||||
|
|
||||||
# @see: https://next-auth.js.org/configuration/options#nextauth_secret
|
|
||||||
# You can use: `openssl rand -hex 32` to generate a secure one
|
|
||||||
NEXTAUTH_SECRET=RANDOM_STRING
|
|
||||||
|
|
||||||
# Set this to your public-facing URL, e.g., https://example.com
|
|
||||||
# You do not need the NEXTAUTH_URL environment variable in Vercel.
|
|
||||||
NEXTAUTH_URL=http://localhost:3000
|
|
||||||
|
|
||||||
# Cron Secret
|
|
||||||
# You can use: `openssl rand -hex 32` to generate a secure one
|
|
||||||
CRON_SECRET=
|
|
||||||
|
|
||||||
################
|
################
|
||||||
# MAIL SETUP #
|
# MAIL SETUP #
|
||||||
################
|
################
|
||||||
@@ -47,6 +42,7 @@ CRON_SECRET=
|
|||||||
# 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)
|
||||||
@@ -54,6 +50,12 @@ 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).
|
||||||
|
# SMTP_REJECT_UNAUTHORIZED_TLS=0
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
# ------------------------------ OPTIONAL -----------------------------#
|
# ------------------------------ OPTIONAL -----------------------------#
|
||||||
########################################################################
|
########################################################################
|
||||||
@@ -75,6 +77,11 @@ S3_BUCKET_NAME=
|
|||||||
# Configure a third party S3 compatible storage service endpoint like StorJ leave empty if you use Amazon S3
|
# Configure a third party S3 compatible storage service endpoint like StorJ leave empty if you use Amazon S3
|
||||||
# e.g., https://gateway.storjshare.io
|
# e.g., https://gateway.storjshare.io
|
||||||
S3_ENDPOINT_URL=
|
S3_ENDPOINT_URL=
|
||||||
|
# Force path style for S3 compatible storage (0 for disabled, 1 for enabled)
|
||||||
|
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 #
|
||||||
@@ -96,6 +103,9 @@ PASSWORD_RESET_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 #
|
||||||
##########
|
##########
|
||||||
@@ -104,6 +114,11 @@ 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=
|
||||||
|
|
||||||
# Configure Github Login
|
# Configure Github Login
|
||||||
GITHUB_ID=
|
GITHUB_ID=
|
||||||
@@ -125,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=
|
||||||
|
|
||||||
@@ -137,9 +155,8 @@ STRIPE_SECRET_KEY=
|
|||||||
STRIPE_WEBHOOK_SECRET=
|
STRIPE_WEBHOOK_SECRET=
|
||||||
|
|
||||||
# Configure Formbricks usage within Formbricks
|
# Configure Formbricks usage within Formbricks
|
||||||
NEXT_PUBLIC_FORMBRICKS_API_HOST=
|
FORMBRICKS_API_HOST=
|
||||||
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=
|
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=
|
||||||
@@ -160,11 +177,11 @@ ENTERPRISE_LICENSE_KEY=
|
|||||||
# 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=
|
# DEFAULT_ORGANIZATION_ID=
|
||||||
# DEFAULT_ORGANIZATION_ROLE=admin
|
# DEFAULT_ORGANIZATION_ROLE=owner
|
||||||
|
|
||||||
# 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
|
||||||
@@ -176,7 +193,34 @@ 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)
|
||||||
# REDIS_URL:
|
# You can also add more configuration to Redis using the redis.conf file in the root directory
|
||||||
|
REDIS_URL=redis://localhost:6379
|
||||||
|
REDIS_DEFAULT_TTL=86400 # 1 day
|
||||||
|
|
||||||
# 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:
|
||||||
|
|
||||||
|
# The below is used for Rate Limiting for management API
|
||||||
|
UNKEY_ROOT_KEY=
|
||||||
|
|
||||||
|
# Disable custom cache handler if necessary (e.g. if deployed on Vercel)
|
||||||
|
# CUSTOM_CACHE_DISABLED=1
|
||||||
|
|
||||||
|
# Azure AI settings
|
||||||
|
# AI_AZURE_RESSOURCE_NAME=
|
||||||
|
# AI_AZURE_API_KEY=
|
||||||
|
# AI_AZURE_EMBEDDINGS_DEPLOYMENT_ID=
|
||||||
|
# AI_AZURE_LLM_DEPLOYMENT_ID=
|
||||||
|
|
||||||
|
# INTERCOM_APP_ID=
|
||||||
|
# INTERCOM_SECRET_KEY=
|
||||||
|
|
||||||
|
# Enable Prometheus metrics
|
||||||
|
# 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=
|
||||||
|
|||||||
69
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
69
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,8 +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"
|
||||||
title: "[BUG]"
|
type: bug
|
||||||
labels: bug
|
labels: ["bug"]
|
||||||
assignees: []
|
|
||||||
body:
|
body:
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: issue-summary
|
id: issue-summary
|
||||||
@@ -12,70 +11,22 @@ body:
|
|||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: steps-to-reproduce
|
id: issue-expected-behavior
|
||||||
attributes:
|
attributes:
|
||||||
label: Steps to Reproduce
|
label: Expected Behavior
|
||||||
value: |
|
|
||||||
1. (for example) Went to ...
|
|
||||||
2. Clicked on...
|
|
||||||
3. ...
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: expected-behavior
|
|
||||||
attributes:
|
|
||||||
label: Expected behavior
|
|
||||||
description: A clear and concise description of what you expected to happen.
|
description: A clear and concise description of what you expected to happen.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: other-information
|
id: other-information
|
||||||
attributes:
|
attributes:
|
||||||
label: Other information
|
label: Other information (incl. screenshots, Formbricks version, steps to reproduce,...)
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
- type: dropdown
|
||||||
id: screenshots
|
|
||||||
attributes:
|
|
||||||
label: Screenshots
|
|
||||||
description: If applicable, add screenshots to help explain your problem.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: checkboxes
|
|
||||||
id: environment
|
id: environment
|
||||||
attributes:
|
attributes:
|
||||||
label: Environment
|
label: Your Environment
|
||||||
options:
|
options:
|
||||||
- label: Formbricks Cloud (app.formbricks.com)
|
- Formbricks Cloud (app.formbricks.com)
|
||||||
- label: Self-hosted Formbricks
|
- Self-hosted Formbricks
|
||||||
- type: textarea
|
|
||||||
id: desktop-version
|
|
||||||
attributes:
|
|
||||||
label: Desktop (please complete the following information)
|
|
||||||
description: |
|
|
||||||
examples:
|
|
||||||
- **OS**: [e.g. iOS]
|
|
||||||
- **Browser**: [e.g. chrome, safari]
|
|
||||||
- **Version**: [e.g. 22]
|
|
||||||
value: |
|
|
||||||
- OS:
|
|
||||||
- Node:
|
|
||||||
- npm:
|
|
||||||
render: markdown
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: markdown
|
|
||||||
id: nodejs-version
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
#### Node.JS version
|
|
||||||
|
|
||||||
[e.g. v18.15.0]
|
|
||||||
- type: markdown
|
|
||||||
id: anything-else
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
#### Anything else?
|
|
||||||
|
|
||||||
- Screen recording, console logs, network requests: You can make a recording with [Loom](https://www.loom.com).
|
|
||||||
- Anything else that you think could be an issue?
|
|
||||||
|
|||||||
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,5 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Questions
|
- name: Questions
|
||||||
url: https://formbricks.com/discord
|
url: https://github.com/formbricks/formbricks/discussions
|
||||||
about: Ask a general question about the project on our Discord server
|
about: Need help selfhosting or ask a general question about the project? Open a discussion
|
||||||
|
|||||||
23
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
23
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,8 +1,6 @@
|
|||||||
name: Feature request
|
name: Feature request
|
||||||
description: "Suggest an idea for this project \U0001F680"
|
description: "Suggest an idea for this project \U0001F680"
|
||||||
title: "[FEATURE]"
|
type: feature
|
||||||
labels: enhancement
|
|
||||||
assignees: []
|
|
||||||
body:
|
body:
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: problem-description
|
id: problem-description
|
||||||
@@ -18,13 +16,6 @@ body:
|
|||||||
description: A clear and concise description of what you want to happen.
|
description: A clear and concise description of what you want to happen.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
|
||||||
id: alternate-solution-description
|
|
||||||
attributes:
|
|
||||||
label: Describe alternatives you've considered
|
|
||||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: additional-context
|
id: additional-context
|
||||||
attributes:
|
attributes:
|
||||||
@@ -33,15 +24,9 @@ body:
|
|||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: markdown
|
- type: markdown
|
||||||
id: formbricks-info
|
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
### How we code at Formbricks 🤓
|
### Additional resources 🤓
|
||||||
|
|
||||||
- Follow Best Practices lined out in our [Contributor Docs](https://formbricks.com/docs/contributing/how-we-code)
|
- Check out our [Contributor Docs](https://formbricks.com/docs/developer-docs/contributing/get-started)
|
||||||
- First time: Please read our [introductory blog post](https://formbricks.com/blog/join-the-formtribe)
|
- Anything unclear? [Ask in Github Discussions](https://github.com/formbricks/formbricks/discussions)
|
||||||
- All UI components are in the package `formbricks/ui`
|
|
||||||
- Run `pnpm go` to find a demo app to test in-app surveys at `localhost:3002`
|
|
||||||
- Everything is type-safe.
|
|
||||||
- We use **chatGPT** to help refactor code.
|
|
||||||
- Anything unclear? [Ask in Discord](https://formbricks.com/discord)
|
|
||||||
|
|||||||
11
.github/ISSUE_TEMPLATE/task.yml
vendored
Normal file
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
|
||||||
40
.github/PULL_REQUEST_TEMPLATE.md
vendored
40
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,40 +0,0 @@
|
|||||||
<!-- We require pull request titles to follow the Conventional Commits specification ( https://www.conventionalcommits.org/en/v1.0.0/#summary ). Please make sure your title follow these conventions -->
|
|
||||||
|
|
||||||
## What does this PR do?
|
|
||||||
|
|
||||||
<!-- Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. -->
|
|
||||||
|
|
||||||
Fixes # (issue)
|
|
||||||
|
|
||||||
<!-- Please provide a screenshots or a loom video for visual changes to speed up reviews
|
|
||||||
Loom Video: https://www.loom.com/
|
|
||||||
-->
|
|
||||||
|
|
||||||
## How should this be tested?
|
|
||||||
|
|
||||||
<!-- Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration -->
|
|
||||||
|
|
||||||
- Test A
|
|
||||||
- Test B
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
<!-- We're starting to get more and more contributions. Please help us making this efficient for all of us and go through this checklist. Please tick off what you did -->
|
|
||||||
|
|
||||||
### Required
|
|
||||||
|
|
||||||
- [ ] Filled out the "How to test" section in this PR
|
|
||||||
- [ ] Read [How we Code at Formbricks](<[https://github.com/formbricks/formbricks/blob/main/CONTRIBUTING.md](https://formbricks.com/docs/contributing/how-we-code)>)
|
|
||||||
- [ ] Self-reviewed my own code
|
|
||||||
- [ ] Commented on my code in hard-to-understand bits
|
|
||||||
- [ ] Ran `pnpm build`
|
|
||||||
- [ ] Checked for warnings, there are none
|
|
||||||
- [ ] Removed all `console.logs`
|
|
||||||
- [ ] Merged the latest changes from main onto my branch with `git pull origin main`
|
|
||||||
- [ ] My changes don't cause any responsiveness issues
|
|
||||||
- [ ] First PR at Formbricks? [Please sign the CLA!](https://cla-assistant.io/formbricks/formbricks) Without it we wont be able to merge it 🙏
|
|
||||||
|
|
||||||
### Appreciated
|
|
||||||
|
|
||||||
- [ ] If a UI change was made: Added a screen recording or screenshots to this PR
|
|
||||||
- [ ] Updated the Formbricks Docs if changes were necessary
|
|
||||||
21
.github/actions/cache-build-web/action.yml
vendored
21
.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:
|
||||||
@@ -30,6 +38,10 @@ runs:
|
|||||||
**/dist/**
|
**/dist/**
|
||||||
key: ${{ runner.os }}-${{ env.cache-name }}-${{ env.key-1 }}-${{ env.key-2 }}
|
key: ${{ runner.os }}-${{ env.cache-name }}-${{ env.key-1 }}-${{ env.key-2 }}
|
||||||
|
|
||||||
|
- name: Set Cache Hit Status
|
||||||
|
run: echo "cache-hit=${{ steps.cache-build.outputs.cache-hit }}" >> "$GITHUB_OUTPUT"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Setup Node.js 20.x
|
- name: Setup Node.js 20.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
@@ -37,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@v2
|
uses: pnpm/action-setup@v4
|
||||||
if: steps.cache-build.outputs.cache-hit != 'true'
|
if: steps.cache-build.outputs.cache-hit != 'true'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
@@ -49,16 +61,17 @@ runs:
|
|||||||
run: cp .env.example .env
|
run: cp .env.example .env
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Fill ENCRYPTION_KE, ENTERPRISE_LICENSE_KEY and E2E_TESTING in .env
|
- name: Fill ENCRYPTION_KEY, ENTERPRISE_LICENSE_KEY and E2E_TESTING in .env
|
||||||
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/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 }}
|
||||||
|
|||||||
84
.github/dependabot.yml
vendored
Normal file
84
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "npm" # For pnpm monorepos, use npm ecosystem
|
||||||
|
directory: "/" # Root package.json
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
versioning-strategy: increase
|
||||||
|
|
||||||
|
# Apps directory packages
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/apps/demo"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/apps/demo-react-native"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/apps/storybook"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/apps/web"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|
||||||
|
# Packages directory
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/packages/database"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/packages/lib"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/packages/types"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/packages/config-eslint"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/packages/config-prettier"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/packages/config-typescript"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/packages/js-core"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/packages/surveys"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/packages/logger"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
40
.github/pull_request_template.md
vendored
Normal file
40
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<!-- We require pull request titles to follow the Conventional Commits specification ( https://www.conventionalcommits.org/en/v1.0.0/#summary ). Please make sure your title follow these conventions -->
|
||||||
|
|
||||||
|
## What does this PR do?
|
||||||
|
|
||||||
|
<!-- Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. -->
|
||||||
|
|
||||||
|
Fixes #(issue)
|
||||||
|
|
||||||
|
<!-- Please provide a screenshots or a loom video for visual changes to speed up reviews
|
||||||
|
Loom Video: https://www.loom.com/
|
||||||
|
-->
|
||||||
|
|
||||||
|
## How should this be tested?
|
||||||
|
|
||||||
|
<!-- Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration -->
|
||||||
|
|
||||||
|
- Test A
|
||||||
|
- Test B
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
<!-- We're starting to get more and more contributions. Please help us making this efficient for all of us and go through this checklist. Please tick off what you did -->
|
||||||
|
|
||||||
|
### Required
|
||||||
|
|
||||||
|
- [ ] Filled out the "How to test" section in this PR
|
||||||
|
- [ ] Read [How we Code at Formbricks](<[https://github.com/formbricks/formbricks/blob/main/CONTRIBUTING.md](https://formbricks.com/docs/contributing/how-we-code)>)
|
||||||
|
- [ ] Self-reviewed my own code
|
||||||
|
- [ ] Commented on my code in hard-to-understand bits
|
||||||
|
- [ ] Ran `pnpm build`
|
||||||
|
- [ ] Checked for warnings, there are none
|
||||||
|
- [ ] Removed all `console.logs`
|
||||||
|
- [ ] Merged the latest changes from main onto my branch with `git pull origin main`
|
||||||
|
- [ ] My changes don't cause any responsiveness issues
|
||||||
|
- [ ] First PR at Formbricks? [Please sign the CLA!](https://cla-assistant.io/formbricks/formbricks) Without it we wont be able to merge it 🙏
|
||||||
|
|
||||||
|
### Appreciated
|
||||||
|
|
||||||
|
- [ ] If a UI change was made: Added a screen recording or screenshots to this PR
|
||||||
|
- [ ] Updated the Formbricks Docs if changes were necessary
|
||||||
10
.github/workflows/apply-issue-labels-to-pr.yml
vendored
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@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.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@211cb3fefb35a799baa5156f9321bb774fe56294 # v5.2.0
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
script: |
|
script: |
|
||||||
|
|||||||
39
.github/workflows/build-web.yml
vendored
39
.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,27 +12,18 @@ 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@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||||
- uses: ./.github/actions/dangerous-git-checkout
|
- uses: ./.github/actions/dangerous-git-checkout
|
||||||
|
|
||||||
- name: Setup Node.js 20.x
|
- name: Build & Cache Web Binaries
|
||||||
uses: actions/setup-node@v3
|
uses: ./.github/actions/cache-build-web
|
||||||
|
id: cache-build-web
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
e2e_testing_mode: "0"
|
||||||
|
turbo_token: ${{ secrets.TURBO_TOKEN }}
|
||||||
- name: Install pnpm
|
turbo_team: ${{ vars.TURBO_TEAM }}
|
||||||
uses: pnpm/action-setup@v2
|
|
||||||
|
|
||||||
- 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
|
|
||||||
run: |
|
|
||||||
SECRET=$(openssl rand -hex 32)
|
|
||||||
echo "ENCRYPTION_KEY=$SECRET" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Build Formbricks-web
|
|
||||||
run: pnpm build --filter=@formbricks/web...
|
|
||||||
|
|||||||
35
.github/workflows/chromatic.yml
vendored
Normal file
35
.github/workflows/chromatic.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
name: "Chromatic"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
chromatic:
|
||||||
|
name: Run Chromatic
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||||
|
- name: Run Chromatic
|
||||||
|
uses: chromaui/action@c93e0bc3a63aa176e14a75b61a31847cbfdd341c # latest
|
||||||
|
with:
|
||||||
|
# ⚠️ Make sure to configure a `CHROMATIC_PROJECT_TOKEN` repository secret
|
||||||
|
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||||
|
workingDir: apps/storybook
|
||||||
24
.github/workflows/cron-surveyStatusUpdate.yml
vendored
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
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
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@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: 'Checkout Repository'
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
- name: 'Dependency Review'
|
||||||
|
uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0
|
||||||
64
.github/workflows/deploy-formbricks-cloud.yml
vendored
Normal file
64
.github/workflows/deploy-formbricks-cloud.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
name: Formbricks Cloud Deployment
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
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'
|
||||||
|
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'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
helmfile-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Configure AWS Credentials
|
||||||
|
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
|
||||||
|
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
|
||||||
|
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:
|
||||||
|
helm-plugins: >
|
||||||
|
https://github.com/databus23/helm-diff,
|
||||||
|
https://github.com/jkroepke/helm-secrets
|
||||||
|
helmfile-args: apply
|
||||||
|
helmfile-auto-init: "false"
|
||||||
|
helmfile-workdirectory: infra/formbricks-cloud-helm
|
||||||
|
|
||||||
167
.github/workflows/docker-build-validation.yml
vendored
Normal file
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@v3
|
||||||
|
|
||||||
|
- 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!"
|
||||||
152
.github/workflows/e2e.yml
vendored
152
.github/workflows/e2e.yml
vendored
@@ -1,77 +1,155 @@
|
|||||||
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
|
||||||
|
# Add other secrets if necessary
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
TELEMETRY_DISABLED: 1
|
||||||
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
|
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: read
|
||||||
|
actions: read
|
||||||
|
checks: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Run E2E Tests
|
name: Run E2E Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: pgvector/pgvector:pg17
|
||||||
|
env:
|
||||||
|
POSTGRES_DB: postgres
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
options: >-
|
||||||
|
--health-cmd="pg_isready -U testuser"
|
||||||
|
--health-interval=10s
|
||||||
|
--health-timeout=5s
|
||||||
|
--health-retries=5
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||||
- uses: ./.github/actions/dangerous-git-checkout
|
- uses: ./.github/actions/dangerous-git-checkout
|
||||||
|
|
||||||
- name: Build & Cache Web Binaries
|
- name: Setup Node.js 20.x
|
||||||
uses: ./.github/actions/cache-build-web
|
uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2
|
||||||
with:
|
with:
|
||||||
e2e_testing_mode: "1"
|
node-version: 20.x
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v2
|
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Start PostgreSQL
|
- name: create .env
|
||||||
|
run: cp .env.example .env
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Fill ENCRYPTION_KEY, ENTERPRISE_LICENSE_KEY and E2E_TESTING in .env
|
||||||
run: |
|
run: |
|
||||||
cd packages/database && pnpm db:up &
|
RANDOM_KEY=$(openssl rand -hex 32)
|
||||||
for attempt in {1..20}; do
|
sed -i "s/ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${RANDOM_KEY}/" .env
|
||||||
if nc -zv localhost 5432; then
|
sed -i "s/CRON_SECRET=.*/CRON_SECRET=${RANDOM_KEY}/" .env
|
||||||
echo "Ready"
|
sed -i "s/NEXTAUTH_SECRET=.*/NEXTAUTH_SECRET=${RANDOM_KEY}/" .env
|
||||||
break
|
sed -i "s/ENTERPRISE_LICENSE_KEY=.*/ENTERPRISE_LICENSE_KEY=${RANDOM_KEY}/" .env
|
||||||
fi
|
echo "" >> .env
|
||||||
echo "Waiting..."
|
echo "E2E_TESTING=1" >> .env
|
||||||
sleep 5
|
shell: bash
|
||||||
done
|
|
||||||
|
- name: Build App
|
||||||
|
run: |
|
||||||
|
pnpm build --filter=@formbricks/web...
|
||||||
|
|
||||||
|
- name: Apply Prisma Migrations
|
||||||
|
run: |
|
||||||
|
# pnpm prisma migrate deploy
|
||||||
pnpm db:migrate:dev
|
pnpm db:migrate:dev
|
||||||
- name: Serve packages for lazy loading
|
|
||||||
run: |
|
|
||||||
cd packages/surveys && pnpm serve &
|
|
||||||
|
|
||||||
- name: Run App
|
- name: Run App
|
||||||
run: |
|
run: |
|
||||||
NODE_ENV=test pnpm start --filter=@formbricks/web &
|
NODE_ENV=test pnpm start --filter=@formbricks/web | tee app.log 2>&1 &
|
||||||
for attempt in {1..20}; do
|
sleep 10 # Optional: gives some buffer for the app to start
|
||||||
|
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
|
||||||
echo "Ready"
|
echo "Application is ready."
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
echo "Waiting..."
|
if [ $attempt -eq 10 ]; then
|
||||||
|
echo "Application failed to start in time."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Still waiting for the application to be ready..."
|
||||||
sleep 10
|
sleep 10
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Test Serve endpoints
|
|
||||||
run: |
|
|
||||||
curl -s http://localhost:3003
|
|
||||||
|
|
||||||
- name: Cache Playwright
|
|
||||||
uses: actions/cache@v3
|
|
||||||
id: playwright-cache
|
|
||||||
with:
|
|
||||||
path: ~/.cache/ms-playwright
|
|
||||||
key: ${{ runner.os }}-playwright-${{ hashFiles('pnpm-lock.yaml') }}
|
|
||||||
|
|
||||||
- name: Install Playwright
|
- name: Install Playwright
|
||||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
|
||||||
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
|
||||||
|
|||||||
33
.github/workflows/formbricks-release.yml
vendored
Normal file
33
.github/workflows/formbricks-release.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
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: ${{ needs.docker-build.outputs.VERSION }}
|
||||||
130
.github/workflows/kamal-deploy.yml
vendored
130
.github/workflows/kamal-deploy.yml
vendored
@@ -1,130 +0,0 @@
|
|||||||
name: Kamal Deploy
|
|
||||||
concurrency:
|
|
||||||
group: deploy-to-kamal
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
#push:
|
|
||||||
# branches:
|
|
||||||
# - main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
Deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
environment: production
|
|
||||||
env:
|
|
||||||
DOCKER_BUILDKIT: 1
|
|
||||||
IS_FORMBRICKS_CLOUD: ${{ vars.IS_FORMBRICKS_CLOUD }}
|
|
||||||
WEBAPP_URL: ${{ vars.WEBAPP_URL }}
|
|
||||||
MIGRATE_DATABASE_URL: ${{ secrets.MIGRATE_DATABASE_URL }}
|
|
||||||
NEXTAUTH_URL: ${{ vars.NEXTAUTH_URL }}
|
|
||||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
|
||||||
NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }}
|
|
||||||
ENCRYPTION_KEY: ${{ secrets.ENCRYPTION_KEY }}
|
|
||||||
SHORT_URL_BASE: ${{ vars.SHORT_URL_BASE }}
|
|
||||||
MAIL_FROM: ${{ secrets.MAIL_FROM }}
|
|
||||||
SMTP_HOST: ${{ secrets.SMTP_HOST }}
|
|
||||||
SMTP_PORT: ${{ secrets.SMTP_PORT }}
|
|
||||||
SMTP_USER: ${{ secrets.SMTP_USER }}
|
|
||||||
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
|
|
||||||
PRIVACY_URL: ${{ vars.PRIVACY_URL }}
|
|
||||||
TERMS_URL: ${{ vars.TERMS_URL }}
|
|
||||||
IMPRINT_URL: ${{ vars.IMPRINT_URL }}
|
|
||||||
GITHUB_ID: ${{ secrets.FB_GITHUB_ID }}
|
|
||||||
GITHUB_SECRET: ${{ secrets.FB_GITHUB_SECRET }}
|
|
||||||
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
|
|
||||||
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
|
|
||||||
AZUREAD_CLIENT_ID: ${{ secrets.AZUREAD_CLIENT_ID }}
|
|
||||||
AZUREAD_CLIENT_SECRET: ${{ secrets.AZUREAD_CLIENT_SECRET }}
|
|
||||||
AZUREAD_TENANT_ID: ${{ secrets.AZUREAD_TENANT_ID }}
|
|
||||||
OIDC_CLIENT_ID: ${{ secrets.OIDC_CLIENT_ID }}
|
|
||||||
OIDC_CLIENT_SECRET: ${{ secrets.OIDC_CLIENT_SECRET }}
|
|
||||||
OIDC_ISSUER: ${{ secrets.OIDC_ISSUER }}
|
|
||||||
OIDC_DISPLAY_NAME: ${{ secrets.OIDC_DISPLAY_NAME }}
|
|
||||||
OIDC_SIGNING_ALGORITHM: ${{ secrets.OIDC_SIGNING_ALGORITHM }}
|
|
||||||
CRON_SECRET: ${{ secrets.CRON_SECRET }}
|
|
||||||
ASSET_PREFIX_URL: ${{ vars.ASSET_PREFIX_URL }}
|
|
||||||
NOTION_OAUTH_CLIENT_ID: ${{ secrets.NOTION_OAUTH_CLIENT_ID }}
|
|
||||||
NOTION_OAUTH_CLIENT_SECRET: ${{ secrets.NOTION_OAUTH_CLIENT_SECRET }}
|
|
||||||
SLACK_CLIENT_ID: ${{ secrets.SLACK_CLIENT_ID }}
|
|
||||||
SLACK_CLIENT_SECRET: ${{ secrets.SLACK_CLIENT_SECRET }}
|
|
||||||
STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}
|
|
||||||
STRIPE_WEBHOOK_SECRET: ${{ secrets.STRIPE_WEBHOOK_SECRET }}
|
|
||||||
GOOGLE_SHEETS_CLIENT_ID: ${{ secrets.GOOGLE_SHEETS_CLIENT_ID }}
|
|
||||||
GOOGLE_SHEETS_CLIENT_SECRET: ${{ secrets.GOOGLE_SHEETS_CLIENT_SECRET }}
|
|
||||||
GOOGLE_SHEETS_REDIRECT_URL: ${{ secrets.GOOGLE_SHEETS_REDIRECT_URL }}
|
|
||||||
AIRTABLE_CLIENT_ID: ${{ secrets.AIRTABLE_CLIENT_ID }}
|
|
||||||
ENTERPRISE_LICENSE_KEY: ${{ secrets.ENTERPRISE_LICENSE_KEY }}
|
|
||||||
DEFAULT_ORGANIZATION_ID: ${{ vars.DEFAULT_ORGANIZATION_ID }}
|
|
||||||
CUSTOMER_IO_API_KEY: ${{ secrets.CUSTOMER_IO_API_KEY }}
|
|
||||||
CUSTOMER_IO_SITE_ID: ${{ secrets.CUSTOMER_IO_SITE_ID }}
|
|
||||||
NEXT_PUBLIC_POSTHOG_API_KEY: ${{ vars.NEXT_PUBLIC_POSTHOG_API_KEY }}
|
|
||||||
NEXT_PUBLIC_POSTHOG_API_HOST: ${{ vars.NEXT_PUBLIC_POSTHOG_API_HOST }}
|
|
||||||
NEXT_PUBLIC_FORMBRICKS_API_HOST: ${{ vars.NEXT_PUBLIC_FORMBRICKS_API_HOST }}
|
|
||||||
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID: ${{ vars.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID }}
|
|
||||||
NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID: ${{ vars.NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID }}
|
|
||||||
NEXT_PUBLIC_SENTRY_DSN: ${{ vars.NEXT_PUBLIC_SENTRY_DSN }}
|
|
||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
|
||||||
NODE_ENV: production
|
|
||||||
CLOUDFLARE_EMAIL: ${{ secrets.CLOUDFLARE_EMAIL }}
|
|
||||||
CLOUDFLARE_DNS_API_TOKEN: ${{ secrets.CLOUDFLARE_DNS_API_TOKEN }}
|
|
||||||
S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }}
|
|
||||||
S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }}
|
|
||||||
S3_REGION: ${{ vars.S3_REGION }}
|
|
||||||
S3_BUCKET_NAME: ${{ vars.S3_BUCKET_NAME }}
|
|
||||||
OPENTELEMETRY_LISTENER_URL: ${{ vars.OPENTELEMETRY_LISTENER_URL }}
|
|
||||||
RATE_LIMITING_DISABLED: ${{ vars.RATE_LIMITING_DISABLED }}
|
|
||||||
KAMAL_REGISTRY_PASSWORD: ${{ secrets.KAMAL_REGISTRY_PASSWORD }}
|
|
||||||
DB_HOST: ${{ secrets.DB_HOST }}
|
|
||||||
DB_USER: ${{ secrets.DB_USER }}
|
|
||||||
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
|
|
||||||
DB_NAME: ${{ secrets.DB_NAME }}
|
|
||||||
REDIS_URL: ${{ secrets.REDIS_URL }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Ruby
|
|
||||||
uses: ruby/setup-ruby@v1
|
|
||||||
with:
|
|
||||||
ruby-version: 3.3.0
|
|
||||||
bundler-cache: true
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
gem install kamal
|
|
||||||
|
|
||||||
- uses: webfactory/ssh-agent@v0.9.0
|
|
||||||
with:
|
|
||||||
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
id: buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
|
|
||||||
- name: Create builder
|
|
||||||
run: docker buildx create --use --name formbricks-gh-actions-builder
|
|
||||||
if: steps.buildx.outputs.should_create_builder == 'true'
|
|
||||||
|
|
||||||
- name: Push env variables to Kamal
|
|
||||||
run: |
|
|
||||||
kamal() { command kamal "$@" -c kamal/deploy.yml; }
|
|
||||||
kamal env push
|
|
||||||
|
|
||||||
- name: Run deploy command
|
|
||||||
run: |
|
|
||||||
kamal() { command kamal "$@" -c kamal/deploy.yml; }
|
|
||||||
set +e
|
|
||||||
DEPLOY_OUTPUT=$(kamal deploy 2>&1)
|
|
||||||
DEPLOY_EXIT_CODE=$?
|
|
||||||
echo "$DEPLOY_OUTPUT"
|
|
||||||
if [[ "$DEPLOY_OUTPUT" == *"container not unhealthy (healthy)"* ]]; then
|
|
||||||
echo "Deployment reported healthy container. Considering as success."
|
|
||||||
kamal lock release
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
exit $DEPLOY_EXIT_CODE
|
|
||||||
fi
|
|
||||||
shell: bash
|
|
||||||
127
.github/workflows/kamal-setup.yml
vendored
127
.github/workflows/kamal-setup.yml
vendored
@@ -1,127 +0,0 @@
|
|||||||
name: Kamal Setup
|
|
||||||
concurrency:
|
|
||||||
group: setup-kamal
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch: # Only to be triggered when accessories are updated
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
Setup:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
environment: production
|
|
||||||
env:
|
|
||||||
DOCKER_BUILDKIT: 1
|
|
||||||
IS_FORMBRICKS_CLOUD: ${{ vars.IS_FORMBRICKS_CLOUD }}
|
|
||||||
WEBAPP_URL: ${{ vars.WEBAPP_URL }}
|
|
||||||
NEXTAUTH_URL: ${{ vars.NEXTAUTH_URL }}
|
|
||||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
|
||||||
MIGRATE_DATABASE_URL: ${{ secrets.MIGRATE_DATABASE_URL }}
|
|
||||||
NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }}
|
|
||||||
ENCRYPTION_KEY: ${{ secrets.ENCRYPTION_KEY }}
|
|
||||||
SHORT_URL_BASE: ${{ vars.SHORT_URL_BASE }}
|
|
||||||
MAIL_FROM: ${{ secrets.MAIL_FROM }}
|
|
||||||
SMTP_HOST: ${{ secrets.SMTP_HOST }}
|
|
||||||
SMTP_PORT: ${{ secrets.SMTP_PORT }}
|
|
||||||
SMTP_USER: ${{ secrets.SMTP_USER }}
|
|
||||||
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
|
|
||||||
PRIVACY_URL: ${{ vars.PRIVACY_URL }}
|
|
||||||
TERMS_URL: ${{ vars.TERMS_URL }}
|
|
||||||
IMPRINT_URL: ${{ vars.IMPRINT_URL }}
|
|
||||||
GITHUB_ID: ${{ secrets.FB_GITHUB_ID }}
|
|
||||||
GITHUB_SECRET: ${{ secrets.FB_GITHUB_SECRET }}
|
|
||||||
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
|
|
||||||
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
|
|
||||||
AZUREAD_CLIENT_ID: ${{ secrets.AZUREAD_CLIENT_ID }}
|
|
||||||
AZUREAD_CLIENT_SECRET: ${{ secrets.AZUREAD_CLIENT_SECRET }}
|
|
||||||
AZUREAD_TENANT_ID: ${{ secrets.AZUREAD_TENANT_ID }}
|
|
||||||
OIDC_CLIENT_ID: ${{ secrets.OIDC_CLIENT_ID }}
|
|
||||||
OIDC_CLIENT_SECRET: ${{ secrets.OIDC_CLIENT_SECRET }}
|
|
||||||
OIDC_ISSUER: ${{ secrets.OIDC_ISSUER }}
|
|
||||||
OIDC_DISPLAY_NAME: ${{ secrets.OIDC_DISPLAY_NAME }}
|
|
||||||
OIDC_SIGNING_ALGORITHM: ${{ secrets.OIDC_SIGNING_ALGORITHM }}
|
|
||||||
CRON_SECRET: ${{ secrets.CRON_SECRET }}
|
|
||||||
ASSET_PREFIX_URL: ${{ vars.ASSET_PREFIX_URL }}
|
|
||||||
NOTION_OAUTH_CLIENT_ID: ${{ secrets.NOTION_OAUTH_CLIENT_ID }}
|
|
||||||
NOTION_OAUTH_CLIENT_SECRET: ${{ secrets.NOTION_OAUTH_CLIENT_SECRET }}
|
|
||||||
SLACK_CLIENT_ID: ${{ secrets.SLACK_CLIENT_ID }}
|
|
||||||
SLACK_CLIENT_SECRET: ${{ secrets.SLACK_CLIENT_SECRET }}
|
|
||||||
STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}
|
|
||||||
STRIPE_WEBHOOK_SECRET: ${{ secrets.STRIPE_WEBHOOK_SECRET }}
|
|
||||||
GOOGLE_SHEETS_CLIENT_ID: ${{ secrets.GOOGLE_SHEETS_CLIENT_ID }}
|
|
||||||
GOOGLE_SHEETS_CLIENT_SECRET: ${{ secrets.GOOGLE_SHEETS_CLIENT_SECRET }}
|
|
||||||
GOOGLE_SHEETS_REDIRECT_URL: ${{ secrets.GOOGLE_SHEETS_REDIRECT_URL }}
|
|
||||||
AIRTABLE_CLIENT_ID: ${{ secrets.AIRTABLE_CLIENT_ID }}
|
|
||||||
ENTERPRISE_LICENSE_KEY: ${{ secrets.ENTERPRISE_LICENSE_KEY }}
|
|
||||||
DEFAULT_ORGANIZATION_ID: ${{ vars.DEFAULT_ORGANIZATION_ID }}
|
|
||||||
CUSTOMER_IO_API_KEY: ${{ secrets.CUSTOMER_IO_API_KEY }}
|
|
||||||
CUSTOMER_IO_SITE_ID: ${{ secrets.CUSTOMER_IO_SITE_ID }}
|
|
||||||
NEXT_PUBLIC_POSTHOG_API_KEY: ${{ vars.NEXT_PUBLIC_POSTHOG_API_KEY }}
|
|
||||||
NEXT_PUBLIC_POSTHOG_API_HOST: ${{ vars.NEXT_PUBLIC_POSTHOG_API_HOST }}
|
|
||||||
NEXT_PUBLIC_FORMBRICKS_API_HOST: ${{ vars.NEXT_PUBLIC_FORMBRICKS_API_HOST }}
|
|
||||||
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID: ${{ vars.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID }}
|
|
||||||
NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID: ${{ vars.NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID }}
|
|
||||||
NEXT_PUBLIC_SENTRY_DSN: ${{ vars.NEXT_PUBLIC_SENTRY_DSN }}
|
|
||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
|
||||||
NODE_ENV: production
|
|
||||||
CLOUDFLARE_EMAIL: ${{ secrets.CLOUDFLARE_EMAIL }}
|
|
||||||
CLOUDFLARE_DNS_API_TOKEN: ${{ secrets.CLOUDFLARE_DNS_API_TOKEN }}
|
|
||||||
S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }}
|
|
||||||
S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }}
|
|
||||||
S3_REGION: ${{ vars.S3_REGION }}
|
|
||||||
S3_BUCKET_NAME: ${{ vars.S3_BUCKET_NAME }}
|
|
||||||
OPENTELEMETRY_LISTENER_URL: ${{ vars.OPENTELEMETRY_LISTENER_URL }}
|
|
||||||
RATE_LIMITING_DISABLED: ${{ vars.RATE_LIMITING_DISABLED }}
|
|
||||||
KAMAL_REGISTRY_PASSWORD: ${{ secrets.KAMAL_REGISTRY_PASSWORD }}
|
|
||||||
DB_HOST: ${{ secrets.DB_HOST }}
|
|
||||||
DB_USER: ${{ secrets.DB_USER }}
|
|
||||||
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
|
|
||||||
DB_NAME: ${{ secrets.DB_NAME }}
|
|
||||||
REDIS_URL: ${{ secrets.REDIS_URL }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Ruby
|
|
||||||
uses: ruby/setup-ruby@v1
|
|
||||||
with:
|
|
||||||
ruby-version: 3.3.0
|
|
||||||
bundler-cache: true
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
gem install kamal
|
|
||||||
|
|
||||||
- uses: webfactory/ssh-agent@v0.9.0
|
|
||||||
with:
|
|
||||||
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
id: buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
|
|
||||||
- name: Create builder
|
|
||||||
run: docker buildx create --use --name formbricks-gh-actions-builder
|
|
||||||
if: steps.buildx.outputs.should_create_builder == 'true'
|
|
||||||
|
|
||||||
- name: Push env variables to Kamal
|
|
||||||
run: |
|
|
||||||
kamal() { command kamal "$@" -c kamal/deploy.yml; }
|
|
||||||
kamal env push
|
|
||||||
|
|
||||||
- name: Run setup command
|
|
||||||
run: |
|
|
||||||
kamal() { command kamal "$@" -c kamal/deploy.yml; }
|
|
||||||
set +e
|
|
||||||
DEPLOY_OUTPUT=$(kamal setup 2>&1)
|
|
||||||
DEPLOY_EXIT_CODE=$?
|
|
||||||
echo "$DEPLOY_OUTPUT"
|
|
||||||
if [[ "$DEPLOY_OUTPUT" == *"container not unhealthy (healthy)"* ]]; then
|
|
||||||
echo "Deployment reported healthy container. Considering as success."
|
|
||||||
kamal lock release
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
exit $DEPLOY_EXIT_CODE
|
|
||||||
fi
|
|
||||||
shell: bash
|
|
||||||
10
.github/workflows/labeler.yml
vendored
10
.github/workflows/labeler.yml
vendored
@@ -4,6 +4,9 @@ on:
|
|||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
labeler:
|
labeler:
|
||||||
name: Pull Request Labeler
|
name: Pull Request Labeler
|
||||||
@@ -12,7 +15,12 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/labeler@v4
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/labeler@ac9175f8a1f3625fd0d4fb234536d26811351594 # v4.3.0
|
||||||
with:
|
with:
|
||||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
# https://github.com/actions/labeler/issues/442#issuecomment-1297359481
|
# https://github.com/actions/labeler/issues/442#issuecomment-1297359481
|
||||||
|
|||||||
27
.github/workflows/lint.yml
vendored
27
.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@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.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 18.x
|
- name: Setup Node.js 20.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
|
||||||
with:
|
with:
|
||||||
node-version: 18.x
|
node-version: 20.x
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v2
|
uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||||
@@ -25,10 +34,12 @@ jobs:
|
|||||||
- name: create .env
|
- name: create .env
|
||||||
run: cp .env.example .env
|
run: cp .env.example .env
|
||||||
|
|
||||||
- name: Generate Random ENCRYPTION_KEY and fill in .env
|
- name: Generate Random ENCRYPTION_KEY, CRON_SECRET & NEXTAUTH_SECRET and fill in .env
|
||||||
run: |
|
run: |
|
||||||
ENCRYPTION_KEY=$(openssl rand -hex 32)
|
RANDOM_KEY=$(openssl rand -hex 32)
|
||||||
sed -i "s/ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${ENCRYPTION_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
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: pnpm lint
|
run: pnpm lint
|
||||||
|
|||||||
41
.github/workflows/pr.yml
vendored
41
.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,48 +20,23 @@ 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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
@@ -62,7 +45,15 @@ jobs:
|
|||||||
needs: [lint, test, build, e2e-test]
|
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@4d991eb9b905ef189e4c376166672c3f2f230481
|
||||||
|
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
|
||||||
|
|||||||
18
.github/workflows/release-changesets.yml
vendored
18
.github/workflows/release-changesets.yml
vendored
@@ -6,6 +6,11 @@ on:
|
|||||||
# branches:
|
# branches:
|
||||||
# - main
|
# - main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
packages: write
|
||||||
|
|
||||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@@ -21,23 +26,28 @@ jobs:
|
|||||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||||
steps:
|
steps:
|
||||||
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Checkout Repo
|
- name: Checkout Repo
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0
|
||||||
|
|
||||||
- name: Setup Node.js 18.x
|
- name: Setup Node.js 18.x
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@7c12f8017d5436eb855f1ed4399f037a36fbd9e8 # v2.5.2
|
||||||
with:
|
with:
|
||||||
node-version: 18.x
|
node-version: 18.x
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v2.2.4
|
uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd # v2.2.4
|
||||||
|
|
||||||
- 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: Create Release Pull Request or Publish to npm
|
- name: Create Release Pull Request or Publish to npm
|
||||||
id: changesets
|
id: changesets
|
||||||
uses: changesets/action@v1
|
uses: changesets/action@c8bada60c408975afd1a20b3db81d6eee6789308 # v1.4.9
|
||||||
with:
|
with:
|
||||||
# This expects you to have a script called release which does a build for your packages and calls changeset publish
|
# This expects you to have a script called release which does a build for your packages and calls changeset publish
|
||||||
publish: pnpm release
|
publish: pnpm release
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
name: Docker for Data Migrations
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v*"
|
|
||||||
|
|
||||||
env:
|
|
||||||
# Use docker.io for Docker Hub if empty
|
|
||||||
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
|
|
||||||
# This is used to complete the identity challenge
|
|
||||||
# with sigstore/fulcio when running outside of PRs.
|
|
||||||
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 }}
|
|
||||||
|
|
||||||
- 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 }}
|
|
||||||
@@ -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@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||||
|
|
||||||
- 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@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0
|
||||||
|
|
||||||
# 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@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.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
|
||||||
|
|||||||
51
.github/workflows/release-docker-github.yml
vendored
51
.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,9 @@ 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"
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -30,24 +33,45 @@ jobs:
|
|||||||
# with sigstore/fulcio when running outside of PRs.
|
# with sigstore/fulcio when running outside of PRs.
|
||||||
id-token: write
|
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@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||||
|
|
||||||
|
- 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@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0
|
||||||
|
|
||||||
# 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@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -57,7 +81,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 +89,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 +99,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
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
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@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.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
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@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: "Checkout code"
|
||||||
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
|
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
|
||||||
25
.github/workflows/semantic-pull-requests.yml
vendored
25
.github/workflows/semantic-pull-requests.yml
vendored
@@ -16,12 +16,31 @@ 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@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.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 }}
|
||||||
|
with:
|
||||||
|
types: |
|
||||||
|
fix
|
||||||
|
feat
|
||||||
|
chore
|
||||||
|
docs
|
||||||
|
style
|
||||||
|
refactor
|
||||||
|
perf
|
||||||
|
test
|
||||||
|
build
|
||||||
|
ci
|
||||||
|
revert
|
||||||
|
ossgg
|
||||||
|
|
||||||
- uses: marocchino/sticky-pull-request-comment@v2
|
- uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1
|
||||||
# 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)
|
||||||
@@ -40,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@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1
|
||||||
with:
|
with:
|
||||||
header: pr-title-lint-error
|
header: pr-title-lint-error
|
||||||
message: |
|
message: |
|
||||||
|
|||||||
54
.github/workflows/sonarqube.yml
vendored
Normal file
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@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.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@fe02b34f77f8bc703788d5817da081398fad5dd2
|
||||||
|
|
||||||
|
- 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@aa494459d7c39c106cc77b166de8b4250a32bb97
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||||
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
79
.github/workflows/terrafrom-plan-and-apply.yml
vendored
Normal file
79
.github/workflows/terrafrom-plan-and-apply.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
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/**"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
terraform:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- name: Harden the runner (Audit all outbound calls)
|
||||||
|
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
|
- name: Configure AWS Credentials
|
||||||
|
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
|
||||||
|
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@3399d8dbae8b05185e815e02361ede2949cd99c4 # v2.4.0
|
||||||
|
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"
|
||||||
|
|
||||||
24
.github/workflows/test.yml
vendored
24
.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@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||||
- 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@v2
|
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
|
||||||
@@ -25,10 +35,12 @@ jobs:
|
|||||||
- name: create .env
|
- name: create .env
|
||||||
run: cp .env.example .env
|
run: cp .env.example .env
|
||||||
|
|
||||||
- name: Generate Random ENCRYPTION_KEY and fill in .env
|
- name: Generate Random ENCRYPTION_KEY, CRON_SECRET & NEXTAUTH_SECRET and fill in .env
|
||||||
run: |
|
run: |
|
||||||
ENCRYPTION_KEY=$(openssl rand -hex 32)
|
RANDOM_KEY=$(openssl rand -hex 32)
|
||||||
sed -i "s/ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${ENCRYPTION_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
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: pnpm test
|
run: pnpm test
|
||||||
|
|||||||
51
.github/workflows/tolgee-missing-key-check.yml
vendored
Normal file
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@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.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
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@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.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
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@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.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. 😊
|
||||||
|
|||||||
59
.gitignore
vendored
59
.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,34 +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
|
||||||
|
apps/web/public/js
|
||||||
|
packages/database/migrations
|
||||||
|
branch.json
|
||||||
|
.vercel
|
||||||
|
|
||||||
# Vite Timestamps
|
# Terraform
|
||||||
vite.config.*.timestamp-*
|
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
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
|
|
||||||
78
.gitpod.yml
78
.gitpod.yml
@@ -1,78 +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: website
|
|
||||||
command: gp sync-await init && turbo --filter "@formbricks/formbricks-com" dev
|
|
||||||
|
|
||||||
- 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 &&
|
|
||||||
sed -i -r "s#^(NEXTAUTH_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
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,4 +1,21 @@
|
|||||||
#!/usr/bin/env sh
|
#!/bin/sh
|
||||||
. "$(dirname -- "$0")/_/husky.sh"
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
# Load environment variables from .env files
|
||||||
|
if [ -f .env ]; then
|
||||||
|
set -a
|
||||||
|
. .env
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
|
||||||
pnpm lint-staged
|
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 packages/lib/messages
|
||||||
|
fi
|
||||||
|
fi
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# A sample post-deploy hook
|
|
||||||
#
|
|
||||||
# These environment variables are available:
|
|
||||||
# KAMAL_RECORDED_AT
|
|
||||||
# KAMAL_PERFORMER
|
|
||||||
# KAMAL_VERSION
|
|
||||||
# KAMAL_HOSTS
|
|
||||||
# KAMAL_ROLE (if set)
|
|
||||||
# KAMAL_DESTINATION (if set)
|
|
||||||
# KAMAL_RUNTIME
|
|
||||||
|
|
||||||
echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds"
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
echo "Rebooted Traefik on $KAMAL_HOSTS"
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# A sample pre-build hook
|
|
||||||
#
|
|
||||||
# Checks:
|
|
||||||
# 1. We have a clean checkout
|
|
||||||
# 2. A remote is configured
|
|
||||||
# 3. The branch has been pushed to the remote
|
|
||||||
# 4. The version we are deploying matches the remote
|
|
||||||
#
|
|
||||||
# These environment variables are available:
|
|
||||||
# KAMAL_RECORDED_AT
|
|
||||||
# KAMAL_PERFORMER
|
|
||||||
# KAMAL_VERSION
|
|
||||||
# KAMAL_HOSTS
|
|
||||||
# KAMAL_ROLE (if set)
|
|
||||||
# KAMAL_DESTINATION (if set)
|
|
||||||
|
|
||||||
if [ -n "$(git status --porcelain)" ]; then
|
|
||||||
echo "Git checkout is not clean, aborting..." >&2
|
|
||||||
git status --porcelain >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
first_remote=$(git remote)
|
|
||||||
|
|
||||||
if [ -z "$first_remote" ]; then
|
|
||||||
echo "No git remote set, aborting..." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
current_branch=$(git branch --show-current)
|
|
||||||
|
|
||||||
if [ -z "$current_branch" ]; then
|
|
||||||
echo "Not on a git branch, aborting..." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1)
|
|
||||||
|
|
||||||
if [ -z "$remote_head" ]; then
|
|
||||||
echo "Branch not pushed to remote, aborting..." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$KAMAL_VERSION" != "$remote_head" ]; then
|
|
||||||
echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
|
|
||||||
# A sample pre-connect check
|
|
||||||
#
|
|
||||||
# Warms DNS before connecting to hosts in parallel
|
|
||||||
#
|
|
||||||
# These environment variables are available:
|
|
||||||
# KAMAL_RECORDED_AT
|
|
||||||
# KAMAL_PERFORMER
|
|
||||||
# KAMAL_VERSION
|
|
||||||
# KAMAL_HOSTS
|
|
||||||
# KAMAL_ROLE (if set)
|
|
||||||
# KAMAL_DESTINATION (if set)
|
|
||||||
# KAMAL_RUNTIME
|
|
||||||
|
|
||||||
hosts = ENV["KAMAL_HOSTS"].split(",")
|
|
||||||
results = nil
|
|
||||||
max = 3
|
|
||||||
|
|
||||||
elapsed = Benchmark.realtime do
|
|
||||||
results = hosts.map do |host|
|
|
||||||
Thread.new do
|
|
||||||
tries = 1
|
|
||||||
|
|
||||||
begin
|
|
||||||
Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)
|
|
||||||
rescue SocketError
|
|
||||||
if tries < max
|
|
||||||
puts "Retrying DNS warmup: #{host}"
|
|
||||||
tries += 1
|
|
||||||
sleep rand
|
|
||||||
retry
|
|
||||||
else
|
|
||||||
puts "DNS warmup failed: #{host}"
|
|
||||||
host
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
tries
|
|
||||||
end
|
|
||||||
end.map(&:value)
|
|
||||||
end
|
|
||||||
|
|
||||||
retries = results.sum - hosts.size
|
|
||||||
nopes = results.count { |r| r == max }
|
|
||||||
|
|
||||||
puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ]
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
|
|
||||||
# A sample pre-deploy hook
|
|
||||||
#
|
|
||||||
# Checks the Github status of the build, waiting for a pending build to complete for up to 720 seconds.
|
|
||||||
#
|
|
||||||
# Fails unless the combined status is "success"
|
|
||||||
#
|
|
||||||
# These environment variables are available:
|
|
||||||
# KAMAL_RECORDED_AT
|
|
||||||
# KAMAL_PERFORMER
|
|
||||||
# KAMAL_VERSION
|
|
||||||
# KAMAL_HOSTS
|
|
||||||
# KAMAL_COMMAND
|
|
||||||
# KAMAL_SUBCOMMAND
|
|
||||||
# KAMAL_ROLE (if set)
|
|
||||||
# KAMAL_DESTINATION (if set)
|
|
||||||
|
|
||||||
# Only check the build status for production deployments
|
|
||||||
if ENV["KAMAL_COMMAND"] == "rollback" || ENV["KAMAL_DESTINATION"] != "production"
|
|
||||||
exit 0
|
|
||||||
end
|
|
||||||
|
|
||||||
require "bundler/inline"
|
|
||||||
|
|
||||||
# true = install gems so this is fast on repeat invocations
|
|
||||||
gemfile(true, quiet: true) do
|
|
||||||
source "https://rubygems.org"
|
|
||||||
|
|
||||||
gem "octokit"
|
|
||||||
gem "faraday-retry"
|
|
||||||
end
|
|
||||||
|
|
||||||
MAX_ATTEMPTS = 72
|
|
||||||
ATTEMPTS_GAP = 10
|
|
||||||
|
|
||||||
def exit_with_error(message)
|
|
||||||
$stderr.puts message
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
class GithubStatusChecks
|
|
||||||
attr_reader :remote_url, :git_sha, :github_client, :combined_status
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
@remote_url = `git config --get remote.origin.url`.strip.delete_prefix("https://github.com/")
|
|
||||||
@git_sha = `git rev-parse HEAD`.strip
|
|
||||||
@github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
|
|
||||||
refresh!
|
|
||||||
end
|
|
||||||
|
|
||||||
def refresh!
|
|
||||||
@combined_status = github_client.combined_status(remote_url, git_sha)
|
|
||||||
end
|
|
||||||
|
|
||||||
def state
|
|
||||||
combined_status[:state]
|
|
||||||
end
|
|
||||||
|
|
||||||
def first_status_url
|
|
||||||
first_status = combined_status[:statuses].find { |status| status[:state] == state }
|
|
||||||
first_status && first_status[:target_url]
|
|
||||||
end
|
|
||||||
|
|
||||||
def complete_count
|
|
||||||
combined_status[:statuses].count { |status| status[:state] != "pending"}
|
|
||||||
end
|
|
||||||
|
|
||||||
def total_count
|
|
||||||
combined_status[:statuses].count
|
|
||||||
end
|
|
||||||
|
|
||||||
def current_status
|
|
||||||
if total_count > 0
|
|
||||||
"Completed #{complete_count}/#{total_count} checks, see #{first_status_url} ..."
|
|
||||||
else
|
|
||||||
"Build not started..."
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
$stdout.sync = true
|
|
||||||
|
|
||||||
puts "Checking build status..."
|
|
||||||
attempts = 0
|
|
||||||
checks = GithubStatusChecks.new
|
|
||||||
|
|
||||||
begin
|
|
||||||
loop do
|
|
||||||
case checks.state
|
|
||||||
when "success"
|
|
||||||
puts "Checks passed, see #{checks.first_status_url}"
|
|
||||||
exit 0
|
|
||||||
when "failure"
|
|
||||||
exit_with_error "Checks failed, see #{checks.first_status_url}"
|
|
||||||
when "pending"
|
|
||||||
attempts += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS
|
|
||||||
|
|
||||||
puts checks.current_status
|
|
||||||
sleep(ATTEMPTS_GAP)
|
|
||||||
checks.refresh!
|
|
||||||
end
|
|
||||||
rescue Octokit::NotFound
|
|
||||||
exit_with_error "Build status could not be found"
|
|
||||||
end
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
echo "Rebooting Traefik on $KAMAL_HOSTS..."
|
|
||||||
1
.npmrc
1
.npmrc
@@ -6,3 +6,4 @@ access = public
|
|||||||
enable-pre-post-scripts = true
|
enable-pre-post-scripts = true
|
||||||
legacy-peer-deps=true
|
legacy-peer-deps=true
|
||||||
node-linker=hoisted
|
node-linker=hoisted
|
||||||
|
save-exact=true
|
||||||
@@ -2,5 +2,10 @@ const baseConfig = require("./packages/config-prettier/prettier-preset");
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
plugins: ["@trivago/prettier-plugin-sort-imports", "prettier-plugin-tailwindcss"],
|
plugins: [
|
||||||
|
"@trivago/prettier-plugin-sort-imports",
|
||||||
|
"prettier-plugin-tailwindcss",
|
||||||
|
"prettier-plugin-sort-json",
|
||||||
|
],
|
||||||
|
jsonRecursiveSort: true,
|
||||||
};
|
};
|
||||||
|
|||||||
39
.tolgeerc.json
Normal file
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": "./packages/lib/messages"
|
||||||
|
},
|
||||||
|
"push": {
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"language": "en-US",
|
||||||
|
"path": "./packages/lib/messages/en-US.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "de-DE",
|
||||||
|
"path": "./packages/lib/messages/de-DE.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "fr-FR",
|
||||||
|
"path": "./packages/lib/messages/fr-FR.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "pt-BR",
|
||||||
|
"path": "./packages/lib/messages/pt-BR.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "zh-Hant-TW",
|
||||||
|
"path": "./packages/lib/messages/zh-Hant-TW.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"language": "pt-PT",
|
||||||
|
"path": "./packages/lib/messages/pt-PT.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"forceMode": "OVERRIDE"
|
||||||
|
},
|
||||||
|
"strictNamespace": false
|
||||||
|
}
|
||||||
4
.vscode/extensions.json
vendored
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
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
18
.vscode/launch.json
vendored
18
.vscode/launch.json
vendored
@@ -1,21 +1,21 @@
|
|||||||
{
|
{
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "Launch localhost:3002",
|
"name": "Launch localhost:3002",
|
||||||
"type": "firefox",
|
|
||||||
"request": "launch",
|
|
||||||
"reAttach": true,
|
"reAttach": true,
|
||||||
|
"request": "launch",
|
||||||
|
"type": "firefox",
|
||||||
"url": "http://localhost:3002/",
|
"url": "http://localhost:3002/",
|
||||||
"webRoot": "${workspaceFolder}"
|
"webRoot": "${workspaceFolder}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Attach",
|
"name": "Attach",
|
||||||
"type": "firefox",
|
"request": "attach",
|
||||||
"request": "attach"
|
"type": "firefox"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0"
|
||||||
}
|
}
|
||||||
|
|||||||
15
.vscode/settings.json
vendored
15
.vscode/settings.json
vendored
@@ -1,4 +1,15 @@
|
|||||||
{
|
{
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"github.copilot.chat.codeGeneration.instructions": [
|
||||||
"typescript.preferences.importModuleSpecifier": "non-relative"
|
{
|
||||||
|
"text": "When generating tests, always use vitest and use the `test` function instead of `it`."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"javascript.updateImportsOnFileMove.enabled": "always",
|
||||||
|
"sonarlint.connectedMode.project": {
|
||||||
|
"connectionId": "formbricks",
|
||||||
|
"projectKey": "formbricks_formbricks"
|
||||||
|
},
|
||||||
|
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||||
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
|
"typescript.updateImportsOnFileMove.enabled": "always"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,15 +16,15 @@ Are you brimming with brilliant ideas? For new features that can elevate Formbri
|
|||||||
|
|
||||||
Ready to dive into the code and make a real impact? Here's your path:
|
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/contributing/how-we-code) 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/contributing/gitpod)
|
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
|
||||||
|
|
||||||
|
|||||||
6
LICENSE
6
LICENSE
@@ -1,9 +1,9 @@
|
|||||||
Copyright (c) 2023 Formbricks GmbH
|
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/" directory of this repository, if that directory exists, 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/errors/" 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/react-native/", "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.
|
||||||
|
|
||||||
|
|||||||
24
README.md
24
README.md
@@ -1,5 +1,7 @@
|
|||||||
<div id="top"></div>
|
<div id="top"></div>
|
||||||
|
|
||||||
|
<p align="center">Help us grow and star us on Github! ⭐️</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
|
||||||
<a href="https://formbricks.com">
|
<a href="https://formbricks.com">
|
||||||
@@ -11,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>
|
||||||
@@ -144,6 +146,12 @@ Or you can also deploy Formbricks on [RepoCloud](https://repocloud.io) using the
|
|||||||
|
|
||||||
[](https://repocloud.io/details/?app_id=254)
|
[](https://repocloud.io/details/?app_id=254)
|
||||||
|
|
||||||
|
##### Zeabur
|
||||||
|
|
||||||
|
Or you can also deploy Formbricks on [Zeabur](https://zeabur.com) using the button below.
|
||||||
|
|
||||||
|
[](https://zeabur.com/templates/G4TUJL)
|
||||||
|
|
||||||
<a id="development"></a>
|
<a id="development"></a>
|
||||||
|
|
||||||
## 👨💻 Development
|
## 👨💻 Development
|
||||||
@@ -160,7 +168,7 @@ Here is what you need to be able to run Formbricks:
|
|||||||
|
|
||||||
### Local Setup
|
### Local Setup
|
||||||
|
|
||||||
To get started locally, we've got a [guide to help you](https://formbricks.com/docs/contributing/setup).
|
To get started locally, we've got a [guide to help you](https://formbricks.com/docs/developer-docs/contributing/get-started#local-machine-setup).
|
||||||
|
|
||||||
### Gitpod Setup
|
### Gitpod Setup
|
||||||
|
|
||||||
@@ -184,7 +192,7 @@ Here are a few options:
|
|||||||
|
|
||||||
- Upvote issues with 👍 reaction so we know what the demand for a particular issue is to prioritize it within the roadmap.
|
- Upvote issues with 👍 reaction so we know what the demand for a particular issue is to prioritize it within the roadmap.
|
||||||
|
|
||||||
Please check out [our contribution guide](https://formbricks.com/docs/contributing/introduction) and our [list of open issues](https://github.com/formbricks/formbricks/issues) for more information.
|
Please check out [our contribution guide](https://formbricks.com/docs/developer-docs/contributing/get-started) and our [list of open issues](https://github.com/formbricks/formbricks/issues) for more information.
|
||||||
|
|
||||||
## All Thanks To Our Contributors
|
## All Thanks To Our Contributors
|
||||||
|
|
||||||
@@ -220,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.
|
||||||
|
|
||||||
@@ -51,13 +51,13 @@ In the interest of responsibly managing vulnerabilities, please adhere to the fo
|
|||||||
> Do not reveal the problem to others until it has been resolved.
|
> Do not reveal the problem to others until it has been resolved.
|
||||||
|
|
||||||
1. **Send a Detailed Report**:
|
1. **Send a Detailed Report**:
|
||||||
- Address emails to [security@formbricks.com](mailto:security@formbricks.com).
|
- Raise a security report on [Github](https://github.com/formbricks/formbricks/issues/new/choose) or send an email to [security@formbricks.com](mailto:security@formbricks.com).
|
||||||
- Include:
|
- Include:
|
||||||
- Problem description.
|
- Problem description.
|
||||||
- Detailed, reproducible steps, with screenshots where possible.
|
- Detailed, reproducible steps, with screenshots where possible.
|
||||||
- Affected version(s).
|
- Affected version(s).
|
||||||
- Known possible mitigations.
|
- Known possible mitigations.
|
||||||
- Your Discord username or preferred contact method.
|
- Your preferred contact method.
|
||||||
2. **Acknowledgement of Receipt**:
|
2. **Acknowledgement of Receipt**:
|
||||||
- Our security team will acknowledge receipt and provide an initial response within 48 hours.
|
- Our security team will acknowledge receipt and provide an initial response within 48 hours.
|
||||||
- Following verification of the vulnerability and the fix, a release plan will be formulated, with the fix deployed between 7 to 28 days, depending on the severity and complexity.
|
- Following verification of the vulnerability and the fix, a release plan will be formulated, with the fix deployed between 7 to 28 days, depending on the severity and complexity.
|
||||||
|
|||||||
2
apps/demo-react-native/.env.example
Normal file
2
apps/demo-react-native/.env.example
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
EXPO_PUBLIC_APP_URL=http://192.168.0.197:3000
|
||||||
|
EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=cm5p0cs7r000819182b32j0a1
|
||||||
7
apps/demo-react-native/.eslintrc.js
Normal file
7
apps/demo-react-native/.eslintrc.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: ["@formbricks/eslint-config/react.js"],
|
||||||
|
parserOptions: {
|
||||||
|
project: "tsconfig.json",
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
},
|
||||||
|
};
|
||||||
35
apps/demo-react-native/.gitignore
vendored
Normal file
35
apps/demo-react-native/.gitignore
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# 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
|
||||||
0
apps/demo-react-native/.npmrc
Normal file
0
apps/demo-react-native/.npmrc
Normal file
35
apps/demo-react-native/app.json
Normal file
35
apps/demo-react-native/app.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"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",
|
||||||
|
"newArchEnabled": true,
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
apps/demo-react-native/assets/adaptive-icon.png
Normal file
BIN
apps/demo-react-native/assets/adaptive-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
apps/demo-react-native/assets/favicon.png
Normal file
BIN
apps/demo-react-native/assets/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
apps/demo-react-native/assets/icon.png
Normal file
BIN
apps/demo-react-native/assets/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
apps/demo-react-native/assets/splash.png
Normal file
BIN
apps/demo-react-native/assets/splash.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
6
apps/demo-react-native/babel.config.js
Normal file
6
apps/demo-react-native/babel.config.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = function babel(api) {
|
||||||
|
api.cache(true);
|
||||||
|
return {
|
||||||
|
presets: ["babel-preset-expo"],
|
||||||
|
};
|
||||||
|
};
|
||||||
7
apps/demo-react-native/index.js
Normal file
7
apps/demo-react-native/index.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { registerRootComponent } from "expo";
|
||||||
|
import { LogBox } from "react-native";
|
||||||
|
import App from "./src/app";
|
||||||
|
|
||||||
|
registerRootComponent(App);
|
||||||
|
|
||||||
|
LogBox.ignoreAllLogs();
|
||||||
21
apps/demo-react-native/metro.config.js
Normal file
21
apps/demo-react-native/metro.config.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// 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;
|
||||||
30
apps/demo-react-native/package.json
Normal file
30
apps/demo-react-native/package.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"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:*",
|
||||||
|
"@react-native-async-storage/async-storage": "2.1.0",
|
||||||
|
"expo": "52.0.28",
|
||||||
|
"expo-status-bar": "2.0.1",
|
||||||
|
"react": "18.3.1",
|
||||||
|
"react-dom": "18.3.1",
|
||||||
|
"react-native": "0.76.6",
|
||||||
|
"react-native-webview": "13.12.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "7.26.0",
|
||||||
|
"@types/react": "18.3.18",
|
||||||
|
"typescript": "5.7.2"
|
||||||
|
},
|
||||||
|
"private": true
|
||||||
|
}
|
||||||
117
apps/demo-react-native/src/app.tsx
Normal file
117
apps/demo-react-native/src/app.tsx
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
import { StatusBar } from "expo-status-bar";
|
||||||
|
import React, { type JSX } from "react";
|
||||||
|
import { Button, LogBox, StyleSheet, Text, View } from "react-native";
|
||||||
|
import Formbricks, {
|
||||||
|
logout,
|
||||||
|
setAttribute,
|
||||||
|
setAttributes,
|
||||||
|
setLanguage,
|
||||||
|
setUserId,
|
||||||
|
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_APP_URL) {
|
||||||
|
throw new Error("EXPO_PUBLIC_APP_URL is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text>Formbricks React Native SDK Demo</Text>
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 10,
|
||||||
|
}}>
|
||||||
|
<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);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
title="Set User Id"
|
||||||
|
onPress={() => {
|
||||||
|
setUserId("random-user-id").catch((error: unknown) => {
|
||||||
|
// eslint-disable-next-line no-console -- logging is allowed in demo apps
|
||||||
|
console.error("Error setting user id:", error);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
title="Set User Attributess (multiple)"
|
||||||
|
onPress={() => {
|
||||||
|
setAttributes({
|
||||||
|
testAttr: "attr-test",
|
||||||
|
testAttr2: "attr-test-2",
|
||||||
|
testAttr3: "attr-test-3",
|
||||||
|
testAttr4: "attr-test-4",
|
||||||
|
}).catch((error: unknown) => {
|
||||||
|
// eslint-disable-next-line no-console -- logging is allowed in demo apps
|
||||||
|
console.error("Error setting user attributes:", error);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
title="Set User Attributes (single)"
|
||||||
|
onPress={() => {
|
||||||
|
setAttribute("testSingleAttr", "testSingleAttr").catch((error: unknown) => {
|
||||||
|
// eslint-disable-next-line no-console -- logging is allowed in demo apps
|
||||||
|
console.error("Error setting user attributes:", error);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
title="Logout"
|
||||||
|
onPress={() => {
|
||||||
|
logout().catch((error: unknown) => {
|
||||||
|
// eslint-disable-next-line no-console -- logging is allowed in demo apps
|
||||||
|
console.error("Error logging out:", error);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
title="Set Language (de)"
|
||||||
|
onPress={() => {
|
||||||
|
setLanguage("de").catch((error: unknown) => {
|
||||||
|
// eslint-disable-next-line no-console -- logging is allowed in demo apps
|
||||||
|
console.error("Error setting language:", error);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<StatusBar style="auto" />
|
||||||
|
|
||||||
|
<Formbricks
|
||||||
|
appUrl={process.env.EXPO_PUBLIC_APP_URL as string}
|
||||||
|
environmentId={process.env.EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID as string}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
},
|
||||||
|
});
|
||||||
6
apps/demo-react-native/tsconfig.json
Normal file
6
apps/demo-react-native/tsconfig.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"extends": "expo/tsconfig.base"
|
||||||
|
}
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
extends: ["@formbricks/eslint-config/legacy-next.js"],
|
extends: ["@formbricks/eslint-config/next.js"],
|
||||||
|
parserOptions: {
|
||||||
|
project: "tsconfig.json",
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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,22 +0,0 @@
|
|||||||
interface SurveySwitchProps {
|
|
||||||
value: "website" | "app";
|
|
||||||
formbricks: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SurveySwitch = ({ value, formbricks }: SurveySwitchProps) => {
|
|
||||||
return (
|
|
||||||
<select
|
|
||||||
value={value}
|
|
||||||
onChange={(v) => {
|
|
||||||
formbricks.logout();
|
|
||||||
window.location.href = `/${v.target.value}`;
|
|
||||||
}}>
|
|
||||||
<option value="website" className="h-10 px-4 hover:bg-slate-100">
|
|
||||||
Website Surveys
|
|
||||||
</option>
|
|
||||||
<option value="app" className="hover:bg-slate-10 h-10 px-4">
|
|
||||||
App Surveys
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
13
apps/demo/components/layout-app.tsx
Normal file
13
apps/demo/components/layout-app.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Sidebar } from "./sidebar";
|
||||||
|
|
||||||
|
export function LayoutApp({ children }: { children: React.ReactNode }): React.JSX.Element {
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
65
apps/demo/components/sidebar.tsx
Normal file
65
apps/demo/components/sidebar.tsx
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
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 function Sidebar(): React.JSX.Element {
|
||||||
|
return (
|
||||||
|
<div className="flex grow flex-col overflow-y-auto bg-cyan-700 pt-5 pb-4">
|
||||||
|
<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 leading-6 font-medium"
|
||||||
|
)}
|
||||||
|
aria-current={item.current ? "page" : undefined}>
|
||||||
|
<item.icon className="mr-4 h-6 w-6 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 leading-6 font-medium 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
apps/demo/globals.css
Normal file
23
apps/demo/globals.css
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
@import 'tailwindcss';
|
||||||
|
|
||||||
|
@plugin '@tailwindcss/forms';
|
||||||
|
|
||||||
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
|
/*
|
||||||
|
The default border color has changed to `currentcolor` in Tailwind CSS v4,
|
||||||
|
so we've added these compatibility styles to make sure everything still
|
||||||
|
looks the same as it did with Tailwind CSS v3.
|
||||||
|
|
||||||
|
If we ever want to remove these styles, we need to add an explicit border
|
||||||
|
color utility to any element that depends on these defaults.
|
||||||
|
*/
|
||||||
|
@layer base {
|
||||||
|
*,
|
||||||
|
::after,
|
||||||
|
::before,
|
||||||
|
::backdrop,
|
||||||
|
::file-selector-button {
|
||||||
|
border-color: var(--color-gray-200, currentcolor);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
export const classNames = (...classes: any) => {
|
export function classNames(...classes: string[]): string {
|
||||||
return classes.filter(Boolean).join(" ");
|
return classes.filter(Boolean).join(" ");
|
||||||
};
|
}
|
||||||
|
|||||||
2
apps/demo/next-env.d.ts
vendored
2
apps/demo/next-env.d.ts
vendored
@@ -2,4 +2,4 @@
|
|||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
|
||||||
|
|||||||
@@ -1,15 +1,5 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
reactStrictMode: true,
|
|
||||||
async redirects() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
source: "/",
|
|
||||||
destination: "/app",
|
|
||||||
permanent: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
images: {
|
images: {
|
||||||
remotePatterns: [
|
remotePatterns: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,25 +1,28 @@
|
|||||||
{
|
{
|
||||||
"name": "@formbricks/demo",
|
"name": "@formbricks/demo",
|
||||||
"version": "0.1.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rimraf .turbo node_modules .next",
|
"clean": "rimraf .turbo node_modules .next",
|
||||||
"dev": "next dev -p 3002 --turbo",
|
"dev": "next dev -p 3002 --turbopack",
|
||||||
"go": "next dev -p 3002",
|
"go": "next dev -p 3002 --turbopack",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formbricks/js": "workspace:*",
|
"@formbricks/js": "workspace:*",
|
||||||
"@formbricks/ui": "workspace:*",
|
"@tailwindcss/forms": "0.5.9",
|
||||||
"lucide-react": "^0.395.0",
|
"@tailwindcss/postcss": "4.1.3",
|
||||||
"next": "14.2.4",
|
"lucide-react": "0.486.0",
|
||||||
"react": "18.3.1",
|
"next": "15.2.4",
|
||||||
"react-dom": "18.3.1"
|
"postcss": "8.5.3",
|
||||||
|
"react": "19.1.0",
|
||||||
|
"react-dom": "19.1.0",
|
||||||
|
"tailwindcss": "4.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@formbricks/eslint-config": "workspace:*",
|
"@formbricks/config-typescript": "workspace:*",
|
||||||
"@formbricks/config-typescript": "workspace:*"
|
"@formbricks/eslint-config": "workspace:*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { AppProps } from "next/app";
|
import type { AppProps } from "next/app";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import "../styles/globals.css";
|
import "../globals.css";
|
||||||
|
|
||||||
const App = ({ Component, pageProps }: AppProps) => {
|
export default function App({ Component, pageProps }: AppProps): React.JSX.Element {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
@@ -17,6 +17,4 @@ const App = ({ Component, pageProps }: AppProps) => {
|
|||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default App;
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Head, Html, Main, NextScript } from "next/document";
|
import { Head, Html, Main, NextScript } from "next/document";
|
||||||
|
|
||||||
const Document = () => {
|
export default function Document(): React.JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Html lang="en" className="h-full bg-slate-50">
|
<Html lang="en" className="h-full bg-slate-50">
|
||||||
<Head />
|
<Head />
|
||||||
@@ -10,6 +10,4 @@ const Document = () => {
|
|||||||
</body>
|
</body>
|
||||||
</Html>
|
</Html>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default Document;
|
|
||||||
|
|||||||
@@ -1,241 +0,0 @@
|
|||||||
import Image from "next/image";
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import formbricks from "@formbricks/js/app";
|
|
||||||
import { SurveySwitch } from "../../components/SurveySwitch";
|
|
||||||
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="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 items-center gap-2">
|
|
||||||
<SurveySwitch value="app" formbricks={formbricks} />
|
|
||||||
<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-800">
|
|
||||||
<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;
|
|
||||||
359
apps/demo/pages/index.tsx
Normal file
359
apps/demo/pages/index.tsx
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
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: Window;
|
||||||
|
|
||||||
|
export default function AppPage(): React.JSX.Element {
|
||||||
|
const [darkMode, setDarkMode] = useState(false);
|
||||||
|
const router = useRouter();
|
||||||
|
const userId = "THIS-IS-A-VERY-LONG-USER-ID-FOR-TESTING";
|
||||||
|
const userAttributes = {
|
||||||
|
"Attribute 1": "one",
|
||||||
|
"Attribute 2": "two",
|
||||||
|
"Attribute 3": "three",
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (darkMode) {
|
||||||
|
document.body.classList.add("dark");
|
||||||
|
} else {
|
||||||
|
document.body.classList.remove("dark");
|
||||||
|
}
|
||||||
|
}, [darkMode]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const initFormbricks = () => {
|
||||||
|
// enable Formbricks debug mode by adding formbricksDebug=true GET parameter
|
||||||
|
const addFormbricksDebugParam = (): void => {
|
||||||
|
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) {
|
||||||
|
void formbricks.setup({
|
||||||
|
environmentId: process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID,
|
||||||
|
appUrl: process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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", () => {
|
||||||
|
void handleRouteChange();
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
router.events.off("routeChangeComplete", () => {
|
||||||
|
void handleRouteChange();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
initFormbricks();
|
||||||
|
}, [router.events]);
|
||||||
|
|
||||||
|
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
|
||||||
|
type="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-xs" 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:mr-2 sm:mb-0">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 className="relative inline-flex h-3 w-3 rounded-full bg-green-500" />
|
||||||
|
</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>
|
||||||
|
</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">
|
||||||
|
Set a user ID / pull data from Formbricks app
|
||||||
|
</h3>
|
||||||
|
<p className="text-slate-700 dark:text-slate-300">
|
||||||
|
On formbricks.setUserId() the user state will <strong>be fetched from Formbricks</strong> and
|
||||||
|
the local state gets <strong>updated with the user state</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"
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
void formbricks.setUserId(userId);
|
||||||
|
}}>
|
||||||
|
Set user ID
|
||||||
|
</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
|
||||||
|
type="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/xm-and-surveys/surveys/website-app-surveys/actions#setting-up-no-code-actions"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
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/xm-and-surveys/surveys/website-app-surveys/actions#setting-up-no-code-actions"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
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
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
void 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/xm-and-surveys/surveys/website-app-surveys/user-identification#setting-custom-user-attributes"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
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
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
void 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/xm-and-surveys/surveys/website-app-surveys/user-identification#setting-custom-user-attributes"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
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
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
void 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/xm-and-surveys/surveys/website-app-surveys/user-identification"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="underline dark:text-blue-500">
|
||||||
|
user email
|
||||||
|
</a>{" "}
|
||||||
|
'test@web.com'
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-6">
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
void formbricks.setAttributes(userAttributes);
|
||||||
|
}}
|
||||||
|
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 Multiple Attributes
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-slate-700 dark:text-slate-300">
|
||||||
|
This button sets the{" "}
|
||||||
|
<a
|
||||||
|
href="https://formbricks.com/docs/xm-and-surveys/surveys/website-app-surveys/user-identification#setting-custom-user-attributes"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="underline dark:text-blue-500">
|
||||||
|
user attributes
|
||||||
|
</a>{" "}
|
||||||
|
to 'one', 'two', 'three'.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-6">
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
void formbricks.setLanguage("de");
|
||||||
|
}}
|
||||||
|
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 Language to 'de'
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-slate-700 dark:text-slate-300">
|
||||||
|
This button sets the{" "}
|
||||||
|
<a
|
||||||
|
href="https://formbricks.com/docs/xm-and-surveys/surveys/general-features/multi-language-surveys"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="underline dark:text-blue-500">
|
||||||
|
language
|
||||||
|
</a>{" "}
|
||||||
|
to 'de'.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-6">
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="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"
|
||||||
|
onClick={() => {
|
||||||
|
void formbricks.track("code");
|
||||||
|
}}>
|
||||||
|
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/xm-and-surveys/surveys/website-app-surveys/actions#setting-up-code-actions"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="underline dark:text-blue-500"
|
||||||
|
target="_blank">
|
||||||
|
Code Action
|
||||||
|
</a>{" "}
|
||||||
|
as long as you created it beforehand in the Formbricks App.{" "}
|
||||||
|
<a
|
||||||
|
href="https://formbricks.com/docs/xm-and-surveys/surveys/website-app-surveys/actions#setting-up-code-actions"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
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
|
||||||
|
type="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"
|
||||||
|
onClick={() => {
|
||||||
|
void formbricks.logout();
|
||||||
|
}}>
|
||||||
|
Logout
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-slate-700 dark:text-slate-300">
|
||||||
|
This button logs out the user and syncs the local state with Formbricks. (Only works if a
|
||||||
|
userId is set)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
import Image from "next/image";
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import formbricks from "@formbricks/js/website";
|
|
||||||
import { SurveySwitch } from "../../components/SurveySwitch";
|
|
||||||
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 defaultAttributes = {
|
|
||||||
language: "en",
|
|
||||||
};
|
|
||||||
|
|
||||||
formbricks.init({
|
|
||||||
environmentId: process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID,
|
|
||||||
apiHost: process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST,
|
|
||||||
attributes: defaultAttributes,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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="h-screen bg-white px-12 py-6 dark:bg-slate-800">
|
|
||||||
<div className="flex flex-col items-center justify-between md:flex-row">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<SurveySwitch value="website" formbricks={formbricks} />
|
|
||||||
<div>
|
|
||||||
<h1 className="text-2xl font-bold text-slate-900 dark:text-white">
|
|
||||||
Formbricks Website 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-800">
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AppPage;
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
"@tailwindcss/postcss": {},
|
||||||
autoprefixer: {},
|
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 15 KiB |
@@ -1,26 +0,0 @@
|
|||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
/* Example on overriding packages/js colors */
|
|
||||||
.dark {
|
|
||||||
--fb-brand-color: red;
|
|
||||||
--fb-brand-text-color: white;
|
|
||||||
--fb-border-color: green;
|
|
||||||
--fb-border-color-highlight: var(--slate-500);
|
|
||||||
--fb-focus-color: red;
|
|
||||||
--fb-heading-color: yellow;
|
|
||||||
--fb-subheading-color: green;
|
|
||||||
--fb-info-text-color: orange;
|
|
||||||
--fb-signature-text-color: blue;
|
|
||||||
--fb-survey-background-color: black;
|
|
||||||
--fb-accent-background-color: rgb(13, 13, 12);
|
|
||||||
--fb-accent-background-color-selected: red;
|
|
||||||
--fb-placeholder-color: white;
|
|
||||||
--fb-shadow-color: yellow;
|
|
||||||
--fb-rating-fill: var(--yellow-300);
|
|
||||||
--fb-rating-hover: var(--yellow-500);
|
|
||||||
--fb-back-btn-border: currentColor;
|
|
||||||
--fb-submit-btn-border: transparent;
|
|
||||||
--fb-rating-selected: black;
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user