wao 0.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/cjs/accounts.js +96 -0
- package/cjs/ao.js +1543 -0
- package/cjs/aoconnect.js +758 -0
- package/cjs/ar.js +794 -0
- package/cjs/dirname.js +7 -0
- package/cjs/helpers.js +381 -0
- package/cjs/index.js +20 -0
- package/cjs/utils.js +438 -0
- package/esm/.cache/opt.json +1 -0
- package/esm/accounts.js +91 -0
- package/esm/ao.js +737 -0
- package/esm/aoconnect.js +318 -0
- package/esm/ar.js +243 -0
- package/esm/dirname.js +1 -0
- package/esm/helpers.js +161 -0
- package/esm/index.js +4 -0
- package/esm/lua/aos-sqlite.wasm +0 -0
- package/esm/lua/aos.wasm +0 -0
- package/esm/lua/aos2.lua +33 -0
- package/esm/lua/aos2_0_1.wasm +0 -0
- package/esm/lua/atomic-asset.lua +238 -0
- package/esm/lua/atomic-note-library.lua +2274 -0
- package/esm/lua/atomic-note.lua +11 -0
- package/esm/lua/collection-registry.lua +202 -0
- package/esm/lua/collection.lua +173 -0
- package/esm/lua/notebook.lua +173 -0
- package/esm/lua/profile.lua +858 -0
- package/esm/lua/profile000.lua +666 -0
- package/esm/lua/proxy.lua +24 -0
- package/esm/lua/registry.lua +858 -0
- package/esm/lua/registry000.lua +636 -0
- package/esm/test.js +94 -0
- package/esm/utils.js +342 -0
- package/package.json +21 -0
- package/test/.cache/opt.json +1 -0
- package/test/accounts.js +91 -0
- package/test/ao.js +737 -0
- package/test/aoconnect.js +318 -0
- package/test/ar.js +243 -0
- package/test/dirname.js +1 -0
- package/test/helpers.js +161 -0
- package/test/index.js +4 -0
- package/test/lua/aos-sqlite.wasm +0 -0
- package/test/lua/aos.wasm +0 -0
- package/test/lua/aos2.lua +33 -0
- package/test/lua/aos2_0_1.wasm +0 -0
- package/test/lua/atomic-asset.lua +238 -0
- package/test/lua/atomic-note-library.lua +2274 -0
- package/test/lua/atomic-note.lua +11 -0
- package/test/lua/collection-registry.lua +202 -0
- package/test/lua/collection.lua +173 -0
- package/test/lua/notebook.lua +173 -0
- package/test/lua/profile.lua +858 -0
- package/test/lua/profile000.lua +666 -0
- package/test/lua/proxy.lua +24 -0
- package/test/lua/registry.lua +858 -0
- package/test/lua/registry000.lua +636 -0
- package/test/package.json +21 -0
- package/test/test.js +94 -0
- package/test/utils.js +342 -0
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
local bint = require('.bint')(256)
|
|
2
|
+
local json = require('json')
|
|
3
|
+
|
|
4
|
+
-- Profile: {
|
|
5
|
+
-- UserName
|
|
6
|
+
-- DisplayName
|
|
7
|
+
-- Description
|
|
8
|
+
-- CoverImage
|
|
9
|
+
-- ProfileImage
|
|
10
|
+
-- DateCreated
|
|
11
|
+
-- DateUpdated
|
|
12
|
+
-- }
|
|
13
|
+
|
|
14
|
+
CurrentProfileVersion = '0.0.1'
|
|
15
|
+
|
|
16
|
+
if not Profile then Profile = {} end
|
|
17
|
+
|
|
18
|
+
if not Profile.Version then Profile.Version = CurrentProfileVersion end
|
|
19
|
+
|
|
20
|
+
if not FirstRunCompleted then FirstRunCompleted = false end
|
|
21
|
+
-- Assets: { Id, Type, Quantity } []
|
|
22
|
+
|
|
23
|
+
if not Assets then Assets = {} end
|
|
24
|
+
|
|
25
|
+
-- Collections: { Id, Name, Items, SortOrder } []
|
|
26
|
+
|
|
27
|
+
if not Collections then Collections = {} end
|
|
28
|
+
|
|
29
|
+
if not Roles then Roles = {} end
|
|
30
|
+
|
|
31
|
+
REGISTRY = '<REGISTRY>'
|
|
32
|
+
|
|
33
|
+
local function check_valid_address(address)
|
|
34
|
+
if not address or type(address) ~= 'string' then
|
|
35
|
+
return false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
return string.match(address, "^[%w%-_]+$") ~= nil and #address == 43
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
local function check_required_data(data, required_fields)
|
|
42
|
+
for _, field in ipairs(required_fields) do
|
|
43
|
+
if data[field] ~= nil then
|
|
44
|
+
return true
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
return false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
local function decode_message_data(data)
|
|
51
|
+
local status, decoded_data = pcall(json.decode, data)
|
|
52
|
+
|
|
53
|
+
if not status or type(decoded_data) ~= 'table' then
|
|
54
|
+
return false, nil
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
return true, decoded_data
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
local function authorizeRoles(msg)
|
|
61
|
+
-- If Roles is blank, the initial call should be from the owner
|
|
62
|
+
if msg.From ~= Owner and msg.From ~= ao.id and #Roles == 0 then
|
|
63
|
+
return false, {
|
|
64
|
+
Target = msg.From,
|
|
65
|
+
Action = 'Authorization-Error',
|
|
66
|
+
Tags = {
|
|
67
|
+
Status = 'Error',
|
|
68
|
+
ErrorMessage = 'Initial Roles not set, owner is not authorized for this handler'
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
local existingRole = false
|
|
74
|
+
for _, role in pairs(Roles) do
|
|
75
|
+
if role.AddressOrProfile == msg.From then
|
|
76
|
+
existingRole = true
|
|
77
|
+
break
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
if not existingRole and msg.From == Owner then
|
|
82
|
+
-- If Roles table is empty or owner doesn't exist, authorize the owner
|
|
83
|
+
table.insert(Roles, { Role = 'Owner', AddressOrProfile = msg.From })
|
|
84
|
+
existingRole = true
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
if not existingRole then
|
|
88
|
+
return false, {
|
|
89
|
+
Target = msg.From,
|
|
90
|
+
Action = 'Authorization-Error',
|
|
91
|
+
Tags = {
|
|
92
|
+
Status = 'Error',
|
|
93
|
+
ErrorMessage = 'Unauthorized to access this handler'
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
return true
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
local function sort_collections()
|
|
102
|
+
table.sort(Collections, function(a, b)
|
|
103
|
+
return a.SortOrder < b.SortOrder
|
|
104
|
+
end)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
Handlers.add('Info', Handlers.utils.hasMatchingTag('Action', 'Info'),
|
|
108
|
+
function(msg)
|
|
109
|
+
ao.send({
|
|
110
|
+
Target = msg.From,
|
|
111
|
+
Action = 'Read-Success',
|
|
112
|
+
Data = json.encode({
|
|
113
|
+
Profile = Profile,
|
|
114
|
+
Assets = Assets,
|
|
115
|
+
Collections = Collections,
|
|
116
|
+
Owner = Owner
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
end)
|
|
120
|
+
|
|
121
|
+
-- Data - { UserName?, DisplayName?, Description?, CoverImage, ProfileImage }
|
|
122
|
+
--[[
|
|
123
|
+
This function handles the 'Update-Profile' action. It first checks if the sender of the message is authorized to perform this action.
|
|
124
|
+
If the sender is authorized, it then decodes the data from the message. If the data is valid and contains at least one of the required fields,
|
|
125
|
+
it updates the profile with the new data and sends a success message to the sender and the registry. If the data is not valid or does not contain
|
|
126
|
+
any of the required fields, it sends an error message to the sender.
|
|
127
|
+
|
|
128
|
+
Parameters:
|
|
129
|
+
msg:
|
|
130
|
+
{
|
|
131
|
+
data: { },
|
|
132
|
+
tags: { }
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
None. This function sends messages to the sender or the registry but does not return anything.
|
|
136
|
+
--]]
|
|
137
|
+
Handlers.add('Update-Profile', Handlers.utils.hasMatchingTag('Action', 'Update-Profile'),
|
|
138
|
+
function(msg)
|
|
139
|
+
local authorizeResult, message = authorizeRoles(msg)
|
|
140
|
+
if not authorizeResult then
|
|
141
|
+
ao.send(message)
|
|
142
|
+
return
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
local decode_check, data = decode_message_data(msg.Data)
|
|
146
|
+
|
|
147
|
+
if decode_check and data then
|
|
148
|
+
if not check_required_data(data, { "UserName", "DisplayName", "Description", "CoverImage", "ProfileImage" }) then
|
|
149
|
+
ao.send({
|
|
150
|
+
Target = msg.From,
|
|
151
|
+
Action = 'Input-Error',
|
|
152
|
+
Tags = {
|
|
153
|
+
Status = 'Error',
|
|
154
|
+
EMessage =
|
|
155
|
+
'Invalid arguments, required at least one of { UserName, DisplayName, Description, CoverImage, ProfileImage }'
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
return
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
Profile.UserName = msg.Tags.UserName or data.UserName or Profile.UserName or ''
|
|
162
|
+
Profile.DisplayName = msg.Tags.DisplayName or data.DisplayName or Profile.DisplayName or ''
|
|
163
|
+
Profile.Description = msg.Tags.Description or data.Description or Profile.Description or ''
|
|
164
|
+
Profile.CoverImage = msg.Tags.CoverImage or data.CoverImage or Profile.CoverImage or ''
|
|
165
|
+
Profile.ProfileImage = msg.Tags.ProfileImage or data.ProfileImage or Profile.ProfileImage or ''
|
|
166
|
+
Profile.DateCreated = Profile.DateCreated or msg.Timestamp
|
|
167
|
+
Profile.DateUpdated = msg.Timestamp
|
|
168
|
+
|
|
169
|
+
-- if FirstRunCompleted then
|
|
170
|
+
-- ao.assign({Processes = { REGISTRY }, Message = msg.Id})
|
|
171
|
+
-- else
|
|
172
|
+
ao.send({
|
|
173
|
+
Target = REGISTRY,
|
|
174
|
+
Action = 'Create-Profile',
|
|
175
|
+
Data = json.encode({
|
|
176
|
+
AuthorizedAddress = msg.From,
|
|
177
|
+
UserName = Profile.UserName or nil,
|
|
178
|
+
DisplayName = Profile.DisplayName or nil,
|
|
179
|
+
Description = Profile.Description or nil,
|
|
180
|
+
CoverImage = Profile.CoverImage or nil,
|
|
181
|
+
ProfileImage = Profile.ProfileImage or nil,
|
|
182
|
+
DateCreated = Profile.DateCreated,
|
|
183
|
+
DateUpdated = Profile.DateUpdated
|
|
184
|
+
}),
|
|
185
|
+
Tags = msg.Tags
|
|
186
|
+
})
|
|
187
|
+
FirstRunCompleted = true
|
|
188
|
+
-- end
|
|
189
|
+
|
|
190
|
+
ao.send({
|
|
191
|
+
Target = msg.From,
|
|
192
|
+
Action = 'Profile-Success',
|
|
193
|
+
Tags = {
|
|
194
|
+
Status = 'Success',
|
|
195
|
+
Message = 'Profile updated'
|
|
196
|
+
}
|
|
197
|
+
})
|
|
198
|
+
else
|
|
199
|
+
ao.send({
|
|
200
|
+
Target = msg.From,
|
|
201
|
+
Action = 'Input-Error',
|
|
202
|
+
Tags = {
|
|
203
|
+
Status = 'Error',
|
|
204
|
+
EMessage = string.format(
|
|
205
|
+
'Failed to parse data, received: %s. %s.', msg.Data,
|
|
206
|
+
'Data must be an object - { UserName, DisplayName, Description, CoverImage, ProfileImage }')
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
end
|
|
210
|
+
end)
|
|
211
|
+
|
|
212
|
+
-- Data - { Target, Recipient, Quantity }
|
|
213
|
+
Handlers.add('Transfer', Handlers.utils.hasMatchingTag('Action', 'Transfer'),
|
|
214
|
+
function(msg)
|
|
215
|
+
local authorizeResult, message = authorizeRoles(msg)
|
|
216
|
+
if not authorizeResult then
|
|
217
|
+
ao.send(message)
|
|
218
|
+
return
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
ao.send({
|
|
222
|
+
Target = msg.Tags.Target,
|
|
223
|
+
Action = 'Transfer',
|
|
224
|
+
Tags = msg.Tags,
|
|
225
|
+
Data = msg.Data
|
|
226
|
+
})
|
|
227
|
+
end)
|
|
228
|
+
|
|
229
|
+
-- Data - { Recipient, Quantity }
|
|
230
|
+
Handlers.add('Debit-Notice', Handlers.utils.hasMatchingTag('Action', 'Debit-Notice'),
|
|
231
|
+
function(msg)
|
|
232
|
+
if not msg.Tags.Recipient or not msg.Tags.Quantity then
|
|
233
|
+
ao.send({
|
|
234
|
+
Target = msg.From,
|
|
235
|
+
Action = 'Input-Error',
|
|
236
|
+
Tags = {
|
|
237
|
+
Status = 'Error',
|
|
238
|
+
Message =
|
|
239
|
+
'Invalid arguments, required { Recipient, Quantity }'
|
|
240
|
+
}
|
|
241
|
+
})
|
|
242
|
+
return
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
if not check_valid_address(msg.Tags.Recipient) then
|
|
246
|
+
ao.send({ Target = msg.From, Action = 'Validation-Error', Tags = { Status = 'Error', Message = 'Recipient must be a valid address' } })
|
|
247
|
+
return
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
local asset_index = -1
|
|
251
|
+
for i, asset in ipairs(Assets) do
|
|
252
|
+
if asset.Id == msg.From then
|
|
253
|
+
asset_index = i
|
|
254
|
+
break
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
if asset_index > -1 then
|
|
259
|
+
local updated_quantity = tonumber(Assets[asset_index].Quantity) - tonumber(msg.Tags.Quantity)
|
|
260
|
+
|
|
261
|
+
if updated_quantity <= 0 then
|
|
262
|
+
table.remove(Assets, asset_index)
|
|
263
|
+
else
|
|
264
|
+
Assets[asset_index].Quantity = tostring(updated_quantity)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
ao.send({
|
|
268
|
+
Target = Owner,
|
|
269
|
+
Action = 'Transfer-Success',
|
|
270
|
+
Tags = {
|
|
271
|
+
Status = 'Success',
|
|
272
|
+
Message = 'Balance transferred'
|
|
273
|
+
}
|
|
274
|
+
})
|
|
275
|
+
else
|
|
276
|
+
ao.send({
|
|
277
|
+
Target = msg.From,
|
|
278
|
+
Action = 'Transfer-Failed',
|
|
279
|
+
Tags = {
|
|
280
|
+
Status = 'Error',
|
|
281
|
+
Message = 'No asset found to debit'
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
end
|
|
285
|
+
end)
|
|
286
|
+
|
|
287
|
+
-- Data - { Sender, Quantity }
|
|
288
|
+
Handlers.add('Credit-Notice', Handlers.utils.hasMatchingTag('Action', 'Credit-Notice'),
|
|
289
|
+
function(msg)
|
|
290
|
+
if not msg.Tags.Sender or not msg.Tags.Quantity then
|
|
291
|
+
ao.send({
|
|
292
|
+
Target = msg.From,
|
|
293
|
+
Action = 'Input-Error',
|
|
294
|
+
Tags = {
|
|
295
|
+
Status = 'Error',
|
|
296
|
+
Message =
|
|
297
|
+
'Invalid arguments, required { Sender, Quantity }'
|
|
298
|
+
}
|
|
299
|
+
})
|
|
300
|
+
return
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
if not check_valid_address(msg.Tags.Sender) then
|
|
304
|
+
ao.send({ Target = msg.From, Action = 'Validation-Error', Tags = { Status = 'Error', Message = 'Sender must be a valid address' } })
|
|
305
|
+
return
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
local asset_index = -1
|
|
309
|
+
for i, asset in ipairs(Assets) do
|
|
310
|
+
if asset.Id == msg.From then
|
|
311
|
+
asset_index = i
|
|
312
|
+
break
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
if asset_index > -1 then
|
|
317
|
+
local updated_quantity = tonumber(Assets[asset_index].Quantity) + tonumber(msg.Tags.Quantity)
|
|
318
|
+
|
|
319
|
+
Assets[asset_index].Quantity = tostring(updated_quantity)
|
|
320
|
+
else
|
|
321
|
+
table.insert(Assets, { Id = msg.From, Quantity = msg.Tags.Quantity })
|
|
322
|
+
|
|
323
|
+
ao.send({
|
|
324
|
+
Target = Owner,
|
|
325
|
+
Action = 'Transfer-Success',
|
|
326
|
+
Tags = {
|
|
327
|
+
Status = 'Success',
|
|
328
|
+
Message = 'Balance transferred'
|
|
329
|
+
}
|
|
330
|
+
})
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
if msg.Tags.Sender ~= Owner then
|
|
334
|
+
local walletTransferTokens = { 'xU9zFkq3X2ZQ6olwNVvr1vUWIjc3kXTWr7xKQD6dh10' }
|
|
335
|
+
local runWalletTransfer = false
|
|
336
|
+
for _, value in pairs(walletTransferTokens) do
|
|
337
|
+
if value == msg.From then
|
|
338
|
+
runWalletTransfer = true
|
|
339
|
+
break
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
if runWalletTransfer then
|
|
344
|
+
ao.send({
|
|
345
|
+
Target = msg.From,
|
|
346
|
+
Action = 'Transfer',
|
|
347
|
+
Tags = {
|
|
348
|
+
Recipient = Owner,
|
|
349
|
+
Quantity = msg.Tags.Quantity
|
|
350
|
+
}
|
|
351
|
+
})
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
end)
|
|
355
|
+
|
|
356
|
+
-- Data - { Id, Quantity }
|
|
357
|
+
Handlers.add('Add-Uploaded-Asset', Handlers.utils.hasMatchingTag('Action', 'Add-Uploaded-Asset'),
|
|
358
|
+
function(msg)
|
|
359
|
+
-- local authorizeResult, message = authorizeRoles(msg)
|
|
360
|
+
-- if not authorizeResult then
|
|
361
|
+
-- ao.send(message)
|
|
362
|
+
-- return
|
|
363
|
+
-- end
|
|
364
|
+
|
|
365
|
+
local decode_check, data = decode_message_data(msg.Data)
|
|
366
|
+
|
|
367
|
+
if decode_check and data then
|
|
368
|
+
if not data.Id or not data.Quantity then
|
|
369
|
+
ao.send({
|
|
370
|
+
Target = msg.From,
|
|
371
|
+
Action = 'Input-Error',
|
|
372
|
+
Tags = {
|
|
373
|
+
Status = 'Error',
|
|
374
|
+
Message =
|
|
375
|
+
'Invalid arguments, required { Id, Quantity }'
|
|
376
|
+
}
|
|
377
|
+
})
|
|
378
|
+
return
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
if not check_valid_address(data.Id) then
|
|
382
|
+
ao.send({ Target = msg.From, Action = 'Validation-Error', Tags = { Status = 'Error', Message = 'Asset Id must be a valid address' } })
|
|
383
|
+
return
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
local exists = false
|
|
387
|
+
for _, asset in ipairs(Assets) do
|
|
388
|
+
if asset.Id == data.Id then
|
|
389
|
+
exists = true
|
|
390
|
+
break
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
if not exists then
|
|
395
|
+
table.insert(Assets, { Id = data.Id, Type = 'Upload', Quantity = data.Quantity })
|
|
396
|
+
ao.send({
|
|
397
|
+
Target = msg.From,
|
|
398
|
+
Action = 'Add-Uploaded-Asset-Success',
|
|
399
|
+
Tags = {
|
|
400
|
+
Status = 'Success',
|
|
401
|
+
Message = 'Asset added to profile'
|
|
402
|
+
}
|
|
403
|
+
})
|
|
404
|
+
else
|
|
405
|
+
ao.send({
|
|
406
|
+
Target = msg.From,
|
|
407
|
+
Action = 'Validation-Error',
|
|
408
|
+
Tags = {
|
|
409
|
+
Status = 'Error',
|
|
410
|
+
Message = string.format(
|
|
411
|
+
'Asset with Id %s already exists', data.Id)
|
|
412
|
+
}
|
|
413
|
+
})
|
|
414
|
+
end
|
|
415
|
+
else
|
|
416
|
+
ao.send({
|
|
417
|
+
Target = msg.From,
|
|
418
|
+
Action = 'Input-Error',
|
|
419
|
+
Tags = {
|
|
420
|
+
Status = 'Error',
|
|
421
|
+
Message = string.format(
|
|
422
|
+
'Failed to parse data, received: %s. %s.', msg.Data,
|
|
423
|
+
'Data must be an object - { Id, Quantity }')
|
|
424
|
+
}
|
|
425
|
+
})
|
|
426
|
+
end
|
|
427
|
+
end)
|
|
428
|
+
|
|
429
|
+
-- Data - { Id, Name, Items }
|
|
430
|
+
Handlers.add('Add-Collection', Handlers.utils.hasMatchingTag('Action', 'Add-Collection'),
|
|
431
|
+
function(msg)
|
|
432
|
+
-- local authorizeResult, message = authorizeRoles(msg)
|
|
433
|
+
-- if not authorizeResult then
|
|
434
|
+
-- ao.send(message)
|
|
435
|
+
-- return
|
|
436
|
+
-- end
|
|
437
|
+
|
|
438
|
+
local decode_check, data = decode_message_data(msg.Data)
|
|
439
|
+
|
|
440
|
+
if decode_check and data then
|
|
441
|
+
if not data.Id or not data.Name then
|
|
442
|
+
ao.send({
|
|
443
|
+
Target = msg.From,
|
|
444
|
+
Action = 'Input-Error',
|
|
445
|
+
Tags = {
|
|
446
|
+
Status = 'Error',
|
|
447
|
+
Message =
|
|
448
|
+
'Invalid arguments, required { Id, Name }'
|
|
449
|
+
}
|
|
450
|
+
})
|
|
451
|
+
return
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
if not check_valid_address(data.Id) then
|
|
455
|
+
ao.send({ Target = msg.From, Action = 'Validation-Error', Tags = { Status = 'Error', Message = 'Collection Id must be a valid address' } })
|
|
456
|
+
return
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
local exists = false
|
|
460
|
+
for _, collection in ipairs(Collections) do
|
|
461
|
+
if collection.Id == data.Id then
|
|
462
|
+
exists = true
|
|
463
|
+
break
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
-- Ensure the highest SortOrder for new items
|
|
468
|
+
local highestSortOrder = 0
|
|
469
|
+
for _, collection in ipairs(Collections) do
|
|
470
|
+
if collection.SortOrder > highestSortOrder then
|
|
471
|
+
highestSortOrder = collection.SortOrder
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
if not exists then
|
|
476
|
+
table.insert(Collections, { Id = data.Id, Name = data.Name, SortOrder = highestSortOrder + 1 })
|
|
477
|
+
sort_collections()
|
|
478
|
+
ao.send({
|
|
479
|
+
Target = msg.From,
|
|
480
|
+
Action = 'Add-Collection-Success',
|
|
481
|
+
Tags = {
|
|
482
|
+
Status = 'Success',
|
|
483
|
+
Message = 'Collection added'
|
|
484
|
+
}
|
|
485
|
+
})
|
|
486
|
+
else
|
|
487
|
+
ao.send({
|
|
488
|
+
Target = msg.From,
|
|
489
|
+
Action = 'Validation-Error',
|
|
490
|
+
Tags = {
|
|
491
|
+
Status = 'Error',
|
|
492
|
+
Message = string.format(
|
|
493
|
+
'Collection with Id %s already exists', data.Id)
|
|
494
|
+
}
|
|
495
|
+
})
|
|
496
|
+
end
|
|
497
|
+
else
|
|
498
|
+
ao.send({
|
|
499
|
+
Target = msg.From,
|
|
500
|
+
Action = 'Input-Error',
|
|
501
|
+
Tags = {
|
|
502
|
+
Status = 'Error',
|
|
503
|
+
Message = string.format(
|
|
504
|
+
'Failed to parse data, received: %s. %s.', msg.Data,
|
|
505
|
+
'Data must be an object - { Id, Name, Items }')
|
|
506
|
+
}
|
|
507
|
+
})
|
|
508
|
+
end
|
|
509
|
+
end)
|
|
510
|
+
|
|
511
|
+
-- Data - { Ids: [Id1, Id2, ..., IdN] }
|
|
512
|
+
Handlers.add('Update-Collection-Sort', Handlers.utils.hasMatchingTag('Action', 'Update-Collection-Sort'),
|
|
513
|
+
function(msg)
|
|
514
|
+
local authorizeResult, message = authorizeRoles(msg)
|
|
515
|
+
if not authorizeResult then
|
|
516
|
+
ao.send(message)
|
|
517
|
+
return
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
local decode_check, data = decode_message_data(msg.Data)
|
|
521
|
+
|
|
522
|
+
if decode_check and data then
|
|
523
|
+
if not data.Ids then
|
|
524
|
+
ao.send({
|
|
525
|
+
Target = msg.From,
|
|
526
|
+
Action = 'Input-Error',
|
|
527
|
+
Tags = {
|
|
528
|
+
Status = 'Error',
|
|
529
|
+
Message = 'Invalid arguments, required { Ids }'
|
|
530
|
+
}
|
|
531
|
+
})
|
|
532
|
+
return
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
-- Validate all IDs exist in the Collections table
|
|
536
|
+
local valid_ids = {}
|
|
537
|
+
local id_set = {}
|
|
538
|
+
for _, id in ipairs(data.Ids) do
|
|
539
|
+
for _, collection in ipairs(Collections) do
|
|
540
|
+
if collection.Id == id then
|
|
541
|
+
table.insert(valid_ids, id)
|
|
542
|
+
id_set[id] = true
|
|
543
|
+
break
|
|
544
|
+
end
|
|
545
|
+
end
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
-- Update SortOrder for valid collections
|
|
549
|
+
for i, id in ipairs(valid_ids) do
|
|
550
|
+
for _, collection in ipairs(Collections) do
|
|
551
|
+
if collection.Id == id then
|
|
552
|
+
collection.SortOrder = i
|
|
553
|
+
end
|
|
554
|
+
end
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
-- Place any collections not in the valid_ids list at the end, preserving their relative order
|
|
558
|
+
local remaining_collections = {}
|
|
559
|
+
for _, collection in ipairs(Collections) do
|
|
560
|
+
if not id_set[collection.Id] then
|
|
561
|
+
table.insert(remaining_collections, collection)
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
-- Sort remaining collections by their current SortOrder
|
|
566
|
+
table.sort(remaining_collections, function(a, b)
|
|
567
|
+
return a.SortOrder < b.SortOrder
|
|
568
|
+
end)
|
|
569
|
+
|
|
570
|
+
-- Assign new SortOrder to remaining collections
|
|
571
|
+
local new_sort_order = #valid_ids + 1
|
|
572
|
+
for _, collection in ipairs(remaining_collections) do
|
|
573
|
+
collection.SortOrder = new_sort_order
|
|
574
|
+
new_sort_order = new_sort_order + 1
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
-- Sort collections by SortOrder
|
|
578
|
+
sort_collections()
|
|
579
|
+
|
|
580
|
+
ao.send({
|
|
581
|
+
Target = msg.From,
|
|
582
|
+
Action = 'Update-Collection-Sort-Success',
|
|
583
|
+
Tags = {
|
|
584
|
+
Status = 'Success',
|
|
585
|
+
Message = 'Collections sorted'
|
|
586
|
+
}
|
|
587
|
+
})
|
|
588
|
+
else
|
|
589
|
+
ao.send({
|
|
590
|
+
Target = msg.From,
|
|
591
|
+
Action = 'Input-Error',
|
|
592
|
+
Tags = {
|
|
593
|
+
Status = 'Error',
|
|
594
|
+
Message = string.format(
|
|
595
|
+
'Failed to parse data, received: %s. %s.', msg.Data,
|
|
596
|
+
'Data must be an object - { Ids }')
|
|
597
|
+
}
|
|
598
|
+
})
|
|
599
|
+
end
|
|
600
|
+
end)
|
|
601
|
+
|
|
602
|
+
Handlers.add('Action-Response', Handlers.utils.hasMatchingTag('Action', 'Action-Response'),
|
|
603
|
+
function(msg)
|
|
604
|
+
if msg.Tags['Status'] and msg.Tags['Message'] then
|
|
605
|
+
local response_tags = {
|
|
606
|
+
Status = msg.Tags['Status'],
|
|
607
|
+
Message = msg.Tags['Message']
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if msg.Tags['Handler'] then response_tags.Handler = msg.Tags['Handler'] end
|
|
611
|
+
|
|
612
|
+
ao.send({
|
|
613
|
+
Target = Owner,
|
|
614
|
+
Action = 'Action-Response',
|
|
615
|
+
Tags = response_tags
|
|
616
|
+
})
|
|
617
|
+
end
|
|
618
|
+
end)
|
|
619
|
+
|
|
620
|
+
Handlers.add('Run-Action', Handlers.utils.hasMatchingTag('Action', 'Run-Action'),
|
|
621
|
+
function(msg)
|
|
622
|
+
local authorizeResult, message = authorizeRoles(msg)
|
|
623
|
+
if not authorizeResult then
|
|
624
|
+
ao.send(message)
|
|
625
|
+
return
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
local decode_check, data = decode_message_data(msg.Data)
|
|
629
|
+
|
|
630
|
+
if decode_check and data then
|
|
631
|
+
if not data.Target or not data.Action or not data.Input then
|
|
632
|
+
ao.send({
|
|
633
|
+
Target = msg.From,
|
|
634
|
+
Action = 'Input-Error',
|
|
635
|
+
Tags = {
|
|
636
|
+
Status = 'Error',
|
|
637
|
+
Message =
|
|
638
|
+
'Invalid arguments, required { Target, Action, Input }'
|
|
639
|
+
}
|
|
640
|
+
})
|
|
641
|
+
return
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
if not check_valid_address(data.Target) then
|
|
645
|
+
ao.send({ Target = msg.From, Action = 'Validation-Error', Tags = { Status = 'Error', Message = 'Target must be a valid address' } })
|
|
646
|
+
return
|
|
647
|
+
end
|
|
648
|
+
|
|
649
|
+
ao.send({
|
|
650
|
+
Target = data.Target,
|
|
651
|
+
Action = data.Action,
|
|
652
|
+
Data = data.Input
|
|
653
|
+
})
|
|
654
|
+
else
|
|
655
|
+
ao.send({
|
|
656
|
+
Target = msg.From,
|
|
657
|
+
Action = 'Input-Error',
|
|
658
|
+
Tags = {
|
|
659
|
+
Status = 'Error',
|
|
660
|
+
Message = string.format(
|
|
661
|
+
'Failed to parse data, received: %s. %s.', msg.Data,
|
|
662
|
+
'Data must be an object - { Target, Action, Input }')
|
|
663
|
+
}
|
|
664
|
+
})
|
|
665
|
+
end
|
|
666
|
+
end)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
local ao = require("ao")
|
|
2
|
+
|
|
3
|
+
Handlers.add(
|
|
4
|
+
"allow",
|
|
5
|
+
Handlers.utils.hasMatchingTag("Action", "Allow"),
|
|
6
|
+
function (msg)
|
|
7
|
+
ao.addAssignable({ From = msg.From })
|
|
8
|
+
Handlers.utils.reply("allowed!")(msg)
|
|
9
|
+
end
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
Handlers.add(
|
|
13
|
+
"assign",
|
|
14
|
+
Handlers.utils.hasMatchingTag("Type", "Process"),
|
|
15
|
+
function (msg)
|
|
16
|
+
assert(msg.From == msg.Owner, 'only process owner can execute!')
|
|
17
|
+
assert(msg.Tags["Content-Type"] == "text/markdown" or msg.Tags["Content-Type"] == "text/plain", 'only markdown and text are allowed!')
|
|
18
|
+
ao.send({
|
|
19
|
+
Target = msg.Id,
|
|
20
|
+
Data = msg.Data,
|
|
21
|
+
Tags = { Action = "Assigned" }
|
|
22
|
+
})
|
|
23
|
+
end
|
|
24
|
+
)
|