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.
Files changed (36) hide show
  1. package/dist/commands/docs.js +1 -5
  2. package/dist/lib/docs-resolver.d.ts +8 -0
  3. package/dist/lib/docs-resolver.js +27 -0
  4. package/dist/lib/security.js +5 -0
  5. package/dist/mcp.js +56 -43
  6. package/docs/dev-guide/1-01-architecture.md +358 -0
  7. package/docs/dev-guide/1-02-multi-tenant.md +415 -0
  8. package/docs/dev-guide/1-03-column-types.md +594 -0
  9. package/docs/dev-guide/1-04-json-config.md +442 -0
  10. package/docs/dev-guide/1-05-authentication.md +427 -0
  11. package/docs/dev-guide/2-01-api-reference.md +564 -0
  12. package/docs/dev-guide/2-02-automations.md +508 -0
  13. package/docs/dev-guide/2-03-gufi-cli.md +568 -0
  14. package/docs/dev-guide/2-04-realtime.md +401 -0
  15. package/docs/dev-guide/2-05-permissions.md +497 -0
  16. package/docs/dev-guide/2-06-integrations-overview.md +104 -0
  17. package/docs/dev-guide/2-07-stripe.md +173 -0
  18. package/docs/dev-guide/2-08-nayax.md +297 -0
  19. package/docs/dev-guide/2-09-ourvend.md +226 -0
  20. package/docs/dev-guide/2-10-tns.md +177 -0
  21. package/docs/dev-guide/2-11-custom-http.md +268 -0
  22. package/docs/dev-guide/3-01-custom-views.md +555 -0
  23. package/docs/dev-guide/3-02-webhooks-api.md +446 -0
  24. package/docs/mcp/00-overview.md +329 -0
  25. package/docs/mcp/01-architecture.md +226 -0
  26. package/docs/mcp/02-modules.md +285 -0
  27. package/docs/mcp/03-fields.md +357 -0
  28. package/docs/mcp/04-views.md +613 -0
  29. package/docs/mcp/05-automations.md +461 -0
  30. package/docs/mcp/06-api.md +531 -0
  31. package/docs/mcp/07-packages.md +246 -0
  32. package/docs/mcp/08-common-errors.md +284 -0
  33. package/docs/mcp/09-examples.md +453 -0
  34. package/docs/mcp/README.md +71 -0
  35. package/docs/mcp/tool-descriptions.json +64 -0
  36. 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.