@appaflytech/zeyrek-ai-mcp 0.0.62 → 0.0.64
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/.env.example +6 -0
- package/dist/config.js +2 -0
- package/dist/knowledge/backendConstraints.js +2 -2
- package/dist/knowledge/componentCatalog.js +1 -1
- package/dist/knowledge/mobileStore.js +24 -4
- package/dist/knowledge/normalize.js +10 -0
- package/dist/knowledge/platform/mobileLayoutLint.js +8 -0
- package/dist/knowledge/platform/mobileRules.js +6 -0
- package/dist/knowledge/rules.js +89 -0
- package/dist/observability/gapSink.js +43 -0
- package/dist/platform/argumentAliases.js +6 -0
- package/dist/platform/capture/actions.js +71 -2
- package/dist/platform/capture/capture.js +5 -2
- package/dist/platform/responses.js +5 -2
- package/dist/platform/writeDiff.js +52 -1
- package/dist/resources/examples/middleware-password-reset.json +1281 -0
- package/dist/resources/examples/mobile-reorder-persist.json +33 -7
- package/dist/resources/examples/workflow-email-step.json +67 -0
- package/dist/resources/guides/deployment.md +13 -1
- package/dist/resources/guides/entity.md +7 -0
- package/dist/resources/guides/expressions.md +5 -0
- package/dist/resources/guides/getting-started.md +15 -30
- package/dist/resources/guides/microservice.md +30 -2
- package/dist/resources/guides/middleware.md +83 -3
- package/dist/resources/guides/mobile-layout.md +35 -3
- package/dist/resources/guides/mobile-publish.md +33 -1
- package/dist/resources/guides/mobile-steps.md +12 -2
- package/dist/resources/guides/mobile.md +11 -8
- package/dist/resources/guides/page.md +28 -3
- package/dist/resources/guides/translation.md +6 -0
- package/dist/resources/guides/workflow.md +30 -1
- package/dist/resources/schema-fingerprint.json +2 -2
- package/dist/tools/applications.js +7 -4
- package/dist/tools/common.js +8 -0
- package/dist/tools/designSchemaPatch.js +4 -1
- package/dist/tools/flows.js +14 -4
- package/dist/tools/layouts.js +3 -1
- package/dist/tools/microservices.js +8 -2
- package/dist/tools/mobileStore.js +13 -2
- package/dist/tools/pages.js +27 -12
- package/dist/tools/platformInfo.js +15 -6
- package/dist/tools/workflows.js +4 -2
- package/package.json +1 -1
|
@@ -0,0 +1,1281 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": {
|
|
3
|
+
"synthetic": false,
|
|
4
|
+
"note": "Kodla şifre sıfırlama: iki anonim uç (kod iste, sıfırla) ve kullanıcı başına tek satırlık kod tablosu. Lokal yığında bu örnekten kurularak ölçüldü (2026-11-04, test projesi, atımlık sifre-deneme uygulaması); ölçüm sonuçları platform_guide('middleware') §Tarif 4'te.",
|
|
5
|
+
"sourceRef": "platform_guide('middleware') §Tarif 4; platform_guide('workflow') §Email adımı; zeyrek-backend Entity.HashCredentialFields, ExecuteMicroserviceBehavior (2xx dışı yanıt rollback)"
|
|
6
|
+
},
|
|
7
|
+
"_rules": {
|
|
8
|
+
"ikiUc": "İki uç da source: 'workflow', httpMethod post, middlewareIds BOŞ (anonimdir; kimlik middleware'i bağlanırsa oturumu olmayan kullanıcı 401 alır).",
|
|
9
|
+
"yanitAyni": "Kod iste ucu hesap olsun olmasın, hız sınırına ya da deneme sınırına takılsın takılmasın aynı 200 gövdesini döner; kod üretilmeyen dallar sureEsitle adımından geçer (crypto.SHA256.encode 350.000 tur PBKDF2, süreyi kod üreten dala yaklaştırır). Sıfırla ucunda da kod karşılaştırılmayan ret dalları (kullanıcı yok, kod yok/dolmuş/kullanılmış/hakkı bitmiş) aynı adımdan geçer; yoksa kayıtsız adres yanlış koddan ~10 kat hızlı döner ve hesabın varlığını sızdırır (ölçüldü: 9 ms'ye karşı 130 ms).",
|
|
10
|
+
"ozet": "Kod düz saklanmaz: crypto.SHA256.encode(salt + ':' + code), salt satıra özgü yeni GUID. Kod GenerateGuid()'in son 10 hex hanesinden (v7'nin rastgele bitleri) üretilir; Math.random kullanılmaz.",
|
|
11
|
+
"tekSatir": "password_reset_codes.user_id UNIQUE: yeni kod satırı günceller, önceki kod böylece geçersiz olur; used_at null'a döner, attempts pencere dolmadıkça korunur.",
|
|
12
|
+
"ikiYuzYanit": "Sıfırla ucunun her sonucu 2xx döner ({ ok }): uç 4xx/5xx ile biterse platform işlemin bütün yazımlarını geri alır ve sayaç hiç artmaz (ölçüldü: 400 dönen sürümde beş yanlış denemeden sonra attempts 0 kaldı, altıncı doğru kod kabul edildi). İstemci başarıyı body.ok'tan okur.",
|
|
13
|
+
"parola": "users.password düz yazılır; platform users tablosunun parola alanını yazarken özetler (zaten özetlenmiş değere dokunmaz). Elle crypto.SHA256.encode da çalışır, çift özetlemez.",
|
|
14
|
+
"oturum": "Parola değişince açık oturumlar kapanmaz: basit projede uygulamanın kendi jetonu süresi dolana kadar geçerlidir; kompleks projede platform yenileme jetonu ailesi parola değişiminde iptal edilmez (PS-32, açık karar).",
|
|
15
|
+
"verify": "microservice_execute(anonymous: true) sifre-kodu-iste { email } → 200; curl -s 'localhost:8025/api/v1/search?query=to:<email>' tek ileti, gövdede 6 hane. Aynı anda 10 yanlış sifre-sifirla → hepsi ok false, attempts ≤ 5. Aynı anda 5 doğru kodla sifre-sifirla → tam bir ok true. Beş hak bitince yeni kod istenirse ileti gitmez. Yeni parolayla giriş 200, eskisiyle 401.",
|
|
16
|
+
"karsilastirVeYaz": "Deneme hakkı karşılaştırmadan ÖNCE harcanır: denemeSay adımı attempts'i yalnız 'id eq X AND attempts eq <okunan>' filtresiyle artırır. Satırı Postgres kilitler; aynı anda gelen öteki istek kilit açılınca koşulu yeniden değerlendirir, attempts artık farklı olduğu için satır güncellenmez ve adım boş döner — o istek kodu karşılaştırmadan reddedilir. Okuyup-artıran sürümde paralel istekler aynı sayacı okuyup hepsi karşılaştırma yapıyordu (inceleme bulgusu).",
|
|
17
|
+
"pencere": "Deneme sayacı yeni kodla sıfırlanmaz: window_started_at'ten 24 saat geçmedikçe attempts taşınır, hak bitmişse yeni kod da gönderilmez. Böylece hesap başına günde en çok 5 tahmin (10⁶'da 5) yapılır; her yeni kodla 5 hak açan sürüm günde ~7 200 tahmine izin veriyordu. Bedel: 5 kez yanlış yazan kullanıcı 24 saat bekler.",
|
|
18
|
+
"eszamanliIstek": "Aynı anda gelen iki 'kod iste' isteği eski sent_at'i birlikte okuyup 60 sn sınırını aşabilir (iki ileti gider). Etkisi gelen kutusunda fazladan iletidir; sayaç ve pencere korunduğu için tahmin hakkı artmaz."
|
|
19
|
+
},
|
|
20
|
+
"entity": {
|
|
21
|
+
"name": "Password reset codes",
|
|
22
|
+
"slug": "password_reset_codes",
|
|
23
|
+
"description": "Şifre sıfırlama kodları; kullanıcı başına tek satır.",
|
|
24
|
+
"fields": [
|
|
25
|
+
{
|
|
26
|
+
"name": "ID",
|
|
27
|
+
"slug": "id",
|
|
28
|
+
"dbType": "UUID",
|
|
29
|
+
"commonType": "guid",
|
|
30
|
+
"primaryKey": true,
|
|
31
|
+
"nullable": false,
|
|
32
|
+
"unique": true,
|
|
33
|
+
"identity": true,
|
|
34
|
+
"defaultValueType": "none",
|
|
35
|
+
"defaultValue": "",
|
|
36
|
+
"properties": {
|
|
37
|
+
"generationType": 0
|
|
38
|
+
},
|
|
39
|
+
"relationType": "none"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "user_id",
|
|
43
|
+
"slug": "user_id",
|
|
44
|
+
"dbType": "UUID",
|
|
45
|
+
"commonType": "guid",
|
|
46
|
+
"nullable": false,
|
|
47
|
+
"relationType": "none",
|
|
48
|
+
"unique": true
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "salt",
|
|
52
|
+
"slug": "salt",
|
|
53
|
+
"dbType": "TEXT",
|
|
54
|
+
"commonType": "text",
|
|
55
|
+
"nullable": false,
|
|
56
|
+
"relationType": "none"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "code_hash",
|
|
60
|
+
"slug": "code_hash",
|
|
61
|
+
"dbType": "TEXT",
|
|
62
|
+
"commonType": "text",
|
|
63
|
+
"nullable": false,
|
|
64
|
+
"relationType": "none"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"name": "expires_at",
|
|
68
|
+
"slug": "expires_at",
|
|
69
|
+
"dbType": "TIMESTAMPTZ",
|
|
70
|
+
"commonType": "datetime",
|
|
71
|
+
"nullable": false,
|
|
72
|
+
"relationType": "none"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"name": "attempts",
|
|
76
|
+
"slug": "attempts",
|
|
77
|
+
"dbType": "INT4",
|
|
78
|
+
"commonType": "number",
|
|
79
|
+
"nullable": false,
|
|
80
|
+
"relationType": "none"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"name": "sent_at",
|
|
84
|
+
"slug": "sent_at",
|
|
85
|
+
"dbType": "TIMESTAMPTZ",
|
|
86
|
+
"commonType": "datetime",
|
|
87
|
+
"nullable": false,
|
|
88
|
+
"relationType": "none"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"name": "window_started_at",
|
|
92
|
+
"slug": "window_started_at",
|
|
93
|
+
"dbType": "TIMESTAMPTZ",
|
|
94
|
+
"commonType": "datetime",
|
|
95
|
+
"nullable": false,
|
|
96
|
+
"relationType": "none"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"name": "used_at",
|
|
100
|
+
"slug": "used_at",
|
|
101
|
+
"dbType": "TIMESTAMPTZ",
|
|
102
|
+
"commonType": "datetime",
|
|
103
|
+
"nullable": true,
|
|
104
|
+
"relationType": "none"
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
"dtos": {
|
|
109
|
+
"sifre-kodu-iste-girdi": [
|
|
110
|
+
{
|
|
111
|
+
"name": "email",
|
|
112
|
+
"slug": "email",
|
|
113
|
+
"type": "string"
|
|
114
|
+
}
|
|
115
|
+
],
|
|
116
|
+
"sifre-sifirla-girdi": [
|
|
117
|
+
{
|
|
118
|
+
"name": "email",
|
|
119
|
+
"slug": "email",
|
|
120
|
+
"type": "string"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"name": "code",
|
|
124
|
+
"slug": "code",
|
|
125
|
+
"type": "string"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"name": "newPassword",
|
|
129
|
+
"slug": "newPassword",
|
|
130
|
+
"type": "string"
|
|
131
|
+
}
|
|
132
|
+
],
|
|
133
|
+
"sifre-kodu-iste-sonuc": [
|
|
134
|
+
{
|
|
135
|
+
"name": "message",
|
|
136
|
+
"slug": "message",
|
|
137
|
+
"type": "string"
|
|
138
|
+
}
|
|
139
|
+
],
|
|
140
|
+
"sifre-sifirla-sonuc": [
|
|
141
|
+
{
|
|
142
|
+
"name": "ok",
|
|
143
|
+
"slug": "ok",
|
|
144
|
+
"type": "bool"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"name": "message",
|
|
148
|
+
"slug": "message",
|
|
149
|
+
"type": "string"
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
},
|
|
153
|
+
"workflows": [
|
|
154
|
+
{
|
|
155
|
+
"name": "Şifre Kodu İste",
|
|
156
|
+
"slug": "sifre-kodu-iste",
|
|
157
|
+
"description": "Anonim: kayıtlı adrese şifre sıfırlama kodu gönderir.",
|
|
158
|
+
"type": "workflow",
|
|
159
|
+
"parameterDtoId": "<sifre-kodu-iste-girdi DTO id>",
|
|
160
|
+
"parameterBehavior": "object",
|
|
161
|
+
"returnDtoId": "<sifre-kodu-iste-sonuc DTO id>",
|
|
162
|
+
"returnBehavior": "object",
|
|
163
|
+
"steps": [
|
|
164
|
+
{
|
|
165
|
+
"id": "<iste-start>",
|
|
166
|
+
"name": "start",
|
|
167
|
+
"type": "start",
|
|
168
|
+
"description": "Akışı başlatır.",
|
|
169
|
+
"branches": [
|
|
170
|
+
{
|
|
171
|
+
"id": "<iste-b0>",
|
|
172
|
+
"name": "start",
|
|
173
|
+
"type": "success",
|
|
174
|
+
"nextStepId": "<iste-kullanici>"
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"id": "<iste-kullanici>",
|
|
180
|
+
"name": "kullanici",
|
|
181
|
+
"type": "table",
|
|
182
|
+
"description": "E-postanın sahibini users tablosundan okur.",
|
|
183
|
+
"rule": {
|
|
184
|
+
"stepType": "table",
|
|
185
|
+
"queryStepSourceType": "library",
|
|
186
|
+
"isExpression": false,
|
|
187
|
+
"datasourceId": "<datasource id>",
|
|
188
|
+
"entityId": "<users entity id>",
|
|
189
|
+
"operation": "get",
|
|
190
|
+
"alias": "users",
|
|
191
|
+
"isSingle": true,
|
|
192
|
+
"selectFields": [
|
|
193
|
+
{
|
|
194
|
+
"alias": "users",
|
|
195
|
+
"fieldId": "<users.id alan id>",
|
|
196
|
+
"fieldSlug": "id"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"alias": "users",
|
|
200
|
+
"fieldId": "<users.email alan id>",
|
|
201
|
+
"fieldSlug": "email"
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
"joins": [],
|
|
205
|
+
"filters": [
|
|
206
|
+
{
|
|
207
|
+
"alias": "users",
|
|
208
|
+
"fieldId": "<users.email alan id>",
|
|
209
|
+
"fieldSlug": "email",
|
|
210
|
+
"operator": "eq",
|
|
211
|
+
"source": "expression",
|
|
212
|
+
"value": "input.email",
|
|
213
|
+
"filterType": "single",
|
|
214
|
+
"conditionType": "and",
|
|
215
|
+
"isRequired": true,
|
|
216
|
+
"children": [],
|
|
217
|
+
"order": 0
|
|
218
|
+
}
|
|
219
|
+
],
|
|
220
|
+
"fields": [],
|
|
221
|
+
"returnFields": [],
|
|
222
|
+
"sort": {
|
|
223
|
+
"name": "sortField",
|
|
224
|
+
"type": "sortType",
|
|
225
|
+
"source": "query-string"
|
|
226
|
+
},
|
|
227
|
+
"limit": {
|
|
228
|
+
"name": "limit",
|
|
229
|
+
"offset": "offset",
|
|
230
|
+
"source": "query-string"
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
"branches": [
|
|
234
|
+
{
|
|
235
|
+
"id": "<iste-b1>",
|
|
236
|
+
"name": "success",
|
|
237
|
+
"type": "success",
|
|
238
|
+
"nextStepId": "<iste-kullaniciVar>"
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"id": "<iste-kullaniciVar>",
|
|
244
|
+
"name": "kullaniciVar",
|
|
245
|
+
"type": "condition",
|
|
246
|
+
"description": "Kullanıcı varsa koda, yoksa süre eşitlemeye gider.",
|
|
247
|
+
"rule": {
|
|
248
|
+
"stepType": "condition",
|
|
249
|
+
"items": [
|
|
250
|
+
{
|
|
251
|
+
"expression": "kullanici.data && kullanici.data.id",
|
|
252
|
+
"branchId": "<iste-b2>"
|
|
253
|
+
}
|
|
254
|
+
]
|
|
255
|
+
},
|
|
256
|
+
"branches": [
|
|
257
|
+
{
|
|
258
|
+
"id": "<iste-b2>",
|
|
259
|
+
"name": "<iste-b2>",
|
|
260
|
+
"type": "success",
|
|
261
|
+
"nextStepId": "<iste-mevcutKod>"
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"id": "<iste-b3>",
|
|
265
|
+
"name": "else",
|
|
266
|
+
"type": "success",
|
|
267
|
+
"nextStepId": "<iste-sureEsitle>"
|
|
268
|
+
}
|
|
269
|
+
]
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"id": "<iste-sureEsitle>",
|
|
273
|
+
"name": "sureEsitle",
|
|
274
|
+
"type": "code",
|
|
275
|
+
"description": "Kod üretilmeyen dallarda da bir özet hesaplar; yanıt süresi hesabın varlığını sızdırmaz.",
|
|
276
|
+
"rule": {
|
|
277
|
+
"stepType": "code",
|
|
278
|
+
"expression": "crypto.SHA256.encode(GenerateGuid()); return true"
|
|
279
|
+
},
|
|
280
|
+
"branches": [
|
|
281
|
+
{
|
|
282
|
+
"id": "<iste-b4>",
|
|
283
|
+
"name": "success",
|
|
284
|
+
"type": "success",
|
|
285
|
+
"nextStepId": "<iste-yanit>"
|
|
286
|
+
}
|
|
287
|
+
]
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"id": "<iste-mevcutKod>",
|
|
291
|
+
"name": "mevcutKod",
|
|
292
|
+
"type": "table",
|
|
293
|
+
"description": "Kullanıcının kod satırını okur (kullanıcı başına tek satır).",
|
|
294
|
+
"rule": {
|
|
295
|
+
"stepType": "table",
|
|
296
|
+
"queryStepSourceType": "library",
|
|
297
|
+
"isExpression": false,
|
|
298
|
+
"datasourceId": "<datasource id>",
|
|
299
|
+
"entityId": "<password_reset_codes entity id>",
|
|
300
|
+
"operation": "get",
|
|
301
|
+
"alias": "password_reset_codes",
|
|
302
|
+
"isSingle": true,
|
|
303
|
+
"selectFields": [
|
|
304
|
+
{
|
|
305
|
+
"alias": "password_reset_codes",
|
|
306
|
+
"fieldId": "<password_reset_codes.id alan id>",
|
|
307
|
+
"fieldSlug": "id"
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"alias": "password_reset_codes",
|
|
311
|
+
"fieldId": "<password_reset_codes.sent_at alan id>",
|
|
312
|
+
"fieldSlug": "sent_at"
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
"alias": "password_reset_codes",
|
|
316
|
+
"fieldId": "<password_reset_codes.attempts alan id>",
|
|
317
|
+
"fieldSlug": "attempts"
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
"alias": "password_reset_codes",
|
|
321
|
+
"fieldId": "<password_reset_codes.window_started_at alan id>",
|
|
322
|
+
"fieldSlug": "window_started_at"
|
|
323
|
+
}
|
|
324
|
+
],
|
|
325
|
+
"joins": [],
|
|
326
|
+
"filters": [
|
|
327
|
+
{
|
|
328
|
+
"alias": "password_reset_codes",
|
|
329
|
+
"fieldId": "<password_reset_codes.user_id alan id>",
|
|
330
|
+
"fieldSlug": "user_id",
|
|
331
|
+
"operator": "eq",
|
|
332
|
+
"source": "expression",
|
|
333
|
+
"value": "kullanici.data.id",
|
|
334
|
+
"filterType": "single",
|
|
335
|
+
"conditionType": "and",
|
|
336
|
+
"isRequired": true,
|
|
337
|
+
"children": [],
|
|
338
|
+
"order": 0
|
|
339
|
+
}
|
|
340
|
+
],
|
|
341
|
+
"fields": [],
|
|
342
|
+
"returnFields": [],
|
|
343
|
+
"sort": {
|
|
344
|
+
"name": "sortField",
|
|
345
|
+
"type": "sortType",
|
|
346
|
+
"source": "query-string"
|
|
347
|
+
},
|
|
348
|
+
"limit": {
|
|
349
|
+
"name": "limit",
|
|
350
|
+
"offset": "offset",
|
|
351
|
+
"source": "query-string"
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
"branches": [
|
|
355
|
+
{
|
|
356
|
+
"id": "<iste-b5>",
|
|
357
|
+
"name": "success",
|
|
358
|
+
"type": "success",
|
|
359
|
+
"nextStepId": "<iste-hizSiniri>"
|
|
360
|
+
}
|
|
361
|
+
]
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
"id": "<iste-hizSiniri>",
|
|
365
|
+
"name": "hizSiniri",
|
|
366
|
+
"type": "condition",
|
|
367
|
+
"description": "Son kod 60 saniyeden yeniyse ya da 24 saatlik pencerede deneme hakkı bittiyse yeni kod üretmez.",
|
|
368
|
+
"rule": {
|
|
369
|
+
"stepType": "condition",
|
|
370
|
+
"items": [
|
|
371
|
+
{
|
|
372
|
+
"expression": "mevcutKod.data && mevcutKod.data.id && ((UtcNow().getTime() - new Date(mevcutKod.data.sent_at).getTime()) < 60000 || ((UtcNow().getTime() - new Date(mevcutKod.data.window_started_at).getTime()) < 86400000 && mevcutKod.data.attempts >= 5))",
|
|
373
|
+
"branchId": "<iste-b6>"
|
|
374
|
+
}
|
|
375
|
+
]
|
|
376
|
+
},
|
|
377
|
+
"branches": [
|
|
378
|
+
{
|
|
379
|
+
"id": "<iste-b6>",
|
|
380
|
+
"name": "<iste-b6>",
|
|
381
|
+
"type": "success",
|
|
382
|
+
"nextStepId": "<iste-sureEsitle>"
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
"id": "<iste-b7>",
|
|
386
|
+
"name": "else",
|
|
387
|
+
"type": "success",
|
|
388
|
+
"nextStepId": "<iste-kodUret>"
|
|
389
|
+
}
|
|
390
|
+
]
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
"id": "<iste-kodUret>",
|
|
394
|
+
"name": "kodUret",
|
|
395
|
+
"type": "code",
|
|
396
|
+
"description": "6 haneli kodu GUID'in rastgele bitlerinden üretir, satıra özgü tuzla özetler; deneme sayacı 24 saatlik pencere dolmadıkça sıfırlanmaz.",
|
|
397
|
+
"rule": {
|
|
398
|
+
"stepType": "code",
|
|
399
|
+
"expression": "const hex = GenerateGuid().replace(/-/g, ''); const code = String(parseInt(hex.slice(-10), 16) % 1000000).padStart(6, '0'); const salt = GenerateGuid(); const now = UtcNow(); const fresh = !(mevcutKod.data && mevcutKod.data.id) || (now.getTime() - new Date(mevcutKod.data.window_started_at).getTime()) >= 86400000; return { code: code, salt: salt, hash: crypto.SHA256.encode(salt + ':' + code), now: now.toISOString(), expiresAt: new Date(now.getTime() + 15 * 60000).toISOString(), attempts: fresh ? 0 : mevcutKod.data.attempts, windowStartedAt: fresh ? now.toISOString() : new Date(mevcutKod.data.window_started_at).toISOString() }"
|
|
400
|
+
},
|
|
401
|
+
"branches": [
|
|
402
|
+
{
|
|
403
|
+
"id": "<iste-b8>",
|
|
404
|
+
"name": "success",
|
|
405
|
+
"type": "success",
|
|
406
|
+
"nextStepId": "<iste-satirVar>"
|
|
407
|
+
}
|
|
408
|
+
]
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
"id": "<iste-satirVar>",
|
|
412
|
+
"name": "satirVar",
|
|
413
|
+
"type": "condition",
|
|
414
|
+
"description": "Satır varsa günceller, yoksa açar.",
|
|
415
|
+
"rule": {
|
|
416
|
+
"stepType": "condition",
|
|
417
|
+
"items": [
|
|
418
|
+
{
|
|
419
|
+
"expression": "mevcutKod.data && mevcutKod.data.id",
|
|
420
|
+
"branchId": "<iste-b9>"
|
|
421
|
+
}
|
|
422
|
+
]
|
|
423
|
+
},
|
|
424
|
+
"branches": [
|
|
425
|
+
{
|
|
426
|
+
"id": "<iste-b9>",
|
|
427
|
+
"name": "<iste-b9>",
|
|
428
|
+
"type": "success",
|
|
429
|
+
"nextStepId": "<iste-kodGuncelle>"
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
"id": "<iste-b10>",
|
|
433
|
+
"name": "else",
|
|
434
|
+
"type": "success",
|
|
435
|
+
"nextStepId": "<iste-kodYaz>"
|
|
436
|
+
}
|
|
437
|
+
]
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
"id": "<iste-kodGuncelle>",
|
|
441
|
+
"name": "kodGuncelle",
|
|
442
|
+
"type": "table",
|
|
443
|
+
"description": "Önceki kodu yenisiyle değiştirir (eski kod geçersiz olur); deneme sayacını pencereyle birlikte taşır.",
|
|
444
|
+
"rule": {
|
|
445
|
+
"stepType": "table",
|
|
446
|
+
"queryStepSourceType": "library",
|
|
447
|
+
"isExpression": false,
|
|
448
|
+
"datasourceId": "<datasource id>",
|
|
449
|
+
"entityId": "<password_reset_codes entity id>",
|
|
450
|
+
"operation": "update",
|
|
451
|
+
"alias": "password_reset_codes",
|
|
452
|
+
"isSingle": true,
|
|
453
|
+
"selectFields": [],
|
|
454
|
+
"joins": [],
|
|
455
|
+
"filters": [
|
|
456
|
+
{
|
|
457
|
+
"alias": "password_reset_codes",
|
|
458
|
+
"fieldId": "<password_reset_codes.id alan id>",
|
|
459
|
+
"fieldSlug": "id",
|
|
460
|
+
"operator": "eq",
|
|
461
|
+
"source": "expression",
|
|
462
|
+
"value": "mevcutKod.data.id",
|
|
463
|
+
"filterType": "single",
|
|
464
|
+
"conditionType": "and",
|
|
465
|
+
"isRequired": true,
|
|
466
|
+
"children": [],
|
|
467
|
+
"order": 0
|
|
468
|
+
}
|
|
469
|
+
],
|
|
470
|
+
"fields": [
|
|
471
|
+
{
|
|
472
|
+
"fieldId": "<password_reset_codes.salt alan id>",
|
|
473
|
+
"fieldSlug": "salt",
|
|
474
|
+
"source": "expression",
|
|
475
|
+
"value": "kodUret.data.salt"
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
"fieldId": "<password_reset_codes.code_hash alan id>",
|
|
479
|
+
"fieldSlug": "code_hash",
|
|
480
|
+
"source": "expression",
|
|
481
|
+
"value": "kodUret.data.hash"
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
"fieldId": "<password_reset_codes.expires_at alan id>",
|
|
485
|
+
"fieldSlug": "expires_at",
|
|
486
|
+
"source": "expression",
|
|
487
|
+
"value": "kodUret.data.expiresAt"
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
"fieldId": "<password_reset_codes.attempts alan id>",
|
|
491
|
+
"fieldSlug": "attempts",
|
|
492
|
+
"source": "expression",
|
|
493
|
+
"value": "kodUret.data.attempts"
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
"fieldId": "<password_reset_codes.window_started_at alan id>",
|
|
497
|
+
"fieldSlug": "window_started_at",
|
|
498
|
+
"source": "expression",
|
|
499
|
+
"value": "kodUret.data.windowStartedAt"
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
"fieldId": "<password_reset_codes.sent_at alan id>",
|
|
503
|
+
"fieldSlug": "sent_at",
|
|
504
|
+
"source": "expression",
|
|
505
|
+
"value": "kodUret.data.now"
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
"fieldId": "<password_reset_codes.used_at alan id>",
|
|
509
|
+
"fieldSlug": "used_at",
|
|
510
|
+
"source": "expression",
|
|
511
|
+
"value": "null"
|
|
512
|
+
}
|
|
513
|
+
],
|
|
514
|
+
"returnFields": [
|
|
515
|
+
{
|
|
516
|
+
"alias": "password_reset_codes",
|
|
517
|
+
"fieldId": "<password_reset_codes.id alan id>",
|
|
518
|
+
"fieldSlug": "id"
|
|
519
|
+
}
|
|
520
|
+
],
|
|
521
|
+
"sort": {
|
|
522
|
+
"name": "sortField",
|
|
523
|
+
"type": "sortType",
|
|
524
|
+
"source": "query-string"
|
|
525
|
+
},
|
|
526
|
+
"limit": {
|
|
527
|
+
"name": "limit",
|
|
528
|
+
"offset": "offset",
|
|
529
|
+
"source": "query-string"
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
"branches": [
|
|
533
|
+
{
|
|
534
|
+
"id": "<iste-b11>",
|
|
535
|
+
"name": "success",
|
|
536
|
+
"type": "success",
|
|
537
|
+
"nextStepId": "<iste-eposta>"
|
|
538
|
+
}
|
|
539
|
+
]
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
"id": "<iste-kodYaz>",
|
|
543
|
+
"name": "kodYaz",
|
|
544
|
+
"type": "table",
|
|
545
|
+
"description": "Kullanıcının ilk kod satırını yazar.",
|
|
546
|
+
"rule": {
|
|
547
|
+
"stepType": "table",
|
|
548
|
+
"queryStepSourceType": "library",
|
|
549
|
+
"isExpression": false,
|
|
550
|
+
"datasourceId": "<datasource id>",
|
|
551
|
+
"entityId": "<password_reset_codes entity id>",
|
|
552
|
+
"operation": "create",
|
|
553
|
+
"alias": "password_reset_codes",
|
|
554
|
+
"isSingle": true,
|
|
555
|
+
"selectFields": [],
|
|
556
|
+
"joins": [],
|
|
557
|
+
"filters": [],
|
|
558
|
+
"fields": [
|
|
559
|
+
{
|
|
560
|
+
"fieldId": "<password_reset_codes.user_id alan id>",
|
|
561
|
+
"fieldSlug": "user_id",
|
|
562
|
+
"source": "expression",
|
|
563
|
+
"value": "kullanici.data.id"
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
"fieldId": "<password_reset_codes.salt alan id>",
|
|
567
|
+
"fieldSlug": "salt",
|
|
568
|
+
"source": "expression",
|
|
569
|
+
"value": "kodUret.data.salt"
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
"fieldId": "<password_reset_codes.code_hash alan id>",
|
|
573
|
+
"fieldSlug": "code_hash",
|
|
574
|
+
"source": "expression",
|
|
575
|
+
"value": "kodUret.data.hash"
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
"fieldId": "<password_reset_codes.expires_at alan id>",
|
|
579
|
+
"fieldSlug": "expires_at",
|
|
580
|
+
"source": "expression",
|
|
581
|
+
"value": "kodUret.data.expiresAt"
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
"fieldId": "<password_reset_codes.attempts alan id>",
|
|
585
|
+
"fieldSlug": "attempts",
|
|
586
|
+
"source": "expression",
|
|
587
|
+
"value": "0"
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
"fieldId": "<password_reset_codes.window_started_at alan id>",
|
|
591
|
+
"fieldSlug": "window_started_at",
|
|
592
|
+
"source": "expression",
|
|
593
|
+
"value": "kodUret.data.now"
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
"fieldId": "<password_reset_codes.sent_at alan id>",
|
|
597
|
+
"fieldSlug": "sent_at",
|
|
598
|
+
"source": "expression",
|
|
599
|
+
"value": "kodUret.data.now"
|
|
600
|
+
}
|
|
601
|
+
],
|
|
602
|
+
"returnFields": [
|
|
603
|
+
{
|
|
604
|
+
"alias": "password_reset_codes",
|
|
605
|
+
"fieldId": "<password_reset_codes.id alan id>",
|
|
606
|
+
"fieldSlug": "id"
|
|
607
|
+
}
|
|
608
|
+
],
|
|
609
|
+
"sort": {
|
|
610
|
+
"name": "sortField",
|
|
611
|
+
"type": "sortType",
|
|
612
|
+
"source": "query-string"
|
|
613
|
+
},
|
|
614
|
+
"limit": {
|
|
615
|
+
"name": "limit",
|
|
616
|
+
"offset": "offset",
|
|
617
|
+
"source": "query-string"
|
|
618
|
+
}
|
|
619
|
+
},
|
|
620
|
+
"branches": [
|
|
621
|
+
{
|
|
622
|
+
"id": "<iste-b12>",
|
|
623
|
+
"name": "success",
|
|
624
|
+
"type": "success",
|
|
625
|
+
"nextStepId": "<iste-eposta>"
|
|
626
|
+
}
|
|
627
|
+
]
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
"id": "<iste-eposta>",
|
|
631
|
+
"name": "eposta",
|
|
632
|
+
"type": "email",
|
|
633
|
+
"description": "Kodu kullanıcının adresine gönderir; kod yanıta girmez.",
|
|
634
|
+
"rule": {
|
|
635
|
+
"stepType": "email",
|
|
636
|
+
"expression": "({ to: [kullanici.data.email] })",
|
|
637
|
+
"subject": "'Şifre sıfırlama kodu'",
|
|
638
|
+
"body": "`<p>Şifre sıfırlama kodunuz: <b>${kodUret.data.code}</b></p><p>Kod 15 dakika geçerlidir.</p>`",
|
|
639
|
+
"displayName": "'<Uygulama adı>'",
|
|
640
|
+
"attachment": "[]"
|
|
641
|
+
},
|
|
642
|
+
"branches": [
|
|
643
|
+
{
|
|
644
|
+
"id": "<iste-b13>",
|
|
645
|
+
"name": "success",
|
|
646
|
+
"type": "success",
|
|
647
|
+
"nextStepId": "<iste-yanit>"
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
"id": "<iste-b14>",
|
|
651
|
+
"name": "fail",
|
|
652
|
+
"type": "fail",
|
|
653
|
+
"nextStepId": "<iste-yanit>"
|
|
654
|
+
}
|
|
655
|
+
]
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
"id": "<iste-yanit>",
|
|
659
|
+
"name": "yanit",
|
|
660
|
+
"type": "response",
|
|
661
|
+
"description": "Hesap olsun olmasın aynı yanıtı döner.",
|
|
662
|
+
"rule": {
|
|
663
|
+
"stepType": "response",
|
|
664
|
+
"bodyExpression": "({ message: 'Kayıtlıysa e-posta gönderildi.' })",
|
|
665
|
+
"httpStatusCode": 200
|
|
666
|
+
},
|
|
667
|
+
"branches": []
|
|
668
|
+
}
|
|
669
|
+
]
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
"name": "Şifre Sıfırla",
|
|
673
|
+
"slug": "sifre-sifirla",
|
|
674
|
+
"description": "Anonim: geçerli kodla parolayı değiştirir.",
|
|
675
|
+
"type": "workflow",
|
|
676
|
+
"parameterDtoId": "<sifre-sifirla-girdi DTO id>",
|
|
677
|
+
"parameterBehavior": "object",
|
|
678
|
+
"returnDtoId": "<sifre-sifirla-sonuc DTO id>",
|
|
679
|
+
"returnBehavior": "object",
|
|
680
|
+
"steps": [
|
|
681
|
+
{
|
|
682
|
+
"id": "<sifirla-start>",
|
|
683
|
+
"name": "start",
|
|
684
|
+
"type": "start",
|
|
685
|
+
"description": "Akışı başlatır.",
|
|
686
|
+
"branches": [
|
|
687
|
+
{
|
|
688
|
+
"id": "<sifirla-b0>",
|
|
689
|
+
"name": "start",
|
|
690
|
+
"type": "success",
|
|
691
|
+
"nextStepId": "<sifirla-parolaUygun>"
|
|
692
|
+
}
|
|
693
|
+
]
|
|
694
|
+
},
|
|
695
|
+
{
|
|
696
|
+
"id": "<sifirla-parolaUygun>",
|
|
697
|
+
"name": "parolaUygun",
|
|
698
|
+
"type": "condition",
|
|
699
|
+
"description": "Yeni parola en az 8 karakter mi.",
|
|
700
|
+
"rule": {
|
|
701
|
+
"stepType": "condition",
|
|
702
|
+
"items": [
|
|
703
|
+
{
|
|
704
|
+
"expression": "typeof input.newPassword === 'string' && input.newPassword.length >= 8",
|
|
705
|
+
"branchId": "<sifirla-b1>"
|
|
706
|
+
}
|
|
707
|
+
]
|
|
708
|
+
},
|
|
709
|
+
"branches": [
|
|
710
|
+
{
|
|
711
|
+
"id": "<sifirla-b1>",
|
|
712
|
+
"name": "<sifirla-b1>",
|
|
713
|
+
"type": "success",
|
|
714
|
+
"nextStepId": "<sifirla-kullanici>"
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
"id": "<sifirla-b2>",
|
|
718
|
+
"name": "else",
|
|
719
|
+
"type": "success",
|
|
720
|
+
"nextStepId": "<sifirla-zayif>"
|
|
721
|
+
}
|
|
722
|
+
]
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
"id": "<sifirla-kullanici>",
|
|
726
|
+
"name": "kullanici",
|
|
727
|
+
"type": "table",
|
|
728
|
+
"description": "E-postanın sahibini okur.",
|
|
729
|
+
"rule": {
|
|
730
|
+
"stepType": "table",
|
|
731
|
+
"queryStepSourceType": "library",
|
|
732
|
+
"isExpression": false,
|
|
733
|
+
"datasourceId": "<datasource id>",
|
|
734
|
+
"entityId": "<users entity id>",
|
|
735
|
+
"operation": "get",
|
|
736
|
+
"alias": "users",
|
|
737
|
+
"isSingle": true,
|
|
738
|
+
"selectFields": [
|
|
739
|
+
{
|
|
740
|
+
"alias": "users",
|
|
741
|
+
"fieldId": "<users.id alan id>",
|
|
742
|
+
"fieldSlug": "id"
|
|
743
|
+
}
|
|
744
|
+
],
|
|
745
|
+
"joins": [],
|
|
746
|
+
"filters": [
|
|
747
|
+
{
|
|
748
|
+
"alias": "users",
|
|
749
|
+
"fieldId": "<users.email alan id>",
|
|
750
|
+
"fieldSlug": "email",
|
|
751
|
+
"operator": "eq",
|
|
752
|
+
"source": "expression",
|
|
753
|
+
"value": "input.email",
|
|
754
|
+
"filterType": "single",
|
|
755
|
+
"conditionType": "and",
|
|
756
|
+
"isRequired": true,
|
|
757
|
+
"children": [],
|
|
758
|
+
"order": 0
|
|
759
|
+
}
|
|
760
|
+
],
|
|
761
|
+
"fields": [],
|
|
762
|
+
"returnFields": [],
|
|
763
|
+
"sort": {
|
|
764
|
+
"name": "sortField",
|
|
765
|
+
"type": "sortType",
|
|
766
|
+
"source": "query-string"
|
|
767
|
+
},
|
|
768
|
+
"limit": {
|
|
769
|
+
"name": "limit",
|
|
770
|
+
"offset": "offset",
|
|
771
|
+
"source": "query-string"
|
|
772
|
+
}
|
|
773
|
+
},
|
|
774
|
+
"branches": [
|
|
775
|
+
{
|
|
776
|
+
"id": "<sifirla-b3>",
|
|
777
|
+
"name": "success",
|
|
778
|
+
"type": "success",
|
|
779
|
+
"nextStepId": "<sifirla-kullaniciVar>"
|
|
780
|
+
}
|
|
781
|
+
]
|
|
782
|
+
},
|
|
783
|
+
{
|
|
784
|
+
"id": "<sifirla-kullaniciVar>",
|
|
785
|
+
"name": "kullaniciVar",
|
|
786
|
+
"type": "condition",
|
|
787
|
+
"description": "Kullanıcı yoksa genel ret.",
|
|
788
|
+
"rule": {
|
|
789
|
+
"stepType": "condition",
|
|
790
|
+
"items": [
|
|
791
|
+
{
|
|
792
|
+
"expression": "kullanici.data && kullanici.data.id",
|
|
793
|
+
"branchId": "<sifirla-b4>"
|
|
794
|
+
}
|
|
795
|
+
]
|
|
796
|
+
},
|
|
797
|
+
"branches": [
|
|
798
|
+
{
|
|
799
|
+
"id": "<sifirla-b4>",
|
|
800
|
+
"name": "<sifirla-b4>",
|
|
801
|
+
"type": "success",
|
|
802
|
+
"nextStepId": "<sifirla-kod>"
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
"id": "<sifirla-b5>",
|
|
806
|
+
"name": "else",
|
|
807
|
+
"type": "success",
|
|
808
|
+
"nextStepId": "<sifirla-sureEsitle>"
|
|
809
|
+
}
|
|
810
|
+
]
|
|
811
|
+
},
|
|
812
|
+
{
|
|
813
|
+
"id": "<sifirla-kod>",
|
|
814
|
+
"name": "kod",
|
|
815
|
+
"type": "table",
|
|
816
|
+
"description": "Kullanıcının kod satırını okur.",
|
|
817
|
+
"rule": {
|
|
818
|
+
"stepType": "table",
|
|
819
|
+
"queryStepSourceType": "library",
|
|
820
|
+
"isExpression": false,
|
|
821
|
+
"datasourceId": "<datasource id>",
|
|
822
|
+
"entityId": "<password_reset_codes entity id>",
|
|
823
|
+
"operation": "get",
|
|
824
|
+
"alias": "password_reset_codes",
|
|
825
|
+
"isSingle": true,
|
|
826
|
+
"selectFields": [
|
|
827
|
+
{
|
|
828
|
+
"alias": "password_reset_codes",
|
|
829
|
+
"fieldId": "<password_reset_codes.id alan id>",
|
|
830
|
+
"fieldSlug": "id"
|
|
831
|
+
},
|
|
832
|
+
{
|
|
833
|
+
"alias": "password_reset_codes",
|
|
834
|
+
"fieldId": "<password_reset_codes.salt alan id>",
|
|
835
|
+
"fieldSlug": "salt"
|
|
836
|
+
},
|
|
837
|
+
{
|
|
838
|
+
"alias": "password_reset_codes",
|
|
839
|
+
"fieldId": "<password_reset_codes.code_hash alan id>",
|
|
840
|
+
"fieldSlug": "code_hash"
|
|
841
|
+
},
|
|
842
|
+
{
|
|
843
|
+
"alias": "password_reset_codes",
|
|
844
|
+
"fieldId": "<password_reset_codes.expires_at alan id>",
|
|
845
|
+
"fieldSlug": "expires_at"
|
|
846
|
+
},
|
|
847
|
+
{
|
|
848
|
+
"alias": "password_reset_codes",
|
|
849
|
+
"fieldId": "<password_reset_codes.attempts alan id>",
|
|
850
|
+
"fieldSlug": "attempts"
|
|
851
|
+
},
|
|
852
|
+
{
|
|
853
|
+
"alias": "password_reset_codes",
|
|
854
|
+
"fieldId": "<password_reset_codes.used_at alan id>",
|
|
855
|
+
"fieldSlug": "used_at"
|
|
856
|
+
}
|
|
857
|
+
],
|
|
858
|
+
"joins": [],
|
|
859
|
+
"filters": [
|
|
860
|
+
{
|
|
861
|
+
"alias": "password_reset_codes",
|
|
862
|
+
"fieldId": "<password_reset_codes.user_id alan id>",
|
|
863
|
+
"fieldSlug": "user_id",
|
|
864
|
+
"operator": "eq",
|
|
865
|
+
"source": "expression",
|
|
866
|
+
"value": "kullanici.data.id",
|
|
867
|
+
"filterType": "single",
|
|
868
|
+
"conditionType": "and",
|
|
869
|
+
"isRequired": true,
|
|
870
|
+
"children": [],
|
|
871
|
+
"order": 0
|
|
872
|
+
}
|
|
873
|
+
],
|
|
874
|
+
"fields": [],
|
|
875
|
+
"returnFields": [],
|
|
876
|
+
"sort": {
|
|
877
|
+
"name": "sortField",
|
|
878
|
+
"type": "sortType",
|
|
879
|
+
"source": "query-string"
|
|
880
|
+
},
|
|
881
|
+
"limit": {
|
|
882
|
+
"name": "limit",
|
|
883
|
+
"offset": "offset",
|
|
884
|
+
"source": "query-string"
|
|
885
|
+
}
|
|
886
|
+
},
|
|
887
|
+
"branches": [
|
|
888
|
+
{
|
|
889
|
+
"id": "<sifirla-b6>",
|
|
890
|
+
"name": "success",
|
|
891
|
+
"type": "success",
|
|
892
|
+
"nextStepId": "<sifirla-kodGecerli>"
|
|
893
|
+
}
|
|
894
|
+
]
|
|
895
|
+
},
|
|
896
|
+
{
|
|
897
|
+
"id": "<sifirla-kodGecerli>",
|
|
898
|
+
"name": "kodGecerli",
|
|
899
|
+
"type": "condition",
|
|
900
|
+
"description": "Satır var, kullanılmamış, süresi dolmamış ve 24 saatlik pencerede deneme hakkı kalmış mı.",
|
|
901
|
+
"rule": {
|
|
902
|
+
"stepType": "condition",
|
|
903
|
+
"items": [
|
|
904
|
+
{
|
|
905
|
+
"expression": "kod.data && kod.data.id && !kod.data.used_at && kod.data.attempts < 5 && new Date(kod.data.expires_at).getTime() > UtcNow().getTime()",
|
|
906
|
+
"branchId": "<sifirla-b7>"
|
|
907
|
+
}
|
|
908
|
+
]
|
|
909
|
+
},
|
|
910
|
+
"branches": [
|
|
911
|
+
{
|
|
912
|
+
"id": "<sifirla-b7>",
|
|
913
|
+
"name": "<sifirla-b7>",
|
|
914
|
+
"type": "success",
|
|
915
|
+
"nextStepId": "<sifirla-denemeSay>"
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
"id": "<sifirla-b8>",
|
|
919
|
+
"name": "else",
|
|
920
|
+
"type": "success",
|
|
921
|
+
"nextStepId": "<sifirla-sureEsitle>"
|
|
922
|
+
}
|
|
923
|
+
]
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
"id": "<sifirla-denemeSay>",
|
|
927
|
+
"name": "denemeSay",
|
|
928
|
+
"type": "table",
|
|
929
|
+
"description": "Karşılaştırmadan ÖNCE deneme hakkını harcar: yalnız sayaç okunduğu değerdeyse artırır (karşılaştır-ve-yaz). Aynı anda gelen istekten yalnız biri satırı günceller, ötekiler boş sonuç alır.",
|
|
930
|
+
"rule": {
|
|
931
|
+
"stepType": "table",
|
|
932
|
+
"queryStepSourceType": "library",
|
|
933
|
+
"isExpression": false,
|
|
934
|
+
"datasourceId": "<datasource id>",
|
|
935
|
+
"entityId": "<password_reset_codes entity id>",
|
|
936
|
+
"operation": "update",
|
|
937
|
+
"alias": "password_reset_codes",
|
|
938
|
+
"isSingle": true,
|
|
939
|
+
"selectFields": [],
|
|
940
|
+
"joins": [],
|
|
941
|
+
"filters": [
|
|
942
|
+
{
|
|
943
|
+
"alias": "password_reset_codes",
|
|
944
|
+
"fieldId": "<password_reset_codes.id alan id>",
|
|
945
|
+
"fieldSlug": "id",
|
|
946
|
+
"operator": "eq",
|
|
947
|
+
"source": "expression",
|
|
948
|
+
"value": "kod.data.id",
|
|
949
|
+
"filterType": "single",
|
|
950
|
+
"conditionType": "and",
|
|
951
|
+
"isRequired": true,
|
|
952
|
+
"children": [],
|
|
953
|
+
"order": 0
|
|
954
|
+
},
|
|
955
|
+
{
|
|
956
|
+
"alias": "password_reset_codes",
|
|
957
|
+
"fieldId": "<password_reset_codes.attempts alan id>",
|
|
958
|
+
"fieldSlug": "attempts",
|
|
959
|
+
"operator": "eq",
|
|
960
|
+
"source": "expression",
|
|
961
|
+
"value": "kod.data.attempts",
|
|
962
|
+
"filterType": "single",
|
|
963
|
+
"conditionType": "and",
|
|
964
|
+
"isRequired": true,
|
|
965
|
+
"children": [],
|
|
966
|
+
"order": 1
|
|
967
|
+
}
|
|
968
|
+
],
|
|
969
|
+
"fields": [
|
|
970
|
+
{
|
|
971
|
+
"fieldId": "<password_reset_codes.attempts alan id>",
|
|
972
|
+
"fieldSlug": "attempts",
|
|
973
|
+
"source": "expression",
|
|
974
|
+
"value": "kod.data.attempts + 1"
|
|
975
|
+
}
|
|
976
|
+
],
|
|
977
|
+
"returnFields": [
|
|
978
|
+
{
|
|
979
|
+
"alias": "password_reset_codes",
|
|
980
|
+
"fieldId": "<password_reset_codes.id alan id>",
|
|
981
|
+
"fieldSlug": "id"
|
|
982
|
+
}
|
|
983
|
+
],
|
|
984
|
+
"sort": {
|
|
985
|
+
"name": "sortField",
|
|
986
|
+
"type": "sortType",
|
|
987
|
+
"source": "query-string"
|
|
988
|
+
},
|
|
989
|
+
"limit": {
|
|
990
|
+
"name": "limit",
|
|
991
|
+
"offset": "offset",
|
|
992
|
+
"source": "query-string"
|
|
993
|
+
}
|
|
994
|
+
},
|
|
995
|
+
"branches": [
|
|
996
|
+
{
|
|
997
|
+
"id": "<sifirla-b-say>",
|
|
998
|
+
"name": "success",
|
|
999
|
+
"type": "success",
|
|
1000
|
+
"nextStepId": "<sifirla-sayildi>"
|
|
1001
|
+
},
|
|
1002
|
+
{
|
|
1003
|
+
"id": "<sifirla-b-sayfail>",
|
|
1004
|
+
"name": "fail",
|
|
1005
|
+
"type": "fail",
|
|
1006
|
+
"nextStepId": "<sifirla-sureEsitle>"
|
|
1007
|
+
}
|
|
1008
|
+
]
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
"id": "<sifirla-sayildi>",
|
|
1012
|
+
"name": "sayildi",
|
|
1013
|
+
"type": "condition",
|
|
1014
|
+
"description": "Deneme bu istekte sayıldı mı; sayılmadıysa paralel bir istek hakkı aldı, kod karşılaştırılmaz.",
|
|
1015
|
+
"rule": {
|
|
1016
|
+
"stepType": "condition",
|
|
1017
|
+
"items": [
|
|
1018
|
+
{
|
|
1019
|
+
"expression": "denemeSay.data && typeof denemeSay.data !== 'string' && (typeof denemeSay.data.length === 'number' ? denemeSay.data.length > 0 : !!denemeSay.data.id)",
|
|
1020
|
+
"branchId": "<sifirla-b-sayildi>"
|
|
1021
|
+
}
|
|
1022
|
+
]
|
|
1023
|
+
},
|
|
1024
|
+
"branches": [
|
|
1025
|
+
{
|
|
1026
|
+
"id": "<sifirla-b-sayildi>",
|
|
1027
|
+
"name": "<sifirla-b-sayildi>",
|
|
1028
|
+
"type": "success",
|
|
1029
|
+
"nextStepId": "<sifirla-kodEslesiyor>"
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
"id": "<sifirla-b-sayilmadi>",
|
|
1033
|
+
"name": "else",
|
|
1034
|
+
"type": "success",
|
|
1035
|
+
"nextStepId": "<sifirla-sureEsitle>"
|
|
1036
|
+
}
|
|
1037
|
+
]
|
|
1038
|
+
},
|
|
1039
|
+
{
|
|
1040
|
+
"id": "<sifirla-kodEslesiyor>",
|
|
1041
|
+
"name": "kodEslesiyor",
|
|
1042
|
+
"type": "condition",
|
|
1043
|
+
"description": "Girilen kodun özeti saklanan özetle aynı mı.",
|
|
1044
|
+
"rule": {
|
|
1045
|
+
"stepType": "condition",
|
|
1046
|
+
"items": [
|
|
1047
|
+
{
|
|
1048
|
+
"expression": "crypto.SHA256.encode(kod.data.salt + ':' + String(input.code)) === kod.data.code_hash",
|
|
1049
|
+
"branchId": "<sifirla-b9>"
|
|
1050
|
+
}
|
|
1051
|
+
]
|
|
1052
|
+
},
|
|
1053
|
+
"branches": [
|
|
1054
|
+
{
|
|
1055
|
+
"id": "<sifirla-b9>",
|
|
1056
|
+
"name": "<sifirla-b9>",
|
|
1057
|
+
"type": "success",
|
|
1058
|
+
"nextStepId": "<sifirla-parolaYaz>"
|
|
1059
|
+
},
|
|
1060
|
+
{
|
|
1061
|
+
"id": "<sifirla-b10>",
|
|
1062
|
+
"name": "else",
|
|
1063
|
+
"type": "success",
|
|
1064
|
+
"nextStepId": "<sifirla-gecersiz>"
|
|
1065
|
+
}
|
|
1066
|
+
]
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
"id": "<sifirla-sureEsitle>",
|
|
1070
|
+
"name": "sureEsitle",
|
|
1071
|
+
"type": "code",
|
|
1072
|
+
"description": "Kod karşılaştırılmayan ret dallarında da bir özet hesaplar; yanıt süresi hesabın ya da kodun varlığını sızdırmaz.",
|
|
1073
|
+
"rule": {
|
|
1074
|
+
"stepType": "code",
|
|
1075
|
+
"expression": "crypto.SHA256.encode(GenerateGuid()); return true"
|
|
1076
|
+
},
|
|
1077
|
+
"branches": [
|
|
1078
|
+
{
|
|
1079
|
+
"id": "<sifirla-b-sure>",
|
|
1080
|
+
"name": "success",
|
|
1081
|
+
"type": "success",
|
|
1082
|
+
"nextStepId": "<sifirla-gecersiz>"
|
|
1083
|
+
}
|
|
1084
|
+
]
|
|
1085
|
+
},
|
|
1086
|
+
{
|
|
1087
|
+
"id": "<sifirla-parolaYaz>",
|
|
1088
|
+
"name": "parolaYaz",
|
|
1089
|
+
"type": "table",
|
|
1090
|
+
"description": "Yeni parolayı düz yazar; platform users.password alanını yazarken özetler.",
|
|
1091
|
+
"rule": {
|
|
1092
|
+
"stepType": "table",
|
|
1093
|
+
"queryStepSourceType": "library",
|
|
1094
|
+
"isExpression": false,
|
|
1095
|
+
"datasourceId": "<datasource id>",
|
|
1096
|
+
"entityId": "<users entity id>",
|
|
1097
|
+
"operation": "update",
|
|
1098
|
+
"alias": "users",
|
|
1099
|
+
"isSingle": true,
|
|
1100
|
+
"selectFields": [],
|
|
1101
|
+
"joins": [],
|
|
1102
|
+
"filters": [
|
|
1103
|
+
{
|
|
1104
|
+
"alias": "users",
|
|
1105
|
+
"fieldId": "<users.id alan id>",
|
|
1106
|
+
"fieldSlug": "id",
|
|
1107
|
+
"operator": "eq",
|
|
1108
|
+
"source": "expression",
|
|
1109
|
+
"value": "kullanici.data.id",
|
|
1110
|
+
"filterType": "single",
|
|
1111
|
+
"conditionType": "and",
|
|
1112
|
+
"isRequired": true,
|
|
1113
|
+
"children": [],
|
|
1114
|
+
"order": 0
|
|
1115
|
+
}
|
|
1116
|
+
],
|
|
1117
|
+
"fields": [
|
|
1118
|
+
{
|
|
1119
|
+
"fieldId": "<users.password alan id>",
|
|
1120
|
+
"fieldSlug": "password",
|
|
1121
|
+
"source": "expression",
|
|
1122
|
+
"value": "input.newPassword"
|
|
1123
|
+
}
|
|
1124
|
+
],
|
|
1125
|
+
"returnFields": [
|
|
1126
|
+
{
|
|
1127
|
+
"alias": "users",
|
|
1128
|
+
"fieldId": "<users.id alan id>",
|
|
1129
|
+
"fieldSlug": "id"
|
|
1130
|
+
}
|
|
1131
|
+
],
|
|
1132
|
+
"sort": {
|
|
1133
|
+
"name": "sortField",
|
|
1134
|
+
"type": "sortType",
|
|
1135
|
+
"source": "query-string"
|
|
1136
|
+
},
|
|
1137
|
+
"limit": {
|
|
1138
|
+
"name": "limit",
|
|
1139
|
+
"offset": "offset",
|
|
1140
|
+
"source": "query-string"
|
|
1141
|
+
}
|
|
1142
|
+
},
|
|
1143
|
+
"branches": [
|
|
1144
|
+
{
|
|
1145
|
+
"id": "<sifirla-b12>",
|
|
1146
|
+
"name": "success",
|
|
1147
|
+
"type": "success",
|
|
1148
|
+
"nextStepId": "<sifirla-kodKullanildi>"
|
|
1149
|
+
}
|
|
1150
|
+
]
|
|
1151
|
+
},
|
|
1152
|
+
{
|
|
1153
|
+
"id": "<sifirla-kodKullanildi>",
|
|
1154
|
+
"name": "kodKullanildi",
|
|
1155
|
+
"type": "table",
|
|
1156
|
+
"description": "Kodu kullanıldı olarak işaretler.",
|
|
1157
|
+
"rule": {
|
|
1158
|
+
"stepType": "table",
|
|
1159
|
+
"queryStepSourceType": "library",
|
|
1160
|
+
"isExpression": false,
|
|
1161
|
+
"datasourceId": "<datasource id>",
|
|
1162
|
+
"entityId": "<password_reset_codes entity id>",
|
|
1163
|
+
"operation": "update",
|
|
1164
|
+
"alias": "password_reset_codes",
|
|
1165
|
+
"isSingle": true,
|
|
1166
|
+
"selectFields": [],
|
|
1167
|
+
"joins": [],
|
|
1168
|
+
"filters": [
|
|
1169
|
+
{
|
|
1170
|
+
"alias": "password_reset_codes",
|
|
1171
|
+
"fieldId": "<password_reset_codes.id alan id>",
|
|
1172
|
+
"fieldSlug": "id",
|
|
1173
|
+
"operator": "eq",
|
|
1174
|
+
"source": "expression",
|
|
1175
|
+
"value": "kod.data.id",
|
|
1176
|
+
"filterType": "single",
|
|
1177
|
+
"conditionType": "and",
|
|
1178
|
+
"isRequired": true,
|
|
1179
|
+
"children": [],
|
|
1180
|
+
"order": 0
|
|
1181
|
+
}
|
|
1182
|
+
],
|
|
1183
|
+
"fields": [
|
|
1184
|
+
{
|
|
1185
|
+
"fieldId": "<password_reset_codes.used_at alan id>",
|
|
1186
|
+
"fieldSlug": "used_at",
|
|
1187
|
+
"source": "expression",
|
|
1188
|
+
"value": "UtcNow().toISOString()"
|
|
1189
|
+
}
|
|
1190
|
+
],
|
|
1191
|
+
"returnFields": [
|
|
1192
|
+
{
|
|
1193
|
+
"alias": "password_reset_codes",
|
|
1194
|
+
"fieldId": "<password_reset_codes.id alan id>",
|
|
1195
|
+
"fieldSlug": "id"
|
|
1196
|
+
}
|
|
1197
|
+
],
|
|
1198
|
+
"sort": {
|
|
1199
|
+
"name": "sortField",
|
|
1200
|
+
"type": "sortType",
|
|
1201
|
+
"source": "query-string"
|
|
1202
|
+
},
|
|
1203
|
+
"limit": {
|
|
1204
|
+
"name": "limit",
|
|
1205
|
+
"offset": "offset",
|
|
1206
|
+
"source": "query-string"
|
|
1207
|
+
}
|
|
1208
|
+
},
|
|
1209
|
+
"branches": [
|
|
1210
|
+
{
|
|
1211
|
+
"id": "<sifirla-b13>",
|
|
1212
|
+
"name": "success",
|
|
1213
|
+
"type": "success",
|
|
1214
|
+
"nextStepId": "<sifirla-tamam>"
|
|
1215
|
+
}
|
|
1216
|
+
]
|
|
1217
|
+
},
|
|
1218
|
+
{
|
|
1219
|
+
"id": "<sifirla-tamam>",
|
|
1220
|
+
"name": "tamam",
|
|
1221
|
+
"type": "response",
|
|
1222
|
+
"description": "Parola değişti.",
|
|
1223
|
+
"rule": {
|
|
1224
|
+
"stepType": "response",
|
|
1225
|
+
"bodyExpression": "({ ok: true, message: 'Parola değiştirildi.' })",
|
|
1226
|
+
"httpStatusCode": 200
|
|
1227
|
+
},
|
|
1228
|
+
"branches": []
|
|
1229
|
+
},
|
|
1230
|
+
{
|
|
1231
|
+
"id": "<sifirla-gecersiz>",
|
|
1232
|
+
"name": "gecersiz",
|
|
1233
|
+
"type": "response",
|
|
1234
|
+
"description": "Kullanıcı, kod ya da süre hatasında tek genel ret; 200 döner ki deneme sayacı geri alınmasın.",
|
|
1235
|
+
"rule": {
|
|
1236
|
+
"stepType": "response",
|
|
1237
|
+
"bodyExpression": "({ ok: false, message: 'Kod geçersiz ya da süresi dolmuş.' })",
|
|
1238
|
+
"httpStatusCode": 200
|
|
1239
|
+
},
|
|
1240
|
+
"branches": []
|
|
1241
|
+
},
|
|
1242
|
+
{
|
|
1243
|
+
"id": "<sifirla-zayif>",
|
|
1244
|
+
"name": "zayif",
|
|
1245
|
+
"type": "response",
|
|
1246
|
+
"description": "Parola kuralı.",
|
|
1247
|
+
"rule": {
|
|
1248
|
+
"stepType": "response",
|
|
1249
|
+
"bodyExpression": "({ ok: false, message: 'Parola en az 8 karakter olmalı.' })",
|
|
1250
|
+
"httpStatusCode": 200
|
|
1251
|
+
},
|
|
1252
|
+
"branches": []
|
|
1253
|
+
}
|
|
1254
|
+
]
|
|
1255
|
+
}
|
|
1256
|
+
],
|
|
1257
|
+
"microservices": [
|
|
1258
|
+
{
|
|
1259
|
+
"name": "Şifre Kodu İste",
|
|
1260
|
+
"slug": "sifre-kodu-iste",
|
|
1261
|
+
"url": "sifre-kodu-iste",
|
|
1262
|
+
"description": "Anonim: kayıtlı adrese şifre sıfırlama kodu gönderir.",
|
|
1263
|
+
"type": "rest-api",
|
|
1264
|
+
"httpMethod": "post",
|
|
1265
|
+
"source": "workflow",
|
|
1266
|
+
"workflowId": "<sifre-kodu-iste workflow id>",
|
|
1267
|
+
"middlewareIds": []
|
|
1268
|
+
},
|
|
1269
|
+
{
|
|
1270
|
+
"name": "Şifre Sıfırla",
|
|
1271
|
+
"slug": "sifre-sifirla",
|
|
1272
|
+
"url": "sifre-sifirla",
|
|
1273
|
+
"description": "Anonim: geçerli kodla parolayı değiştirir.",
|
|
1274
|
+
"type": "rest-api",
|
|
1275
|
+
"httpMethod": "post",
|
|
1276
|
+
"source": "workflow",
|
|
1277
|
+
"workflowId": "<sifre-sifirla workflow id>",
|
|
1278
|
+
"middlewareIds": []
|
|
1279
|
+
}
|
|
1280
|
+
]
|
|
1281
|
+
}
|