gufi-cli 0.1.49 → 0.1.51
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/dist/commands/docs.js +1 -5
- package/dist/lib/docs-resolver.d.ts +8 -0
- package/dist/lib/docs-resolver.js +27 -0
- package/dist/lib/security.js +5 -0
- package/dist/mcp.js +56 -43
- package/docs/dev-guide/1-01-architecture.md +358 -0
- package/docs/dev-guide/1-02-multi-tenant.md +415 -0
- package/docs/dev-guide/1-03-column-types.md +594 -0
- package/docs/dev-guide/1-04-json-config.md +442 -0
- package/docs/dev-guide/1-05-authentication.md +427 -0
- package/docs/dev-guide/2-01-api-reference.md +564 -0
- package/docs/dev-guide/2-02-automations.md +508 -0
- package/docs/dev-guide/2-03-gufi-cli.md +568 -0
- package/docs/dev-guide/2-04-realtime.md +401 -0
- package/docs/dev-guide/2-05-permissions.md +497 -0
- package/docs/dev-guide/2-06-integrations-overview.md +104 -0
- package/docs/dev-guide/2-07-stripe.md +173 -0
- package/docs/dev-guide/2-08-nayax.md +297 -0
- package/docs/dev-guide/2-09-ourvend.md +226 -0
- package/docs/dev-guide/2-10-tns.md +177 -0
- package/docs/dev-guide/2-11-custom-http.md +268 -0
- package/docs/dev-guide/3-01-custom-views.md +555 -0
- package/docs/dev-guide/3-02-webhooks-api.md +446 -0
- package/docs/mcp/00-overview.md +329 -0
- package/docs/mcp/01-architecture.md +226 -0
- package/docs/mcp/02-modules.md +285 -0
- package/docs/mcp/03-fields.md +357 -0
- package/docs/mcp/04-views.md +613 -0
- package/docs/mcp/05-automations.md +461 -0
- package/docs/mcp/06-api.md +531 -0
- package/docs/mcp/07-packages.md +246 -0
- package/docs/mcp/08-common-errors.md +284 -0
- package/docs/mcp/09-examples.md +453 -0
- package/docs/mcp/README.md +71 -0
- package/docs/mcp/tool-descriptions.json +64 -0
- package/package.json +3 -2
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: permissions
|
|
3
|
+
title: "Permissions System"
|
|
4
|
+
description: "Role-based access control implementation"
|
|
5
|
+
icon: Shield
|
|
6
|
+
category: dev
|
|
7
|
+
part: 2
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Permissions System
|
|
11
|
+
|
|
12
|
+
Role-based access control implementation
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
Gufi implements a comprehensive permission system with multiple layers:
|
|
17
|
+
|
|
18
|
+
1. **Platform Roles** - Global access (admin, consultant, client)
|
|
19
|
+
2. **Company Roles** - Per-company access (Admin, Manager, User, custom)
|
|
20
|
+
3. **Entity Permissions** - CRUD operations per entity
|
|
21
|
+
4. **Field Permissions** - View/edit per field
|
|
22
|
+
5. **Record Permissions** - Row-level security
|
|
23
|
+
|
|
24
|
+
## Permission Architecture
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
28
|
+
│ Permission Layers │
|
|
29
|
+
├─────────────────────────────────────────────────────────────┤
|
|
30
|
+
│ │
|
|
31
|
+
│ ┌──────────────────────────────────────────────────────┐ │
|
|
32
|
+
│ │ Platform Role │ │
|
|
33
|
+
│ │ (admin / consultant / client) │ │
|
|
34
|
+
│ └──────────────────────────────────────────────────────┘ │
|
|
35
|
+
│ │ │
|
|
36
|
+
│ ▼ │
|
|
37
|
+
│ ┌──────────────────────────────────────────────────────┐ │
|
|
38
|
+
│ │ Company Roles │ │
|
|
39
|
+
│ │ (Admin / Manager / Custom...) │ │
|
|
40
|
+
│ └──────────────────────────────────────────────────────┘ │
|
|
41
|
+
│ │ │
|
|
42
|
+
│ ▼ │
|
|
43
|
+
│ ┌──────────────────────────────────────────────────────┐ │
|
|
44
|
+
│ │ Entity Permissions │ │
|
|
45
|
+
│ │ (view / create / edit / delete) │ │
|
|
46
|
+
│ └──────────────────────────────────────────────────────┘ │
|
|
47
|
+
│ │ │
|
|
48
|
+
│ ▼ │
|
|
49
|
+
│ ┌──────────────────────────────────────────────────────┐ │
|
|
50
|
+
│ │ Field Permissions │ │
|
|
51
|
+
│ │ (view / edit per field) │ │
|
|
52
|
+
│ └──────────────────────────────────────────────────────┘ │
|
|
53
|
+
│ │ │
|
|
54
|
+
│ ▼ │
|
|
55
|
+
│ ┌──────────────────────────────────────────────────────┐ │
|
|
56
|
+
│ │ Record Permissions │ │
|
|
57
|
+
│ │ (all / own / team / custom rules) │ │
|
|
58
|
+
│ └──────────────────────────────────────────────────────┘ │
|
|
59
|
+
│ │
|
|
60
|
+
└─────────────────────────────────────────────────────────────┘
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Platform Roles
|
|
64
|
+
|
|
65
|
+
### Definition
|
|
66
|
+
|
|
67
|
+
Stored in `core.users.platform_role`:
|
|
68
|
+
|
|
69
|
+
```sql
|
|
70
|
+
ALTER TABLE core.users ADD COLUMN platform_role VARCHAR(20)
|
|
71
|
+
DEFAULT 'client'
|
|
72
|
+
CHECK (platform_role IN ('admin', 'consultant', 'client'));
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Access Matrix
|
|
76
|
+
|
|
77
|
+
| Feature | admin | consultant | client |
|
|
78
|
+
|---|---|---|---|
|
|
79
|
+
| Company Management | ✓ | ✗ | ✗ |
|
|
80
|
+
| Global Users | ✓ | ✗ | ✗ |
|
|
81
|
+
| Developer Center | ✓ | ✓ | ✗ |
|
|
82
|
+
| Marketplace Admin | ✓ | ✓ | ✗ |
|
|
83
|
+
| API Keys | ✓ | ✓ | ✗ |
|
|
84
|
+
| Environment Variables | ✓ | ✓ | ✗ |
|
|
85
|
+
|
|
86
|
+
### Frontend Hook
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { usePlatformRole } from '@/hooks/usePlatformRole';
|
|
90
|
+
|
|
91
|
+
function AdminPanel() {
|
|
92
|
+
const {
|
|
93
|
+
platformRole,
|
|
94
|
+
isAdmin,
|
|
95
|
+
isConsultant,
|
|
96
|
+
canAccessDeveloperCenter,
|
|
97
|
+
canAccessCompanies
|
|
98
|
+
} = usePlatformRole();
|
|
99
|
+
|
|
100
|
+
if (!isAdmin) {
|
|
101
|
+
return <NotAuthorized />;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return <AdminDashboard />;
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Backend Middleware
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
const requirePlatformAdmin = (req, res, next) => {
|
|
112
|
+
if (req.user?.platform_role !== 'admin') {
|
|
113
|
+
return res.status(403).json({
|
|
114
|
+
error: 'Platform admin access required'
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
next();
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Usage
|
|
121
|
+
router.get('/admin/companies', requirePlatformAdmin, getCompanies);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Company Roles
|
|
125
|
+
|
|
126
|
+
### Definition
|
|
127
|
+
|
|
128
|
+
Stored in `core.company_users.roles` (JSONB array):
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
["Admin", "Sales Manager", "Support"]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Default Roles
|
|
135
|
+
|
|
136
|
+
| Role | Description | Typical Permissions |
|
|
137
|
+
|---|---|---|
|
|
138
|
+
| Admin | Full access | All operations on all entities |
|
|
139
|
+
| Manager | Operational access | CRUD on most entities, limited settings |
|
|
140
|
+
| User | Standard access | Create/edit own records |
|
|
141
|
+
| Viewer | Read-only | View only |
|
|
142
|
+
|
|
143
|
+
### Custom Roles
|
|
144
|
+
|
|
145
|
+
Companies can create custom roles:
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
// Create custom role
|
|
149
|
+
POST /api/companies/:id/roles
|
|
150
|
+
{
|
|
151
|
+
"name": "Sales Representative",
|
|
152
|
+
"permissions": {
|
|
153
|
+
"entities": {
|
|
154
|
+
"customers": ["view", "create", "edit"],
|
|
155
|
+
"orders": ["view", "create"],
|
|
156
|
+
"products": ["view"]
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Entity Permissions
|
|
163
|
+
|
|
164
|
+
### Permission Types
|
|
165
|
+
|
|
166
|
+
| Permission | Description | Operations |
|
|
167
|
+
|---|---|---|
|
|
168
|
+
| view | Can see entity and records | GET |
|
|
169
|
+
| create | Can add new records | POST |
|
|
170
|
+
| edit | Can modify records | PUT |
|
|
171
|
+
| delete | Can remove records | DELETE |
|
|
172
|
+
| export | Can export data | GET + file |
|
|
173
|
+
| import | Can bulk import | POST + file |
|
|
174
|
+
|
|
175
|
+
### Configuration
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"roles": {
|
|
180
|
+
"Sales Rep": {
|
|
181
|
+
"entities": {
|
|
182
|
+
"customers": {
|
|
183
|
+
"permissions": ["view", "create", "edit"],
|
|
184
|
+
"recordAccess": "own"
|
|
185
|
+
},
|
|
186
|
+
"products": {
|
|
187
|
+
"permissions": ["view"]
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Backend Enforcement
|
|
196
|
+
|
|
197
|
+
```javascript
|
|
198
|
+
const checkEntityPermission = async (req, res, next) => {
|
|
199
|
+
const { entityId } = req.params;
|
|
200
|
+
const { user } = req;
|
|
201
|
+
const operation = getOperationType(req.method);
|
|
202
|
+
|
|
203
|
+
const hasPermission = await hasEntityPermission(
|
|
204
|
+
user.id,
|
|
205
|
+
req.companyId,
|
|
206
|
+
entityId,
|
|
207
|
+
operation
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
if (!hasPermission) {
|
|
211
|
+
return res.status(403).json({
|
|
212
|
+
error: `No ${operation} permission for this entity`
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
next();
|
|
217
|
+
};
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Field Permissions
|
|
221
|
+
|
|
222
|
+
### Configuration
|
|
223
|
+
|
|
224
|
+
```json
|
|
225
|
+
{
|
|
226
|
+
"Sales Rep": {
|
|
227
|
+
"entities": {
|
|
228
|
+
"customers": {
|
|
229
|
+
"fields": {
|
|
230
|
+
"name": ["view", "edit"],
|
|
231
|
+
"email": ["view", "edit"],
|
|
232
|
+
"internal_notes": [],
|
|
233
|
+
"credit_limit": ["view"]
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Backend Enforcement
|
|
242
|
+
|
|
243
|
+
```javascript
|
|
244
|
+
// Filter fields on read
|
|
245
|
+
const filterResponseFields = (data, allowedFields) => {
|
|
246
|
+
return Object.keys(data).reduce((acc, key) => {
|
|
247
|
+
if (allowedFields.includes(key) || isSystemField(key)) {
|
|
248
|
+
acc[key] = data[key];
|
|
249
|
+
}
|
|
250
|
+
return acc;
|
|
251
|
+
}, {});
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// Validate fields on write
|
|
255
|
+
const validateWriteFields = (data, editableFields) => {
|
|
256
|
+
const invalidFields = Object.keys(data).filter(
|
|
257
|
+
key => !editableFields.includes(key) && !isSystemField(key)
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
if (invalidFields.length > 0) {
|
|
261
|
+
throw new ForbiddenError(
|
|
262
|
+
`Cannot edit fields: ${invalidFields.join(', ')}`
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Record-Level Security
|
|
269
|
+
|
|
270
|
+
### Access Types
|
|
271
|
+
|
|
272
|
+
| Type | Description |
|
|
273
|
+
|---|---|
|
|
274
|
+
| all | Can access all records |
|
|
275
|
+
| own | Only records created by user |
|
|
276
|
+
| team | Records from same team |
|
|
277
|
+
| custom | Based on field conditions |
|
|
278
|
+
|
|
279
|
+
### Implementation
|
|
280
|
+
|
|
281
|
+
```sql
|
|
282
|
+
-- Row-level security policy
|
|
283
|
+
CREATE POLICY user_owns_record ON orders
|
|
284
|
+
FOR ALL
|
|
285
|
+
USING (created_by = current_setting('app.user_id')::int);
|
|
286
|
+
|
|
287
|
+
-- Team-based access
|
|
288
|
+
CREATE POLICY team_access ON orders
|
|
289
|
+
FOR ALL
|
|
290
|
+
USING (
|
|
291
|
+
team_id IN (
|
|
292
|
+
SELECT team_id FROM team_members
|
|
293
|
+
WHERE user_id = current_setting('app.user_id')::int
|
|
294
|
+
)
|
|
295
|
+
);
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Custom Rules
|
|
299
|
+
|
|
300
|
+
```json
|
|
301
|
+
{
|
|
302
|
+
"Sales Rep": {
|
|
303
|
+
"entities": {
|
|
304
|
+
"orders": {
|
|
305
|
+
"recordAccess": {
|
|
306
|
+
"type": "custom",
|
|
307
|
+
"conditions": {
|
|
308
|
+
"OR": [
|
|
309
|
+
{ "assigned_to": "@currentUser" },
|
|
310
|
+
{ "region": "@user.region" }
|
|
311
|
+
]
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Frontend Permission Checks
|
|
321
|
+
|
|
322
|
+
### usePermissions Hook
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
import { usePermissions } from '@/hooks/usePermissions';
|
|
326
|
+
|
|
327
|
+
function EntityView({ entityId }) {
|
|
328
|
+
const { can, loading } = usePermissions(entityId);
|
|
329
|
+
|
|
330
|
+
if (loading) return <Spinner />;
|
|
331
|
+
|
|
332
|
+
return (
|
|
333
|
+
<div>
|
|
334
|
+
{can('view') && <DataTable />}
|
|
335
|
+
{can('create') && <NewButton />}
|
|
336
|
+
{can('export') && <ExportButton />}
|
|
337
|
+
</div>
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Permission Store
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
import { usePermissionsStore } from '@/stores/permissionsStore';
|
|
346
|
+
|
|
347
|
+
function Component() {
|
|
348
|
+
const { hasPermission, checkFieldPermission } = usePermissionsStore();
|
|
349
|
+
|
|
350
|
+
const canEdit = hasPermission(entityId, 'edit');
|
|
351
|
+
const canViewSalary = checkFieldPermission(entityId, 'salary', 'view');
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## View Permissions
|
|
356
|
+
|
|
357
|
+
### View Access Control
|
|
358
|
+
|
|
359
|
+
Views can have permissions separate from entities:
|
|
360
|
+
|
|
361
|
+
```json
|
|
362
|
+
{
|
|
363
|
+
"viewPermissions": {
|
|
364
|
+
"analytics_dashboard": {
|
|
365
|
+
"allowedRoles": ["Admin", "Manager"],
|
|
366
|
+
"denyRoles": []
|
|
367
|
+
},
|
|
368
|
+
"employee_salaries": {
|
|
369
|
+
"allowedRoles": ["Admin", "HR Manager"]
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Checking View Access
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
import { useViewPermissions } from '@/hooks/useViewPermissions';
|
|
379
|
+
|
|
380
|
+
function ViewGateway({ viewId }) {
|
|
381
|
+
const { canAccess, loading } = useViewPermissions(viewId);
|
|
382
|
+
|
|
383
|
+
if (!canAccess) {
|
|
384
|
+
return <NotAuthorized />;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return <ViewComponent />;
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## API Permission Patterns
|
|
392
|
+
|
|
393
|
+
### Resource ID Format
|
|
394
|
+
|
|
395
|
+
Permissions use Resource IDs (RIDs):
|
|
396
|
+
|
|
397
|
+
```
|
|
398
|
+
view:{type}:{entity_id}
|
|
399
|
+
|
|
400
|
+
Examples:
|
|
401
|
+
- view:customers:4589
|
|
402
|
+
- edit:orders:4590
|
|
403
|
+
- delete:products:4591
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Permission Check Endpoint
|
|
407
|
+
|
|
408
|
+
```http
|
|
409
|
+
GET /api/permissions/check?resource=view:customers:4589
|
|
410
|
+
|
|
411
|
+
Response:
|
|
412
|
+
{
|
|
413
|
+
"allowed": true,
|
|
414
|
+
"reason": "Role 'Admin' grants this permission"
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Bulk Permission Check
|
|
419
|
+
|
|
420
|
+
```http
|
|
421
|
+
POST /api/permissions/check-bulk
|
|
422
|
+
{
|
|
423
|
+
"resources": [
|
|
424
|
+
"view:customers:4589",
|
|
425
|
+
"edit:customers:4589",
|
|
426
|
+
"delete:customers:4589"
|
|
427
|
+
]
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
Response:
|
|
431
|
+
{
|
|
432
|
+
"permissions": {
|
|
433
|
+
"view:customers:4589": true,
|
|
434
|
+
"edit:customers:4589": true,
|
|
435
|
+
"delete:customers:4589": false
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## Best Practices
|
|
441
|
+
|
|
442
|
+
### Least Privilege
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
// Good: Check specific permission
|
|
446
|
+
if (can('edit', { recordId: record.id })) {
|
|
447
|
+
showEditButton();
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Bad: Check broad role
|
|
451
|
+
if (userRole === 'Admin') {
|
|
452
|
+
showEditButton();
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Consistent Enforcement
|
|
457
|
+
|
|
458
|
+
```javascript
|
|
459
|
+
// Always check on both frontend AND backend
|
|
460
|
+
|
|
461
|
+
// Frontend (UX)
|
|
462
|
+
{can('delete') && <DeleteButton />}
|
|
463
|
+
|
|
464
|
+
// Backend (security)
|
|
465
|
+
router.delete('/:id', checkPermission('delete'), deleteRecord);
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Cache Invalidation
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
// Invalidate on role changes
|
|
472
|
+
const onRoleChange = async (userId) => {
|
|
473
|
+
await invalidatePermissionCache(userId);
|
|
474
|
+
notifyUser(userId, 'permissions_changed');
|
|
475
|
+
};
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Audit Permission Changes
|
|
479
|
+
|
|
480
|
+
```javascript
|
|
481
|
+
// Log all permission modifications
|
|
482
|
+
const updatePermissions = async (roleId, newPermissions) => {
|
|
483
|
+
const oldPermissions = await getPermissions(roleId);
|
|
484
|
+
|
|
485
|
+
await db.query(
|
|
486
|
+
'UPDATE roles SET permissions = $1 WHERE id = $2',
|
|
487
|
+
[newPermissions, roleId]
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
await auditLog('PERMISSIONS_CHANGED', {
|
|
491
|
+
roleId,
|
|
492
|
+
before: oldPermissions,
|
|
493
|
+
after: newPermissions,
|
|
494
|
+
changedBy: currentUser.id
|
|
495
|
+
});
|
|
496
|
+
};
|
|
497
|
+
```
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: integrations-overview
|
|
3
|
+
title: "Integrations Overview"
|
|
4
|
+
description: "Connect automations to external services"
|
|
5
|
+
icon: Plug
|
|
6
|
+
category: dev
|
|
7
|
+
part: 2
|
|
8
|
+
group: integrations
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Integrations Overview
|
|
12
|
+
|
|
13
|
+
Connect automations to external services
|
|
14
|
+
|
|
15
|
+
## What are Integrations?
|
|
16
|
+
|
|
17
|
+
Integrations let your automations communicate with external APIs. Gufi provides:
|
|
18
|
+
|
|
19
|
+
- **Built-in connectors**: Stripe, Nayax, OurVend, TNS
|
|
20
|
+
- **Custom HTTP**: Connect to any REST API with `gufi.http()`
|
|
21
|
+
|
|
22
|
+
## How They Work
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Trigger → Automation Script → api.integration.method() → External Service
|
|
26
|
+
↓
|
|
27
|
+
context.env.API_KEY
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
1. An event triggers your automation (insert, update, click, scheduled)
|
|
31
|
+
2. Your script calls an integration method: `gufi.integrations.stripe.createCheckoutSession()`
|
|
32
|
+
3. Credentials come from environment variables: `env.STRIPE_SECRET_KEY`
|
|
33
|
+
4. The integration communicates with the external service
|
|
34
|
+
|
|
35
|
+
## Setting Up Credentials
|
|
36
|
+
|
|
37
|
+
All credentials are stored as **Environment Variables**:
|
|
38
|
+
|
|
39
|
+
1. Go to **Settings → Environment Variables**
|
|
40
|
+
2. Click **Add Variable**
|
|
41
|
+
3. Enter name (e.g., `STRIPE_SECRET_KEY`) and value
|
|
42
|
+
4. Access in automations via `context.env`
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
async function my_automation(gufi) {
|
|
46
|
+
const { env } = gufi.context;
|
|
47
|
+
|
|
48
|
+
// Your credentials
|
|
49
|
+
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
50
|
+
const nayaxToken = env.NAYAX_TOKEN;
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
:::warning
|
|
55
|
+
Never hardcode credentials in scripts. Always use environment variables.
|
|
56
|
+
:::
|
|
57
|
+
|
|
58
|
+
## Available Built-in Integrations
|
|
59
|
+
|
|
60
|
+
| Integration | Category | Use Case |
|
|
61
|
+
|-------------|----------|----------|
|
|
62
|
+
| **Stripe** | Payments | Create checkout sessions, process payments |
|
|
63
|
+
| **Nayax** | Vending | Sync machines, products, sales, alerts |
|
|
64
|
+
| **OurVend** | Vending | RedPanda vending machine system |
|
|
65
|
+
| **TNS** | Accounting | Colombian product catalog |
|
|
66
|
+
|
|
67
|
+
Each integration is documented in detail in the following sections.
|
|
68
|
+
|
|
69
|
+
## Custom HTTP for Any API
|
|
70
|
+
|
|
71
|
+
For services without built-in integrations, use `gufi.http()`:
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
const response = await gufi.http({
|
|
75
|
+
url: 'https://api.example.com/endpoint',
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: {
|
|
78
|
+
'Authorization': 'Bearer ' + env.API_KEY
|
|
79
|
+
},
|
|
80
|
+
body: { data: 'value' }
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
See the **Custom HTTP** section for full documentation.
|
|
85
|
+
|
|
86
|
+
## Viewing Integrations
|
|
87
|
+
|
|
88
|
+
**From CLI:**
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
gufi integrations # List all
|
|
92
|
+
gufi integrations stripe # Details for Stripe
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**From MCP:**
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
gufi_automation_integrations()
|
|
99
|
+
gufi_automation_integrations({ name: 'stripe' })
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**From UI:**
|
|
103
|
+
|
|
104
|
+
Go to **Settings → Integrations** to see available connectors.
|