fix heromodels

This commit is contained in:
Timur Gordon
2025-08-31 15:12:14 +02:00
parent 738bb16084
commit ab80ba8628
20 changed files with 175 additions and 182 deletions

View File

@@ -175,7 +175,7 @@ fn test_get_u64_default() {
assert params.get_u64_default('key3', 17)! == 17
}
fn test_get_u32() {
fn test_get_u32()! {
text := '
key1: val1
key2: 19

View File

@@ -87,7 +87,7 @@ fn (mut e Election) vote_for(candidate string) {
// Report node health status
fn (mut e Election) report_node_health(target_id string, status string) {
now := time.now().unix_time()
now := time.now().unix()
msg := '${target_id}:${status}:${now}'
sig_hex := e.keys.sign(msg)
@@ -111,7 +111,7 @@ fn (mut e Election) report_node_health(target_id string, status string) {
// Collect health reports and check for consensus on unavailable nodes
fn (mut e Election) check_node_availability() {
now := time.now().unix_time()
now := time.now().unix()
mut unavailable_reports := map[string]map[string]i64{} // target_id -> reporter_id -> timestamp
for mut c in e.clients {
@@ -179,13 +179,13 @@ fn (mut e Election) promote_buffer_node(failed_node_id string) {
for mut c in e.clients {
k := node_status_key(buffer_id)
c.hset(k, 'status', 'active') or {}
c.hset(k, 'promoted_at', time.now().unix_time().str()) or {}
c.hset(k, 'promoted_at', time.now().unix().str()) or {}
c.hset(k, 'replaced_node', failed_node_id) or {}
// Mark failed node as unavailable
failed_k := node_status_key(failed_node_id)
c.hset(failed_k, 'status', 'unavailable') or {}
c.hset(failed_k, 'failed_at', time.now().unix_time().str()) or {}
c.hset(failed_k, 'failed_at', time.now().unix().str()) or {}
}
println('[${e.self.id}] Promoted buffer node $buffer_id to replace failed node $failed_node_id')
@@ -264,7 +264,7 @@ fn (mut e Election) health_monitor_loop() {
heartbeat_key := 'heartbeat:${node_id}'
val := c.get(heartbeat_key) or { continue }
last_heartbeat := val.i64()
if (time.now().unix_time() - last_heartbeat) < 60 { // 60 seconds threshold
if (time.now().unix() - last_heartbeat) < 60 { // 60 seconds threshold
is_available = true
break
}
@@ -286,7 +286,7 @@ fn (mut e Election) health_monitor_loop() {
fn (mut e Election) heartbeat_loop() {
for {
// Update own heartbeat
now := time.now().unix_time()
now := time.now().unix()
for mut c in e.clients {
heartbeat_key := 'heartbeat:${e.self.id}'
c.set(heartbeat_key, now.str()) or {}

View File

@@ -101,7 +101,7 @@ fn (mut e Election) vote_for(candidate string) {
// Report node health status
fn (mut e Election) report_node_health(target_id string, status string) {
now := time.now().unix_time()
now := time.now().unix()
msg := '${target_id}:${status}:${now}'
sig_hex := e.keys.sign(msg)
@@ -125,7 +125,7 @@ fn (mut e Election) report_node_health(target_id string, status string) {
// Collect health reports and check for consensus on unavailable nodes
fn (mut e Election) check_node_availability() {
now := time.now().unix_time()
now := time.now().unix()
mut unavailable_reports := map[string]map[string]i64{} // target_id -> reporter_id -> timestamp
for mut c in e.clients {
@@ -193,13 +193,13 @@ fn (mut e Election) promote_buffer_node(failed_node_id string) {
for mut c in e.clients {
k := node_status_key(buffer_id)
c.hset(k, 'status', 'active') or {}
c.hset(k, 'promoted_at', time.now().unix_time().str()) or {}
c.hset(k, 'promoted_at', time.now().unix().str()) or {}
c.hset(k, 'replaced_node', failed_node_id) or {}
// Mark failed node as unavailable
failed_k := node_status_key(failed_node_id)
c.hset(failed_k, 'status', 'unavailable') or {}
c.hset(failed_k, 'failed_at', time.now().unix_time().str()) or {}
c.hset(failed_k, 'failed_at', time.now().unix().str()) or {}
}
println('[${e.self.id}] Promoted buffer node $buffer_id to replace failed node $failed_node_id')
@@ -278,7 +278,7 @@ fn (mut e Election) health_monitor_loop() {
heartbeat_key := 'heartbeat:${node_id}'
val := c.get(heartbeat_key) or { continue }
last_heartbeat := val.i64()
if (time.now().unix_time() - last_heartbeat) < 60 { // 60 seconds threshold
if (time.now().unix() - last_heartbeat) < 60 { // 60 seconds threshold
is_available = true
break
}
@@ -300,7 +300,7 @@ fn (mut e Election) health_monitor_loop() {
fn (mut e Election) heartbeat_loop() {
for {
// Update own heartbeat
now := time.now().unix_time()
now := time.now().unix()
for mut c in e.clients {
heartbeat_key := 'heartbeat:${e.self.id}'
c.set(heartbeat_key, now.str()) or {}

View File

@@ -1,12 +1,9 @@
module heromodels
import crypto.md5
import json
import freeflowuniverse.herolib.core.redisclient
import freeflowuniverse.herolib.data.encoder
import freeflowuniverse.herolib.data.ourtime
// Group represents a collection of users with roles and permissions
@[heap]
@@ -22,10 +19,8 @@ pub mut:
comments []u32
}
@[heap]
pub struct SecurityPolicy {å
pub struct SecurityPolicy {
pub mut:
id u32
read []u32 //links to users & groups
@@ -58,44 +53,63 @@ pub mut:
comments []CommentArg
}
//make it easy to get a base object
pub fn new_base(args BaseArgs) !Base {
mut redis := redisclient.core_get()!
commentids:=comment_multiset(args.comments)!
tags:=tags2id(args.tags)!
return Base {
id: args.id or { 0 }
name: args.name
description: args.description
created_at: ourtime.now().unix()
updated_at: ourtime.now().unix()
securitypolicy: args.securitypolicy or { 0 }
tags: tags
comments: commentids
}
}
pub fn tags2id(tags []string) !u32 {
mut myid:=0
if tags.len>0{
mytags:=tags.map(it.to_lower_ascii().trim_space()).sort().join(",")
mymd5:=crypto.hexhash(mytags)
tags:=redis.hget("db:tags", mymd5)!
if tags == ""{
myid = u32(redis.incr("db:tags:id")!)
redis.hset("db:tags", mymd5, myid)!
redis.hset("db:tags", myid, mytags)!
mut redis := redisclient.core_get()!
return if tags.len>0{
mut tags_fixed := tags.map(it.to_lower_ascii().trim_space()).filter(it != "")
tags_fixed.sort_ignore_case()
hash :=md5.hexhash(tags_fixed.join(","))
tags_found := redis.hget("db:tags", hash)!
return if tags_found == ""{
id := u32(redis.incr("db:tags:id")!)
redis.hset("db:tags", hash, id.str())!
redis.hset("db:tags", id.str(), tags_fixed.join(","))!
id
}else{
myid = tags.int()
tags_found.u32()
}
} else {
0
}
return myid
}
pub fn comments2id(comments []CommentArg) !u32 {
mut myid:=0
if comments.len>0{
mycomments:=comments.map(it.to_lower_ascii().trim_space()).sort().join(",")
mymd5:=crypto.hexhash(mycomments)
comments:=redis.hget("db:comments", mymd5)!
if comments == ""{
myid = u32(redis.incr("db:comments:id")!)
redis.hset("db:comments", mymd5, myid)!
redis.hset("db:comments", myid, mycomments)!
}else{
myid = comments.int()
}
}
return myid
pub fn comments2ids(args []CommentArg) ![]u32 {
return args.map(comment2id(it.comment)!)
}
pub fn comment2id(comment string) !u32 {
comment_fixed := comment.to_lower_ascii().trim_space()
mut redis := redisclient.core_get()!
return if comment_fixed.len > 0{
hash := md5.hexhash(comment_fixed)
comment_found := redis.hget("db:comments", hash)!
if comment_found == ""{
id := u32(redis.incr("db:comments:id")!)
redis.hset("db:comments", hash, id.str())!
redis.hset("db:comments", id.str(), comment_fixed)!
id
}else{
comment_found.u32()
}
} else { 0 }
}
// Convert CommentArg array to u32 array
mut comment_ids := []u32{}
for comment in args.comments {
comment_ids << comment_set(comment)!
}

View File

@@ -1,7 +1,5 @@
module heromodels
import crypto.blake3
import json
import freeflowuniverse.herolib.data.ourtime
import time
@@ -28,19 +26,19 @@ pub mut:
is_public bool
}
pub fn calendar_new(args CalendarArgs) Calendar {
commentids:=[]u32{}
pub fn calendar_new(args CalendarArgs) !Calendar {
mut commentids:=[]u32{}
for comment in args.comments{
commentids << comment_set(comment)!
}
mut obj := Calendar{
id: args.id
id: args.id or {0} // Will be set by DB?
name: args.name
description: args.description
created_at: ourtime.now().unix()
updated_at: ourtime.now().unix()
securitypolicy: args.securitypolicy
tags: args.tags
securitypolicy: args.securitypolicy or {0}
tags: tags2id(args.tags)!
comments: commentids
group_id: args.group_id
events: args.events

View File

@@ -108,11 +108,10 @@ pub fn calendar_event_new(args CalendarEventArgs) !CalendarEvent {
updated_at: ourtime.now().unix()
securitypolicy: args.securitypolicy or { 0 }
tags: tags_id
comments: comment_ids
comments: comments2ids(args.comments)!
// CalendarEvent specific fields
title: args.title
description: args.description
start_time: ourtime.new(args.start_time)!.unix()
end_time: ourtime.new(args.end_time)!.unix()
location: args.location
@@ -177,49 +176,49 @@ pub fn (mut e CalendarEvent) dump() ![]u8 {
return enc.data
}
pub fn calendar_event_load(data []u8) !CalendarEvent {
pub fn (ce CalendarEvent) load(data []u8) !CalendarEvent {
// Create a new decoder
mut dec := encoder.decoder_new(data)
// Read version byte
version := dec.get_u8()
version := dec.get_u8()!
if version != 1 {
return error('wrong version in calendar event load')
}
// Decode Base fields
id := dec.get_u32()
name := dec.get_string()
description := dec.get_string()
created_at := dec.get_i64()
updated_at := dec.get_i64()
securitypolicy := dec.get_u32()
tags := dec.get_u32()
comments := dec.get_list_u32()
id := dec.get_u32()!
name := dec.get_string()!
description := dec.get_string()!
created_at := dec.get_i64()!
updated_at := dec.get_i64()!
securitypolicy := dec.get_u32()!
tags := dec.get_u32()!
comments := dec.get_list_u32()!
// Decode CalendarEvent specific fields
title := dec.get_string()
description2 := dec.get_string() // Second description field
start_time := dec.get_i64()
end_time := dec.get_i64()
location := dec.get_string()
attendees := dec.get_list_u32()
fs_items := dec.get_list_u32()
calendar_id := dec.get_u32()
status := EventStatus(dec.get_u8())
is_all_day := dec.get_bool()
is_recurring := dec.get_bool()
title := dec.get_string()!
description2 := dec.get_string()! // Second description field
start_time := dec.get_i64()!
end_time := dec.get_i64()!
location := dec.get_string()!
attendees := dec.get_list_u32()!
fs_items := dec.get_list_u32()!
calendar_id := dec.get_u32()!
status := unsafe { EventStatus(dec.get_u8()!) }
is_all_day := dec.get_bool()!
is_recurring := dec.get_bool()!
// Decode recurrence array
recurrence_len := dec.get_u16()
recurrence_len := dec.get_u16()!
mut recurrence := []RecurrenceRule{}
for _ in 0..recurrence_len {
frequency := RecurrenceFreq(dec.get_u8())
interval := dec.get_int()
until := dec.get_i64()
count := dec.get_int()
by_weekday := dec.get_list_int()
by_monthday := dec.get_list_int()
frequency := unsafe{RecurrenceFreq(dec.get_u8()!)}
interval := dec.get_int()!
until := dec.get_i64()!
count := dec.get_int()!
by_weekday := dec.get_list_int()!
by_monthday := dec.get_list_int()!
recurrence << RecurrenceRule{
frequency: frequency
@@ -231,9 +230,9 @@ pub fn calendar_event_load(data []u8) !CalendarEvent {
}
}
reminder_mins := dec.get_list_int()
color := dec.get_string()
timezone := dec.get_string()
reminder_mins := dec.get_list_int()!
color := dec.get_string()!
timezone := dec.get_string()!
return CalendarEvent{
// Base fields
@@ -248,7 +247,6 @@ pub fn calendar_event_load(data []u8) !CalendarEvent {
// CalendarEvent specific fields
title: title
description: description2
start_time: start_time
end_time: end_time
location: location

View File

@@ -1,5 +1,6 @@
module heromodels
import time
import crypto.blake3
import json
@@ -54,9 +55,9 @@ pub fn new_chat_group(name string, group_id string, chat_type ChatType) ChatGrou
name: name
group_id: group_id
chat_type: chat_type
created_at: time.now().unix_time()
updated_at: time.now().unix_time()
last_activity: time.now().unix_time()
created_at: time.now().unix()
updated_at: time.now().unix()
last_activity: time.now().unix()
}
chat_group.calculate_id()
return chat_group

View File

@@ -1,5 +1,6 @@
module heromodels
import time
import crypto.blake3
import json
@@ -95,8 +96,8 @@ pub fn new_chat_message(content string, chat_group_id string, sender_id string)
sender_id: sender_id
message_type: .text
status: .sent
created_at: time.now().unix_time()
updated_at: time.now().unix_time()
created_at: time.now().unix()
updated_at: time.now().unix()
}
message.calculate_id()
return message

View File

@@ -1,8 +1,5 @@
module heromodels
import freeflowuniverse.herolib.core.redisclient
import json
import freeflowuniverse.herolib.core.redisclient
import freeflowuniverse.herolib.data.encoder
import freeflowuniverse.herolib.data.ourtime
@@ -34,16 +31,16 @@ pub fn (self Comment) dump() ![]u8{
pub fn comment_load(data []u8) !Comment{
// Create a new decoder
mut e := encoder.decoder_new(data)
version := e.get_u8()
version := e.get_u8()!
if version != 1 {
panic("wrong version in comment load")
}
mut comment := Comment{}
comment.id = e.get_u32()
comment.comment = e.get_string()
comment.parent = e.get_u32()
comment.updated_at = e.get_i64()
comment.author = e.get_u32()
comment.id = e.get_u32()!
comment.comment = e.get_string()!
comment.parent = e.get_u32()!
comment.updated_at = e.get_i64()!
comment.author = e.get_u32()!
return comment
}
@@ -79,22 +76,22 @@ pub fn comment_set(args CommentArg) !u32{
mut redis := redisclient.core_get()!
mut o:=comment_new(args)!
myid := redis.incr("db:comments:id")!
o.id = myid
o.id = u32(myid)
data := o.dump()!
redis.hset("db:comments:data", myid, data)!
return myid
redis.hset("db:comments:data", myid.str(), data.bytestr())!
return o.id
}
pub fn comment_exist(id u32) !bool{
mut redis := redisclient.core_get()!
return redis.hexist("db:comments:data",id)!
return redis.hexists("db:comments:data", id.str())!
}
pub fn comment_get(id u32) !Comment{
mut redis := redisclient.core_get()!
mut data:= redis.hget("db:comments:data",id)!
mut data:= redis.hget("db:comments:data", id.str())!
if data.len>0{
return comment_load(data)!
return comment_load(data.bytes())!
}else{
return error("Can't find comment with id: ${id}")
}

View File

@@ -1,71 +1,44 @@
module heromodels
import crypto.md5
import json
import freeflowuniverse.herolib.core.redisclient
import freeflowuniverse.herolib.data.encoder
pub fn [T] set(obj T) !Base {
//todo: get the dump() from the obj , save the
pub fn set[T](obj T) ! {
mut redis := redisclient.core_get()!
data := obj.dump()
id := obj.id
data := obj.dump()!
redis.hset("db:${name}",id,data)!
}
pub fn [T] get(id u32) !T {
//todo: get the dump() from the obj , save the
pub fn get[T](id u32) !T {
mut redis := redisclient.core_get()!
data := redis.hget("db:${name}",id)!
obj:=$name_load(data) or {
return error("could not load ${name} from data")
}
return obj
t := T{}
return t.load(data)!
}
pub fn [T] exists(id u32) !T {
//todo: get the dump() from the obj , save the
pub fn exists[T](id u32) !bool {
name := T{}.type_name()
mut redis := redisclient.core_get()!
return redis.hexists("db:${name}",id)!
return obj
}
pub fn [T] delete(id u32) !T {
//todo: get the dump() from the obj , save the
pub fn delete[T](id u32) ! {
name := T{}.type_name()
mut redis := redisclient.core_get()!
return redis.hdel("db:${name}",id)!
return obj
redis.hdel("db:${name}", id.str())!
}
pub fn list[T]() ![]T {
mut redis := redisclient.core_get()!
ids := redis.hkeys("db:${name}")!
mut result := []T{}
for id in ids {
result << get[T](id.u32())!
}
return result
}
//make it easy to get a base object
pub fn [T] new_from_base(args BaseArgs) !T {
mut redis := redisclient.core_get()!
commentids:=comment_multiset(args.comments)!
tags:=tags2id(args.tags)!
return T{
id: args.id or { 0 }
name: args.name
description: args.description
created_at: ourtime.now().unix()
updated_at: ourtime.now().unix()
securitypolicy: args.securitypolicy or { 0 }
tags: tags
comments: commentids)
}
pub fn new_from_base[T](args BaseArgs) !Base {
return T { Base: new_base(args)! }
}

View File

@@ -1,5 +1,6 @@
module heromodels
import time
import crypto.blake3
import json
@@ -43,8 +44,8 @@ pub fn new_fs(name string, group_id string) Fs {
mut fs := Fs{
name: name
group_id: group_id
created_at: time.now().unix_time()
updated_at: time.now().unix_time()
created_at: time.now().unix()
updated_at: time.now().unix()
}
fs.calculate_id()
return fs

View File

@@ -1,5 +1,6 @@
module heromodels
import time
import crypto.blake3
// FsBlob represents binary data up to 1MB
@@ -27,7 +28,7 @@ pub fn new_fs_blob(data []u8) !FsBlob {
mut blob := FsBlob{
data: data
size_bytes: data.len
created_at: time.now().unix_time()
created_at: time.now().unix()
encoding: 'none'
}
blob.calculate_id()

View File

@@ -1,5 +1,6 @@
module heromodels
import time
import crypto.blake3
import json
@@ -44,8 +45,8 @@ pub fn new_fs_dir(name string, fs_id string, parent_id string, group_id string)
fs_id: fs_id
parent_id: parent_id
group_id: group_id
created_at: time.now().unix_time()
updated_at: time.now().unix_time()
created_at: time.now().unix()
updated_at: time.now().unix()
}
dir.calculate_id()
return dir

View File

@@ -1,5 +1,6 @@
module heromodels
import time
import crypto.blake3
import json
@@ -55,9 +56,9 @@ pub fn new_fs_file(name string, fs_id string, directories []string) FsFile {
name: name
fs_id: fs_id
directories: directories
created_at: time.now().unix_time()
updated_at: time.now().unix_time()
accessed_at: time.now().unix_time()
created_at: time.now().unix()
updated_at: time.now().unix()
accessed_at: time.now().unix()
}
file.calculate_id()
return file

View File

@@ -1,5 +1,6 @@
module heromodels
import time
import crypto.blake3
import json
@@ -52,8 +53,8 @@ pub fn new_fs_symlink(name string, fs_id string, parent_id string, target_id str
parent_id: parent_id
target_id: target_id
target_type: target_type
created_at: time.now().unix_time()
updated_at: time.now().unix_time()
created_at: time.now().unix()
updated_at: time.now().unix()
}
symlink.calculate_id()
return symlink

View File

@@ -1,5 +1,6 @@
module heromodels
import time
import crypto.blake3
import json
@@ -61,8 +62,8 @@ pub fn new_group(name string, description string) Group {
mut group := Group{
name: name
description: description
created_at: time.now().unix_time()
updated_at: time.now().unix_time()
created_at: time.now().unix()
updated_at: time.now().unix()
is_public: false
}
group.calculate_id()
@@ -73,8 +74,8 @@ pub fn (mut g Group) add_member(user_id string, role GroupRole) {
g.members << GroupMember{
user_id: user_id
role: role
joined_at: time.now().unix_time()
joined_at: time.now().unix()
}
g.updated_at = time.now().unix_time()
g.updated_at = time.now().unix()
g.calculate_id()
}

View File

@@ -1,5 +1,6 @@
module heromodels
import time
import crypto.blake3
import json
@@ -89,8 +90,8 @@ pub fn new_project(name string, description string, group_id string) Project {
description: description
group_id: group_id
status: .planning
created_at: time.now().unix_time()
updated_at: time.now().unix_time()
created_at: time.now().unix()
updated_at: time.now().unix()
swimlanes: [
Swimlane{id: 'todo', name: 'To Do', order: 1, color: '#f1c40f'},
Swimlane{id: 'in_progress', name: 'In Progress', order: 2, color: '#3498db'},

View File

@@ -1,5 +1,6 @@
module heromodels
import time
import crypto.blake3
import json
@@ -107,8 +108,8 @@ pub fn new_project_issue(title string, project_id string, reporter string, issue
priority: .medium
status: .open
swimlane_id: 'todo'
created_at: time.now().unix_time()
updated_at: time.now().unix_time()
created_at: time.now().unix()
updated_at: time.now().unix()
}
issue.calculate_id()
return issue

View File

@@ -1,5 +1,6 @@
module heromodels
import time
import crypto.blake3
import json
@@ -58,8 +59,8 @@ pub fn new_user(name string, email string) User {
mut user := User{
name: name
email: email
created_at: time.now().unix_time()
updated_at: time.now().unix_time()
created_at: time.now().unix()
updated_at: time.now().unix()
status: .active
}
user.calculate_id()

View File

@@ -1,5 +1,7 @@
module heromodels
import time
// VersionHistory tracks the evolution of objects by their blake192 IDs
@[heap]
pub struct VersionHistory {
@@ -28,7 +30,7 @@ pub fn new_version_history(current_id string, previous_id string, object_type st
object_type: object_type
change_type: change_type
changed_by: changed_by
changed_at: time.now().unix_time()
changed_at: time.now().unix()
}
}