Compare commits
512 Commits
v2.1.1
...
action-env
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41bc62fb3b | ||
|
|
041f26ab2d | ||
|
|
d03b01cb67 | ||
|
|
c7981fc4d9 | ||
|
|
a1719c2258 | ||
|
|
80d338c998 | ||
|
|
306784c31b | ||
|
|
bcd68e0f19 | ||
|
|
5764148753 | ||
|
|
e7edfe3ba1 | ||
|
|
da6f54eede | ||
|
|
ade5c3d80e | ||
|
|
cc2600cfba | ||
|
|
9b191ef3e4 | ||
|
|
c450c35baf | ||
|
|
a5275c2ead | ||
|
|
eec71d9a98 | ||
|
|
d547a70013 | ||
|
|
b35b82f4ee | ||
|
|
ea52624ab2 | ||
|
|
0484bccfd1 | ||
|
|
d094f63faa | ||
|
|
dc6bc61442 | ||
|
|
5918c42cf9 | ||
|
|
c34a08561e | ||
|
|
7213c726b4 | ||
|
|
f650ac4e76 | ||
|
|
8870b7d710 | ||
|
|
2ff1be2c4a | ||
|
|
3c8f6945ba | ||
|
|
61ac306ef3 | ||
|
|
022569e404 | ||
|
|
ebf22df7b6 | ||
|
|
adcc596875 | ||
|
|
1bcdf06b43 | ||
|
|
3be78f0312 | ||
|
|
5633499834 | ||
|
|
88847a153b | ||
|
|
3e7c3a45c3 | ||
|
|
1af1a92fec | ||
|
|
7b38923b7d | ||
|
|
38500e1d79 | ||
|
|
e1bd66b009 | ||
|
|
abbd69e355 | ||
|
|
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 | ||
|
|
17279cb3fe | ||
|
|
1b4823421f | ||
|
|
deef604325 | ||
|
|
22e44b47e6 | ||
|
|
08052b13cf | ||
|
|
5e7fe96ef6 | ||
|
|
83ff98cd3a | ||
|
|
eb54230445 | ||
|
|
59d57c50eb | ||
|
|
f358254e3c | ||
|
|
3ab8092a82 | ||
|
|
3b08b718ff | ||
|
|
a473719eee | ||
|
|
cd40e655fb | ||
|
|
5d468b4420 | ||
|
|
96806c613f | ||
|
|
6f2a4b2b03 | ||
|
|
d33efa0274 | ||
|
|
9b35ebc114 | ||
|
|
dba62157bf | ||
|
|
556184f442 | ||
|
|
c33532f952 | ||
|
|
8f61ceb4ea | ||
|
|
e19dcdc0c4 | ||
|
|
65f977786d | ||
|
|
95c13fd9a9 | ||
|
|
f8adf28df8 | ||
|
|
9d468379f2 | ||
|
|
8071e82390 | ||
|
|
3ce775ec05 | ||
|
|
1223a30127 | ||
|
|
f3906cab55 | ||
|
|
d33304e3ad | ||
|
|
ebed950392 | ||
|
|
4e6ab1c2bb | ||
|
|
543d85eb28 | ||
|
|
afe01a61ae | ||
|
|
b4c86835ed | ||
|
|
ab80bc1bf2 | ||
|
|
5e5a9fac00 | ||
|
|
09b6805886 |
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@2.2.0/schema.json",
|
||||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"fixed": [],
|
||||
"ignore": ["@formbricks/demo", "@formbricks/web"],
|
||||
"linked": [],
|
||||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": ["@formbricks/formbricks-com", "@formbricks/demo", "@formbricks/web"]
|
||||
"updateInternalDependencies": "patch"
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
"dockerComposeFile": "docker-compose.yml",
|
||||
"service": "app",
|
||||
"workspaceFolder": "/workspace",
|
||||
|
||||
// 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"
|
||||
"features": {},
|
||||
"image": "mcr.microsoft.com/devcontainers/universal:2",
|
||||
"postAttachCommand": "pnpm go",
|
||||
"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"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
49
.env.example
@@ -8,8 +8,8 @@
|
||||
|
||||
WEBAPP_URL=http://localhost:3000
|
||||
|
||||
# Set this if you want to have a shorter link for surveys
|
||||
SHORT_URL_BASE=
|
||||
# Required for next-auth. Should be the same as WEBAPP_URL
|
||||
NEXTAUTH_URL=http://localhost:3000
|
||||
|
||||
# Encryption keys
|
||||
# Please set both for now, we will change this in the future
|
||||
@@ -17,28 +17,20 @@ SHORT_URL_BASE=
|
||||
# You can use: `openssl rand -hex 32` to generate one
|
||||
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=
|
||||
|
||||
##############
|
||||
# DATABASE #
|
||||
##############
|
||||
|
||||
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 #
|
||||
################
|
||||
@@ -54,6 +46,9 @@ SMTP_SECURE_ENABLED=0
|
||||
SMTP_USER=smtpUser
|
||||
SMTP_PASSWORD=smtpPassword
|
||||
|
||||
# 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 -----------------------------#
|
||||
########################################################################
|
||||
@@ -75,6 +70,8 @@ S3_BUCKET_NAME=
|
||||
# Configure a third party S3 compatible storage service endpoint like StorJ leave empty if you use Amazon S3
|
||||
# e.g., https://gateway.storjshare.io
|
||||
S3_ENDPOINT_URL=
|
||||
# Force path style for S3 compatible storage (0 for disabled, 1 for enabled)
|
||||
S3_FORCE_PATH_STYLE=0
|
||||
|
||||
#####################
|
||||
# Disable Features #
|
||||
@@ -160,10 +157,7 @@ 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)
|
||||
# (Role Management is an Enterprise feature)
|
||||
# DEFAULT_ORGANIZATION_ID=
|
||||
# DEFAULT_ORGANIZATION_ROLE=admin
|
||||
|
||||
# set to 1 to skip onboarding for new users
|
||||
# ONBOARDING_DISABLED=1
|
||||
# DEFAULT_ORGANIZATION_ROLE=owner
|
||||
|
||||
# Send new users to customer.io
|
||||
# CUSTOMER_IO_API_KEY=
|
||||
@@ -179,7 +173,16 @@ ENTERPRISE_LICENSE_KEY=
|
||||
UNSPLASH_ACCESS_KEY=
|
||||
|
||||
# The below is used for Next Caching (uses In-Memory from Next Cache if not provided)
|
||||
# REDIS_URL:
|
||||
# REDIS_URL=redis://localhost:6379
|
||||
|
||||
# The below is used for Rate Limiting (uses In-Memory LRU Cache if not provided) (You can use a service like Webdis for this)
|
||||
# REDIS_HTTP_URL:
|
||||
|
||||
# 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=
|
||||
107
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,81 +1,32 @@
|
||||
name: Bug report
|
||||
description: "Found a bug? Please fill out the sections below. \U0001F44D"
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
assignees: []
|
||||
labels:
|
||||
- bug
|
||||
body:
|
||||
- type: textarea
|
||||
id: issue-summary
|
||||
attributes:
|
||||
label: Issue Summary
|
||||
description: A summary of the issue. This needs to be a clear detailed-rich summary.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: steps-to-reproduce
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
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.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: other-information
|
||||
attributes:
|
||||
label: Other information
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: screenshots
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: If applicable, add screenshots to help explain your problem.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
id: environment
|
||||
attributes:
|
||||
label: Environment
|
||||
options:
|
||||
- label: Formbricks Cloud (app.formbricks.com)
|
||||
- label: 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?
|
||||
- type: textarea
|
||||
id: issue-summary
|
||||
attributes:
|
||||
label: Issue Summary
|
||||
description: A summary of the issue. This needs to be a clear detailed-rich summary.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: issue-expected-behavior
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A clear and concise description of what you expected to happen.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: other-information
|
||||
attributes:
|
||||
label: Other information (incl. screenshots, Formbricks version, steps to reproduce,...)
|
||||
validations:
|
||||
required: false
|
||||
- type: dropdown
|
||||
id: environment
|
||||
attributes:
|
||||
label: Your Environment
|
||||
options:
|
||||
- Formbricks Cloud (app.formbricks.com)
|
||||
- Self-hosted Formbricks
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Questions
|
||||
url: https://formbricks.com/discord
|
||||
about: Ask a general question about the project on our Discord server
|
||||
url: https://github.com/formbricks/formbricks/discussions
|
||||
about: Need help selfhosting or ask a general question about the project? Open a discussion
|
||||
|
||||
22
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,8 +1,7 @@
|
||||
name: Feature request
|
||||
description: "Suggest an idea for this project \U0001F680"
|
||||
title: "[FEATURE]"
|
||||
labels: enhancement
|
||||
assignees: []
|
||||
labels:
|
||||
- enhancement
|
||||
body:
|
||||
- type: textarea
|
||||
id: problem-description
|
||||
@@ -18,13 +17,6 @@ body:
|
||||
description: A clear and concise description of what you want to happen.
|
||||
validations:
|
||||
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
|
||||
id: additional-context
|
||||
attributes:
|
||||
@@ -33,15 +25,9 @@ body:
|
||||
validations:
|
||||
required: false
|
||||
- type: markdown
|
||||
id: formbricks-info
|
||||
attributes:
|
||||
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)
|
||||
- First time: Please read our [introductory blog post](https://formbricks.com/blog/join-the-formtribe)
|
||||
- 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.
|
||||
- Check out our [Contributor Docs](https://formbricks.com/docs/developer-docs/contributing/get-started)
|
||||
- Anything unclear? [Ask in Discord](https://formbricks.com/discord)
|
||||
|
||||
10
.github/actions/cache-build-web/action.yml
vendored
@@ -30,6 +30,10 @@ runs:
|
||||
**/dist/**
|
||||
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
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
@@ -37,7 +41,7 @@ runs:
|
||||
if: steps.cache-build.outputs.cache-hit != 'true'
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
uses: pnpm/action-setup@v4
|
||||
if: steps.cache-build.outputs.cache-hit != 'true'
|
||||
|
||||
- name: Install dependencies
|
||||
@@ -49,10 +53,12 @@ runs:
|
||||
run: cp .env.example .env
|
||||
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: |
|
||||
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
|
||||
sed -i "s/ENTERPRISE_LICENSE_KEY=.*/ENTERPRISE_LICENSE_KEY=${RANDOM_KEY}/" .env
|
||||
echo "E2E_TESTING=${{ inputs.e2e_testing_mode }}" >> .env
|
||||
shell: bash
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<!-- 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)
|
||||
Fixes #(issue)
|
||||
|
||||
<!-- Please provide a screenshots or a loom video for visual changes to speed up reviews
|
||||
Loom Video: https://www.loom.com/
|
||||
28
.github/workflows/build-docs.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Build Docs
|
||||
on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
build:
|
||||
name: Build Docs
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/dangerous-git-checkout
|
||||
|
||||
- name: Setup Node.js 20.x
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20.x
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||
shell: bash
|
||||
|
||||
- run: |
|
||||
pnpm build --filter=@formbricks/docs...
|
||||
shell: bash
|
||||
26
.github/workflows/build-web.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Build web
|
||||
name: Build Web
|
||||
on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
@@ -11,24 +11,8 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/dangerous-git-checkout
|
||||
|
||||
- name: Setup Node.js 20.x
|
||||
uses: actions/setup-node@v3
|
||||
- name: Build & Cache Web Binaries
|
||||
uses: ./.github/actions/cache-build-web
|
||||
id: cache-build-web
|
||||
with:
|
||||
node-version: 20.x
|
||||
|
||||
- name: Install pnpm
|
||||
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...
|
||||
e2e_testing_mode: "0"
|
||||
|
||||
30
.github/workflows/chromatic.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: "Chromatic"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
chromatic:
|
||||
name: Run Chromatic
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
- name: Install dependencies
|
||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||
- name: Run Chromatic
|
||||
uses: chromaui/action@latest
|
||||
with:
|
||||
# ⚠️ Make sure to configure a `CHROMATIC_PROJECT_TOKEN` repository secret
|
||||
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||
workingDir: apps/storybook
|
||||
92
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL Advanced"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
schedule:
|
||||
- cron: '17 1 * * 1'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze (${{ matrix.language }})
|
||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||
# - https://gh.io/supported-runners-and-hardware-resources
|
||||
# - https://gh.io/using-larger-runners (GitHub.com only)
|
||||
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
# required to fetch internal or private CodeQL packs
|
||||
packages: read
|
||||
|
||||
# only required for workflows in private repositories
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- language: javascript-typescript
|
||||
build-mode: none
|
||||
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
|
||||
# Use `c-cpp` to analyze code written in C, C++ or both
|
||||
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
||||
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
||||
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
||||
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
||||
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
# If the analyze step fails for one of the languages you are analyzing with
|
||||
# "We were unable to automatically build your code", modify the matrix above
|
||||
# to set the build mode to "manual" for that language. Then modify this step
|
||||
# to build your code.
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
- if: matrix.build-mode == 'manual'
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'If you are using a "manual" build mode for one or more of the' \
|
||||
'languages you are analyzing, replace this with the commands to build' \
|
||||
'your code, for example:'
|
||||
echo ' make bootstrap'
|
||||
echo ' make release'
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
24
.github/workflows/cron-reportUsageToStripe.yml
vendored
@@ -1,24 +0,0 @@
|
||||
name: Cron - Report usage to Stripe
|
||||
|
||||
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:
|
||||
# This will run the job at 20:00 UTC every day of every month.
|
||||
# - cron: "0 20 * * *"
|
||||
jobs:
|
||||
cron-reportUsageToStripe:
|
||||
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/report-usage \
|
||||
-X POST \
|
||||
-H 'x-api-key: ${{ env.CRON_SECRET }}' \
|
||||
-H 'Cache-Control: no-cache' \
|
||||
--fail
|
||||
@@ -4,9 +4,9 @@ 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 * * *"
|
||||
schedule:
|
||||
# Runs "At 00:00." (see https://crontab.guru)
|
||||
- cron: "0 0 * * *"
|
||||
jobs:
|
||||
cron-weeklySummary:
|
||||
env:
|
||||
|
||||
6
.github/workflows/cron-weeklySummary.yml
vendored
@@ -4,9 +4,9 @@ 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"
|
||||
schedule:
|
||||
# Runs “At 08:00 on Monday.” (see https://crontab.guru)
|
||||
- cron: "0 8 * * 1"
|
||||
jobs:
|
||||
cron-weeklySummary:
|
||||
env:
|
||||
|
||||
83
.github/workflows/e2e.yml
vendored
@@ -2,67 +2,84 @@ name: E2E Tests
|
||||
on:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
env:
|
||||
TELEMETRY_DISABLED: 1
|
||||
jobs:
|
||||
build:
|
||||
name: Run E2E Tests
|
||||
runs-on: ubuntu-latest
|
||||
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:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/dangerous-git-checkout
|
||||
|
||||
- name: Build & Cache Web Binaries
|
||||
uses: ./.github/actions/cache-build-web
|
||||
- name: Setup Node.js 20.x
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
e2e_testing_mode: "1"
|
||||
node-version: 20.x
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- 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: |
|
||||
cd packages/database && pnpm db:up &
|
||||
for attempt in {1..20}; do
|
||||
if nc -zv localhost 5432; then
|
||||
echo "Ready"
|
||||
break
|
||||
fi
|
||||
echo "Waiting..."
|
||||
sleep 5
|
||||
done
|
||||
pnpm db:migrate:dev
|
||||
- name: Serve packages for lazy loading
|
||||
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
|
||||
sed -i "s/ENTERPRISE_LICENSE_KEY=.*/ENTERPRISE_LICENSE_KEY=${RANDOM_KEY}/" .env
|
||||
echo "" >> .env
|
||||
echo "E2E_TESTING=1" >> .env
|
||||
shell: bash
|
||||
|
||||
- name: Build App
|
||||
run: |
|
||||
cd packages/surveys && pnpm serve &
|
||||
pnpm build --filter=@formbricks/web...
|
||||
|
||||
- name: Apply Prisma Migrations
|
||||
run: |
|
||||
pnpm prisma migrate deploy
|
||||
|
||||
- name: Run App
|
||||
run: |
|
||||
NODE_ENV=test pnpm start --filter=@formbricks/web &
|
||||
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
|
||||
echo "Ready"
|
||||
echo "Application is ready."
|
||||
break
|
||||
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
|
||||
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
|
||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
||||
run: pnpm exec playwright install --with-deps
|
||||
|
||||
- name: Run E2E Tests
|
||||
|
||||
131
.github/workflows/kamal-deploy.yml
vendored
@@ -1,131 +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 }}
|
||||
ONBOARDING_DISABLED: ${{ vars.ONBOARDING_DISABLED }}
|
||||
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
|
||||
128
.github/workflows/kamal-setup.yml
vendored
@@ -1,128 +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 }}
|
||||
ONBOARDING_DISABLED: ${{ vars.ONBOARDING_DISABLED }}
|
||||
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
|
||||
14
.github/workflows/lint.yml
vendored
@@ -11,13 +11,13 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/dangerous-git-checkout
|
||||
|
||||
- name: Setup Node.js 18.x
|
||||
- name: Setup Node.js 20.x
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 20.x
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||
@@ -25,10 +25,12 @@ jobs:
|
||||
- name: create .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: |
|
||||
ENCRYPTION_KEY=$(openssl rand -hex 32)
|
||||
sed -i "s/ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${ENCRYPTION_KEY}/" .env
|
||||
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: Lint
|
||||
run: pnpm lint
|
||||
|
||||
9
.github/workflows/pr.yml
vendored
@@ -50,6 +50,13 @@ jobs:
|
||||
uses: ./.github/workflows/build-web.yml
|
||||
secrets: inherit
|
||||
|
||||
docs:
|
||||
name: Build Docs
|
||||
needs: [changes]
|
||||
if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
|
||||
uses: ./.github/workflows/build-docs.yml
|
||||
secrets: inherit
|
||||
|
||||
e2e-test:
|
||||
name: Run E2E Tests
|
||||
needs: [changes]
|
||||
@@ -59,7 +66,7 @@ jobs:
|
||||
|
||||
required:
|
||||
name: PR Check Summary
|
||||
needs: [lint, test, build, e2e-test]
|
||||
needs: [lint, test, build, e2e-test, docs]
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
7
.github/workflows/release-changesets.yml
vendored
@@ -1,9 +1,10 @@
|
||||
name: Release Changesets
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
#push:
|
||||
# branches:
|
||||
# - main
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
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:
|
||||
@@ -12,7 +7,6 @@ on:
|
||||
- "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"
|
||||
@@ -23,8 +17,6 @@ jobs:
|
||||
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:
|
||||
@@ -50,6 +42,7 @@ jobs:
|
||||
tags: |
|
||||
type=ref,event=tag
|
||||
type=raw,value=${{ github.ref_name }}
|
||||
type=raw,value=latest
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v3
|
||||
@@ -66,3 +59,4 @@ jobs:
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
run: |
|
||||
cosign sign --yes ghcr.io/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
|
||||
cosign sign --yes ghcr.io/${{ env.IMAGE_NAME }}:latest
|
||||
|
||||
14
.github/workflows/semantic-pull-requests.yml
vendored
@@ -20,6 +20,20 @@ jobs:
|
||||
id: lint_pr_title
|
||||
env:
|
||||
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
|
||||
# When the previous steps fails, the workflow would stop. By adding this
|
||||
|
||||
10
.github/workflows/test.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
node-version: 20.x
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||
@@ -25,10 +25,12 @@ jobs:
|
||||
- name: create .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: |
|
||||
ENCRYPTION_KEY=$(openssl rand -hex 32)
|
||||
sed -i "s/ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${ENCRYPTION_KEY}/" .env
|
||||
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: Test
|
||||
run: pnpm test
|
||||
|
||||
4
.gitignore
vendored
@@ -56,5 +56,7 @@ Zone.Identifier
|
||||
packages/lib/uploads
|
||||
|
||||
# Vite Timestamps
|
||||
vite.config.*.timestamp-*
|
||||
*vite.config.*.timestamp-*
|
||||
|
||||
# js compiled assets
|
||||
apps/web/public/js
|
||||
|
||||
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
@@ -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,4 +1 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
pnpm lint-staged
|
||||
@@ -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..."
|
||||
3
.npmrc
@@ -5,4 +5,5 @@ shared-workspace-shrinkwrap = true
|
||||
access = public
|
||||
enable-pre-post-scripts = 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 = {
|
||||
...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,
|
||||
};
|
||||
|
||||
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": [
|
||||
{
|
||||
"name": "Launch localhost:3002",
|
||||
"type": "firefox",
|
||||
"request": "launch",
|
||||
"reAttach": true,
|
||||
"request": "launch",
|
||||
"type": "firefox",
|
||||
"url": "http://localhost:3002/",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
}
|
||||
|
||||
4
.vscode/settings.json
vendored
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative"
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@ 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:
|
||||
|
||||
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.
|
||||
|
||||
|
||||
6
LICENSE
@@ -1,9 +1,9 @@
|
||||
Copyright (c) 2023 Formbricks GmbH
|
||||
Copyright (c) 2024 Formbricks GmbH
|
||||
|
||||
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 "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/ee/", "apps/web/modules/ee" & "apps/web/app/(ee)" directories of this repository, if these directories exist, is licensed under the license defined in "packages/ee/LICENSE".
|
||||
- All content that resides under the "packages/js/", "packages/react-native/" and "packages/api/" directories of this repository, if that directories exist, is licensed under the "MIT" license as defined in the "LICENSE" files of these packages.
|
||||
- All 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.
|
||||
|
||||
|
||||
12
README.md
@@ -1,5 +1,7 @@
|
||||
<div id="top"></div>
|
||||
|
||||
<p align="center">Help us grow and star us on Github! ⭐️</p>
|
||||
|
||||
<p align="center">
|
||||
|
||||
<a href="https://formbricks.com">
|
||||
@@ -144,6 +146,12 @@ Or you can also deploy Formbricks on [RepoCloud](https://repocloud.io) using the
|
||||
|
||||
[](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>
|
||||
|
||||
## 👨💻 Development
|
||||
@@ -160,7 +168,7 @@ Here is what you need to be able to run Formbricks:
|
||||
|
||||
### 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
|
||||
|
||||
@@ -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.
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
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:
|
||||
- Problem description.
|
||||
- Detailed, reproducible steps, with screenshots where possible.
|
||||
- Affected version(s).
|
||||
- Known possible mitigations.
|
||||
- Your Discord username or preferred contact method.
|
||||
- Your preferred contact method.
|
||||
2. **Acknowledgement of Receipt**:
|
||||
- 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.
|
||||
|
||||
2
apps/demo-react-native/.env.example
Normal file
@@ -0,0 +1,2 @@
|
||||
EXPO_PUBLIC_API_HOST=http://192.168.178.20:3000
|
||||
EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=clzr04nkd000bcdl110j0ijyq
|
||||
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
@@ -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
34
apps/demo-react-native/app.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"expo": {
|
||||
"android": {
|
||||
"adaptiveIcon": {
|
||||
"backgroundColor": "#ffffff",
|
||||
"foregroundImage": "./assets/adaptive-icon.png"
|
||||
}
|
||||
},
|
||||
"assetBundlePatterns": ["**/*"],
|
||||
"icon": "./assets/icon.png",
|
||||
"ios": {
|
||||
"infoPlist": {
|
||||
"NSCameraUsageDescription": "Take pictures for certain activities.",
|
||||
"NSMicrophoneUsageDescription": "Need microphone access for recording videos.",
|
||||
"NSPhotoLibraryUsageDescription": "Select pictures for certain activities."
|
||||
},
|
||||
"supportsTablet": true
|
||||
},
|
||||
"jsEngine": "hermes",
|
||||
"name": "react-native-demo",
|
||||
"orientation": "portrait",
|
||||
"slug": "react-native-demo",
|
||||
"splash": {
|
||||
"backgroundColor": "#ffffff",
|
||||
"image": "./assets/splash.png",
|
||||
"resizeMode": "contain"
|
||||
},
|
||||
"userInterfaceStyle": "light",
|
||||
"version": "1.0.0",
|
||||
"web": {
|
||||
"favicon": "./assets/favicon.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
apps/demo-react-native/assets/adaptive-icon.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
apps/demo-react-native/assets/favicon.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
apps/demo-react-native/assets/icon.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
apps/demo-react-native/assets/splash.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
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
@@ -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
@@ -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;
|
||||
28
apps/demo-react-native/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "@formbricks/demo-react-native",
|
||||
"version": "1.0.0",
|
||||
"main": "./index.js",
|
||||
"scripts": {
|
||||
"dev": "expo start",
|
||||
"android": "expo start --android",
|
||||
"ios": "expo start --ios",
|
||||
"web": "expo start --web",
|
||||
"eject": "expo eject",
|
||||
"clean": "rimraf .turbo node_modules .expo"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formbricks/js": "workspace:*",
|
||||
"@formbricks/react-native": "workspace:*",
|
||||
"expo": "51.0.26",
|
||||
"expo-status-bar": "1.12.1",
|
||||
"react": "18.3.1",
|
||||
"react-native": "0.74.4",
|
||||
"react-native-webview": "13.8.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.25.2",
|
||||
"@types/react": "18.3.11",
|
||||
"typescript": "5.3.3"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
52
apps/demo-react-native/src/app.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import { StatusBar } from "expo-status-bar";
|
||||
import { Button, LogBox, StyleSheet, Text, View } from "react-native";
|
||||
import Formbricks, { track } from "@formbricks/react-native";
|
||||
|
||||
LogBox.ignoreAllLogs();
|
||||
|
||||
export default function App(): JSX.Element {
|
||||
if (!process.env.EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID) {
|
||||
throw new Error("EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID is required");
|
||||
}
|
||||
|
||||
if (!process.env.EXPO_PUBLIC_API_HOST) {
|
||||
throw new Error("EXPO_PUBLIC_API_HOST is required");
|
||||
}
|
||||
|
||||
const config = {
|
||||
environmentId: process.env.EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID,
|
||||
apiHost: process.env.EXPO_PUBLIC_API_HOST,
|
||||
userId: "random-user-id",
|
||||
attributes: {
|
||||
language: "en",
|
||||
testAttr: "attr-test",
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text>Formbricks React Native SDK Demo</Text>
|
||||
|
||||
<Button
|
||||
title="Trigger Code Action"
|
||||
onPress={() => {
|
||||
track("code").catch((error: unknown) => {
|
||||
// eslint-disable-next-line no-console -- logging is allowed in demo apps
|
||||
console.error("Error tracking event:", error);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<StatusBar style="auto" />
|
||||
<Formbricks initConfig={config} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: "#fff",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
});
|
||||
6
apps/demo-react-native/tsconfig.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true
|
||||
},
|
||||
"extends": "expo/tsconfig.base"
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
ShieldCheckIcon,
|
||||
UsersIcon,
|
||||
} from "lucide-react";
|
||||
|
||||
import { classNames } from "../lib/utils";
|
||||
|
||||
const navigation = [
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
2
apps/demo/next-env.d.ts
vendored
@@ -2,4 +2,4 @@
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: "/",
|
||||
destination: "/app",
|
||||
permanent: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"scripts": {
|
||||
"clean": "rimraf .turbo node_modules .next",
|
||||
"dev": "next dev -p 3002 --turbo",
|
||||
"go": "next dev -p 3002",
|
||||
"go": "next dev -p 3002 --turbo",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
@@ -13,8 +13,8 @@
|
||||
"dependencies": {
|
||||
"@formbricks/js": "workspace:*",
|
||||
"@formbricks/ui": "workspace:*",
|
||||
"lucide-react": "^0.379.0",
|
||||
"next": "14.2.3",
|
||||
"lucide-react": "0.452.0",
|
||||
"next": "14.2.16",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { AppProps } from "next/app";
|
||||
import Head from "next/head";
|
||||
|
||||
import "../styles/globals.css";
|
||||
import "@formbricks/ui/globals.css";
|
||||
|
||||
const App = ({ Component, pageProps }: AppProps) => {
|
||||
return (
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
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";
|
||||
import formbricks from "@formbricks/js";
|
||||
import fbsetup from "../public/fb-setup.png";
|
||||
|
||||
declare const window: any;
|
||||
|
||||
@@ -62,10 +59,9 @@ const AppPage = ({}) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="h-screen bg-white px-12 py-6 dark:bg-slate-800">
|
||||
<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 items-center gap-2">
|
||||
<SurveySwitch value="app" formbricks={formbricks} />
|
||||
<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
|
||||
@@ -119,7 +115,7 @@ const AppPage = ({}) => {
|
||||
</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">
|
||||
<div className="col-span-3 self-start rounded-lg border border-slate-300 bg-slate-100 p-6 dark:border-slate-600 dark:bg-slate-900">
|
||||
<h3 className="text-lg font-semibold dark:text-white">
|
||||
Reset person / pull data from Formbricks app
|
||||
</h3>
|
||||
@@ -142,7 +138,7 @@ const AppPage = ({}) => {
|
||||
|
||||
<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">
|
||||
<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>
|
||||
@@ -171,7 +167,7 @@ const AppPage = ({}) => {
|
||||
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">
|
||||
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>
|
||||
@@ -194,7 +190,7 @@ const AppPage = ({}) => {
|
||||
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">
|
||||
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>
|
||||
@@ -217,7 +213,7 @@ const AppPage = ({}) => {
|
||||
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">
|
||||
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>
|
||||
@@ -1,144 +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: "de",
|
||||
};
|
||||
|
||||
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;
|
||||
@@ -3,4 +3,4 @@ module.exports = {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
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;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"exclude": ["node_modules"],
|
||||
"extends": "@formbricks/config-typescript/nextjs.json",
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
|
||||
}
|
||||
|
||||
@@ -4,25 +4,18 @@ import I1 from "./images/I1.webp";
|
||||
import I2 from "./images/I2.webp";
|
||||
|
||||
export const metadata = {
|
||||
title: "Using Actions in Formbricks | Fine-tuning User Moments",
|
||||
title: "Using Actions in Formbricks",
|
||||
description:
|
||||
"Dive deep into how actions in Formbricks help products and organizations to engage users at precise moments in their journey. Discover the power of actions, from coding to no-code setups, to refine user targeting and generate richer, more detailed user insights.",
|
||||
};
|
||||
|
||||
#### App Surveys
|
||||
# Actions
|
||||
|
||||
# Actions & Targeting
|
||||
|
||||
Understanding user thoughts and feelings at critical moments in their journey is pivotal. To achieve this, Formbricks uses user-centric actions that trigger surveys at precisely the right time. Actions are essentially notifications sent from your application to Formbricks when predefined user activities occur, making it possible to gather insights during key interactions.
|
||||
|
||||
<Note>
|
||||
Ensure that you’ve **initialized Formbricks with a userId** to fully utilize this feature along with other
|
||||
app survey capabilities.
|
||||
</Note>
|
||||
Actions are predefined events within your app that prompt Formbricks to display a survey when triggered. These are detected by the Formbricks widget, which then presents the appropriate survey based on your predefined settings.
|
||||
|
||||
## **How Do Actions Work?**
|
||||
|
||||
Actions in Formbricks App Surveys are deeply integrated with user activities within your app. When a user performs a specified action, the Formbricks widget detects this activity and can present a survey to that specific user if the trigger conditions match of that survey, while also recording the event. This capability ensures that surveys are not only triggered at the right time but are also tailored to the user’s recent interactions within the app. You can set up these actions through a user-friendly No-Code interface within the Formbricks dashboard.
|
||||
Actions in Formbricks App Surveys are deeply integrated with user activities within your app. When a user performs a specified action, the Formbricks widget detects this activity and can present a survey to that specific user if the trigger conditions match for that survey. This capability ensures that surveys are triggered at the right time. You can set up these actions through a user-friendly No-Code interface within the Formbricks dashboard.
|
||||
|
||||
## **Why Are Actions Useful?**
|
||||
|
||||
@@ -30,8 +23,7 @@ Actions are invaluable for enhancing survey relevance and effectiveness:
|
||||
|
||||
- **Personalized Engagement**: Surveys triggered by user actions ensure content is highly relevant and engaging, matching each user’s current context.
|
||||
- **User Attributes**: By tying surveys to specific user attributes, such as activity levels or feature usage, you can customize the survey experience to reflect individual user profiles.
|
||||
- **User Segments**: Analyze action data to create detailed user segments, targeting specific groups with surveys that are pertinent to their behaviors or interactions within the app.
|
||||
- **User Targeting**: Precise targeting based on user actions and attributes ensures that surveys are shown only to users who meet certain criteria, enhancing the relevance and effectiveness of each survey.
|
||||
- **User Targeting**: Precise targeting based on user attributes ensures that surveys are shown only to users who meet certain criteria, enhancing the relevance and effectiveness of each survey.
|
||||
|
||||
## **Setting Up No-Code Actions**
|
||||
|
||||
@@ -127,5 +119,3 @@ return <button onClick={handleClick}>Click Me</button>;
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
This documentation frames actions around user interactions, emphasizing the connection between the user's activities and the survey experience. By leveraging user-centric actions, you can create highly targeted and timely surveys that resonate with users and yield valuable insights.
|
||||
|
||||
@@ -1,34 +1,16 @@
|
||||
import { MdxImage } from "@/components/MdxImage";
|
||||
import { ResponsiveVideo } from "@/components/ResponsiveVideo";
|
||||
|
||||
import GermansGpt from "./germans-gpt.webp";
|
||||
import Hni from "./hni.webp";
|
||||
import PowerUsers from "./power-users.webp";
|
||||
import RideHailing from "./ride-hailing.webp";
|
||||
import UpsellMiro from "./upsell-miro.webp";
|
||||
|
||||
export const metadata = {
|
||||
title: "Advanced Targeting in Surveys | Formbricks",
|
||||
title: "Advanced Targeting for App Surveys | Formbricks",
|
||||
description:
|
||||
"Advanced Targeting allows you to show surveys to just the right group of people. You can target surveys based on user attributes, user events, metadata , literally anything! This helps you get more relevant feedback and make data-driven decisions. All of this without writing a single line of code.",
|
||||
"Advanced Targeting allows you to show surveys to just the right group of people. You can target surveys based on user attributes, metadata, and other segments. This helps you get more relevant feedback and make data-driven decisions.",
|
||||
};
|
||||
|
||||
#### App Surveys
|
||||
|
||||
# Advanced Targeting
|
||||
|
||||
Advanced Targeting allows you to show surveys to the right group of people. You can target surveys based on user attributes, user events, and more instead of spraying and praying. This helps you get more relevant feedback and make data-driven decisions. All of this without writing a single line of code.
|
||||
Advanced Targeting allows you to show surveys to the right group of people. You can target surveys based on user attributes, device type, and more instead of spraying and praying. This helps you get more relevant feedback and make data-driven decisions. All of this without writing a single line of code.
|
||||
|
||||
<ResponsiveVideo title="Formbricks Multi-language Surveys"
|
||||
src="https://www.youtube-nocookie.com/embed/0BQp6N4cXzU?si=KeBM7G7Ch1xtrsOm&controls=0" />
|
||||
# How to setup Advanced Targeting
|
||||
|
||||
|
||||
## How to setup Advanced Targeting
|
||||
|
||||
<Note>
|
||||
Advanced Targeting is available on the Pro plan! Don't worry, you just need to enter your credit card
|
||||
details to start the freemium plan.
|
||||
</Note>
|
||||
<Note>Advanced Targeting is only available on the Pro plan!</Note>
|
||||
|
||||
1. On the Formbricks dashboard, click on **People** tab from the top navigation bar.
|
||||
|
||||
@@ -36,7 +18,7 @@ Advanced Targeting allows you to show surveys to the right group of people. You
|
||||
|
||||
3. Give your segment a title & a description to help you remember what this segment is about.
|
||||
|
||||
4. Now click on the **Add Filter** button to add a filter. You can filter based on actions, user attributes, other segments, devices, and more.
|
||||
4. Now click on the **Add Filter** button to add a filter. You can filter based on user attributes, other segments, devices, and more.
|
||||
|
||||
5. To group a set of filters together, click on the Three Dots icon on the right side of the filter and click on **Create Group**.
|
||||
|
||||
@@ -45,50 +27,3 @@ Advanced Targeting allows you to show surveys to the right group of people. You
|
||||
7. Once you are happy with the segment, click on **Save Segment**.
|
||||
|
||||
8. Now, when you create a survey, you can select this segment to target your survey to.
|
||||
|
||||
## Examples:
|
||||
|
||||
1. Let's say you want to upsell to: Miro, Loom, Figma, Slack and Asana.
|
||||
|
||||
<MdxImage
|
||||
src={UpsellMiro}
|
||||
alt="Upselling Opportunity"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
2. Post-experience surveying for a ride hailing app where users who have taken more than 1 ride are shown a survey.
|
||||
|
||||
<MdxImage
|
||||
src={RideHailing}
|
||||
alt="Ride Hailing Targeting"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
3. Target High Value users who have $100k+ in their bank account, own 20+ stocks, and have are an active user.
|
||||
|
||||
<MdxImage
|
||||
src={Hni}
|
||||
alt="Target Active High Net Worth Individuals"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
4. Target Germans on mobile phones who have regenerated chatGPT answers frequently in the last quarter and did so today.
|
||||
|
||||
<MdxImage
|
||||
src={GermansGpt}
|
||||
alt="Target Germans on Mobile Phones who have regenerated chatGPT answers frequently in the last quarter and did so today"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
5. Sneak Peak: How we at Formbricks automate inviting power users to chat with us
|
||||
|
||||
<MdxImage
|
||||
src={PowerUsers}
|
||||
alt="Automate inviting power users to chat with us at Formbricks"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
@@ -31,12 +31,18 @@ const libraries = [
|
||||
description: "Simply add us to your router change and sit back!",
|
||||
logo: logoVueJs,
|
||||
},
|
||||
{
|
||||
href: "#react-native",
|
||||
name: "React Native",
|
||||
description: "Easily integrate our SDK with your React Native app for seamless survey support!",
|
||||
logo: logoReactJs,
|
||||
},
|
||||
];
|
||||
|
||||
export const Libraries = () => {
|
||||
return (
|
||||
<div className="my-16 xl:max-w-none">
|
||||
<div className="not-prose mt-4 grid grid-cols-1 gap-x-6 gap-y-10 border-slate-900/5 sm:grid-cols-2 xl:max-w-none xl:grid-cols-3 dark:border-white/5">
|
||||
<div className="not-prose mt-4 grid grid-cols-1 gap-x-6 gap-y-10 border-slate-900/5 xl:max-w-none xl:grid-cols-2 2xl:grid-cols-3 dark:border-white/5">
|
||||
{libraries.map((library) => (
|
||||
<a
|
||||
key={library.name}
|
||||
|
||||
@@ -42,8 +42,8 @@ All you need to do is copy a `<script>` tag to your HTML head, and that’s abou
|
||||
!function(){
|
||||
var apiHost = "https://app.formbricks.com";
|
||||
var environmentId = "<your-environment-id>";
|
||||
var userId = "<your-user-id>";
|
||||
var t=document.createElement("script");t.type="text/javascript",t.async=!0,t.src=apiHost+"/api/packages/app";var e=document.getElementsByTagName("script")[0];e.parentNode.insertBefore(t,e),setTimeout(function(){window.formbricks.init({environmentId: environmentId, apiHost: apiHost, userId: userId})},500)}();
|
||||
var userId = "<your-user-id>"; //optional
|
||||
var t=document.createElement("script");t.type="text/javascript",t.async=!0,t.src=apiHost+"/js/formbricks.umd.cjs";var e=document.getElementsByTagName("script")[0];e.parentNode.insertBefore(t,e),setTimeout(function(){window.formbricks.init({environmentId: environmentId, apiHost: apiHost, userId: userId})},500)}();
|
||||
</script>
|
||||
<!-- END Formbricks Surveys -->
|
||||
```
|
||||
@@ -58,29 +58,26 @@ All you need to do is copy a `<script>` tag to your HTML head, and that’s abou
|
||||
<Property name="api-host" type="string">
|
||||
URL of the hosted Formbricks instance.
|
||||
</Property>
|
||||
<Property name="userId" type="string">
|
||||
User ID of the user who has active session.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
Refer to our [Example HTML project](https://github.com/formbricks/examples/tree/main/html) for more help! Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
|
||||
Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
|
||||
|
||||
---
|
||||
|
||||
## ReactJS
|
||||
|
||||
Install the Formbricks SDK using one of the package managers ie `npm`,`pnpm`,`yarn`.
|
||||
Install the Formbricks SDK using one of the package managers ie `npm`,`pnpm`,`yarn`. Note that zod is required as a peer dependency must also be installed in your project.
|
||||
|
||||
<Col>
|
||||
<CodeGroup title="Install Formbricks JS library">
|
||||
```shell {{ title: 'npm' }}
|
||||
npm install @formbricks/js
|
||||
npm install @formbricks/js zod
|
||||
```
|
||||
```shell {{ title: 'pnpm' }}
|
||||
pnpm add @formbricks/js
|
||||
pnpm add @formbricks/js zod
|
||||
```
|
||||
```shell {{ title: 'yarn' }}
|
||||
yarn add @formbricks/js
|
||||
yarn add @formbricks/js zod
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
@@ -91,13 +88,13 @@ Now, update your App.js/ts file to initialise Formbricks.
|
||||
|
||||
```js
|
||||
// other imports
|
||||
import formbricks from "@formbricks/js/app";
|
||||
import formbricks from "@formbricks/js";
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
formbricks.init({
|
||||
environmentId: "<environment-id>",
|
||||
apiHost: "<api-host>",
|
||||
userId: "<user-id>",
|
||||
userId: "<user-id>", //optional
|
||||
});
|
||||
}
|
||||
|
||||
@@ -119,12 +116,9 @@ export default App;
|
||||
<Property name="api-host" type="string">
|
||||
URL of the hosted Formbricks instance.
|
||||
</Property>
|
||||
<Property name="userId" type="string">
|
||||
User ID of the user who has active session.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
Refer to our [Example ReactJs project](https://github.com/formbricks/examples/tree/main/reactjs) for more help! Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
|
||||
Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
|
||||
|
||||
---
|
||||
|
||||
@@ -142,13 +136,13 @@ Code snippets for the integration for both conventions are provided to further a
|
||||
<Col>
|
||||
<CodeGroup title="Install Formbricks JS library">
|
||||
```shell {{ title: 'npm' }}
|
||||
npm install @formbricks/js
|
||||
npm install @formbricks/js zod
|
||||
```
|
||||
```shell {{ title: 'pnpm' }}
|
||||
pnpm add @formbricks/js
|
||||
pnpm add @formbricks/js zod
|
||||
```
|
||||
```shell {{ title: 'yarn' }}
|
||||
yarn add @formbricks/js
|
||||
yarn add @formbricks/js zod
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
@@ -164,8 +158,7 @@ yarn add @formbricks/js
|
||||
|
||||
import { usePathname, useSearchParams } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import formbricks from "@formbricks/js/app";
|
||||
import formbricks from "@formbricks/js";
|
||||
|
||||
export default function FormbricksProvider() {
|
||||
const pathname = usePathname();
|
||||
@@ -175,7 +168,7 @@ export default function FormbricksProvider() {
|
||||
formbricks.init({
|
||||
environmentId: "<environment-id>",
|
||||
apiHost: "<api-host>",
|
||||
userId: "<user-id>",
|
||||
userId: "<user-id>", //optional
|
||||
});
|
||||
}, []);
|
||||
|
||||
@@ -207,8 +200,6 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
Refer to our [Example NextJS App Directory project](https://github.com/formbricks/examples/tree/main/nextjs-app) for more help!
|
||||
|
||||
### Pages Directory
|
||||
|
||||
<Col>
|
||||
@@ -218,14 +209,13 @@ Refer to our [Example NextJS App Directory project](https://github.com/formbrick
|
||||
// other import
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import formbricks from "@formbricks/js/app";
|
||||
import formbricks from "@formbricks/js";
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
formbricks.init({
|
||||
environmentId: "<environment-id>",
|
||||
apiHost: "<api-host>",
|
||||
userId: "<user-id>",
|
||||
userId: "<user-id>", //optional
|
||||
});
|
||||
}
|
||||
|
||||
@@ -247,7 +237,6 @@ export default function App({ Component, pageProps }: AppProps) {
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
Refer to our [Example NextJS Pages Directory project](https://github.com/formbricks/examples/tree/main/nextjs-pages) for more help!
|
||||
|
||||
### Required customizations to be made
|
||||
|
||||
@@ -258,9 +247,6 @@ Refer to our [Example NextJS Pages Directory project](https://github.com/formbri
|
||||
<Property name="api-host" type="string">
|
||||
URL of the hosted Formbricks instance.
|
||||
</Property>
|
||||
<Property name="userId" type="string">
|
||||
User ID of the user who has active session.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
First we initialize the Formbricks SDK, ensuring that it only runs on the client side.
|
||||
@@ -294,13 +280,13 @@ yarn add @formbricks/js
|
||||
<CodeGroup title="src/formbricks.js">
|
||||
|
||||
```js
|
||||
import formbricks from "@formbricks/js/app";
|
||||
import formbricks from "@formbricks/js";
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
formbricks.init({
|
||||
environmentId: "<environment-id>",
|
||||
apiHost: "<api-host>",
|
||||
userId: "<user-id>",
|
||||
userId: "<user-id>", //optional
|
||||
});
|
||||
}
|
||||
|
||||
@@ -341,12 +327,66 @@ router.afterEach((to, from) => {
|
||||
<Property name="api-host" type="string">
|
||||
URL of the hosted Formbricks instance.
|
||||
</Property>
|
||||
<Property name="userId" type="string">
|
||||
User ID of the user who has active session.
|
||||
</Properties>
|
||||
|
||||
Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
|
||||
|
||||
## React Native
|
||||
|
||||
Install the Formbricks React Native SDK using one of the package managers, i.e., npm, pnpm, or yarn.
|
||||
|
||||
<Col>
|
||||
<CodeGroup title="Install Formbricks JS library">
|
||||
```shell {{ title: 'npm' }}
|
||||
npm install @formbricks/react-native
|
||||
```
|
||||
```shell {{ title: 'pnpm' }}
|
||||
pnpm add @formbricks/react-native
|
||||
```
|
||||
```shell {{ title: 'yarn' }}
|
||||
yarn add @formbricks/react-native
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
Now, update your App.js/App.tsx file to initialize Formbricks:
|
||||
<Col>
|
||||
<CodeGroup title="src/App.js">
|
||||
|
||||
```js
|
||||
// other imports
|
||||
import Formbricks from "@formbricks/react-native";
|
||||
|
||||
const config = {
|
||||
environmentId: "<environment-id>",
|
||||
apiHost: "<api-host>",
|
||||
userId: "<user-id>",
|
||||
};
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<>
|
||||
{/* Your app content */}
|
||||
<Formbricks initConfig={config} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
### Required customizations to be made
|
||||
|
||||
<Properties>
|
||||
<Property name="environment-id" type="string">
|
||||
Formbricks Environment ID.
|
||||
</Property>
|
||||
<Property name="api-host" type="string">
|
||||
URL of the hosted Formbricks instance.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
Refer to our [Example VueJs project](https://github.com/formbricks/examples/tree/main/vuejs) for more help! Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
|
||||
---
|
||||
|
||||
## Validate your setup
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 102 KiB |
BIN
apps/docs/app/app-surveys/quickstart/images/I3_1.webp
Normal file
|
After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 149 KiB |
@@ -3,6 +3,7 @@ import { MdxImage } from "@/components/MdxImage";
|
||||
import I1 from "./images/I1.webp";
|
||||
import I2 from "./images/I2.webp";
|
||||
import I3 from "./images/I3.webp";
|
||||
import I3_1 from "./images/I3_1.webp";
|
||||
import I4 from "./images/I4.webp";
|
||||
import I5 from "./images/I5.webp";
|
||||
import I6 from "./images/I6.webp";
|
||||
@@ -15,44 +16,41 @@ export const metadata = {
|
||||
"Formbricks is the easiest way to create and manage app surveys. This quickstart guide will show you how to create your first app survey in under 5 minutes.",
|
||||
};
|
||||
|
||||
#### App Surveys
|
||||
|
||||
# Quickstart
|
||||
|
||||
App surveys have 6-10x better conversion rates than emailed out surveys. This tutorial explains how to run an app survey in your web app in 10 to 15 minutes. Let’s go!
|
||||
App surveys have 6-10x better conversion rates than emailed surveys. This tutorial explains how to run a survey in both your web app and mobile app (React Native) in just 10 to 15 minutes. Let’s go!
|
||||
|
||||
<Note>
|
||||
App Surveys are ideal for websites that **have a user authentication** system. If you are looking to run
|
||||
surveys on your public facing website, head over to the [Website Surveys Quickstart
|
||||
Guide](/website-surveys/quickstart).
|
||||
</Note>
|
||||
|
||||
1. **Create a free Formbricks Cloud account**: While you can [self-host](/self-hosting/deployment) Formbricks, but the quickest and easiest way to get started is with the free Cloud plan. Just [sign up here](https://app.formbricks.com/auth/signup) and you'll be guided to our onboarding like below:
|
||||
|
||||
<Note>
|
||||
Website & App Surveys have the same integration process. The difference will come when we setup our survey.
|
||||
</Note>
|
||||
1. **Create a free Formbricks Cloud account**: While you can [self-host](/self-hosting/deployment) Formbricks, but the quickest and easiest way to get started is with the free Cloud plan. Just [sign up here](https://app.formbricks.com/auth/signup) and you'll be guided to our onboarding like below, choose the "Formbricks Surveys" option:
|
||||
|
||||
<MdxImage
|
||||
src={I1}
|
||||
alt="Choose website survey from survey type"
|
||||
alt="Choose Formbricks Surveys"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl "
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
2. **Connect your App/Website**: Once you get through a couple of onboarding steps, you’ll be asked to connect your app or website. This is where you’ll find the code snippet for both HTML as well as the npm package which you need to embed in your app:
|
||||
2. **Choose your Product Channel**: On this step, you have to choose between the various channels that you want your product to be created in, you can create both app and link surveys from all the channels, but for the onboarding, please choose between the app surveys or the public website options, upon doing this, you'll be prompted to connect your app / website to formbricks.
|
||||
|
||||
<MdxImage
|
||||
src={I2}
|
||||
alt="Choose between app and website surveys product channels"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
3. **Connect your App/Website**: Once you get through a couple of onboarding steps, you’ll be asked to connect your app or website. This is where you’ll find the code snippet for both HTML as well as the npm package which you need to embed in your app:
|
||||
|
||||
<MdxImage
|
||||
src={I3}
|
||||
alt="Code snippet for connecting app with Formbricks"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
Paste the code snippet in your app and reload the page. You should now see the Formbricks widget in the lower right corner of your app! The integration is now complete.
|
||||
Paste the code snippet in your app and reload the page. You should now see a message being displayed that your app or website is now connected with formbricks.
|
||||
|
||||
<MdxImage
|
||||
src={I3}
|
||||
src={I3_1}
|
||||
alt="App connected with Formbricks"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
@@ -67,7 +65,7 @@ Onboarding is complete! Now let’s create our first survey as you should see te
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
3. **Create your first survey**: To be able to see a survey in your app, you need to create one. We’ll choose one of the templates and head over to the survey settings:
|
||||
4. **Create your first survey**: To be able to see a survey in your app, you need to create one. We’ll choose one of the templates and head over to the survey settings:
|
||||
|
||||
Pick the Survey Type as **App Survey**.
|
||||
|
||||
@@ -78,7 +76,7 @@ Pick the Survey Type as **App Survey**.
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
4. **Set Trigger for the Survey**: Scroll down to Survey Trigger and click on **+ Add action**, choose **New Session**. This will cause this survey to appear when the Formbricks Widget tracks a new user session:
|
||||
5. **Set Trigger for the Survey**: Scroll down to Survey Trigger and click on **+ Add action**, choose **New Session**. This will cause this survey to appear when the Formbricks Widget tracks a new user session:
|
||||
|
||||
<MdxImage
|
||||
src={I6}
|
||||
@@ -87,7 +85,7 @@ Pick the Survey Type as **App Survey**.
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
5. **Set Recontact Options for debugging**: In Recontact Options we choose the following settings, so that we can play around with the survey more easily. By default, each survey will be shown only once to each user to prevent survey fatigue:
|
||||
6. **Set Recontact Options for debugging**: In Recontact Options we choose the following settings, so that we can play around with the survey more easily. By default, each survey will be shown only once to each user to prevent survey fatigue:
|
||||
|
||||
<Note>
|
||||
Please change this setting later on after testing your survey to prevent survey fatigue for your users.
|
||||
@@ -100,7 +98,7 @@ Pick the Survey Type as **App Survey**.
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
6. **Publish your survey**: Now hit **Publish** and you’ll be forwarded to the Summary Page. This is where you’ll find the responses to this survey.
|
||||
7. **Publish your survey**: Now hit **Publish** and you’ll be forwarded to the Summary Page. This is where you’ll find the responses to this survey.
|
||||
|
||||
<MdxImage
|
||||
src={I8}
|
||||
@@ -111,7 +109,7 @@ Pick the Survey Type as **App Survey**.
|
||||
|
||||
---
|
||||
|
||||
- We offer framework guides for various frontend tech, head over to the the [App Survey Framework Guides](/app-survey/framework-guides) to get started with your app survey.
|
||||
- Head over to our App Survey SDK documentation to get started with the [App Survey JS SDK](/developer-docs/app-survey-sdk).
|
||||
- We offer framework guides for various frontend tech, head over to the the [App Survey Framework Guides](/app-surveys/framework-guides) to get started with your app survey.
|
||||
- Head over to our JS SDK documentation to get started with the [JS SDK](/developer-docs/js-sdk).
|
||||
|
||||
Still struggling or something not working as expected? [Join our Discord!](https://formbricks.com/discord) and we'd be glad to assist you!
|
||||
|
||||
@@ -11,8 +11,6 @@ export const metadata = {
|
||||
"Explore how to configure Recontact options in Formbricks to control the frequency of survey exposure to users, ensuring effective feedback collection without compromising user experience.",
|
||||
};
|
||||
|
||||
#### App Surveys
|
||||
|
||||
# Recontact Options
|
||||
|
||||
Recontact options in Formbricks enable you to manage how often and under what conditions a survey is shown to a user. This feature is crucial for balancing effective feedback collection with a positive user experience by preventing survey fatigue.
|
||||
|
||||
@@ -4,19 +4,12 @@ export const metadata = {
|
||||
"Dive into the importance of user identification in surveys. Boost your survey response rates and target the right users with Formbricks.",
|
||||
};
|
||||
|
||||
#### App Surveys
|
||||
|
||||
# User Identification
|
||||
|
||||
User Identification helps you to not only segment your users but also to see more information about the user who responded to a survey. This helps you to target surveys to specific user segments and see more information about the user who responded to a survey.
|
||||
|
||||
### Understanding Identified vs Unidentified Users
|
||||
|
||||
<Note>
|
||||
Formbricks App Surveys can **only be used with identified users**. If you are looking to run surveys on your
|
||||
public-facing website, head over to the [Website Surveys](/website-surveys/quickstart) section.
|
||||
</Note>
|
||||
|
||||
In Formbricks, understanding the distinction between identified and unidentified users is crucial for effective survey segmentation and targeted feedback collection.
|
||||
|
||||
| Feature | Unidentified Users | Identified Users |
|
||||
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 44 KiB |
BIN
apps/docs/app/best-practices/contact-form/images/embed.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
apps/docs/app/best-practices/contact-form/images/embed.webp
Normal file
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 48 KiB |
BIN
apps/docs/app/best-practices/contact-form/images/name-field.webp
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
apps/docs/app/best-practices/contact-form/images/query-form.webp
Normal file
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
BIN
apps/docs/app/best-practices/contact-form/images/welcome1.webp
Normal file
|
After Width: | Height: | Size: 69 KiB |
171
apps/docs/app/best-practices/contact-form/page.mdx
Normal file
@@ -0,0 +1,171 @@
|
||||
import { MdxImage } from "@/components/MdxImage";
|
||||
import AddQuestion from "./images/add-question.webp";
|
||||
import EmailField from "./images/email-field.webp";
|
||||
import EmbedImage from "./images/embed.webp";
|
||||
import MessageField from "./images/message-field.webp";
|
||||
import NameField from "./images/name-field.webp";
|
||||
import QueryImage from "./images/query-form.webp";
|
||||
import SingleSelect from "./images/single-select-questionare.webp";
|
||||
import ToggleButton from "./images/welcome1.webp";
|
||||
|
||||
export const metadata = {
|
||||
title: "Creating a Contact Form with Formbricks: A Step-by-Step Guide",
|
||||
description:
|
||||
"Learn how to easily create a professional contact form using Formbricks, perfect for beginners and experts alike.",
|
||||
};
|
||||
|
||||
# Creating a Contact Form with Formbricks: A Step-by-Step Guide
|
||||
|
||||
Welcome to this comprehensive guide on creating a contact form using Formbricks. Whether you're just starting out or you're a seasoned developer, this tutorial will walk you through every step of building an engaging and effective contact form.
|
||||
|
||||
## What We’ll Build
|
||||
|
||||
By the end of this tutorial, you'll have created a simple contact form featuring:
|
||||
1. A welcoming introduction.
|
||||
2. Fields for collecting the user's name and email.
|
||||
3. A question to find out why they’re contacting you.
|
||||
4. A message field for users to share their queries.
|
||||
|
||||
### Setting Up Your Form
|
||||
|
||||
First, let's lay the groundwork for your form:
|
||||
|
||||
1. Head to the Surveys page and click on **New Survey**.
|
||||
2. Select **Start from Scratch** to create a new form.
|
||||
3. In the form editor, click the three dots next to a question, then select **Change Question Type** and choose **Statement (Call to Action)**.
|
||||
|
||||
<MdxImage
|
||||
src={ToggleButton}
|
||||
alt="Toggle button for Statement (Call to Action)"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
4. Add a welcoming statement to greet your users and explain the form's purpose.
|
||||
5. Personalize the greeting to make it inviting and encourage engagement.
|
||||
|
||||
<Note>
|
||||
A warm welcome sets the tone for your form. Make it friendly to encourage users to participate.
|
||||
</Note>
|
||||
|
||||
### Adding the Name Field
|
||||
|
||||
Next, let's capture the user's name:
|
||||
|
||||
1. Click **Add Question**.
|
||||
<MdxImage
|
||||
src={AddQuestion}
|
||||
alt="Adding a question in Formbricks"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
2. Enter the prompts for the name field.
|
||||
3. Turn off the **Long Answer** option at the bottom right.
|
||||
4. Adjust any **settings**, such as making the field required.
|
||||
|
||||
<MdxImage
|
||||
src={NameField}
|
||||
alt="Name field configuration"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
### Adding the Email Field
|
||||
|
||||
Now, let’s add a field to collect the user's email address:
|
||||
|
||||
1. Click **Add Question** again.
|
||||
2. Select Email as the input type.
|
||||
3. Enter a prompt for the email field.
|
||||
|
||||
<MdxImage
|
||||
src={EmailField}
|
||||
alt="Email field configuration"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
### Adding a Reason for Contact
|
||||
|
||||
Let’s now understand why the user is contacting you:
|
||||
|
||||
1. Click **Add Question** once again.
|
||||
2. Select **Change Question** Type and choose **Single Select**.
|
||||
3. Add the question "Why are you contacting us today?"
|
||||
|
||||
<MdxImage
|
||||
src={SingleSelect}
|
||||
alt="Single Select question configuration"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
<Note>Predefined options help categorize inquiries, making it easier for you to respond appropriately.</Note>
|
||||
|
||||
4. Add options like "General Inquiry," "Support," and "Feedback."
|
||||
|
||||
<MdxImage
|
||||
src={QueryImage}
|
||||
alt="Single Select question configuration"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
### Adding a Message Field
|
||||
|
||||
Finally, let’s provide a space for the user’s message:
|
||||
|
||||
1. Click **Add Question** for the last time.
|
||||
2. Add the question: "Your Message."
|
||||
3. Set the placeholder text to something like "Please write your message here."
|
||||
|
||||
<MdxImage
|
||||
src={MessageField}
|
||||
alt="Message field configuration"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
4. Consider setting a minimum character count to ensure detailed messages.
|
||||
|
||||
### Finalizing Your Form
|
||||
|
||||
Once your form is complete, follow these final steps:
|
||||
|
||||
1. Review and rearrange the questions if necessary.
|
||||
2. Test the form by filling it out as a user.
|
||||
3. Customize the **Thank You** message for submissions.
|
||||
4. Publish the form to get a shareable link.
|
||||
5. Enable submission notifications:
|
||||
- Go to your Formbricks account settings.
|
||||
- Verify your email address.
|
||||
- Ensure that **Survey** notifications are enabled.
|
||||
|
||||
### Integrating the Contact Form into Your Website
|
||||
|
||||
After publishing the form, follow these steps to integrate it into your site:
|
||||
|
||||
1. **Copy the Shareable Link**
|
||||
- Find your form in the Formbricks dashboard, and click Share.
|
||||
- Select Embed in a Web Page.
|
||||
|
||||
<MdxImage
|
||||
src={EmbedImage}
|
||||
alt="Embed Image configuration"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
2. **Embed the Code**
|
||||
- Copy the provided code and paste it into your website where you want the form to appear.
|
||||
|
||||
<Note>Note: There is an options toggle button called "Embed Mode." When enabled, it updates the `src` to `"?embed=true"` and displays your survey in a minimalist design, removing padding and background for a cleaner look.</Note>
|
||||
|
||||
3. **Test the Integration**
|
||||
- Check if the form displays correctly on your site.
|
||||
- Submit a test entry to ensure everything works and notifications are received.
|
||||
|
||||
## Conclusion
|
||||
Congratulations! You’ve successfully created and integrated a professional contact form using Formbricks. This form will help you collect valuable information from your visitors in an efficient, user-friendly way.
|
||||
|
||||
A great contact form strikes the balance between collecting necessary details and being simple enough to encourage submissions. **You’ve achieved just that!**
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 7.0 KiB |
@@ -39,13 +39,13 @@ To get this running, you'll need a bit of time. Here are the steps we're going t
|
||||
|
||||
1. To get started, create an account for the [Formbricks Cloud](https://app.formbricks.com/auth/signup).
|
||||
|
||||
2. In the Menu (top right) you see that you can switch between a “Development” and a “Production” environment. These are two separate environments so your test data doesn’t mess up the insights from prod. Switch to “Development”:
|
||||
2. In the Menu (top right) you see that you can toggle switch between a “Development” and a “Production” environment. These are two separate environments so your test data doesn’t mess up the insights from prod. Switch to “Development”:
|
||||
|
||||
<MdxImage
|
||||
src={SwitchToDev}
|
||||
alt="switch to dev environment"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
className="max-w-full rounded-lg sm:max-w-xs"
|
||||
/>
|
||||
|
||||
3. Then, create a survey using the template “Docs Feedback”:
|
||||
@@ -119,7 +119,7 @@ This allows us to capture and analyze partial feedback where the user is not wil
|
||||
|
||||
<MdxImage src={DocsNavi} alt="doc navigation" quality="100" className="max-w-full rounded-lg sm:max-w-3xl" />
|
||||
|
||||
Locate that file. We are using the [Tailwind Template “Syntax”](https://tailwindui.com/templates/syntax) for our docs. Here is our [Layout.tsx](https://github.com/formbricks/formbricks/blob/main/apps/formbricks-com/components/docs/Layout.tsx) file.
|
||||
Locate that file. We are using the [Tailwind Template “Syntax”](https://tailwindui.com/templates/syntax) for our docs.
|
||||
|
||||
3. Write the frontend code for the widget. Here is the full component (we break it down right below):
|
||||
|
||||
@@ -129,10 +129,8 @@ Locate that file. We are using the [Tailwind Template “Syntax”](https://tail
|
||||
```tsx
|
||||
import { useRouter } from "next/router";
|
||||
import { useState } from "react";
|
||||
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@formbricks/ui/Popover";
|
||||
|
||||
import { Button } from "@formbricks/ui/components/Button";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@formbricks/ui/components/Popover";
|
||||
import { handleFeedbackSubmit, updateFeedback } from "../../lib/handleFeedbackSubmit";
|
||||
|
||||
export const DocsFeedback = () => {
|
||||
|
||||
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 462 B |