Merge branch 'development_kristof10' into development

# Conflicts:
#	.github/workflows/build_and_test.yml
#	.github/workflows/hero_build_linux.yml
#	.github/workflows/hero_build_macos.yml
#	install_herolib.vsh
#	install_v.sh
#	workflows/hero_build_macos.yml
This commit is contained in:
2025-02-07 11:20:20 +03:00
347 changed files with 11655 additions and 2930 deletions

View File

@@ -3,7 +3,7 @@ module livekit
// App struct with `livekit.Client`, API keys, and other shared data
pub struct Client {
pub:
url string @[required]
api_key string @[required]
api_secret string @[required]
url string @[required]
api_key string @[required]
api_secret string @[required]
}

View File

@@ -1,6 +1,7 @@
module livekit
pub fn new(client Client) Client {
return Client{...client}
}
return Client{
...client
}
}

View File

@@ -5,47 +5,46 @@ import json
@[params]
pub struct ListRoomsParams {
names []string
names []string
}
pub struct ListRoomsResponse {
pub:
rooms []Room
rooms []Room
}
pub fn (c Client) list_rooms(params ListRoomsParams) !ListRoomsResponse {
// Prepare request body
request := params
request_json := json.encode(request)
// Prepare request body
request := params
request_json := json.encode(request)
// create token and give grant to list rooms
// create token and give grant to list rooms
mut token := c.new_access_token()!
token.grants.video.room_list = true
token.grants.video.room_list = true
// make POST request
url := '${c.url}/twirp/livekit.RoomService/ListRooms'
// Configure HTTP request
mut headers := http.new_header_from_map({
http.CommonHeader.authorization: 'Bearer ${token.to_jwt()!}',
http.CommonHeader.content_type: 'application/json'
})
// make POST request
url := '${c.url}/twirp/livekit.RoomService/ListRooms'
// Configure HTTP request
mut headers := http.new_header_from_map({
http.CommonHeader.authorization: 'Bearer ${token.to_jwt()!}'
http.CommonHeader.content_type: 'application/json'
})
response := http.fetch(http.FetchConfig{
url: url
method: .post
header: headers
data: request_json
})!
response := http.fetch(http.FetchConfig{
url: url
method: .post
header: headers
data: request_json
})!
if response.status_code != 200 {
return error('Failed to list rooms: $response.status_code')
}
if response.status_code != 200 {
return error('Failed to list rooms: ${response.status_code}')
}
// Parse response
rooms_response := json.decode(ListRoomsResponse, response.body) or {
return error('Failed to parse response: $err')
}
return rooms_response
// Parse response
rooms_response := json.decode(ListRoomsResponse, response.body) or {
return error('Failed to parse response: ${err}')
}
return rooms_response
}

View File

@@ -5,29 +5,29 @@ import json
pub struct Codec {
pub:
fmtp_line string
mime string
fmtp_line string
mime string
}
pub struct Version {
pub:
ticks u64
unix_micro string
ticks u64
unix_micro string
}
pub struct Room {
pub:
active_recording bool
creation_time string
departure_timeout int
empty_timeout int
enabled_codecs []Codec
max_participants int
metadata string
name string
num_participants int
num_publishers int
sid string
turn_password string
version Version
}
active_recording bool
creation_time string
departure_timeout int
empty_timeout int
enabled_codecs []Codec
max_participants int
metadata string
name string
num_participants int
num_publishers int
sid string
turn_password string
version Version
}

View File

@@ -6,20 +6,20 @@ import freeflowuniverse.herolib.osal
const env_file = '${os.dir(@FILE)}/.env'
fn testsuite_begin() ! {
if os.exists(env_file) {
osal.load_env_file(env_file)!
}
if os.exists(env_file) {
osal.load_env_file(env_file)!
}
}
fn new_test_client() Client {
return new(
url: os.getenv('LIVEKIT_URL')
api_key: os.getenv('LIVEKIT_API_KEY')
api_secret: os.getenv('LIVEKIT_API_SECRET')
)
return new(
url: os.getenv('LIVEKIT_URL')
api_key: os.getenv('LIVEKIT_API_KEY')
api_secret: os.getenv('LIVEKIT_API_SECRET')
)
}
fn test_client_list_rooms() ! {
client := new_test_client()
rooms := client.list_rooms()!
client := new_test_client()
rooms := client.list_rooms()!
}

View File

@@ -10,25 +10,25 @@ import json
// Define AccessTokenOptions struct
@[params]
pub struct AccessTokenOptions {
pub mut:
ttl int = 21600// TTL in seconds
name string // Display name for the participant
identity string // Identity of the user
metadata string // Custom metadata to be passed to participants
pub mut:
ttl int = 21600 // TTL in seconds
name string // Display name for the participant
identity string // Identity of the user
metadata string // Custom metadata to be passed to participants
}
// Constructor for AccessToken
pub fn (client Client) new_access_token(options AccessTokenOptions) !AccessToken {
return AccessToken{
api_key: client.api_key
api_key: client.api_key
api_secret: client.api_secret
identity: options.identity
ttl: options.ttl
grants: ClaimGrants{
exp: time.now().unix()+ options.ttl
iss: client.api_key
sub: options.name
identity: options.identity
ttl: options.ttl
grants: ClaimGrants{
exp: time.now().unix() + options.ttl
iss: client.api_key
sub: options.name
name: options.name
}
}
}
}

View File

@@ -10,23 +10,23 @@ import json
// Struct representing grants
pub struct ClaimGrants {
pub mut:
video VideoGrant
iss string
exp i64
nbf int
sub string
name string
video VideoGrant
iss string
exp i64
nbf int
sub string
name string
}
// VideoGrant struct placeholder
pub struct VideoGrant {
pub mut:
room string
room_join bool @[json: 'roomJoin']
room_list bool @[json: 'roomList']
can_publish bool @[json: 'canPublish']
can_publish_data bool @[json: 'canPublishData']
can_subscribe bool @[json: 'canSubscribe']
room string
room_join bool @[json: 'roomJoin']
room_list bool @[json: 'roomList']
can_publish bool @[json: 'canPublish']
can_publish_data bool @[json: 'canPublishData']
can_subscribe bool @[json: 'canSubscribe']
}
// SIPGrant struct placeholder
@@ -34,12 +34,12 @@ struct SIPGrant {}
// AccessToken class
pub struct AccessToken {
mut:
api_key string
api_secret string
grants ClaimGrants
identity string
ttl int
mut:
api_key string
api_secret string
grants ClaimGrants
identity string
ttl int
}
// Method to add a video grant to the token
@@ -65,7 +65,8 @@ pub fn (token AccessToken) to_jwt() !string {
unsigned_token := '${header_encoded}.${payload_encoded}'
// Create the HMAC-SHA256 signature
signature := hmac.new(token.api_secret.bytes(), unsigned_token.bytes(), sha256.sum, sha256.block_size)
signature := hmac.new(token.api_secret.bytes(), unsigned_token.bytes(), sha256.sum,
sha256.block_size)
// Encode the signature in base64
signature_encoded := base64.url_encode(signature)
@@ -73,4 +74,4 @@ pub fn (token AccessToken) to_jwt() !string {
// Create the final JWT
jwt := '${unsigned_token}.${signature_encoded}'
return jwt
}
}

View File

@@ -1,107 +0,0 @@
module mailclient
import freeflowuniverse.herolib.core.base
// import freeflowuniverse.herolib.core.playbook
// __global (
// mailclient_global map[string]&MailClient
// mailclient_default string
// )
// /////////FACTORY
// @[params]
// pub struct ArgsGet {
// pub mut:
// name string = 'default'
// }
// fn args_get(args_ ArgsGet) ArgsGet {
// mut args := args_
// if args.name == '' {
// args.name = mailclient_default
// }
// if args.name == '' {
// args.name = 'default'
// }
// return args
// }
// pub fn get(args_ ArgsGet) !&MailClient {
// mut args := args_get(args_)
// if args.name !in mailclient_global {
// if !config_exists() {
// if default {
// config_save()!
// }
// }
// config_load()!
// }
// return mailclient_global[args.name] or { panic('bug') }
// }
// // switch instance to be used for mailclient
// pub fn switch(name string) {
// mailclient_default = name
// }
fn config_exists(args_ ArgsGet) bool {
mut args := args_get(args_)
mut context := base.context() or { panic('bug') }
return context.hero_config_exists('mailclient', args.name)
}
// fn config_load(args_ ArgsGet) ! {
// mut args := args_get(args_)
// mut context := base.context()!
// mut heroscript := context.hero_config_get('mailclient', args.name)!
// play(heroscript: heroscript)!
// }
// fn config_save(args_ ArgsGet) ! {
// mut args := args_get(args_)
// mut context := base.context()!
// context.hero_config_set('mailclient', args.name, heroscript_default())!
// }
// fn set(o MailClient) ! {
// mut o2 := obj_init(o)!
// mailclient_global['default'] = &o2
// }
// @[params]
// pub struct InstallPlayArgs {
// pub mut:
// name string = 'default'
// heroscript string // if filled in then plbook will be made out of it
// plbook ?playbook.PlayBook
// reset bool
// start bool
// stop bool
// restart bool
// delete bool
// configure bool // make sure there is at least one installed
// }
// pub fn play(args_ InstallPlayArgs) ! {
// mut args := args_
// println('debguzo1')
// mut plbook := args.plbook or {
// println('debguzo2')
// heroscript := if args.heroscript == '' {
// heroscript_default()
// } else {
// args.heroscript
// }
// playbook.new(text: heroscript)!
// }
// mut install_actions := plbook.find(filter: 'mailclient.configure')!
// println('debguzo3 ${install_actions}')
// if install_actions.len > 0 {
// for install_action in install_actions {
// mut p := install_action.params
// cfg_play(p)!
// }
// }
// }

View File

@@ -1,103 +1,127 @@
module mailclient
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.data.encoderhero
__global (
mailclient_global map[string]&MailClient
mailclient_default string
mailclient_global map[string]&MailClient
mailclient_default string
)
/////////FACTORY
@[params]
pub struct ArgsGet {
pub struct ArgsGet{
pub mut:
name string
name string
}
fn args_get(args_ ArgsGet) ArgsGet {
mut model := args_
if model.name == '' {
model.name = mailclient_default
}
if model.name == '' {
model.name = 'default'
}
return model
fn args_get (args_ ArgsGet) ArgsGet {
mut args:=args_
if args.name == ""{
args.name = mailclient_default
}
if args.name == ""{
args.name = "default"
}
return args
}
pub fn get(args_ ArgsGet) !&MailClient {
mut args := args_get(args_)
if args.name !in mailclient_global {
if args.name == 'default' {
if !config_exists(args) {
if default {
mut context := base.context() or { panic('bug') }
context.hero_config_set('mailclient', args.name, heroscript_default())!
}
}
load(args)!
}
}
return mailclient_global[args.name] or {
println(mailclient_global)
panic('could not get config for ${args.name} with name:${args.name}')
}
pub fn get(args_ ArgsGet) !&MailClient {
mut args := args_get(args_)
if !(args.name in mailclient_global) {
if ! config_exists(args){
config_save(args)!
}
config_load(args)!
}
return mailclient_global[args.name] or {
println(mailclient_global)
//bug if we get here because should be in globals
panic("could not get config for mailclient with name, is bug:${args.name}")
}
}
// set the model in mem and the config on the filesystem
pub fn set(o MailClient) ! {
mut o2 := obj_init(o)!
mailclient_global[o.name] = &o2
mailclient_default = o.name
pub fn config_exists(args_ ArgsGet) bool {
mut args := args_get(args_)
mut context:=base.context() or { panic("bug") }
return context.hero_config_exists("mailclient",args.name)
}
// check we find the config on the filesystem
pub fn exists(args_ ArgsGet) bool {
mut model := args_get(args_)
mut context := base.context() or { panic('bug') }
return context.hero_config_exists('mailclient', model.name)
pub fn config_load(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context:=base.context()!
mut heroscript := context.hero_config_get("mailclient",args.name)!
play(heroscript:heroscript)!
}
// load the config error if it doesn't exist
pub fn load(args_ ArgsGet) ! {
mut model := args_get(args_)
mut context := base.context()!
mut heroscript := context.hero_config_get('mailclient', model.name)!
play(heroscript: heroscript)!
pub fn config_save(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context:=base.context()!
context.hero_config_set("mailclient",args.name,heroscript_default(instance:args.name)!)!
}
pub fn config_delete(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context:=base.context()!
context.hero_config_delete("mailclient",args.name)!
}
fn set(o MailClient)! {
mut o2:=obj_init(o)!
mailclient_global[o.name] = &o2
mailclient_default = o.name
}
// // save the config to the filesystem in the context
// pub fn save(o MailClient) ! {
// mut context := base.context()!
// heroscript := encoderhero.encode[MailClient](o)!
// context.hero_config_set('mailclient', model.name, heroscript)!
// }
@[params]
pub struct PlayArgs {
pub mut:
heroscript string // if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
heroscript string //if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
}
pub fn play(args_ PlayArgs) ! {
mut model := args_
mut args:=args_
if args.heroscript == "" {
args.heroscript = heroscript_default()!
}
mut plbook := args.plbook or {
playbook.new(text: args.heroscript)!
}
mut install_actions := plbook.find(filter: 'mailclient.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
mut p := install_action.params
cfg_play(p)!
}
}
if model.heroscript == '' {
model.heroscript = heroscript_default()
}
mut plbook := model.plbook or { playbook.new(text: model.heroscript)! }
mut configure_actions := plbook.find(filter: 'mailclient.configure')!
if configure_actions.len > 0 {
for config_action in configure_actions {
mut p := config_action.params
cfg_play(p)!
}
}
}
//switch instance to be used for mailclient
pub fn switch(name string) {
mailclient_default = name
}
//helpers
@[params]
pub struct DefaultConfigArgs{
instance string = 'default'
}

View File

@@ -1,21 +1,21 @@
module mailclient
import freeflowuniverse.herolib.data.paramsparser
import os
pub const version = '1.0.0'
pub const version = '0.0.0'
const singleton = false
const default = true
pub fn heroscript_default() string {
pub fn heroscript_default(args DefaultConfigArgs) !string {
mail_from := os.getenv_opt('MAIL_FROM') or { 'info@example.com' }
mail_password := os.getenv_opt('MAIL_PASSWORD') or { 'secretpassword' }
mail_port := (os.getenv_opt('MAIL_PORT') or { '465' }).int()
mail_server := os.getenv_opt('MAIL_SERVER') or { 'smtp-relay.brevo.com' }
mail_username := os.getenv_opt('MAIL_USERNAME') or { 'kristof@incubaid.com' }
mail_username := os.getenv_opt('MAIL_USERNAME') or { 'mail@incubaid.com' }
heroscript := "
!!mailclient.configure name:'default'
!!mailclient.configure name:'${args.instance}'
mail_from: '${mail_from}'
mail_password: '${mail_password}'
mail_port: ${mail_port}
@@ -23,9 +23,10 @@ pub fn heroscript_default() string {
mail_username: '${mail_username}'
"
return heroscript
return heroscript
}
@[heap]
pub struct MailClient {
pub mut:
name string = 'default'
@@ -39,31 +40,22 @@ pub mut:
}
fn cfg_play(p paramsparser.Params) ! {
mut mycfg := MailClient{
mut mycfg := MailClient{
name: p.get_default('name', 'default')!
mail_from: p.get('mail_from')!
mail_password: p.get('mail_password')!
mail_port: p.get_int_default('mail_port', 465)!
mail_server: p.get('mail_server')!
mail_username: p.get('mail_username')!
}
set(mycfg)!
}
set(mycfg)!
}
fn obj_init(obj_ MailClient)!MailClient{
mut obj:=obj_
return obj
}
fn obj_init(obj_ MailClient) !MailClient {
// never call get here, only thing we can do here is work on object itself
mut obj := obj_
return obj
}
// user needs to us switch to make sure we get the right object
pub fn configure(config MailClient) !MailClient {
client := MailClient{
...config
}
set(client)!
return client
// THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED
// implement if steps need to be done for configuration
}

View File

@@ -1,16 +1,29 @@
# mailclient
To get started
```vlang
import freeflowuniverse.herolib.clients. mailclient
import freeflowuniverse.herolib.clients.mailclient
mut client:= mailclient.get()!
client.send(subject:'this is a test',to:'kds@something.com,kds2@else.com',body:'
//remove the previous one, otherwise the env variables are not read
mailclient.config_delete(name:"test")!
// env variables which need to be set are:
// - MAIL_FROM=...
// - MAIL_PASSWORD=...
// - MAIL_PORT=465
// - MAIL_SERVER=...
// - MAIL_USERNAME=...
mut client:= mailclient.get(name:"test")!
println(client)
client.send(subject:'this is a test',to:'kristof@incubaid.com',body:'
this is my email content
')!

View File

@@ -33,7 +33,7 @@ Note: Configuration is not needed if using a locally running Mycelium server wit
Save as `mycelium_example.vsh`:
```v
#!/usr/bin/env -S v -n -w -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.clients.mycelium

View File

@@ -1,56 +0,0 @@
module postgresql_client
import freeflowuniverse.herolib.core.base
import db.pg
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.ui.console
// pub struct PostgresClient {
// base.BaseConfig
// pub mut:
// config Config
// db pg.DB
// }
// @[params]
// pub struct ClientArgs {
// pub mut:
// instance string @[required]
// // playargs ?play.PlayArgs
// }
// pub fn get(clientargs ClientArgs) !PostgresClient {
// // mut plargs := clientargs.playargs or {
// // // play.PlayArgs
// // // {
// // // }
// // }
// // mut cfg := configurator(clientargs.instance, plargs)!
// // mut args := cfg.get()!
// args.instance = texttools.name_fix(args.instance)
// if args.instance == '' {
// args.instance = 'default'
// }
// // console.print_debug(args)
// mut db := pg.connect(
// host: args.host
// user: args.user
// port: args.port
// password: args.password
// dbname: args.dbname
// )!
// // console.print_debug(postgres_client)
// return PostgresClient{
// instance: args.instance
// db: db
// config: args
// }
// }
// struct LocalConfig {
// name string
// path string
// passwd string
// }

View File

@@ -55,7 +55,7 @@ fn obj_init(obj_ PostgresClient) !PostgresClient {
return obj
}
fn (mut self PostgresClient) db() !pg.DB {
pub fn (mut self PostgresClient) db() !pg.DB {
// console.print_debug(args)
mut db := self.db_ or {
mut db_ := pg.connect(

View File

@@ -9,29 +9,52 @@ The PostgreSQL client can be configured using HeroScript. Configuration settings
### Basic Configuration Example
```v
#!/usr/bin/env -S v -n -w -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.core
import os
import freeflowuniverse.herolib.clients.postgresql_client
// Configure PostgreSQL client
heroscript := "
!!postgresql_client.configure
name:'test'
user: 'root'
port: 5432
host: 'localhost'
password: '1234'
dbname: 'postgres'
name:'test'
user: 'postgres'
port: 5432
host: 'localhost'
password: '1234'
dbname: 'postgres'
"
// Process the heroscript
postgresql_client.play(heroscript:heroscript)!
// Process the heroscript configuration
postgresql_client.play(heroscript: heroscript)!
// Get the configured client
mut db_client := postgresql_client.get(name:"test")!
mut db_client := postgresql_client.get(name: "test")!
// Check if test database exists, create if not
if !db_client.db_exists('test')! {
println('Creating database test...')
db_client.db_create('test')!
}
// Switch to test database
db_client.dbname = 'test'
// Create table if not exists
create_table_sql := "CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)"
println('Creating table users if not exists...')
db_client.exec(create_table_sql)!
println('Database and table setup completed successfully!')
println(db_client)
```
### Configuration Parameters
@@ -94,20 +117,3 @@ db_client.backup(dest: '/path/to/backup/dir')!
Backups are created in custom PostgreSQL format (.bak files) which can be restored using pg_restore.
## Default Configuration
If no configuration is provided, the client uses these default settings:
```v
heroscript := "
!!postgresql_client.configure
name:'default'
user: 'root'
port: 5432
host: 'localhost'
password: ''
dbname: 'postgres'
"
```
You can override these defaults by providing your own configuration using the HeroScript configure command.