@lowdefy/docs-content 0.0.0-experimental-20260928120643 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/content/actions/link.md +0 -17
- package/content/actions/login.md +5 -23
- package/content/actions/logout.md +5 -5
- package/content/actions/pdfmake.md +2 -2
- package/content/actions/setdarkmode.md +2 -2
- package/content/agents/agent-properties.md +2 -4
- package/content/agents/agentchat.md +6 -21
- package/content/agents/agentconversations.md +2 -2
- package/content/concepts/ai-agent-docs.md +101 -313
- package/content/concepts/cli.md +3 -34
- package/content/concepts/connections-and-requests.md +0 -402
- package/content/concepts/custom-html.md +0 -40
- package/content/concepts/custom-styling.md +5 -50
- package/content/concepts/events-and-actions.md +2 -64
- package/content/concepts/i18n.md +2 -7
- package/content/concepts/keyboard-shortcuts.md +2 -2
- package/content/concepts/layout-overview.md +1 -1
- package/content/concepts/logger.md +0 -81
- package/content/concepts/lowdefy-schema.md +1 -4
- package/content/concepts/menus.md +7 -7
- package/content/concepts/module-authoring.md +2 -2
- package/content/connections/anthropic.md +2 -162
- package/content/connections/awss3.md +0 -54
- package/content/connections/google.md +2 -162
- package/content/connections/mongodb.md +3 -121
- package/content/connections/openai-connection.md +2 -162
- package/content/container-blocks/affix.md +7 -47
- package/content/container-blocks/alert.md +24 -76
- package/content/container-blocks/badge.md +24 -100
- package/content/container-blocks/box.md +9 -9
- package/content/container-blocks/card.md +25 -80
- package/content/container-blocks/carousel.md +7 -60
- package/content/container-blocks/collapse.md +9 -87
- package/content/container-blocks/configprovider.md +13 -35
- package/content/container-blocks/confirmmodal.md +23 -69
- package/content/container-blocks/content.md +98 -62
- package/content/container-blocks/descriptions.md +4 -45
- package/content/container-blocks/drawer.md +28 -92
- package/content/container-blocks/dropdownmenu.md +31 -119
- package/content/container-blocks/flex.md +62 -40
- package/content/container-blocks/footer.md +11 -15
- package/content/container-blocks/googlemapsheatmap.md +1 -1
- package/content/container-blocks/googlemapsscript.md +2 -2
- package/content/container-blocks/header.md +36 -71
- package/content/container-blocks/label.md +7 -34
- package/content/container-blocks/layout.md +34 -52
- package/content/container-blocks/masonry.md +71 -17
- package/content/container-blocks/mobilemenu.md +88 -131
- package/content/container-blocks/modal.md +26 -114
- package/content/container-blocks/pageheadermenu.md +31 -81
- package/content/container-blocks/pagesidebarlayout.md +26 -169
- package/content/container-blocks/pagesidermenu.md +42 -95
- package/content/container-blocks/popover.md +31 -34
- package/content/container-blocks/result.md +21 -32
- package/content/container-blocks/sider.md +122 -85
- package/content/container-blocks/span.md +7 -7
- package/content/container-blocks/spinner.md +3 -3
- package/content/container-blocks/splitter.md +68 -150
- package/content/container-blocks/tabs.md +12 -64
- package/content/container-blocks/tooltip.md +46 -22
- package/content/container-blocks/watermark.md +4 -22
- package/content/controls/reject.md +2 -4
- package/content/controls/throw.md +0 -1
- package/content/controls/try.md +2 -3
- package/content/deployment/deployment-environments.md +6 -8
- package/content/deployment/docker.md +7 -22
- package/content/deployment/node-server.md +0 -4
- package/content/deployment/vercel.md +6 -13
- package/content/display-blocks/aggrid.md +11 -26
- package/content/display-blocks/anchor.md +15 -15
- package/content/display-blocks/avatar.md +37 -57
- package/content/display-blocks/breadcrumb.md +21 -70
- package/content/display-blocks/button.md +18 -92
- package/content/display-blocks/calendar.md +0 -12
- package/content/display-blocks/dangeroushtml.md +1 -1
- package/content/display-blocks/difflist.md +1 -1
- package/content/display-blocks/difftimeline.md +1 -1
- package/content/display-blocks/divider.md +3 -64
- package/content/display-blocks/dropdownbutton.md +28 -79
- package/content/display-blocks/echart.md +1 -35
- package/content/display-blocks/floatbutton.md +42 -129
- package/content/display-blocks/html.md +1 -61
- package/content/display-blocks/icon.md +67 -250
- package/content/display-blocks/menu.md +73 -145
- package/content/display-blocks/message.md +124 -38
- package/content/display-blocks/notification.md +222 -94
- package/content/display-blocks/paragraph.md +9 -59
- package/content/display-blocks/progress.md +17 -96
- package/content/display-blocks/qrcode.md +4 -19
- package/content/display-blocks/s3download.md +1 -1
- package/content/display-blocks/search.md +7 -17
- package/content/display-blocks/skeletonbutton.md +0 -1
- package/content/display-blocks/skeletoninput.md +0 -1
- package/content/display-blocks/statistic.md +17 -75
- package/content/display-blocks/steps.md +25 -58
- package/content/display-blocks/tag.md +17 -102
- package/content/display-blocks/title.md +11 -50
- package/content/display-blocks/tour.md +23 -33
- package/content/input-blocks/autocomplete.md +9 -81
- package/content/input-blocks/buttonselector.md +8 -45
- package/content/input-blocks/checkboxselector.md +7 -97
- package/content/input-blocks/checkboxswitch.md +3 -33
- package/content/input-blocks/colorselector.md +14 -63
- package/content/input-blocks/daterangeselector.md +45 -165
- package/content/input-blocks/dateselector.md +41 -139
- package/content/input-blocks/datetimeselector.md +49 -158
- package/content/input-blocks/inputgallery.md +3 -3
- package/content/input-blocks/monthselector.md +52 -120
- package/content/input-blocks/multipleselector.md +39 -150
- package/content/input-blocks/numberinput.md +12 -98
- package/content/input-blocks/pagination.md +14 -70
- package/content/input-blocks/paragraphinput.md +37 -51
- package/content/input-blocks/passwordinput.md +6 -60
- package/content/input-blocks/phonenumberinput.md +26 -59
- package/content/input-blocks/radioselector.md +7 -83
- package/content/input-blocks/ratingslider.md +26 -57
- package/content/input-blocks/s3uploadbutton.md +24 -24
- package/content/input-blocks/s3uploaddragger.md +3 -3
- package/content/input-blocks/s3uploadphoto.md +5 -5
- package/content/input-blocks/segmentedselector.md +32 -72
- package/content/input-blocks/selector.md +21 -109
- package/content/input-blocks/switch.md +17 -65
- package/content/input-blocks/textarea.md +3 -32
- package/content/input-blocks/textinput.md +28 -69
- package/content/input-blocks/tiptapmentioninput.md +1 -1
- package/content/input-blocks/titleinput.md +12 -39
- package/content/input-blocks/treeinput.md +25 -49
- package/content/input-blocks/treemultipleselector.md +10 -127
- package/content/input-blocks/treeselector.md +9 -97
- package/content/input-blocks/upload.md +15 -64
- package/content/input-blocks/uploaddragger.md +1 -17
- package/content/input-blocks/uploadphoto.md +1 -18
- package/content/input-blocks/weekselector.md +55 -120
- package/content/list-blocks/controlledlist.md +15 -24
- package/content/list-blocks/list.md +11 -11
- package/content/list-blocks/masonrylist.md +34 -7
- package/content/migration/upgrade-guide.md +0 -1
- package/content/operators/_app.md +1 -2
- package/content/operators/_error.md +1 -23
- package/content/operators/_get.md +6 -6
- package/content/operators/_operator.md +3 -17
- package/content/operators/_ref.md +0 -258
- package/content/operators/_type.md +3 -14
- package/content/operators/_user.md +3 -3
- package/content/other/introduction.md +1 -1
- package/content/plugins/plugins-dev.md +0 -66
- package/content/plugins/plugins-introduction.md +1 -27
- package/content/testing/e2e-configuration.md +1 -8
- package/content/testing/e2e-getting-started.md +1 -1
- package/content/testing/e2e-introduction.md +1 -1
- package/content/tutorial/tutorial-actions-operators.md +2 -2
- package/content/tutorial/tutorial-add-blocks.md +2 -2
- package/content/tutorial/tutorial-create-page.md +2 -48
- package/content/tutorial/tutorial-display-data-page.md +3 -3
- package/content/tutorial/tutorial-requests-sql.md +0 -153
- package/content/tutorial/tutorial-start.md +1 -66
- package/content/user-authentication/login-and-logout.md +8 -158
- package/content/user-authentication/users-introduction.md +0 -8
- package/index.json +2 -6065
- package/package.json +1 -7
- package/scripts/extractAgentDocs.js +4 -36
- package/content/actions/emailotpsend.md +0 -58
- package/content/actions/emailotpverify.md +0 -77
- package/content/actions/getlocalstorage.md +0 -41
- package/content/actions/magiclinkverify.md +0 -113
- package/content/actions/posthogalias.md +0 -29
- package/content/actions/posthogcapture.md +0 -49
- package/content/actions/posthogcapturepageleave.md +0 -38
- package/content/actions/posthogfeatureflag.md +0 -50
- package/content/actions/posthoggroup.md +0 -37
- package/content/actions/posthogidentify.md +0 -54
- package/content/actions/posthoginit.md +0 -66
- package/content/actions/posthogoptin.md +0 -29
- package/content/actions/posthogoptout.md +0 -29
- package/content/actions/posthogpageview.md +0 -37
- package/content/actions/posthogreloadfeatureflags.md +0 -47
- package/content/actions/posthogreset.md +0 -28
- package/content/actions/posthogsetpersonproperties.md +0 -36
- package/content/actions/removelocalstorage.md +0 -26
- package/content/actions/setlocalstorage.md +0 -32
- package/content/adapter-reference/mongodbauthadapter.md +0 -46
- package/content/agents/agents-introduction.md +0 -107
- package/content/callback-reference/auth0logoutcallback.md +0 -54
- package/content/concepts/blocks.md +0 -150
- package/content/concepts/dynamic-page-content.md +0 -304
- package/content/concepts/html-attributes.md +0 -285
- package/content/concepts/lowdefy-api.md +0 -1110
- package/content/concepts/notifications.md +0 -155
- package/content/concepts/page-and-app-state.md +0 -244
- package/content/concepts/theming.md +0 -582
- package/content/connections/aigateway.md +0 -368
- package/content/connections/axioshttp.md +0 -181
- package/content/controls/while.md +0 -46
- package/content/display-blocks/clickablehtml.md +0 -239
- package/content/display-blocks/eventlog.md +0 -363
- package/content/display-blocks/pollingtimer.md +0 -263
- package/content/input-blocks/captcha.md +0 -40
- package/content/input-blocks/placesautocomplete.md +0 -319
- package/content/input-blocks/tagmultipleselector.md +0 -114
- package/content/input-blocks/tagselector.md +0 -114
- package/content/migration/auth-upgrade.md +0 -327
- package/content/migration/v3-to-v4.md +0 -417
- package/content/migration/v6-to-v7.md +0 -222
- package/content/operators/_cron.md +0 -292
- package/content/other/mongodbadapter.md +0 -3
- package/content/plugins/plugins-icon-sets.md +0 -267
- package/content/plugins/posthog.md +0 -275
- package/content/provider-reference/auth-js-providers.md +0 -107
- package/content/provider-reference/openidconnectprovider.md +0 -44
- package/content/testing/config-tests.md +0 -278
- package/content/tutorial/tutorial-requests-api.md +0 -155
- package/content/user-authentication/auth-configuration.md +0 -266
- package/content/user-authentication/auth-steps.md +0 -138
- package/content/user-authentication/mcp-oauth.md +0 -292
- package/content/user-authentication/organizations.md +0 -221
- package/content/user-authentication/protected-pages-apis.md +0 -97
- package/content/user-authentication/roles.md +0 -49
- package/content/user-authentication/two-factor.md +0 -229
- package/content/user-authentication/user-object.md +0 -58
- package/content/websockets/websocket-publish.md +0 -108
- package/content/websockets/websocket-subscriptions.md +0 -100
- package/content/websockets/websockets-introduction.md +0 -97
- package/hazards.json +0 -44
- package/scripts/hashDocSources.js +0 -38
- package/scripts/recordSourceHashes.js +0 -88
|
@@ -1,51 +1,5 @@
|
|
|
1
1
|
# 2. Creating a page
|
|
2
2
|
|
|
3
|
-
Let's create a page for a web form where users can log a new ticket.
|
|
4
|
-
|
|
5
|
-
#### 2.1. Create `new-ticket.yaml`
|
|
6
|
-
|
|
7
|
-
Create a new YAML file in the project directory (the same directory as the `lowdefy.yaml` file) called `new-ticket.yaml`, with the following content:
|
|
8
|
-
|
|
9
|
-
```yaml
|
|
10
|
-
id: new-ticket
|
|
11
|
-
type: PageHeaderMenu
|
|
12
|
-
properties:
|
|
13
|
-
title: New ticket # The title in the browser tab.
|
|
14
|
-
layout:
|
|
15
|
-
justify: center # Center the contents of the page.
|
|
16
|
-
blocks:
|
|
17
|
-
- id: content_card
|
|
18
|
-
type: Card
|
|
19
|
-
layout:
|
|
20
|
-
size: 800 # Set the size of the card so it does not fill the full screen.
|
|
21
|
-
gap: 16 # Make a 16px gap between all blocks in this card.
|
|
22
|
-
blocks:
|
|
23
|
-
- id: page_heading
|
|
24
|
-
type: Title
|
|
25
|
-
properties:
|
|
26
|
-
content: Log a ticket # Change the title on the page.
|
|
27
|
-
level: 3 # Make the title a little smaller (an html `<h3>`).
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
#### 2.2. Update `lowdefy.yaml`
|
|
31
|
-
|
|
32
|
-
Change the `lowdefy.yaml` to look like this:
|
|
33
|
-
|
|
34
|
-
```yaml
|
|
35
|
-
name: lowdefy-project-template
|
|
36
|
-
lowdefy: 6.0.0
|
|
37
|
-
|
|
38
|
-
pages:
|
|
39
|
-
################ -------- Copy from here -------- ################
|
|
40
|
-
- _ref: new-ticket.yaml
|
|
41
|
-
################ -------- Copy to here ---------- ################
|
|
42
|
-
- id: welcome
|
|
43
|
-
type: PageHeaderMenu
|
|
44
|
-
properties:
|
|
45
|
-
title: Welcome
|
|
46
|
-
# ...
|
|
47
|
-
```
|
|
48
|
-
|
|
49
3
|
> Indentation is important
|
|
50
4
|
|
|
51
5
|
#### 2.3. Navigate to the new page
|
|
@@ -79,13 +33,13 @@ menus:
|
|
|
79
33
|
- id: new-ticket
|
|
80
34
|
type: MenuLink
|
|
81
35
|
properties:
|
|
82
|
-
icon:
|
|
36
|
+
icon: AiOutlineAlert
|
|
83
37
|
title: New ticket
|
|
84
38
|
pageId: new-ticket
|
|
85
39
|
- id: welcome
|
|
86
40
|
type: MenuLink
|
|
87
41
|
properties:
|
|
88
|
-
icon:
|
|
42
|
+
icon: AiOutlineHome
|
|
89
43
|
title: Home
|
|
90
44
|
pageId: welcome
|
|
91
45
|
################ -------- Copy to here ---------- ################
|
|
@@ -140,20 +140,20 @@ menus:
|
|
|
140
140
|
- id: welcome
|
|
141
141
|
type: MenuLink
|
|
142
142
|
properties:
|
|
143
|
-
icon:
|
|
143
|
+
icon: AiOutlineHome
|
|
144
144
|
title: Home
|
|
145
145
|
pageId: welcome
|
|
146
146
|
- id: new-ticket
|
|
147
147
|
type: MenuLink
|
|
148
148
|
properties:
|
|
149
|
-
icon:
|
|
149
|
+
icon: AiOutlineAlert
|
|
150
150
|
title: New ticket
|
|
151
151
|
pageId: new-ticket
|
|
152
152
|
################ -------- Copy from here -------- ################
|
|
153
153
|
- id: view-tickets
|
|
154
154
|
type: MenuLink
|
|
155
155
|
properties:
|
|
156
|
-
icon:
|
|
156
|
+
icon: AiOutlineDatabase
|
|
157
157
|
title: View tickets
|
|
158
158
|
pageId: view-tickets
|
|
159
159
|
############### -------- Copy to here ---------- ################
|
|
@@ -20,156 +20,3 @@ We will make use of an SQLite database with a table called `tickets`, that has t
|
|
|
20
20
|
- created_date
|
|
21
21
|
|
|
22
22
|
The database used in this tutorial can be downloaded <a href="/tutorial/tutorial_db.sqlite" download>here</a>.
|
|
23
|
-
|
|
24
|
-
Alternatively, install [SQLite](https://www.sqlite.org/) on your computer and use the following to create the needed table:
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
CREATE TABLE tickets (
|
|
28
|
-
ticket_title varchar(100),
|
|
29
|
-
ticket_type varchar(50),
|
|
30
|
-
ticket_description varchar(200),
|
|
31
|
-
product varchar(100),
|
|
32
|
-
purchase_in_last_month real,
|
|
33
|
-
created_date real
|
|
34
|
-
);
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
#### 6.2. Setting app environment variables
|
|
38
|
-
|
|
39
|
-
Create a file called `.env` in your project directory with the following content:
|
|
40
|
-
|
|
41
|
-
##### `.env`
|
|
42
|
-
```
|
|
43
|
-
LOWDEFY_SECRET_SQLITE_FILENAME= __ABSOLUTE_PATH_TO_SQLITE_DB_FILE__
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
#### 5.2.3. Setting the connection environment variable secrets
|
|
47
|
-
|
|
48
|
-
In your `lowdefy.yaml` file, add the following:
|
|
49
|
-
|
|
50
|
-
##### `lowdefy.yaml`
|
|
51
|
-
|
|
52
|
-
```yaml
|
|
53
|
-
name: lowdefy-project-template
|
|
54
|
-
version: 6.0.0
|
|
55
|
-
|
|
56
|
-
connections:
|
|
57
|
-
# ...
|
|
58
|
-
################ -------- Copy from here -------- ################
|
|
59
|
-
- id: knex
|
|
60
|
-
type: Knex
|
|
61
|
-
properties:
|
|
62
|
-
client: sqlite
|
|
63
|
-
connection:
|
|
64
|
-
filename:
|
|
65
|
-
_secret: SQLITE_FILENAME
|
|
66
|
-
################ ------- Copy to here ----------- ################
|
|
67
|
-
|
|
68
|
-
menus:
|
|
69
|
-
# ...
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
#### 6.3. Creating a save data request
|
|
73
|
-
|
|
74
|
-
In your `new-ticket.yaml` file, add the following request:
|
|
75
|
-
|
|
76
|
-
##### `pages/new-ticket.yaml`
|
|
77
|
-
|
|
78
|
-
```yaml
|
|
79
|
-
id: new-ticket
|
|
80
|
-
type: PageHeaderMenu
|
|
81
|
-
requests:
|
|
82
|
-
# ...
|
|
83
|
-
################ -------- Copy from here -------- ################
|
|
84
|
-
- id: save_new_ticket
|
|
85
|
-
type: KnexRaw
|
|
86
|
-
connectionId: knex
|
|
87
|
-
payload:
|
|
88
|
-
_state: true
|
|
89
|
-
properties:
|
|
90
|
-
query: |
|
|
91
|
-
INSERT INTO tickets (
|
|
92
|
-
ticket_title,
|
|
93
|
-
ticket_type,
|
|
94
|
-
ticket_description,
|
|
95
|
-
product,
|
|
96
|
-
purchase_in_last_month,
|
|
97
|
-
created_date)
|
|
98
|
-
VALUES(
|
|
99
|
-
:ticket_title,
|
|
100
|
-
:ticket_type,
|
|
101
|
-
:ticket_description,
|
|
102
|
-
:product,
|
|
103
|
-
:purchase_in_last_month,
|
|
104
|
-
:created_date)
|
|
105
|
-
parameters:
|
|
106
|
-
ticket_title:
|
|
107
|
-
_payload: ticket_title
|
|
108
|
-
ticket_type:
|
|
109
|
-
_payload: ticket_type
|
|
110
|
-
ticket_description:
|
|
111
|
-
_payload: ticket_description
|
|
112
|
-
product:
|
|
113
|
-
_payload: product
|
|
114
|
-
purchase_in_last_month:
|
|
115
|
-
_payload: purchase_in_last_month
|
|
116
|
-
created_date:
|
|
117
|
-
_date: now
|
|
118
|
-
################ ------- Copy to here ----------- ################
|
|
119
|
-
|
|
120
|
-
properties:
|
|
121
|
-
title: New ticket # The title in the browser tab.
|
|
122
|
-
layout:
|
|
123
|
-
justify: center # Center the contents of the page
|
|
124
|
-
blocks:
|
|
125
|
-
# ...
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
#### 6.4. Triggering the save data request
|
|
129
|
-
|
|
130
|
-
In your `new-ticket.yaml` file, add the following action to the submit button:
|
|
131
|
-
|
|
132
|
-
##### `pages/new-ticket.yaml`
|
|
133
|
-
|
|
134
|
-
```yaml
|
|
135
|
-
id: new-ticket
|
|
136
|
-
# ...
|
|
137
|
-
blocks:
|
|
138
|
-
- id: content_card
|
|
139
|
-
# ...
|
|
140
|
-
blocks:
|
|
141
|
-
- id: page_heading
|
|
142
|
-
# ...
|
|
143
|
-
# ...
|
|
144
|
-
# ...
|
|
145
|
-
- id: submit_button
|
|
146
|
-
type: Button
|
|
147
|
-
#...
|
|
148
|
-
events:
|
|
149
|
-
onClick:
|
|
150
|
-
- id: validate
|
|
151
|
-
type: Validate
|
|
152
|
-
################ -------- Copy from here -------- ################
|
|
153
|
-
- id: save_new_ticket # Make a request to the SQLite database
|
|
154
|
-
type: Request
|
|
155
|
-
params: save_new_ticket
|
|
156
|
-
- id: reset # Reset the form once data has been submitted
|
|
157
|
-
type: Reset
|
|
158
|
-
################ ------- Copy to here ----------- ################
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
If you click the submit button, your form data should be added into the tickets table.
|
|
162
|
-
|
|
163
|
-
### What happened
|
|
164
|
-
|
|
165
|
-
We set up the table and column names we will be using in our SQLite database. We need to do this to use the `SQLite` connection.
|
|
166
|
-
|
|
167
|
-
We defined the `Knex` connection, with the SQLite client.
|
|
168
|
-
|
|
169
|
-
We also defined a `KnexRaw` request, to save the data to our SQLite database, and called that request when clicking the submit button. This was done by making use of the `onCLick` event on the button to execute the [`Request`](/Request) action to make the API call.
|
|
170
|
-
|
|
171
|
-
> You can find the final configuration files for this section [here](https://github.com/lowdefy/lowdefy-example-tutorial/tree/main/06-requests-sql).
|
|
172
|
-
|
|
173
|
-
### Up next
|
|
174
|
-
|
|
175
|
-
Next, we will add a new page to display all open tickets an a table.
|
|
@@ -4,7 +4,7 @@ In this tutorial, we will be creating a simple ticketing app that allows users t
|
|
|
4
4
|
|
|
5
5
|
### Requirements
|
|
6
6
|
|
|
7
|
-
The Lowdefy CLI (Command Line Interface) is needed to run the development server. To run the Lowdefy CLI you need to install Node.js at version
|
|
7
|
+
The Lowdefy CLI (Command Line Interface) is needed to run the development server. To run the Lowdefy CLI you need to install Node.js at version 22 or greater. If you don't have it installed, download Node.js from https://nodejs.org/en/download/, and follow the installation steps for your computer. All of the default settings given by the installer are fine for what we need. You will also need a text editor to modify the Lowdefy configuration files.
|
|
8
8
|
|
|
9
9
|
Lowdefy also requires [`pnpm`](https://pnpm.io/) to be installed. We recommend making activating [Corepack](https://nodejs.org/api/corepack.html), even though it is an experimental feature, as this is the simplest way to install `pnpm`. Alternative methods to install `pnpm` can also be found [here](https://pnpm.io/installation). To activate Corepack, run the following in your computer's command line interface (Windows CMD, Terminal on MacOS).
|
|
10
10
|
|
|
@@ -58,71 +58,6 @@ Made by a Lowdefy 🤖
|
|
|
58
58
|
|
|
59
59
|
> __Error: Could not find "lowdefy.yaml"__: Make sure your current working directory contains the _lowdefy.yaml_ file. You can verify this by running the `dir` (Windows) or `ls` (MacOS) command.
|
|
60
60
|
|
|
61
|
-
#### 1.5. Open the configuration file
|
|
62
|
-
|
|
63
|
-
Open the `lowdefy.yaml` file using a text editor like [VS Code](https://code.visualstudio.com/download). The content of the file should look like this:
|
|
64
|
-
|
|
65
|
-
```yaml
|
|
66
|
-
lowdefy: 6.0.0
|
|
67
|
-
name: Lowdefy starter
|
|
68
|
-
|
|
69
|
-
pages:
|
|
70
|
-
- id: welcome
|
|
71
|
-
type: PageHeaderMenu
|
|
72
|
-
properties:
|
|
73
|
-
title: Welcome
|
|
74
|
-
slots:
|
|
75
|
-
content:
|
|
76
|
-
justify: center
|
|
77
|
-
blocks:
|
|
78
|
-
- id: content_card
|
|
79
|
-
type: Card
|
|
80
|
-
style:
|
|
81
|
-
maxWidth: 800
|
|
82
|
-
blocks:
|
|
83
|
-
- id: content
|
|
84
|
-
type: Result
|
|
85
|
-
properties:
|
|
86
|
-
title: Welcome to your Lowdefy app
|
|
87
|
-
subTitle: We are excited to see what you are going to build
|
|
88
|
-
icon:
|
|
89
|
-
name: heart
|
|
90
|
-
color: '#f00'
|
|
91
|
-
slots:
|
|
92
|
-
extra:
|
|
93
|
-
blocks:
|
|
94
|
-
- id: docs_button
|
|
95
|
-
type: Button
|
|
96
|
-
properties:
|
|
97
|
-
size: large
|
|
98
|
-
title: Let's build something
|
|
99
|
-
events:
|
|
100
|
-
onClick:
|
|
101
|
-
- id: link_to_docs
|
|
102
|
-
type: Link
|
|
103
|
-
params:
|
|
104
|
-
url: https://docs.lowdefy.com
|
|
105
|
-
newWindow: true
|
|
106
|
-
footer:
|
|
107
|
-
blocks:
|
|
108
|
-
- id: footer
|
|
109
|
-
type: Paragraph
|
|
110
|
-
properties:
|
|
111
|
-
type: secondary
|
|
112
|
-
content: |
|
|
113
|
-
Made by a Lowdefy 🤖
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
This configuration completely describes the app you are running.
|
|
117
|
-
|
|
118
|
-
#### 1.6. Make some changes
|
|
119
|
-
|
|
120
|
-
Let's modify the button to link to the Lowdefy Discord community instead of to the Lowdefy docs.
|
|
121
|
-
|
|
122
|
-
First, change `Let's build something` to `Join Lowdefy on Discord`, then save the file. Your page should automatically refresh and you should see the changed text of the button.
|
|
123
|
-
|
|
124
|
-
Next, update the link url to the Lowdefy Discord link, replace `https://docs.lowdefy.com` with [`https://discord.gg/QQY9eJ7A2D`](https://discord.gg/QQY9eJ7A2D), then save the file. If you now click the button it should open a new window linking to the Lowdefy Discord Channel - Join our community to ask questions or learn more regarding Lowdefy ❤️
|
|
125
|
-
|
|
126
61
|
### What happened
|
|
127
62
|
|
|
128
63
|
The Lowdefy CLI helps you develop a Lowdefy app.
|
|
@@ -8,19 +8,15 @@ The `Login` action is used to start the user login flow. If only one OAuth provi
|
|
|
8
8
|
|
|
9
9
|
The authorization url usually hosts a page where the user can input their credentials. After the user has logged in successfully, the user is redirected to the `/api/auth/callback/<provider_id>` route in the Lowdefy app, where the rest of the authorization code flow is completed. This URL usually needs to be configured in the identity provider's settings.
|
|
10
10
|
|
|
11
|
-
The `callbackUrl`
|
|
12
|
-
|
|
13
|
-
In a protected app a signed-out visitor is sent to the sign-in page for every URL, present or absent, so a `404` means you are signed in — see [Page and API existence](/auth-upgrade#page-and-api-existence-is-no-longer-observable-before-you-sign-in).
|
|
14
|
-
|
|
15
|
-
To sign in without navigating at all - a login form in a modal, or an embedded panel - set `callbackUrl: false`. This is not valid for magic-link or social/OAuth sign-in, which redirect through a hop Lowdefy does not control.
|
|
11
|
+
The `callbackUrl` parameters of the Login action specify where the user is redirected after login is complete. If `callbackUrl` parameters not set, the user will return to the page from which the action was called.
|
|
16
12
|
|
|
17
13
|
The parameters are:
|
|
18
14
|
- `authUrl: object`:
|
|
19
15
|
- `urlQuery: object`: Query parameters to set for the authorization URL.
|
|
20
|
-
- `callbackUrl: object
|
|
16
|
+
- `callbackUrl: object`:
|
|
21
17
|
- `home: boolean`: Redirect to the home page after the login flow is complete.
|
|
22
18
|
- `pageId: string`: The pageId of the page to redirect to after the login flow is complete.
|
|
23
|
-
- `url: string`: The URL to redirect to after the login flow is complete.
|
|
19
|
+
- `url: string`: The URL to redirect to after the login flow is complete.
|
|
24
20
|
- `urlQuery: object`: The urlQuery to set for the page the user is redirected to after login.
|
|
25
21
|
- `providerId: string`: The ID of the provider that should be used for login. If not set and only one provider is configured the configured provider will be used. Else the user will be redirected to a sign in page where they can choose a provider.
|
|
26
22
|
|
|
@@ -95,164 +91,18 @@ events:
|
|
|
95
91
|
screen_hint: signup
|
|
96
92
|
```
|
|
97
93
|
|
|
98
|
-
## Magic link sign-in
|
|
99
|
-
|
|
100
|
-
`Login` with `magicLink: true` emails the user a sign-in link. By default that link points straight at Better Auth's verify endpoint, which consumes the single-use token on the first `GET` of the URL.
|
|
101
|
-
|
|
102
|
-
That is a problem in corporate inboxes. Mail security products — Microsoft Defender Safe Links, Proofpoint URL Defense, Mimecast — fetch every link in a message as it is delivered, so the scanner spends the token minutes before the person clicks, and the person lands on the error page with `INVALID_TOKEN`.
|
|
103
|
-
|
|
104
|
-
Set [`auth.authPages.magicLink`](/auth-configuration#auth-pages) to a page in your app to close that gap. The email then links to your page, carrying the token and the callback destinations on its URL query, and a [`MagicLinkVerify`](/MagicLinkVerify) action on a **button click** walks to the verify endpoint. Fetching the landing page does nothing, so a scanner cannot consume the token.
|
|
105
|
-
|
|
106
|
-
```yaml
|
|
107
|
-
lowdefy: {{ version }}
|
|
108
|
-
auth:
|
|
109
|
-
magicLink:
|
|
110
|
-
enabled: true
|
|
111
|
-
authPages:
|
|
112
|
-
magicLink: /magic-link
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
```yaml
|
|
116
|
-
id: magic-link
|
|
117
|
-
type: PageHeaderMenu
|
|
118
|
-
blocks:
|
|
119
|
-
- id: content
|
|
120
|
-
type: Box
|
|
121
|
-
blocks:
|
|
122
|
-
- id: title
|
|
123
|
-
type: Title
|
|
124
|
-
properties:
|
|
125
|
-
content: Sign in
|
|
126
|
-
level: 3
|
|
127
|
-
- id: description
|
|
128
|
-
type: Paragraph
|
|
129
|
-
properties:
|
|
130
|
-
content: Click the button below to finish signing in.
|
|
131
|
-
- id: verify_button
|
|
132
|
-
type: Button
|
|
133
|
-
properties:
|
|
134
|
-
title: Sign in
|
|
135
|
-
events:
|
|
136
|
-
onClick:
|
|
137
|
-
- id: verify
|
|
138
|
-
type: MagicLinkVerify
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
The landing page must be reachable while signed out, so add it to `auth.pages.public`.
|
|
142
|
-
|
|
143
|
-
Do **not** run `MagicLinkVerify` in the page's `onMount`, and do not auto-redirect to the verify endpoint. Some scanning sandboxes render the page and execute its JavaScript, so an automatic verify hands the token back to the scanner and you are no better off than linking at the endpoint directly. The whole protection is that a human has to click.
|
|
144
|
-
|
|
145
|
-
The action needs no parameters: the token and the `callbackURL`, `newUserCallbackURL` and `errorCallbackURL` values are read from the landing page's own URL query, which the email put there. Pass parameters only to override them.
|
|
146
|
-
|
|
147
|
-
## Email code sign-in
|
|
148
|
-
|
|
149
|
-
With `auth.emailOTP` enabled the app can sign a person in with a code emailed to them. Unlike a magic link, the code is typed into the tab they started from — so it survives corporate mail security that pre-fetches links (and consumes the single-use link token), and it works when the email is read on another device.
|
|
150
|
-
|
|
151
|
-
Two actions drive it:
|
|
152
|
-
|
|
153
|
-
- [`EmailOtpSend`](/EmailOtpSend) sends the code to an email address.
|
|
154
|
-
- [`EmailOtpVerify`](/EmailOtpVerify) signs the person in with the code they typed.
|
|
155
|
-
|
|
156
|
-
`EmailOtpVerify` lands the user through the same three sources as `Login`: the `callbackUrl` param, then the `?callbackUrl=` query, then the app's home page. `callbackUrl: false` verifies without navigating — it is a plain JSON sign-in with no redirect hop, so staying put is valid here.
|
|
157
|
-
|
|
158
|
-
### With magic link, one email carries both
|
|
159
|
-
|
|
160
|
-
When `auth.magicLink` is enabled alongside `auth.emailOTP`, the magic-link email also carries the code, so one email serves both paths. Keep using `Login` with `magicLink: true` to send it — no change is needed — and offer `EmailOtpVerify` on the same page for people whose link does not work. `EmailOtpSend` is then only needed for a code-only page, where no link is offered at all.
|
|
161
|
-
|
|
162
|
-
###### A sign-in page offering a code:
|
|
163
|
-
```yaml
|
|
164
|
-
id: email-code-login
|
|
165
|
-
type: Box
|
|
166
|
-
blocks:
|
|
167
|
-
- id: email
|
|
168
|
-
type: TextInput
|
|
169
|
-
required: true
|
|
170
|
-
properties:
|
|
171
|
-
title: Email
|
|
172
|
-
- id: send_code
|
|
173
|
-
type: Button
|
|
174
|
-
properties:
|
|
175
|
-
title: Send code
|
|
176
|
-
events:
|
|
177
|
-
onClick:
|
|
178
|
-
- id: validate
|
|
179
|
-
type: Validate
|
|
180
|
-
params:
|
|
181
|
-
regex: ^email$
|
|
182
|
-
- id: send
|
|
183
|
-
type: EmailOtpSend
|
|
184
|
-
params:
|
|
185
|
-
email:
|
|
186
|
-
_state: email
|
|
187
|
-
- id: sent
|
|
188
|
-
type: DisplayMessage
|
|
189
|
-
params:
|
|
190
|
-
content: Check your email for the sign-in code.
|
|
191
|
-
- id: otp
|
|
192
|
-
type: TextInput
|
|
193
|
-
properties:
|
|
194
|
-
title: Sign-in code
|
|
195
|
-
- id: sign_in
|
|
196
|
-
type: Button
|
|
197
|
-
properties:
|
|
198
|
-
title: Sign in
|
|
199
|
-
events:
|
|
200
|
-
onClick:
|
|
201
|
-
- id: verify
|
|
202
|
-
type: EmailOtpVerify
|
|
203
|
-
params:
|
|
204
|
-
email:
|
|
205
|
-
_state: email
|
|
206
|
-
otp:
|
|
207
|
-
_state: otp
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
An expired, mistyped or already-used code is the auth server rejecting the attempt, not an app fault: the action throws a `UserError`, so `onClick` `catch` actions run and the message displays, but nothing is reported to the server.
|
|
211
|
-
|
|
212
|
-
## SMS sign-in
|
|
213
|
-
|
|
214
|
-
`PhoneNumberVerify` completes a sign-in with an SMS code, and lands the user through the same three sources as `Login`: the `callbackUrl` param, then the `?callbackUrl=` query, then the app's home page. `callbackUrl: false` verifies without navigating.
|
|
215
|
-
|
|
216
|
-
Two modes are exempt, because neither ends with the user arriving somewhere new:
|
|
217
|
-
|
|
218
|
-
- `disableSession: true` creates no session to arrive with.
|
|
219
|
-
- `updatePhoneNumber: true` is a signed-in user confirming a new number. They already had a session, and the confirmation usually runs in a modal a navigation would yank them out of.
|
|
220
|
-
|
|
221
|
-
### Carrying the destination to a separate SMS form
|
|
222
|
-
|
|
223
|
-
The `?callbackUrl=` query is read from the page the action runs on, and the unauthenticated bounce puts it on `authPages.signIn`. If your SMS form lives on its own page, the link that gets the user there has to carry the parameter, or the destination falls back to the home page:
|
|
224
|
-
|
|
225
|
-
```yaml
|
|
226
|
-
- id: to_phone_login
|
|
227
|
-
type: Link
|
|
228
|
-
params:
|
|
229
|
-
pageId: phone-login
|
|
230
|
-
urlQuery:
|
|
231
|
-
_if:
|
|
232
|
-
test:
|
|
233
|
-
_eq:
|
|
234
|
-
- _url_query: callbackUrl
|
|
235
|
-
- null
|
|
236
|
-
then: null
|
|
237
|
-
else:
|
|
238
|
-
callbackUrl:
|
|
239
|
-
_url_query: callbackUrl
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
The `_if` is what keeps a user who reached the sign-in page directly from being sent to `/phone-login?callbackUrl=null` — `urlQuery` serialises a null value rather than dropping the key.
|
|
243
|
-
|
|
244
94
|
## Logout
|
|
245
95
|
|
|
246
96
|
When the `Logout` action is called, the user data and authorization cookies are cleared by the app.
|
|
247
97
|
|
|
248
|
-
The `callbackUrl`
|
|
98
|
+
The `callbackUrl` parameters of the Logout action specify where the user is redirected after logout is complete. If `callbackUrl` parameters are not set, the user will return to the page from which the action was called.
|
|
249
99
|
|
|
250
100
|
The parameters are:
|
|
251
101
|
- `callbackUrl: object`:
|
|
252
|
-
- `home: boolean`: Redirect to the home page after the
|
|
253
|
-
- `pageId: string`: The pageId of the page to redirect to after the
|
|
254
|
-
- `url: string`: The URL to redirect to after the
|
|
255
|
-
- `urlQuery: object`: The urlQuery to set for the page the user is redirected to after
|
|
102
|
+
- `home: boolean`: Redirect to the home page after the login flow is complete.
|
|
103
|
+
- `pageId: string`: The pageId of the page to redirect to after the login flow is complete.
|
|
104
|
+
- `url: string`: The URL to redirect to after the login flow is complete.
|
|
105
|
+
- `urlQuery: object`: The urlQuery to set for the page the user is redirected to after login.
|
|
256
106
|
- `redirect: boolean`: If set to `false` the user session will be cleared, but the page will not be reloaded.
|
|
257
107
|
|
|
258
108
|
|
|
@@ -29,11 +29,3 @@ The Login and Logout actions are used to start the login and logout processes.
|
|
|
29
29
|
## \_user operator
|
|
30
30
|
|
|
31
31
|
The `_user` operator can be used to access data in the user object received from the identity provider. The OpenID Connect standard claims (fields like `name` or `email`) are mapped to the user object by default and addition fields can be configured using the `userFields` configuration option.
|
|
32
|
-
|
|
33
|
-
### `two_factor_enrolled`
|
|
34
|
-
|
|
35
|
-
`_user: two_factor_enrolled` is `true` when the caller holds a factor that satisfies [`auth.twoFactor.required`](/two-factor) — a TOTP enrolment **or** a registered passkey.
|
|
36
|
-
|
|
37
|
-
It is a fact about the **user**, not the session: it does not assert that a factor was presented on this sign-in, only that the user has one on file. (The name is imperfect — a passkey is not literally a second factor. The field means precisely "holds a factor that satisfies `auth.twoFactor.required`", which a passkey does on its own.)
|
|
38
|
-
|
|
39
|
-
It is present only on session-resolved callers. The API strategy and the injected callers used in dev and e2e carry no session and so do not carry this field.
|