Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 299f6dea06 | |||
| ed025f9acb | |||
| c4ea066927 | |||
| 5f9c6ff2bb | |||
| 8965f7ae89 | |||
| 9a931b65e2 | |||
| 2c149507f6 | |||
|
|
34dea39c52 | ||
|
|
f1a4547961 | ||
|
|
8ae56a8df6 | ||
| b731c4c388 | |||
| e929ce029d | |||
| 5160096a1a | |||
| f219a4041a | |||
| 674eae1c11 | |||
| f62369bd01 | |||
| 7a6660ebd8 | |||
| e20d1bdcc5 | |||
| 3e309b6379 | |||
| ae4e92e090 | |||
| 7b69719f0e | |||
| 1d631fec21 | |||
| 690b1b68c3 | |||
| 6e619622d2 | |||
| eb38bc5e60 | |||
| b0da6d1bd2 | |||
| 1377953dcf | |||
| aa85172700 | |||
| eff269e911 | |||
| 65ec6ee1a3 | |||
| a86b23b2e9 | |||
| bcccd5f247 | |||
| cb8c550ed1 | |||
| 5fc7019dcc | |||
| 8c9248fd94 | |||
| d1a5f1c268 | |||
| 96bc5c9e5d | |||
| 20927485d9 | |||
| a034708f21 | |||
| 19a2577564 | |||
| e34d804dda | |||
| cd6c899661 | |||
|
4a2753d32c
|
|||
|
1c0535a8b4
|
|||
|
a0c253fa05
|
|||
|
17a67870ef
|
|||
|
54cfd4c353
|
|||
| 717eb1e7d8 | |||
| 03f5885980 |
6
.github/workflows/documentation.yml
vendored
6
.github/workflows/documentation.yml
vendored
@@ -17,6 +17,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
deploy-documentation:
|
||||
#if: startsWith(github.ref, 'refs/tags/')
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
@@ -26,12 +27,11 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Vlang
|
||||
run: ./install_v.sh --github-actions
|
||||
run: ./install_v.sh
|
||||
|
||||
- name: Generate documentation
|
||||
run: |
|
||||
./doc.vsh
|
||||
# ls /home/runner/work/herolib/docs
|
||||
./doc.vsh
|
||||
find .
|
||||
|
||||
- name: Setup Pages
|
||||
|
||||
4
.github/workflows/hero_build.yml
vendored
4
.github/workflows/hero_build.yml
vendored
@@ -5,11 +5,7 @@ permissions:
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main","development"]
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
branches: ["main","development"]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,6 +7,7 @@ vls.*
|
||||
vls.log
|
||||
node_modules/
|
||||
docs/
|
||||
vdocs/
|
||||
photonwrapper.so
|
||||
x
|
||||
.env
|
||||
@@ -25,7 +26,6 @@ dump.rdb
|
||||
output/
|
||||
*.db
|
||||
.stellar
|
||||
vdocs/
|
||||
data.ms/
|
||||
test_basic
|
||||
cli/hero
|
||||
@@ -6,7 +6,7 @@
|
||||
## hero install for users
|
||||
|
||||
```bash
|
||||
curl https://raw.githubusercontent.com/freeflowuniverse/herolib/refs/heads/development_kristof10/install_hero.sh > /tmp/install_hero.sh
|
||||
curl https://raw.githubusercontent.com/freeflowuniverse/herolib/refs/heads/development/install_hero.sh > /tmp/install_hero.sh
|
||||
bash /tmp/install_hero.sh
|
||||
|
||||
```
|
||||
@@ -16,7 +16,7 @@ this tool can be used to work with git, build books, play with hero AI, ...
|
||||
## automated install for developers
|
||||
|
||||
```bash
|
||||
curl 'https://raw.githubusercontent.com/freeflowuniverse/herolib/refs/heads/main/install_v.sh' > /tmp/install_v.sh
|
||||
curl 'https://raw.githubusercontent.com/freeflowuniverse/herolib/refs/heads/development/install_v.sh' > /tmp/install_v.sh
|
||||
bash /tmp/install_v.sh --analyzer --herolib
|
||||
#DONT FORGET TO START A NEW SHELL (otherwise the paths will not be set)
|
||||
```
|
||||
|
||||
187
aiprompts/reflection.md
Normal file
187
aiprompts/reflection.md
Normal file
@@ -0,0 +1,187 @@
|
||||
## Compile time reflection
|
||||
|
||||
$ is used as a prefix for compile time (also referred to as 'comptime') operations.
|
||||
|
||||
Having built-in JSON support is nice, but V also allows you to create efficient serializers for any data format. V has compile time if and for constructs:
|
||||
|
||||
.fields
|
||||
You can iterate over struct fields using .fields, it also works with generic types (e.g. T.fields) and generic arguments (e.g. param.fields where fn gen[T](param T) {).
|
||||
|
||||
struct User {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
fn main() {
|
||||
$for field in User.fields {
|
||||
$if field.typ is string {
|
||||
println('${field.name} is of type string')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// name is of type string
|
||||
.values
|
||||
You can read Enum values and their attributes.
|
||||
|
||||
enum Color {
|
||||
red @[RED] // first attribute
|
||||
blue @[BLUE] // second attribute
|
||||
}
|
||||
|
||||
fn main() {
|
||||
$for e in Color.values {
|
||||
println(e.name)
|
||||
println(e.attrs)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// red
|
||||
// ['RED']
|
||||
// blue
|
||||
// ['BLUE']
|
||||
.attributes
|
||||
You can read Struct attributes.
|
||||
|
||||
@[COLOR]
|
||||
struct Foo {
|
||||
a int
|
||||
}
|
||||
|
||||
fn main() {
|
||||
$for e in Foo.attributes {
|
||||
println(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// StructAttribute{
|
||||
// name: 'COLOR'
|
||||
// has_arg: false
|
||||
// arg: ''
|
||||
// kind: plain
|
||||
// }
|
||||
.variants
|
||||
You can read variant types from Sum type.
|
||||
|
||||
type MySum = int | string
|
||||
|
||||
fn main() {
|
||||
$for v in MySum.variants {
|
||||
$if v.typ is int {
|
||||
println('has int type')
|
||||
} $else $if v.typ is string {
|
||||
println('has string type')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// has int type
|
||||
// has string type
|
||||
.methods
|
||||
You can retrieve information about struct methods.
|
||||
|
||||
struct Foo {
|
||||
}
|
||||
|
||||
fn (f Foo) test() int {
|
||||
return 123
|
||||
}
|
||||
|
||||
fn (f Foo) test2() string {
|
||||
return 'foo'
|
||||
}
|
||||
|
||||
fn main() {
|
||||
foo := Foo{}
|
||||
$for m in Foo.methods {
|
||||
$if m.return_type is int {
|
||||
print('${m.name} returns int: ')
|
||||
println(foo.$method())
|
||||
} $else $if m.return_type is string {
|
||||
print('${m.name} returns string: ')
|
||||
println(foo.$method())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test returns int: 123
|
||||
// test2 returns string: foo
|
||||
.params
|
||||
You can retrieve information about struct method params.
|
||||
|
||||
struct Test {
|
||||
}
|
||||
|
||||
fn (t Test) foo(arg1 int, arg2 string) {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
$for m in Test.methods {
|
||||
$for param in m.params {
|
||||
println('${typeof(param.typ).name}: ${param.name}')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// int: arg1
|
||||
// string: arg2
|
||||
|
||||
## Example
|
||||
|
||||
```v
|
||||
// An example deserializer implementation
|
||||
|
||||
struct User {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
fn main() {
|
||||
data := 'name=Alice\nage=18'
|
||||
user := decode[User](data)
|
||||
println(user)
|
||||
}
|
||||
|
||||
fn decode[T](data string) T {
|
||||
mut result := T{}
|
||||
// compile-time `for` loop
|
||||
// T.fields gives an array of a field metadata type
|
||||
$for field in T.fields {
|
||||
$if field.typ is string {
|
||||
// $(string_expr) produces an identifier
|
||||
result.$(field.name) = get_string(data, field.name)
|
||||
} $else $if field.typ is int {
|
||||
result.$(field.name) = get_int(data, field.name)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fn get_string(data string, field_name string) string {
|
||||
for line in data.split_into_lines() {
|
||||
key_val := line.split('=')
|
||||
if key_val[0] == field_name {
|
||||
return key_val[1]
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
fn get_int(data string, field string) int {
|
||||
return get_string(data, field).int()
|
||||
}
|
||||
|
||||
// `decode<User>` generates:
|
||||
// fn decode_User(data string) User {
|
||||
// mut result := User{}
|
||||
// result.name = get_string(data, 'name')
|
||||
// result.age = get_int(data, 'age')
|
||||
// return result
|
||||
// }
|
||||
```
|
||||
2
cli/.gitignore
vendored
2
cli/.gitignore
vendored
@@ -1 +1,3 @@
|
||||
hero
|
||||
compile
|
||||
compile_upload
|
||||
|
||||
24
cli/hero.v
24
cli/hero.v
@@ -19,6 +19,26 @@ fn playcmds_do(path string) ! {
|
||||
}
|
||||
|
||||
fn do() ! {
|
||||
|
||||
if ! core.is_osx()! {
|
||||
if os.getenv('SUDO_COMMAND') != '' || os.getenv('SUDO_USER') != '' {
|
||||
println('Error: Please do not run this program with sudo!')
|
||||
exit(1) // Exit with error code
|
||||
}
|
||||
}
|
||||
|
||||
if os.getuid() == 0 {
|
||||
if core.is_osx()! {
|
||||
eprintln("please do not run hero as root in osx.")
|
||||
exit(1)
|
||||
}
|
||||
} else {
|
||||
if ! core.is_osx()! {
|
||||
eprintln("please do run hero as root, don't use sudo.")
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if os.args.len == 2 {
|
||||
mypath := os.args[1]
|
||||
if mypath.to_lower().ends_with('.hero') {
|
||||
@@ -31,7 +51,7 @@ fn do() ! {
|
||||
mut cmd := Command{
|
||||
name: 'hero'
|
||||
description: 'Your HERO toolset.'
|
||||
version: '2.0.6'
|
||||
version: '1.0.13'
|
||||
}
|
||||
|
||||
// herocmds.cmd_run_add_flags(mut cmd)
|
||||
@@ -94,4 +114,4 @@ fn main() {
|
||||
|
||||
fn pre_func(cmd Command) ! {
|
||||
herocmds.plbook_run(cmd)!
|
||||
}
|
||||
}
|
||||
38
doc.vsh
38
doc.vsh
@@ -26,9 +26,9 @@ os.chdir(herolib_path) or {
|
||||
panic('Failed to change directory to herolib: ${err}')
|
||||
}
|
||||
|
||||
os.rmdir_all('_docs') or {}
|
||||
os.rmdir_all('docs') or {}
|
||||
os.rmdir_all('vdocs') or {}
|
||||
os.mkdir_all('_docs') or {}
|
||||
os.mkdir_all('docs') or {}
|
||||
os.mkdir_all('vdocs') or {}
|
||||
|
||||
// Generate HTML documentation
|
||||
println('Generating HTML documentation...')
|
||||
@@ -42,13 +42,12 @@ os.chdir(abs_dir_of_script) or {
|
||||
|
||||
// Generate Markdown documentation
|
||||
println('Generating Markdown documentation...')
|
||||
os.rmdir_all('vdocs') or {}
|
||||
|
||||
// if os.system('v doc -m -no-color -f md -o ../vdocs/v/') != 0 {
|
||||
// panic('Failed to generate V markdown documentation')
|
||||
// }
|
||||
|
||||
if os.system('v doc -m -no-color -f md -o vdocs/herolib/') != 0 {
|
||||
if os.system('v doc -m -no-color -f md -o vdocs/') != 0 {
|
||||
panic('Failed to generate Hero markdown documentation')
|
||||
}
|
||||
|
||||
@@ -62,4 +61,33 @@ $if !linux {
|
||||
}
|
||||
}
|
||||
|
||||
// Create Jekyll required files
|
||||
println('Creating Jekyll files...')
|
||||
os.mkdir_all('docs/assets/css') or {}
|
||||
|
||||
// Create style.scss
|
||||
style_content := '---\n---\n\n@import "{{ site.theme }}";'
|
||||
os.write_file('docs/assets/css/style.scss', style_content) or {
|
||||
panic('Failed to create style.scss: ${err}')
|
||||
}
|
||||
|
||||
// Create _config.yml
|
||||
config_content := 'title: HeroLib Documentation
|
||||
description: Documentation for the HeroLib project
|
||||
theme: jekyll-theme-primer
|
||||
baseurl: /herolib
|
||||
|
||||
exclude:
|
||||
- Gemfile
|
||||
- Gemfile.lock
|
||||
- node_modules
|
||||
- vendor/bundle/
|
||||
- vendor/cache/
|
||||
- vendor/gems/
|
||||
- vendor/ruby/'
|
||||
|
||||
os.write_file('docs/_config.yml', config_content) or {
|
||||
panic('Failed to create _config.yml: ${err}')
|
||||
}
|
||||
|
||||
println('Documentation generation completed successfully!')
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.clients.mailclient
|
||||
|
||||
import freeflowuniverse.herolib.clients. mailclient
|
||||
|
||||
|
||||
//remove the previous one, otherwise the env variables are not read
|
||||
mailclient.config_delete(name:"test")!
|
||||
// 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=...
|
||||
@@ -14,11 +12,14 @@ mailclient.config_delete(name:"test")!
|
||||
// - MAIL_SERVER=...
|
||||
// - MAIL_USERNAME=...
|
||||
|
||||
|
||||
mut client:= mailclient.get(name:"test")!
|
||||
mut client := mailclient.get(name: 'test')!
|
||||
|
||||
println(client)
|
||||
|
||||
client.send(subject:'this is a test',to:'kristof@incubaid.com',body:'
|
||||
client.send(
|
||||
subject: 'this is a test'
|
||||
to: 'kristof@incubaid.com'
|
||||
body: '
|
||||
this is my email content
|
||||
')!
|
||||
'
|
||||
)!
|
||||
|
||||
108
examples/clients/mycelium.vsh
Executable file
108
examples/clients/mycelium.vsh
Executable file
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.clients.mycelium
|
||||
import freeflowuniverse.herolib.installers.net.mycelium as mycelium_installer
|
||||
import freeflowuniverse.herolib.osal
|
||||
import time
|
||||
import os
|
||||
import encoding.base64
|
||||
|
||||
const server1_port = 9001
|
||||
const server2_port = 9002
|
||||
|
||||
fn terminate(port int) ! {
|
||||
// Step 1: Run lsof to get process details
|
||||
res := os.execute('lsof -i:${port}')
|
||||
if res.exit_code != 0 {
|
||||
return error('no service running at port ${port} due to: ${res.output}')
|
||||
}
|
||||
|
||||
// Step 2: Parse the output to extract the PID
|
||||
lines := res.output.split('\n')
|
||||
if lines.len < 2 {
|
||||
return error('no process found running on port ${port}')
|
||||
}
|
||||
|
||||
// The PID is the second column in the output
|
||||
fields := lines[1].split(' ')
|
||||
if fields.len < 2 {
|
||||
return error('failed to parse lsof output')
|
||||
}
|
||||
pid := fields[1]
|
||||
|
||||
// Step 3: Kill the process using the PID
|
||||
kill_res := os.execute('kill ${pid}')
|
||||
if kill_res.exit_code != 0 {
|
||||
return error('failed to kill process ${pid}: ${kill_res.output}')
|
||||
}
|
||||
|
||||
println('Successfully terminated process ${pid} running on port ${port}')
|
||||
}
|
||||
|
||||
// Check if not installed install it.
|
||||
mut installer := mycelium_installer.get()!
|
||||
installer.install()!
|
||||
|
||||
mycelium.delete()!
|
||||
|
||||
spawn fn () {
|
||||
os.execute('mkdir -p /tmp/mycelium_server1 && cd /tmp/mycelium_server1 && mycelium --peers tcp://188.40.132.242:9651 quic://[2a01:4f8:212:fa6::2]:9651 tcp://185.69.166.7:9651 quic://[2a02:1802:5e:0:ec4:7aff:fe51:e36b]:9651 tcp://65.21.231.58:9651 quic://[2a01:4f9:5a:1042::2]:9651 tcp://[2604:a00:50:17b:9e6b:ff:fe1f:e054]:9651 quic://5.78.122.16:9651 tcp://[2a01:4ff:2f0:3621::1]:9651 quic://142.93.217.194:9651 --tun-name tun2 --tcp-listen-port 9652 --quic-listen-port 9653 --api-addr 127.0.0.1:${server1_port}')
|
||||
}()
|
||||
|
||||
spawn fn () {
|
||||
os.execute('mkdir -p /tmp/mycelium_server2 && cd /tmp/mycelium_server2 && mycelium --peers tcp://188.40.132.242:9651 quic://[2a01:4f8:212:fa6::2]:9651 tcp://185.69.166.7:9651 quic://[2a02:1802:5e:0:ec4:7aff:fe51:e36b]:9651 tcp://65.21.231.58:9651 quic://[2a01:4f9:5a:1042::2]:9651 tcp://[2604:a00:50:17b:9e6b:ff:fe1f:e054]:9651 quic://5.78.122.16:9651 tcp://[2a01:4ff:2f0:3621::1]:9651 quic://142.93.217.194:9651 --tun-name tun3 --tcp-listen-port 9654 --quic-listen-port 9655 --api-addr 127.0.0.1:${server2_port}')
|
||||
}()
|
||||
|
||||
defer {
|
||||
terminate(server1_port) or {}
|
||||
terminate(server2_port) or {}
|
||||
}
|
||||
|
||||
time.sleep(2 * time.second)
|
||||
|
||||
mut client1 := mycelium.get()!
|
||||
client1.server_url = 'http://localhost:${server1_port}'
|
||||
client1.name = 'client1'
|
||||
println(client1)
|
||||
|
||||
mut client2 := mycelium.get()!
|
||||
client2.server_url = 'http://localhost:${server2_port}'
|
||||
client2.name = 'client2'
|
||||
println(client2)
|
||||
|
||||
inspect1 := mycelium.inspect(key_file_path: '/tmp/mycelium_server1/priv_key.bin')!
|
||||
inspect2 := mycelium.inspect(key_file_path: '/tmp/mycelium_server2/priv_key.bin')!
|
||||
|
||||
println('Server 1 public key: ${inspect1.public_key}')
|
||||
println('Server 2 public key: ${inspect2.public_key}')
|
||||
|
||||
// Send a message to a node by public key
|
||||
// Parameters: public_key, payload, topic, wait_for_reply
|
||||
msg := client1.send_msg(
|
||||
public_key: inspect2.public_key // destination public key
|
||||
payload: 'Sending a message from the client 1 to the client 2' // message payload
|
||||
topic: 'testing' // optional topic
|
||||
)!
|
||||
|
||||
println('Sent message ID: ${msg.id}')
|
||||
println('send succeeded')
|
||||
|
||||
// Receive messages
|
||||
// Parameters: wait_for_message, peek_only, topic_filter
|
||||
received := client2.receive_msg(wait: true, peek: false, topic: 'testing')!
|
||||
println('Received message from: ${received.src_pk}')
|
||||
println('Message payload: ${base64.decode_str(received.payload)}')
|
||||
|
||||
// Reply to a message
|
||||
// client1.reply_msg(
|
||||
// id: received.id
|
||||
// public_key: received.src_pk
|
||||
// payload: 'Got your message!'
|
||||
// topic: 'greetings'
|
||||
// )!
|
||||
|
||||
// // // Check message status
|
||||
// // status := client.get_msg_status(msg.id)!
|
||||
// // println('Message status: ${status.state}')
|
||||
// // println('Created at: ${status.created}')
|
||||
// // println('Expires at: ${status.deadline}')
|
||||
@@ -3,7 +3,6 @@
|
||||
import freeflowuniverse.herolib.core
|
||||
import freeflowuniverse.herolib.clients.postgresql_client
|
||||
|
||||
|
||||
// Configure PostgreSQL client
|
||||
heroscript := "
|
||||
!!postgresql_client.configure
|
||||
@@ -19,7 +18,7 @@ heroscript := "
|
||||
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')! {
|
||||
@@ -31,15 +30,14 @@ if !db_client.db_exists('test')! {
|
||||
db_client.dbname = 'test'
|
||||
|
||||
// Create table if not exists
|
||||
create_table_sql := "CREATE TABLE IF NOT EXISTS users (
|
||||
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!')
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ mut:
|
||||
}
|
||||
|
||||
mut person := Person{
|
||||
name: 'Bob'
|
||||
birthday: time.now()
|
||||
name: 'Bob'
|
||||
birthday: time.now()
|
||||
}
|
||||
heroscript := encoderhero.encode[Person](person)!
|
||||
|
||||
@@ -22,9 +22,8 @@ println(heroscript)
|
||||
person2 := encoderhero.decode[Person](heroscript)!
|
||||
println(person2)
|
||||
|
||||
//show that it doesn't matter which action & method is used
|
||||
heroscript2:="!!a.b name:Bob age:20 birthday:'2025-02-06 09:57:30'"
|
||||
// show that it doesn't matter which action & method is used
|
||||
heroscript2 := "!!a.b name:Bob age:20 birthday:'2025-02-06 09:57:30'"
|
||||
person3 := encoderhero.decode[Person](heroscript)!
|
||||
|
||||
println(person3)
|
||||
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
|
||||
import json
|
||||
|
||||
enum JobTitle {
|
||||
manager
|
||||
executive
|
||||
worker
|
||||
manager
|
||||
executive
|
||||
worker
|
||||
}
|
||||
|
||||
struct Employee {
|
||||
mut:
|
||||
name string
|
||||
family string @[json: '-'] // this field will be skipped
|
||||
age int
|
||||
salary f32
|
||||
title JobTitle @[json: 'ETitle'] // the key for this field will be 'ETitle', not 'title'
|
||||
notes string @[omitempty] // the JSON property is not created if the string is equal to '' (an empty string).
|
||||
// TODO: document @[raw]
|
||||
name string
|
||||
family string @[json: '-'] // this field will be skipped
|
||||
age int
|
||||
salary f32
|
||||
title JobTitle @[json: 'ETitle'] // the key for this field will be 'ETitle', not 'title'
|
||||
notes string @[omitempty] // the JSON property is not created if the string is equal to '' (an empty string).
|
||||
// TODO: document @[raw]
|
||||
}
|
||||
|
||||
x := Employee{'Peter', 'Begins', 28, 95000.5, .worker, ''}
|
||||
@@ -34,4 +33,3 @@ println(y)
|
||||
ss := json.encode(y)
|
||||
println('JSON encoding of employee y: ${ss}')
|
||||
assert ss == s
|
||||
|
||||
|
||||
@@ -18,8 +18,7 @@ heroscript := "
|
||||
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')!
|
||||
|
||||
// Create a new location instance
|
||||
mut loc := location.new(mut db_client, false) or { panic(err) }
|
||||
|
||||
@@ -18,8 +18,7 @@ heroscript := "
|
||||
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')!
|
||||
|
||||
// Create a new location instance
|
||||
mut loc := location.new(mut db_client, false) or { panic(err) }
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.develop.gittools
|
||||
import freeflowuniverse.herolib.osal
|
||||
import time
|
||||
|
||||
mut gs_default := gittools.new()!
|
||||
|
||||
println(gs_default)
|
||||
|
||||
// // Initializes the Git structure with the coderoot path.
|
||||
// coderoot := '/tmp/code'
|
||||
// mut gs_tmo := gittools.new(coderoot: coderoot)!
|
||||
|
||||
// // Retrieve the specified repository.
|
||||
// mut repo := gs_default.get_repo(name: 'herolib')!
|
||||
|
||||
// println(repo)
|
||||
14
examples/develop/gittools/gittools_path_get.vsh
Executable file
14
examples/develop/gittools/gittools_path_get.vsh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.develop.gittools
|
||||
import freeflowuniverse.herolib.osal
|
||||
import time
|
||||
|
||||
mut gs := gittools.new()!
|
||||
mydocs_path := gs.get_path(
|
||||
pull: true
|
||||
reset: false
|
||||
url: 'https://git.ourworld.tf/tfgrid/info_docs_depin/src/branch/main/docs'
|
||||
)!
|
||||
|
||||
println(mydocs_path)
|
||||
8
examples/develop/ipapi/example.vsh
Executable file
8
examples/develop/ipapi/example.vsh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -no-retry-compilation -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.clients.ipapi
|
||||
import os
|
||||
|
||||
mut ip_api_client := ipapi.get()!
|
||||
info := ip_api_client.get_ip_info('37.27.132.46')!
|
||||
println('info: ${info}')
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.installers.infra.coredns as coredns_installer
|
||||
import freeflowuniverse.herolib.osal
|
||||
|
||||
coredns_installer.install()!
|
||||
// coredns_installer.delete()!
|
||||
mut installer := coredns_installer.get()!
|
||||
installer.build()!
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
|
||||
import freeflowuniverse.herolib.installers.infra.gitea as gitea_installer
|
||||
|
||||
mut installer := gitea_installer.get(name: 'test')!
|
||||
|
||||
mut installer:= gitea_installer.get(name:'test')!
|
||||
|
||||
//if you want to configure using heroscript
|
||||
gitea_installer.play(heroscript:"
|
||||
// if you want to configure using heroscript
|
||||
gitea_installer.play(
|
||||
heroscript: "
|
||||
!!gitea.configure name:test
|
||||
passwd:'something'
|
||||
domain: 'docs.info.com'
|
||||
")!
|
||||
"
|
||||
)!
|
||||
|
||||
installer.start()!
|
||||
|
||||
@@ -1,5 +1,40 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.installers.net.mycelium as mycelium_installer
|
||||
import freeflowuniverse.herolib.clients.mycelium
|
||||
|
||||
mycelium_installer.start()!
|
||||
mut installer := mycelium_installer.get()!
|
||||
installer.start()!
|
||||
|
||||
mut r := mycelium.inspect()!
|
||||
println(r)
|
||||
|
||||
mut client := mycelium.get()!
|
||||
|
||||
// Send a message to a node by public key
|
||||
// Parameters: public_key, payload, topic, wait_for_reply
|
||||
msg := client.send_msg('abc123...', // destination public key
|
||||
'Hello World', // message payload
|
||||
'greetings', // optional topic
|
||||
true // wait for reply
|
||||
)!
|
||||
println('Sent message ID: ${msg.id}')
|
||||
|
||||
// Receive messages
|
||||
// Parameters: wait_for_message, peek_only, topic_filter
|
||||
received := client.receive_msg(true, false, 'greetings')!
|
||||
println('Received message from: ${received.src_pk}')
|
||||
println('Message payload: ${received.payload}')
|
||||
|
||||
// Reply to a message
|
||||
client.reply_msg(received.id, // original message ID
|
||||
received.src_pk, // sender's public key
|
||||
'Got your message!', // reply payload
|
||||
'greetings' // topic
|
||||
)!
|
||||
|
||||
// Check message status
|
||||
status := client.get_msg_status(msg.id)!
|
||||
println('Message status: ${status.state}')
|
||||
println('Created at: ${status.created}')
|
||||
println('Expires at: ${status.deadline}')
|
||||
|
||||
12
examples/installers/traefik.vsh
Executable file
12
examples/installers/traefik.vsh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import os
|
||||
import freeflowuniverse.herolib.installers.web.traefik as traefik_installer
|
||||
|
||||
traefik_installer.delete()!
|
||||
mut installer := traefik_installer.get()!
|
||||
|
||||
installer.password = 'planet'
|
||||
traefik_installer.set(installer)!
|
||||
|
||||
installer.start()!
|
||||
6
examples/installers/zinit_installer.vsh
Executable file
6
examples/installers/zinit_installer.vsh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.installers.sysadmintools.zinit as zinit_installer
|
||||
|
||||
mut installer := zinit_installer.get()!
|
||||
installer.start()!
|
||||
24
examples/osal/tun.vsh
Executable file
24
examples/osal/tun.vsh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.osal.tun
|
||||
|
||||
// Check if TUN is available
|
||||
if available := tun.available() {
|
||||
if available {
|
||||
println('TUN is available on this system')
|
||||
|
||||
// Get a free TUN interface name
|
||||
if interface_name := tun.free() {
|
||||
println('Found free TUN interface: ${interface_name}')
|
||||
|
||||
// Example: Now you could use this interface name
|
||||
// to set up your tunnel
|
||||
} else {
|
||||
println('Error finding free interface: ${err}')
|
||||
}
|
||||
} else {
|
||||
println('TUN is not available on this system')
|
||||
}
|
||||
} else {
|
||||
println('Error checking TUN availability: ${err}')
|
||||
}
|
||||
4
examples/virt/podman_buildah/.gitignore
vendored
Normal file
4
examples/virt/podman_buildah/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
buildah_example
|
||||
buildah_run_clean
|
||||
buildah_run_mdbook
|
||||
buildah_run
|
||||
@@ -1,15 +1,14 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.virt.herocontainers
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.base
|
||||
import freeflowuniverse.herolib.osal
|
||||
import freeflowuniverse.herolib.installers.virt.pacman
|
||||
import freeflowuniverse.herolib.installers.virt.podman as podman_installer
|
||||
|
||||
mut installer := pacman.get()!
|
||||
|
||||
// installer.destroy()!
|
||||
// installer.install()!
|
||||
mut podman_installer0 := podman_installer.get()!
|
||||
// podman_installer0.destroy()!
|
||||
podman_installer0.install()!
|
||||
|
||||
// exit(0)
|
||||
|
||||
@@ -17,13 +16,13 @@ mut installer := pacman.get()!
|
||||
|
||||
mut engine := herocontainers.new(install: true, herocompile: false)!
|
||||
|
||||
engine.reset_all()!
|
||||
// engine.reset_all()!
|
||||
|
||||
mut builder_gorust := engine.builder_go_rust()!
|
||||
// mut builder_gorust := engine.builder_go_rust()!
|
||||
|
||||
// will build nodejs, python build & herolib, hero
|
||||
// mut builder_hero := engine.builder_hero(reset:true)!
|
||||
|
||||
// mut builder_web := engine.builder_heroweb(reset:true)!
|
||||
|
||||
builder_gorust.shell()!
|
||||
// builder_gorust.shell()!
|
||||
|
||||
@@ -7,14 +7,22 @@ import freeflowuniverse.herolib.core.base
|
||||
import time
|
||||
import os
|
||||
|
||||
mut pm := herocontainers.new(herocompile: true, install: false)!
|
||||
// herocompile means we do it for the host system
|
||||
mut pm := herocontainers.new(herocompile: false, install: false)!
|
||||
|
||||
mut mybuildcontainer := pm.builder_get('builder_heroweb')!
|
||||
// pm.builder_base(reset:true)!
|
||||
|
||||
mut builder := pm.builder_get('base')!
|
||||
builder.shell()!
|
||||
|
||||
println(builder)
|
||||
|
||||
// builder.install_zinit()!
|
||||
|
||||
// bash & python can be executed directly in build container
|
||||
|
||||
// any of the herocommands can be executed like this
|
||||
mybuildcontainer.run(cmd: 'installers -n heroweb', runtime: .herocmd)!
|
||||
// mybuildcontainer.run(cmd: 'installers -n heroweb', runtime: .herocmd)!
|
||||
|
||||
// //following will execute heroscript in the buildcontainer
|
||||
// mybuildcontainer.run(
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import freeflowuniverse.herolib.virt.herocontainers
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.base
|
||||
// import freeflowuniverse.herolib.builder
|
||||
import time
|
||||
import os
|
||||
|
||||
@@ -5,16 +5,15 @@ import freeflowuniverse.herolib.web.docusaurus
|
||||
|
||||
// Create a new docusaurus factory
|
||||
mut docs := docusaurus.new(
|
||||
// build_path: '/tmp/docusaurus_build'
|
||||
build_path: '/tmp/docusaurus_build'
|
||||
)!
|
||||
|
||||
// Create a new docusaurus site
|
||||
mut site := docs.dev(
|
||||
url:'https://git.ourworld.tf/despiegk/docs_kristof'
|
||||
url: 'https://git.ourworld.tf/despiegk/docs_kristof'
|
||||
)!
|
||||
|
||||
|
||||
//FOR FUTURE TO ADD CONTENT FROM DOCTREE
|
||||
// FOR FUTURE TO ADD CONTENT FROM DOCTREE
|
||||
|
||||
// Create a doctree for content
|
||||
// mut tree := doctree.new(name: 'content')!
|
||||
@@ -34,10 +33,10 @@ mut site := docs.dev(
|
||||
// )!
|
||||
|
||||
// Build the docusaurus site
|
||||
//site.build()!
|
||||
// site.build()!
|
||||
|
||||
// Generate the static site
|
||||
//site.generate()!
|
||||
// site.generate()!
|
||||
|
||||
// Optionally open the site in a browser
|
||||
// site.open()!
|
||||
|
||||
@@ -4,19 +4,38 @@ set -e
|
||||
|
||||
os_name="$(uname -s)"
|
||||
arch_name="$(uname -m)"
|
||||
version='1.0.13'
|
||||
|
||||
|
||||
# Base URL for GitHub releases
|
||||
base_url="https://github.com/freeflowuniverse/herolib/releases/download/v${version}"
|
||||
|
||||
# Select the URL based on the platform
|
||||
if [[ "$os_name" == "Linux" && "$arch_name" == "x86_64" ]]; then
|
||||
url="https://f003.backblazeb2.com/file/threefold/linux-i64/hero"
|
||||
url="$base_url/hero-x86_64-unknown-linux-musl"
|
||||
elif [[ "$os_name" == "Linux" && "$arch_name" == "aarch64" ]]; then
|
||||
url="$base_url/hero-aarch64-unknown-linux-musl"
|
||||
elif [[ "$os_name" == "Darwin" && "$arch_name" == "arm64" ]]; then
|
||||
url="https://f003.backblazeb2.com/file/threefold/macos-arm64/hero"
|
||||
# elif [[ "$os_name" == "Darwin" && "$arch_name" == "x86_64" ]]; then
|
||||
# url="https://f003.backblazeb2.com/file/threefold/macos-i64/hero"
|
||||
url="$base_url/hero-aarch64-apple-darwin"
|
||||
elif [[ "$os_name" == "Darwin" && "$arch_name" == "x86_64" ]]; then
|
||||
url="$base_url/hero-x86_64-apple-darwin"
|
||||
else
|
||||
echo "Unsupported platform."
|
||||
echo "Unsupported platform: $os_name $arch_name"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# # Select the URL based on the platform
|
||||
# if [[ "$os_name" == "Linux" && "$arch_name" == "x86_64" ]]; then
|
||||
# url="https://f003.backblazeb2.com/file/threefold/linux-i64/hero"
|
||||
# elif [[ "$os_name" == "Darwin" && "$arch_name" == "arm64" ]]; then
|
||||
# url="https://f003.backblazeb2.com/file/threefold/macos-arm64/hero"
|
||||
# # elif [[ "$os_name" == "Darwin" && "$arch_name" == "x86_64" ]]; then
|
||||
# # url="https://f003.backblazeb2.com/file/threefold/macos-i64/hero"
|
||||
# else
|
||||
# echo "Unsupported platform."
|
||||
# exit 1
|
||||
# fi
|
||||
|
||||
# Check for existing hero installations
|
||||
existing_hero=$(which hero 2>/dev/null || true)
|
||||
if [ ! -z "$existing_hero" ]; then
|
||||
@@ -122,4 +141,4 @@ if [ "$file_size" -ge 2 ]; then
|
||||
else
|
||||
echo "Downloaded file is less than 10 MB. Process aborted."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
8
lib/clients/ipapi/.heroscript
Normal file
8
lib/clients/ipapi/.heroscript
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
!!hero_code.generate_client
|
||||
name:'ipapi'
|
||||
classname:'IPApi'
|
||||
singleton:0
|
||||
default:1
|
||||
hasconfig:1
|
||||
reset:0
|
||||
29
lib/clients/ipapi/client.v
Normal file
29
lib/clients/ipapi/client.v
Normal file
@@ -0,0 +1,29 @@
|
||||
module ipapi
|
||||
|
||||
import json
|
||||
|
||||
pub struct IPInfo {
|
||||
pub:
|
||||
query string
|
||||
status string
|
||||
country string
|
||||
country_code string @[json: 'countryCode']
|
||||
region string
|
||||
region_name string @[json: 'regionName']
|
||||
city string
|
||||
zip string
|
||||
lat f32
|
||||
lon f32
|
||||
timezone string
|
||||
isp string
|
||||
org string
|
||||
as string
|
||||
}
|
||||
|
||||
pub fn (mut a IPApi) get_ip_info(ip string) !IPInfo {
|
||||
mut conn := a.connection()!
|
||||
res := conn.get_json(prefix: 'json/${ip}')!
|
||||
info := json.decode(IPInfo, res)!
|
||||
|
||||
return info
|
||||
}
|
||||
102
lib/clients/ipapi/ipapi_factory_.v
Normal file
102
lib/clients/ipapi/ipapi_factory_.v
Normal file
@@ -0,0 +1,102 @@
|
||||
module ipapi
|
||||
|
||||
import freeflowuniverse.herolib.core.base
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
__global (
|
||||
ipapi_global map[string]&IPApi
|
||||
ipapi_default string
|
||||
)
|
||||
|
||||
/////////FACTORY
|
||||
|
||||
@[params]
|
||||
pub struct ArgsGet {
|
||||
pub mut:
|
||||
name string
|
||||
}
|
||||
|
||||
fn args_get(args_ ArgsGet) ArgsGet {
|
||||
mut args := args_
|
||||
if args.name == '' {
|
||||
args.name = ipapi_default
|
||||
}
|
||||
if args.name == '' {
|
||||
args.name = 'default'
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
pub fn get(args_ ArgsGet) !&IPApi {
|
||||
mut args := args_get(args_)
|
||||
if args.name !in ipapi_global {
|
||||
if args.name == 'default' {
|
||||
if !config_exists(args) {
|
||||
if default {
|
||||
config_save(args)!
|
||||
}
|
||||
}
|
||||
config_load(args)!
|
||||
}
|
||||
}
|
||||
return ipapi_global[args.name] or {
|
||||
println(ipapi_global)
|
||||
panic('could not get config for ipapi with name:${args.name}')
|
||||
}
|
||||
}
|
||||
|
||||
fn config_exists(args_ ArgsGet) bool {
|
||||
mut args := args_get(args_)
|
||||
mut context := base.context() or { panic('bug') }
|
||||
return context.hero_config_exists('ipapi', args.name)
|
||||
}
|
||||
|
||||
fn config_load(args_ ArgsGet) ! {
|
||||
mut args := args_get(args_)
|
||||
mut context := base.context()!
|
||||
mut heroscript := context.hero_config_get('ipapi', args.name)!
|
||||
play(heroscript: heroscript)!
|
||||
}
|
||||
|
||||
fn config_save(args_ ArgsGet) ! {
|
||||
mut args := args_get(args_)
|
||||
mut context := base.context()!
|
||||
context.hero_config_set('ipapi', args.name, heroscript_default()!)!
|
||||
}
|
||||
|
||||
fn set(o IPApi) ! {
|
||||
mut o2 := obj_init(o)!
|
||||
ipapi_global[o.name] = &o2
|
||||
ipapi_default = o.name
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct PlayArgs {
|
||||
pub mut:
|
||||
heroscript string // if filled in then plbook will be made out of it
|
||||
plbook ?playbook.PlayBook
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn play(args_ PlayArgs) ! {
|
||||
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: 'ipapi.configure')!
|
||||
if install_actions.len > 0 {
|
||||
for install_action in install_actions {
|
||||
mut p := install_action.params
|
||||
cfg_play(p)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// switch instance to be used for ipapi
|
||||
pub fn switch(name string) {
|
||||
ipapi_default = name
|
||||
}
|
||||
58
lib/clients/ipapi/ipapi_model.v
Normal file
58
lib/clients/ipapi/ipapi_model.v
Normal file
@@ -0,0 +1,58 @@
|
||||
module ipapi
|
||||
|
||||
import freeflowuniverse.herolib.data.paramsparser
|
||||
import freeflowuniverse.herolib.core.httpconnection
|
||||
import os
|
||||
|
||||
pub const version = '1.14.3'
|
||||
const singleton = false
|
||||
const default = true
|
||||
|
||||
// TODO: THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE TO STRUCT BELOW, IS STRUCTURED AS HEROSCRIPT
|
||||
pub fn heroscript_default() !string {
|
||||
heroscript := "
|
||||
!!ipapi.configure
|
||||
name:'default'
|
||||
"
|
||||
|
||||
return heroscript
|
||||
}
|
||||
|
||||
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
|
||||
|
||||
@[heap]
|
||||
pub struct IPApi {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
|
||||
conn ?&httpconnection.HTTPConnection @[str: skip]
|
||||
}
|
||||
|
||||
fn cfg_play(p paramsparser.Params) ! {
|
||||
// THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE WITH struct above
|
||||
mut mycfg := IPApi{
|
||||
name: p.get_default('name', 'default')!
|
||||
}
|
||||
set(mycfg)!
|
||||
}
|
||||
|
||||
fn obj_init(obj_ IPApi) !IPApi {
|
||||
// never call get here, only thing we can do here is work on object itself
|
||||
mut obj := obj_
|
||||
return obj
|
||||
}
|
||||
|
||||
pub fn (mut client IPApi) connection() !&httpconnection.HTTPConnection {
|
||||
mut c := client.conn or {
|
||||
mut c2 := httpconnection.new(
|
||||
name: 'ipapi_${client.name}'
|
||||
url: 'http://ip-api.com'
|
||||
cache: false
|
||||
retry: 20
|
||||
)!
|
||||
c2
|
||||
}
|
||||
|
||||
client.conn = c
|
||||
return c
|
||||
}
|
||||
30
lib/clients/ipapi/readme.md
Normal file
30
lib/clients/ipapi/readme.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# ipapi
|
||||
|
||||
|
||||
|
||||
To get started
|
||||
|
||||
```vlang
|
||||
|
||||
|
||||
import freeflowuniverse.herolib.clients. ipapi
|
||||
|
||||
mut client:= ipapi.get()!
|
||||
|
||||
client...
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
## example heroscript
|
||||
|
||||
```hero
|
||||
!!ipapi.configure
|
||||
secret: '...'
|
||||
host: 'localhost'
|
||||
port: 8888
|
||||
```
|
||||
|
||||
|
||||
153
lib/clients/livekit/access_token.v
Normal file
153
lib/clients/livekit/access_token.v
Normal file
@@ -0,0 +1,153 @@
|
||||
module livekit
|
||||
|
||||
// import time
|
||||
// import rand
|
||||
// import crypto.hmac
|
||||
// import crypto.sha256
|
||||
// import encoding.base64
|
||||
// import json
|
||||
|
||||
// // Define AccessTokenOptions struct
|
||||
// pub struct AccessTokenOptions {
|
||||
// pub mut:
|
||||
// ttl int | string // TTL in seconds or a time span (e.g., '2d', '5h')
|
||||
// name string // Display name for the participant
|
||||
// identity string // Identity of the user
|
||||
// metadata string // Custom metadata to be passed to participants
|
||||
// }
|
||||
|
||||
// // Struct representing grants
|
||||
// pub struct ClaimGrants {
|
||||
// pub mut:
|
||||
// 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']
|
||||
// can_publish bool @[json: 'canPublish']
|
||||
// can_publish_data bool @[json: 'canPublishData']
|
||||
// can_subscribe bool @[json: 'canSubscribe']
|
||||
// }
|
||||
|
||||
// // SIPGrant struct placeholder
|
||||
// struct SIPGrant {}
|
||||
|
||||
// // AccessToken class
|
||||
// pub struct AccessToken {
|
||||
// mut:
|
||||
// api_key string
|
||||
// api_secret string
|
||||
// grants ClaimGrants
|
||||
// identity string
|
||||
// ttl int | string
|
||||
// }
|
||||
|
||||
// // Constructor for AccessToken
|
||||
// pub fn new_access_token(api_key string, api_secret string, options AccessTokenOptions) !AccessToken {
|
||||
// if api_key == '' || api_secret == '' {
|
||||
// return error('API key and API secret must be set')
|
||||
// }
|
||||
|
||||
// ttl := if options.ttl is int { options.ttl } else { 21600 } // Default TTL of 6 hours (21600 seconds)
|
||||
|
||||
// return AccessToken{
|
||||
// api_key: api_key
|
||||
// api_secret: api_secret
|
||||
// identity: options.identity
|
||||
// ttl: ttl
|
||||
// grants: ClaimGrants{
|
||||
// exp: time.now().unix()+ttl
|
||||
// iss: api_key
|
||||
// sub: options.name
|
||||
// name: options.name
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Method to add a video grant to the token
|
||||
// pub fn (mut token AccessToken) add_video_grant(grant VideoGrant) {
|
||||
// token.grants.video = grant
|
||||
// }
|
||||
|
||||
|
||||
// // Method to generate a JWT token
|
||||
// pub fn (token AccessToken) to_jwt() !string {
|
||||
// // Create JWT payload
|
||||
// payload := json.encode(token.grants)
|
||||
|
||||
// println('payload: ${payload}')
|
||||
|
||||
// // Create JWT header
|
||||
// header := '{"alg":"HS256","typ":"JWT"}'
|
||||
|
||||
// // Encode header and payload in base64
|
||||
// header_encoded := base64.url_encode_str(header)
|
||||
// payload_encoded := base64.url_encode_str(payload)
|
||||
|
||||
// // Create the unsigned token
|
||||
// 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)
|
||||
|
||||
// // Encode the signature in base64
|
||||
// signature_encoded := base64.url_encode(signature)
|
||||
|
||||
// // Create the final JWT
|
||||
// jwt := '${unsigned_token}.${signature_encoded}'
|
||||
// return jwt
|
||||
// }
|
||||
|
||||
// // TokenVerifier class
|
||||
// pub struct TokenVerifier {
|
||||
// api_key string
|
||||
// api_secret string
|
||||
// }
|
||||
|
||||
// // Constructor for TokenVerifier
|
||||
// pub fn new_token_verifier(api_key string, api_secret string) !TokenVerifier {
|
||||
// if api_key == '' || api_secret == '' {
|
||||
// return error('API key and API secret must be set')
|
||||
// }
|
||||
// return TokenVerifier{
|
||||
// api_key: api_key
|
||||
// api_secret: api_secret
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Method to verify the JWT token
|
||||
// pub fn (verifier TokenVerifier) verify(token string) !ClaimGrants {
|
||||
// // Split the token into parts
|
||||
// parts := token.split('.')
|
||||
// if parts.len != 3 {
|
||||
// return error('Invalid token')
|
||||
// }
|
||||
|
||||
// // Decode header, payload, and signature
|
||||
// payload_encoded := parts[1]
|
||||
// signature_encoded := parts[2]
|
||||
|
||||
// // Recompute the HMAC-SHA256 signature
|
||||
// unsigned_token := '${parts[0]}.${parts[1]}'
|
||||
// expected_signature := hmac.new(verifier.api_secret.bytes(), unsigned_token.bytes(), sha256.sum, sha256.block_size)
|
||||
// expected_signature_encoded := base64.url_encode(expected_signature)
|
||||
|
||||
// // Verify the signature
|
||||
// if signature_encoded != expected_signature_encoded {
|
||||
// return error('Invalid token signature')
|
||||
// }
|
||||
|
||||
// // Decode the payload
|
||||
// payload_json := base64.url_decode_str(payload_encoded)
|
||||
|
||||
// // Parse and return the claims as ClaimGrants
|
||||
// return json.decode(ClaimGrants, payload_json)
|
||||
// }
|
||||
199
lib/clients/livekit/server_client.v
Normal file
199
lib/clients/livekit/server_client.v
Normal file
@@ -0,0 +1,199 @@
|
||||
module livekit
|
||||
|
||||
import net.http
|
||||
import json
|
||||
|
||||
// // pub struct Client {
|
||||
// // pub:
|
||||
// // host string
|
||||
// // token string
|
||||
// // }
|
||||
|
||||
// // pub struct Room {
|
||||
// // pub mut:
|
||||
// // sid string
|
||||
// // name string
|
||||
// // empty_timeout string
|
||||
// // max_participants string
|
||||
// // creation_time string
|
||||
// // turn_password string
|
||||
// // metadata string
|
||||
// // num_participants u32
|
||||
// // active_recording bool
|
||||
// // }
|
||||
|
||||
// pub struct ParticipantInfo {
|
||||
// pub mut:
|
||||
// sid string
|
||||
// identity string
|
||||
// name string
|
||||
// state string
|
||||
// tracks []TrackInfo
|
||||
// metadata string
|
||||
// joined_at i64
|
||||
// permission ParticipantPermission
|
||||
// is_publisher bool
|
||||
// }
|
||||
|
||||
// pub struct TrackInfo {
|
||||
// pub mut:
|
||||
// sid string
|
||||
// typ string @[json: 'type']
|
||||
// source string
|
||||
// name string
|
||||
// mime_type string
|
||||
// muted bool
|
||||
// width u32
|
||||
// height u32
|
||||
// simulcast bool
|
||||
// disable_dtx bool
|
||||
// layers []VideoLayer
|
||||
// }
|
||||
|
||||
// pub struct ParticipantPermission {
|
||||
// pub mut:
|
||||
// can_subscribe bool
|
||||
// can_publish bool
|
||||
// can_publish_data bool
|
||||
// }
|
||||
|
||||
// pub struct VideoLayer {
|
||||
// pub mut:
|
||||
// quality string
|
||||
// width u32
|
||||
// height u32
|
||||
// }
|
||||
|
||||
// // Helper method to make POST requests to LiveKit API
|
||||
// fn (client Client) make_post_request(url string, body string) !http.Response {
|
||||
// mut headers := http.new_header()
|
||||
// headers.add_custom('Authorization', 'Bearer ${client.token}')!
|
||||
// headers.add_custom('Content-Type', 'application/json')!
|
||||
|
||||
// req := http.Request{
|
||||
// method: http.Method.post
|
||||
// url: url
|
||||
// data: body
|
||||
// header: headers
|
||||
// }
|
||||
// return req.do()!
|
||||
// }
|
||||
|
||||
// pub struct CreateRoomArgs {
|
||||
// pub:
|
||||
// name string
|
||||
// empty_timeout u32
|
||||
// max_participants u32
|
||||
// metadata string
|
||||
// }
|
||||
|
||||
// // RoomService API methods
|
||||
// pub fn (client Client) create_room(args CreateRoomArgs) !Room {
|
||||
// body := json.encode(args)
|
||||
// url := '${client.host}/twirp/livekit.RoomService/CreateRoom'
|
||||
// response := client.make_post_request(url, body)!
|
||||
|
||||
// return json.decode(Room, response.body)!
|
||||
// }
|
||||
|
||||
// // pub fn (client Client) list_rooms(names []string) ![]Room {
|
||||
// // body := json.encode({
|
||||
// // 'names': names
|
||||
// // })
|
||||
// // url := '${client.host}/twirp/livekit.RoomService/ListRooms'
|
||||
// // response := client.make_post_request(url, body)!
|
||||
|
||||
// // return json.decode([]Room, response.body)!
|
||||
// // }
|
||||
|
||||
// pub fn (client Client) delete_room(room_name string) ! {
|
||||
// body := json.encode({
|
||||
// 'room': room_name
|
||||
// })
|
||||
// url := '${client.host}/twirp/livekit.RoomService/DeleteRoom'
|
||||
// _ := client.make_post_request(url, body)!
|
||||
// }
|
||||
|
||||
// pub fn (client Client) list_participants(room_name string) ![]ParticipantInfo {
|
||||
// body := json.encode({
|
||||
// 'room': room_name
|
||||
// })
|
||||
// url := '${client.host}/twirp/livekit.RoomService/ListParticipants'
|
||||
// response := client.make_post_request(url, body)!
|
||||
|
||||
// return json.decode([]ParticipantInfo, response.body)!
|
||||
// }
|
||||
|
||||
// pub fn (client Client) get_participant(room_name string, identity string) !ParticipantInfo {
|
||||
// body := json.encode({
|
||||
// 'room': room_name
|
||||
// 'identity': identity
|
||||
// })
|
||||
// url := '${client.host}/twirp/livekit.RoomService/GetParticipant'
|
||||
// response := client.make_post_request(url, body)!
|
||||
|
||||
// return json.decode(ParticipantInfo, response.body)!
|
||||
// }
|
||||
|
||||
// pub fn (client Client) remove_participant(room_name string, identity string) ! {
|
||||
// body := json.encode({
|
||||
// 'room': room_name
|
||||
// 'identity': identity
|
||||
// })
|
||||
// url := '${client.host}/twirp/livekit.RoomService/RemoveParticipant'
|
||||
// _ := client.make_post_request(url, body)!
|
||||
// }
|
||||
|
||||
// pub struct MutePublishedTrackArgs {
|
||||
// pub:
|
||||
// room_name string
|
||||
// identity string
|
||||
// track_sid string
|
||||
// muted bool
|
||||
// }
|
||||
|
||||
// pub fn (client Client) mute_published_track(args MutePublishedTrackArgs) ! {
|
||||
// body := json.encode(args)
|
||||
// url := '${client.host}/twirp/livekit.RoomService/MutePublishedTrack'
|
||||
// _ := client.make_post_request(url, body)!
|
||||
// }
|
||||
|
||||
// pub struct UpdateParticipantArgs {
|
||||
// pub:
|
||||
// room_name string @[json: 'room']
|
||||
// identity string
|
||||
// metadata string
|
||||
// permission ParticipantPermission
|
||||
// }
|
||||
|
||||
// pub fn (client Client) update_participant(args UpdateParticipantArgs) ! {
|
||||
// body := json.encode(args)
|
||||
// url := '${client.host}/twirp/livekit.RoomService/UpdateParticipant'
|
||||
// _ := client.make_post_request(url, body)!
|
||||
// }
|
||||
|
||||
// pub struct UpdateRoomMetadataArgs {
|
||||
// pub:
|
||||
// room_name string @[json: 'room']
|
||||
// metadata string
|
||||
// }
|
||||
|
||||
// pub fn (client Client) update_room_metadata(args UpdateRoomMetadataArgs) ! {
|
||||
// body := json.encode(args)
|
||||
// url := '${client.host}/twirp/livekit.RoomService/UpdateRoomMetadata'
|
||||
// _ := client.make_post_request(url, body)!
|
||||
// }
|
||||
|
||||
// pub struct SendDataArgs {
|
||||
// pub:
|
||||
// room_name string @[json: 'room']
|
||||
// data []u8
|
||||
// kind string
|
||||
// destination_identities []string
|
||||
// }
|
||||
|
||||
// pub fn (client Client) send_data(args SendDataArgs) ! {
|
||||
// body := json.encode(args)
|
||||
// url := '${client.host}/twirp/livekit.RoomService/SendData'
|
||||
// _ := client.make_post_request(url, body)!
|
||||
// }
|
||||
@@ -1,127 +1,112 @@
|
||||
|
||||
module mailclient
|
||||
|
||||
import freeflowuniverse.herolib.core.base
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
|
||||
__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 args:=args_
|
||||
if args.name == ""{
|
||||
args.name = mailclient_default
|
||||
}
|
||||
if args.name == ""{
|
||||
args.name = "default"
|
||||
}
|
||||
return args
|
||||
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(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}")
|
||||
}
|
||||
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}')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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)
|
||||
mut args := args_get(args_)
|
||||
mut context := base.context() or { panic('bug') }
|
||||
return context.hero_config_exists('mailclient', args.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)!
|
||||
mut args := args_get(args_)
|
||||
mut context := base.context()!
|
||||
mut heroscript := context.hero_config_get('mailclient', args.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)!)!
|
||||
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)!
|
||||
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
|
||||
fn set(o MailClient) ! {
|
||||
mut o2 := obj_init(o)!
|
||||
mailclient_global[o.name] = &o2
|
||||
mailclient_default = o.name
|
||||
}
|
||||
|
||||
|
||||
@[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 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)!
|
||||
}
|
||||
}
|
||||
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)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//switch instance to be used for mailclient
|
||||
// switch instance to be used for mailclient
|
||||
pub fn switch(name string) {
|
||||
mailclient_default = name
|
||||
mailclient_default = name
|
||||
}
|
||||
|
||||
|
||||
//helpers
|
||||
// helpers
|
||||
|
||||
@[params]
|
||||
pub struct DefaultConfigArgs{
|
||||
instance string = 'default'
|
||||
pub struct DefaultConfigArgs {
|
||||
instance string = 'default'
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
module mailclient
|
||||
|
||||
import freeflowuniverse.herolib.data.paramsparser
|
||||
import os
|
||||
|
||||
@@ -6,7 +7,6 @@ pub const version = '0.0.0'
|
||||
const singleton = false
|
||||
const default = true
|
||||
|
||||
|
||||
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' }
|
||||
@@ -23,7 +23,7 @@ pub fn heroscript_default(args DefaultConfigArgs) !string {
|
||||
mail_username: '${mail_username}'
|
||||
"
|
||||
|
||||
return heroscript
|
||||
return heroscript
|
||||
}
|
||||
|
||||
@[heap]
|
||||
@@ -40,22 +40,18 @@ 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)!
|
||||
}
|
||||
|
||||
|
||||
fn obj_init(obj_ MailClient)!MailClient{
|
||||
mut obj:=obj_
|
||||
return obj
|
||||
}
|
||||
set(mycfg)!
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn obj_init(obj_ MailClient) !MailClient {
|
||||
mut obj := obj_
|
||||
return obj
|
||||
}
|
||||
|
||||
@@ -1,39 +1,65 @@
|
||||
module mycelium
|
||||
|
||||
import json
|
||||
import encoding.base64
|
||||
import freeflowuniverse.herolib.core.httpconnection
|
||||
|
||||
// Represents a destination for a message, can be either IP or public key
|
||||
pub struct MessageDestination {
|
||||
pub:
|
||||
pk string
|
||||
ip string @[omitempty] // IP in the subnet of the receiver node
|
||||
pk string @[omitempty] // hex encoded public key of the receiver node
|
||||
}
|
||||
|
||||
// Body of a message to be sent
|
||||
pub struct PushMessageBody {
|
||||
pub:
|
||||
dst MessageDestination
|
||||
payload string
|
||||
topic ?string // optional message topic
|
||||
payload string // base64 encoded message
|
||||
}
|
||||
|
||||
// Response containing message ID after pushing
|
||||
pub struct PushMessageResponseId {
|
||||
pub:
|
||||
id string // hex encoded message ID
|
||||
}
|
||||
|
||||
// A message received by the system
|
||||
pub struct InboundMessage {
|
||||
pub:
|
||||
id string
|
||||
src_ip string @[json: 'srcIP']
|
||||
src_pk string @[json: 'srcPk']
|
||||
dst_ip string @[json: 'dstIp']
|
||||
dst_pk string @[json: 'dstPk']
|
||||
payload string
|
||||
src_ip string @[json: 'srcIp'] // Sender overlay IP address
|
||||
src_pk string @[json: 'srcPk'] // Sender public key, hex encoded
|
||||
dst_ip string @[json: 'dstIp'] // Receiver overlay IP address
|
||||
dst_pk string @[json: 'dstPk'] // Receiver public key, hex encoded
|
||||
topic string // Optional message topic
|
||||
payload string // Message payload, base64 encoded
|
||||
}
|
||||
|
||||
// Information about an outbound message
|
||||
pub struct MessageStatusResponse {
|
||||
pub:
|
||||
id string
|
||||
dst string
|
||||
state string
|
||||
created string
|
||||
deadline string
|
||||
msg_len string @[json: 'msgLen']
|
||||
dst string // IP address of receiving node
|
||||
state string // pending, received, read, aborted or sending object
|
||||
created i64 // Unix timestamp of creation
|
||||
deadline i64 // Unix timestamp of expiry
|
||||
msg_len int @[json: 'msgLen'] // Length in bytes
|
||||
}
|
||||
|
||||
// General information about a node
|
||||
pub struct Info {
|
||||
pub:
|
||||
node_subnet string @[json: 'nodeSubnet'] // subnet owned by node
|
||||
}
|
||||
|
||||
// Response containing public key for a node IP
|
||||
pub struct PublicKeyResponse {
|
||||
pub:
|
||||
node_pub_key string @[json: 'NodePubKey'] // hex encoded public key
|
||||
}
|
||||
|
||||
// Get connection to mycelium server
|
||||
pub fn (mut self Mycelium) connection() !&httpconnection.HTTPConnection {
|
||||
mut c := self.conn or {
|
||||
mut c2 := httpconnection.new(
|
||||
@@ -47,30 +73,63 @@ pub fn (mut self Mycelium) connection() !&httpconnection.HTTPConnection {
|
||||
return c
|
||||
}
|
||||
|
||||
pub fn (mut self Mycelium) send_msg(pk string, payload string, wait bool) !InboundMessage {
|
||||
@[params]
|
||||
pub struct SendMessageArgs {
|
||||
pub mut:
|
||||
public_key string @[required]
|
||||
payload string @[required]
|
||||
topic ?string
|
||||
wait bool
|
||||
}
|
||||
|
||||
// Send a message to a node identified by public key
|
||||
pub fn (mut self Mycelium) send_msg(args SendMessageArgs) !InboundMessage {
|
||||
mut conn := self.connection()!
|
||||
mut params := {
|
||||
'dst': json.encode(MessageDestination{ pk: pk })
|
||||
'payload': payload
|
||||
mut body := PushMessageBody{
|
||||
dst: MessageDestination{
|
||||
pk: args.public_key
|
||||
ip: ''
|
||||
}
|
||||
payload: base64.encode_str(args.payload)
|
||||
topic: if v := args.topic {
|
||||
base64.encode_str(v)
|
||||
} else {
|
||||
none
|
||||
}
|
||||
}
|
||||
mut prefix := ''
|
||||
if wait {
|
||||
prefix = '?reply_timeout=120'
|
||||
mut prefix := '/api/v1/messages'
|
||||
if args.wait {
|
||||
prefix += '?reply_timeout=120'
|
||||
}
|
||||
return conn.post_json_generic[InboundMessage](
|
||||
method: .post
|
||||
prefix: prefix
|
||||
params: params
|
||||
data: json.encode(body)
|
||||
dataformat: .json
|
||||
)!
|
||||
}
|
||||
|
||||
pub fn (mut self Mycelium) receive_msg(wait bool) !InboundMessage {
|
||||
@[params]
|
||||
pub struct ReceiveMessageArgs {
|
||||
pub mut:
|
||||
topic ?string
|
||||
wait bool
|
||||
peek bool
|
||||
}
|
||||
|
||||
// Receive a message from the queue
|
||||
pub fn (mut self Mycelium) receive_msg(args ReceiveMessageArgs) !InboundMessage {
|
||||
mut conn := self.connection()!
|
||||
mut prefix := ''
|
||||
if wait {
|
||||
prefix = '?timeout=60'
|
||||
mut prefix := '/api/v1/messages?peek=${args.peek}&'
|
||||
|
||||
if args.wait {
|
||||
prefix += 'timeout=120&'
|
||||
}
|
||||
|
||||
if v := args.topic {
|
||||
prefix += 'topic=${base64.encode_str(v)}'
|
||||
}
|
||||
|
||||
return conn.get_json_generic[InboundMessage](
|
||||
method: .get
|
||||
prefix: prefix
|
||||
@@ -78,17 +137,9 @@ pub fn (mut self Mycelium) receive_msg(wait bool) !InboundMessage {
|
||||
)!
|
||||
}
|
||||
|
||||
pub fn (mut self Mycelium) receive_msg_opt(wait bool) ?InboundMessage {
|
||||
mut conn := self.connection()!
|
||||
mut prefix := ''
|
||||
if wait {
|
||||
prefix = '?timeout=60'
|
||||
}
|
||||
res := conn.get_json_generic[InboundMessage](
|
||||
method: .get
|
||||
prefix: prefix
|
||||
dataformat: .json
|
||||
) or {
|
||||
// Optional version of receive_msg that returns none on 204
|
||||
pub fn (mut self Mycelium) receive_msg_opt(args ReceiveMessageArgs) ?InboundMessage {
|
||||
res := self.receive_msg(args) or {
|
||||
if err.msg().contains('204') {
|
||||
return none
|
||||
}
|
||||
@@ -97,25 +148,62 @@ pub fn (mut self Mycelium) receive_msg_opt(wait bool) ?InboundMessage {
|
||||
return res
|
||||
}
|
||||
|
||||
// Get status of a message by ID
|
||||
pub fn (mut self Mycelium) get_msg_status(id string) !MessageStatusResponse {
|
||||
mut conn := self.connection()!
|
||||
return conn.get_json_generic[MessageStatusResponse](
|
||||
method: .get
|
||||
prefix: 'status/${id}'
|
||||
prefix: '/api/v1/messages/status/${id}'
|
||||
dataformat: .json
|
||||
)!
|
||||
}
|
||||
|
||||
pub fn (mut self Mycelium) reply_msg(id string, pk string, payload string) ! {
|
||||
@[params]
|
||||
pub struct ReplyMessageArgs {
|
||||
pub mut:
|
||||
id string @[required]
|
||||
public_key string @[required]
|
||||
payload string @[required]
|
||||
topic ?string
|
||||
}
|
||||
|
||||
// Reply to a message
|
||||
pub fn (mut self Mycelium) reply_msg(args ReplyMessageArgs) ! {
|
||||
mut conn := self.connection()!
|
||||
mut params := {
|
||||
'dst': json.encode(MessageDestination{ pk: pk })
|
||||
'payload': payload
|
||||
mut body := PushMessageBody{
|
||||
dst: MessageDestination{
|
||||
pk: args.public_key
|
||||
ip: ''
|
||||
}
|
||||
payload: base64.encode_str(args.payload)
|
||||
topic: if v := args.topic { base64.encode_str(v) } else { none }
|
||||
}
|
||||
conn.post_json_generic[json.Any](
|
||||
_ := conn.post_json_str(
|
||||
method: .post
|
||||
prefix: 'reply/${id}'
|
||||
params: params
|
||||
prefix: '/api/v1/messages/reply/${args.id}'
|
||||
data: json.encode(body)
|
||||
dataformat: .json
|
||||
)!
|
||||
}
|
||||
|
||||
// curl -v -H 'Content-Type: application/json' -d '{"dst": {"pk": "be4bf135d60b7e43a46be1ad68f955cdc1209a3c55dc30d00c4463b1dace4377"}, "payload": "xuV+"}' http://localhost:8989/api/v1/messages\
|
||||
|
||||
// Get node info
|
||||
pub fn (mut self Mycelium) get_info() !Info {
|
||||
mut conn := self.connection()!
|
||||
return conn.get_json_generic[Info](
|
||||
method: .get
|
||||
prefix: '/api/v1/admin'
|
||||
dataformat: .json
|
||||
)!
|
||||
}
|
||||
|
||||
// Get public key for a node IP
|
||||
pub fn (mut self Mycelium) get_pubkey_from_ip(ip string) !PublicKeyResponse {
|
||||
mut conn := self.connection()!
|
||||
return conn.get_json_generic[PublicKeyResponse](
|
||||
method: .get
|
||||
prefix: '/api/v1/pubkey/${ip}'
|
||||
dataformat: .json
|
||||
)!
|
||||
}
|
||||
|
||||
71
lib/clients/mycelium/mycelium_check.v
Normal file
71
lib/clients/mycelium/mycelium_check.v
Normal file
@@ -0,0 +1,71 @@
|
||||
module mycelium
|
||||
|
||||
import freeflowuniverse.herolib.osal
|
||||
import freeflowuniverse.herolib.core
|
||||
import freeflowuniverse.herolib.installers.lang.rust
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.osal.screen
|
||||
import freeflowuniverse.herolib.ui
|
||||
import freeflowuniverse.herolib.sysadmin.startupmanager
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
|
||||
pub fn check() bool {
|
||||
// if core.is_osx()! {
|
||||
// mut scr := screen.new(reset: false) or {return False}
|
||||
// name := 'mycelium'
|
||||
// if !scr.exists(name) {
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
|
||||
// if !(osal.process_exists_byname('mycelium') or {return False}) {
|
||||
// return false
|
||||
// }
|
||||
|
||||
// TODO: might be dangerous if that one goes out
|
||||
ping_result := osal.ping(address: '40a:152c:b85b:9646:5b71:d03a:eb27:2462', retry: 2) or {
|
||||
return false
|
||||
}
|
||||
if ping_result == .ok {
|
||||
console.print_debug('could reach 40a:152c:b85b:9646:5b71:d03a:eb27:2462')
|
||||
return true
|
||||
}
|
||||
console.print_stderr('could not reach 40a:152c:b85b:9646:5b71:d03a:eb27:2462')
|
||||
return false
|
||||
}
|
||||
|
||||
pub struct MyceliumInspectResult {
|
||||
pub:
|
||||
public_key string @[json: publicKey]
|
||||
address string
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct MyceliumInspectArgs {
|
||||
pub:
|
||||
key_file_path string = '/root/hero/cfg/priv_key.bin'
|
||||
}
|
||||
|
||||
pub fn inspect(args MyceliumInspectArgs) !MyceliumInspectResult {
|
||||
command := 'mycelium inspect --key-file ${args.key_file_path} --json'
|
||||
result := os.execute(command)
|
||||
|
||||
if result.exit_code != 0 {
|
||||
return error('Command failed: ${result.output}')
|
||||
}
|
||||
|
||||
inspect_result := json.decode(MyceliumInspectResult, result.output) or {
|
||||
return error('Failed to parse JSON: ${err}')
|
||||
}
|
||||
|
||||
return inspect_result
|
||||
}
|
||||
|
||||
// if returns empty then probably mycelium is not installed
|
||||
pub fn ipaddr() string {
|
||||
r := inspect() or { MyceliumInspectResult{} }
|
||||
return r.address
|
||||
}
|
||||
@@ -2,8 +2,6 @@ module mycelium
|
||||
|
||||
import freeflowuniverse.herolib.core.base
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.data.encoderhero
|
||||
|
||||
__global (
|
||||
mycelium_global map[string]&Mycelium
|
||||
@@ -12,35 +10,71 @@ __global (
|
||||
|
||||
/////////FACTORY
|
||||
|
||||
// set the model in mem and the config on the filesystem
|
||||
@[params]
|
||||
pub struct ArgsGet {
|
||||
pub mut:
|
||||
name string
|
||||
}
|
||||
|
||||
fn args_get(args_ ArgsGet) ArgsGet {
|
||||
mut args := args_
|
||||
if args.name == '' {
|
||||
args.name = 'default'
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
pub fn get(args_ ArgsGet) !&Mycelium {
|
||||
mut context := base.context()!
|
||||
mut args := args_get(args_)
|
||||
mut obj := Mycelium{}
|
||||
if args.name !in mycelium_global {
|
||||
if !exists(args)! {
|
||||
set(obj)!
|
||||
} else {
|
||||
heroscript := context.hero_config_get('mycelium', args.name)!
|
||||
mut obj_ := heroscript_loads(heroscript)!
|
||||
set_in_mem(obj_)!
|
||||
}
|
||||
}
|
||||
return mycelium_global[args.name] or {
|
||||
println(mycelium_global)
|
||||
// bug if we get here because should be in globals
|
||||
panic('could not get config for mycelium with name, is bug:${args.name}')
|
||||
}
|
||||
}
|
||||
|
||||
// register the config for the future
|
||||
pub fn set(o Mycelium) ! {
|
||||
set_in_mem(o)!
|
||||
mut context := base.context()!
|
||||
heroscript := heroscript_dumps(o)!
|
||||
context.hero_config_set('mycelium', o.name, heroscript)!
|
||||
}
|
||||
|
||||
// does the config exists?
|
||||
pub fn exists(args_ ArgsGet) !bool {
|
||||
mut context := base.context()!
|
||||
mut args := args_get(args_)
|
||||
return context.hero_config_exists('mycelium', args.name)
|
||||
}
|
||||
|
||||
pub fn delete(args_ ArgsGet) ! {
|
||||
mut args := args_get(args_)
|
||||
mut context := base.context()!
|
||||
context.hero_config_delete('mycelium', args.name)!
|
||||
if args.name in mycelium_global {
|
||||
// del mycelium_global[args.name]
|
||||
}
|
||||
}
|
||||
|
||||
// only sets in mem, does not set as config
|
||||
fn set_in_mem(o Mycelium) ! {
|
||||
mut o2 := obj_init(o)!
|
||||
mycelium_global[o.name] = &o2
|
||||
mycelium_default = o.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('mycelium', model.name)
|
||||
}
|
||||
|
||||
// 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('mycelium', model.name)!
|
||||
play(heroscript: heroscript)!
|
||||
}
|
||||
|
||||
// save the config to the filesystem in the context
|
||||
pub fn save(o Mycelium) ! {
|
||||
mut context := base.context()!
|
||||
heroscript := encoderhero.encode[Mycelium](o)!
|
||||
context.hero_config_set('mycelium', model.name, heroscript)!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct PlayArgs {
|
||||
pub mut:
|
||||
@@ -50,21 +84,28 @@ pub mut:
|
||||
}
|
||||
|
||||
pub fn play(args_ PlayArgs) ! {
|
||||
mut model := args_
|
||||
mut args := args_
|
||||
|
||||
if model.heroscript == '' {
|
||||
model.heroscript = heroscript_default()!
|
||||
}
|
||||
mut plbook := model.plbook or { playbook.new(text: model.heroscript)! }
|
||||
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }
|
||||
|
||||
mut configure_actions := plbook.find(filter: 'mycelium.configure')!
|
||||
if configure_actions.len > 0 {
|
||||
for config_action in configure_actions {
|
||||
mut p := config_action.params
|
||||
mycfg := cfg_play(p)!
|
||||
console.print_debug('install action mycelium.configure\n${mycfg}')
|
||||
set(mycfg)!
|
||||
save(mycfg)!
|
||||
mut install_actions := plbook.find(filter: 'mycelium.configure')!
|
||||
if install_actions.len > 0 {
|
||||
for install_action in install_actions {
|
||||
heroscript := install_action.heroscript()
|
||||
mut obj2 := heroscript_loads(heroscript)!
|
||||
set(obj2)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// switch instance to be used for mycelium
|
||||
pub fn switch(name string) {
|
||||
mycelium_default = name
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
@[params]
|
||||
pub struct DefaultConfigArgs {
|
||||
instance string = 'default'
|
||||
}
|
||||
|
||||
@@ -1,39 +1,33 @@
|
||||
module mycelium
|
||||
|
||||
import freeflowuniverse.herolib.data.paramsparser
|
||||
import freeflowuniverse.herolib.core.httpconnection
|
||||
import os
|
||||
import freeflowuniverse.herolib.data.encoderhero
|
||||
|
||||
pub const version = '0.0.0'
|
||||
const singleton = true
|
||||
const default = true
|
||||
|
||||
pub fn heroscript_default() !string {
|
||||
heroscript := "
|
||||
!!mycelium.configure
|
||||
name:'mycelium'
|
||||
"
|
||||
return heroscript
|
||||
}
|
||||
|
||||
@[heap]
|
||||
pub struct Mycelium {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
server_url string
|
||||
conn ?&httpconnection.HTTPConnection
|
||||
server_url string = 'http://localhost:8989'
|
||||
conn ?&httpconnection.HTTPConnection @[skip; str: skip]
|
||||
}
|
||||
|
||||
fn cfg_play(p paramsparser.Params) ! {
|
||||
mut mycfg := Mycelium{
|
||||
name: p.get_default('name', 'default')!
|
||||
server_url: p.get_default('server_url', 'http://localhost:8989/api/v1/messages')!
|
||||
}
|
||||
set(mycfg)!
|
||||
// your checking & initialization code if needed
|
||||
fn obj_init(mycfg_ Mycelium) !Mycelium {
|
||||
mut mycfg := mycfg_
|
||||
return mycfg
|
||||
}
|
||||
|
||||
fn obj_init(obj_ Mycelium) !Mycelium {
|
||||
// never call get here, only thing we can do here is work on object itself
|
||||
mut obj := obj_
|
||||
/////////////NORMALLY NO NEED TO TOUCH
|
||||
|
||||
pub fn heroscript_dumps(obj Mycelium) !string {
|
||||
return encoderhero.encode[Mycelium](obj)!
|
||||
}
|
||||
|
||||
pub fn heroscript_loads(heroscript string) !Mycelium {
|
||||
mut obj := encoderhero.decode[Mycelium](heroscript)!
|
||||
return obj
|
||||
}
|
||||
|
||||
602
lib/clients/mycelium/openapi.yaml
Normal file
602
lib/clients/mycelium/openapi.yaml
Normal file
@@ -0,0 +1,602 @@
|
||||
openapi: 3.0.2
|
||||
info:
|
||||
version: '1.0.0'
|
||||
|
||||
title: Mycelium management
|
||||
contact:
|
||||
url: 'https://github.com/threefoldtech/mycelium'
|
||||
license:
|
||||
name: Apache 2.0
|
||||
url: 'https://github.com/threefoldtech/mycelium/blob/master/LICENSE'
|
||||
|
||||
description: |
|
||||
This is the specification of the **mycelium** management API. It is used to perform admin tasks on the system, and
|
||||
to perform administrative duties.
|
||||
|
||||
externalDocs:
|
||||
description: For full documentation, check out the mycelium github repo.
|
||||
url: 'https://github.com/threefoldtech/mycelium'
|
||||
|
||||
tags:
|
||||
- name: Admin
|
||||
description: Administrative operations
|
||||
- name: Peer
|
||||
description: Operations related to peer management
|
||||
- name: Route
|
||||
description: Operations related to network routes
|
||||
- name: Message
|
||||
description: Operations on the embedded message subsystem
|
||||
|
||||
servers:
|
||||
- url: 'http://localhost:8989'
|
||||
|
||||
paths:
|
||||
'/api/v1/admin':
|
||||
get:
|
||||
tags:
|
||||
- Admin
|
||||
summary: Get general info about the node
|
||||
description: |
|
||||
Get general info about the node, which is not related to other more specific functionality
|
||||
operationId: getInfo
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Info'
|
||||
|
||||
'/api/v1/admin/peers':
|
||||
get:
|
||||
tags:
|
||||
- Admin
|
||||
- Peer
|
||||
summary: List known peers
|
||||
description: |
|
||||
List all peers known in the system, and info about their connection.
|
||||
This includes the endpoint, how we know about the peer, the connection state, and if the connection is alive the amount
|
||||
of bytes we've sent to and received from the peer.
|
||||
operationId: getPeers
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/PeerStats'
|
||||
post:
|
||||
tags:
|
||||
- Admin
|
||||
- Peer
|
||||
summary: Add a new peer
|
||||
description: |
|
||||
Add a new peer identified by the provided endpoint.
|
||||
The peer is added to the list of known peers. It will eventually be connected
|
||||
to by the standard connection loop of the peer manager. This means that a peer
|
||||
which can't be connected to will stay in the system, as it might be reachable
|
||||
later on.
|
||||
operationId: addPeer
|
||||
responses:
|
||||
'204':
|
||||
description: Peer added
|
||||
'400':
|
||||
description: Malformed endpoint
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
description: Details about why the endpoint is not valid
|
||||
'409':
|
||||
description: Peer already exists
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
description: message saying we already know this peer
|
||||
|
||||
'/api/v1/admin/peers/{endpoint}':
|
||||
delete:
|
||||
tags:
|
||||
- Admin
|
||||
- Peer
|
||||
summary: Remove an existing peer
|
||||
description: |
|
||||
Remove an existing peer identified by the provided endpoint.
|
||||
The peer is removed from the list of known peers. If a connection to it
|
||||
is currently active, it will be closed.
|
||||
operationId: deletePeer
|
||||
responses:
|
||||
'204':
|
||||
description: Peer removed
|
||||
'400':
|
||||
description: Malformed endpoint
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
description: Details about why the endpoint is not valid
|
||||
'404':
|
||||
description: Peer doesn't exist
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
description: message saying we don't know this peer
|
||||
|
||||
'/api/v1/admin/routes/selected':
|
||||
get:
|
||||
tags:
|
||||
- Admin
|
||||
- Route
|
||||
summary: List all selected routes
|
||||
description: |
|
||||
List all selected routes in the system, and their next hop identifier, metric and sequence number.
|
||||
It is possible for a route to be selected and have an infinite metric. This route will however not forward packets.
|
||||
operationId: getSelectedRoutes
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Route'
|
||||
|
||||
'/api/v1/admin/routes/fallback':
|
||||
get:
|
||||
tags:
|
||||
- Admin
|
||||
- Route
|
||||
summary: List all active fallback routes
|
||||
description: |
|
||||
List all fallback routes in the system, and their next hop identifier, metric and sequence number.
|
||||
These routes are available to be selected in case the selected route for a destination suddenly fails, or gets retracted.
|
||||
operationId: getSelectedRoutes
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Route'
|
||||
|
||||
'/api/v1/messages':
|
||||
get:
|
||||
tags:
|
||||
- Message
|
||||
summary: Get a message from the inbound message queue
|
||||
description: |
|
||||
Get a message from the inbound message queue. By default, the message is removed from the queue and won't be shown again.
|
||||
If the peek query parameter is set to true, the message will be peeked, and the next call to this endpoint will show the same message.
|
||||
This method returns immediately by default: a message is returned if one is ready, and if there isn't nothing is returned. If the timeout
|
||||
query parameter is set, this call won't return for the given amount of seconds, unless a message is received
|
||||
operationId: popMessage
|
||||
parameters:
|
||||
- in: query
|
||||
name: peek
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
description: Whether to peek the message or not. If this is true, the message won't be removed from the inbound queue when it is read
|
||||
example: true
|
||||
- in: query
|
||||
name: timeout
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 0
|
||||
description: |
|
||||
Amount of seconds to wait for a message to arrive if one is not available. Setting this to 0 is valid and will return
|
||||
a message if present, or return immediately if there isn't
|
||||
example: 60
|
||||
- in: query
|
||||
name: topic
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
format: byte
|
||||
minLength: 0
|
||||
maxLength: 340
|
||||
description: |
|
||||
Optional filter for loading messages. If set, the system checks if the message has the given string at the start. This way
|
||||
a topic can be encoded.
|
||||
example: example.topic
|
||||
responses:
|
||||
'200':
|
||||
description: Message retrieved
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/InboundMessage'
|
||||
'204':
|
||||
description: No message ready
|
||||
post:
|
||||
tags:
|
||||
- Message
|
||||
summary: Submit a new message to the system.
|
||||
description: |
|
||||
Push a new message to the systems outbound message queue. The system will continuously attempt to send the message until
|
||||
it is either fully transmitted, or the send deadline is expired.
|
||||
operationId: pushMessage
|
||||
parameters:
|
||||
- in: query
|
||||
name: reply_timeout
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 0
|
||||
description: |
|
||||
Amount of seconds to wait for a reply to this message to come in. If not set, the system won't wait for a reply and return
|
||||
the ID of the message, which can be used later. If set, the system will wait for at most the given amount of seconds for a reply
|
||||
to come in. If a reply arrives, it is returned to the client. If not, the message ID is returned for later use.
|
||||
example: 120
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PushMessageBody'
|
||||
responses:
|
||||
'200':
|
||||
description: We received a reply within the specified timeout
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/InboundMessage'
|
||||
|
||||
'201':
|
||||
description: Message pushed successfully, and not waiting for a reply
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PushMessageResponseId'
|
||||
'408':
|
||||
description: The system timed out waiting for a reply to the message
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PushMessageResponseId'
|
||||
|
||||
'/api/v1/messsages/reply/{id}':
|
||||
post:
|
||||
tags:
|
||||
- Message
|
||||
summary: Reply to a message with the given ID
|
||||
description: |
|
||||
Submits a reply message to the system, where ID is an id of a previously received message. If the sender is waiting
|
||||
for a reply, it will bypass the queue of open messages.
|
||||
operationId: pushMessageReply
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: hex
|
||||
minLength: 16
|
||||
maxLength: 16
|
||||
example: abcdef0123456789
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PushMessageBody'
|
||||
responses:
|
||||
'204':
|
||||
description: successfully submitted the reply
|
||||
|
||||
'/api/v1/messages/status/{id}':
|
||||
get:
|
||||
tags:
|
||||
- Message
|
||||
summary: Get the status of an outbound message
|
||||
description: |
|
||||
Get information about the current state of an outbound message. This can be used to check the transmission
|
||||
state, size and destination of the message.
|
||||
operationId: getMessageInfo
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: hex
|
||||
minLength: 16
|
||||
maxLength: 16
|
||||
example: abcdef0123456789
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MessageStatusResponse'
|
||||
'404':
|
||||
description: Message not found
|
||||
|
||||
'/api/v1/pubkey/{mycelium_ip}':
|
||||
get:
|
||||
summary: Get the pubkey from node ip
|
||||
description: |
|
||||
Get the node's public key from it's IP address.
|
||||
operationId: getPublicKeyFromIp
|
||||
parameters:
|
||||
- in: path
|
||||
name: mycelium_ip
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: ipv6
|
||||
example: 5fd:7636:b80:9ad0::1
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PublicKeyResponse'
|
||||
'404':
|
||||
description: Public key not found
|
||||
|
||||
|
||||
components:
|
||||
schemas:
|
||||
Info:
|
||||
description: General information about a node
|
||||
type: object
|
||||
properties:
|
||||
nodeSubnet:
|
||||
description: The subnet owned by the node and advertised to peers
|
||||
type: string
|
||||
example: 54f:b680:ba6e:7ced::/64
|
||||
|
||||
Endpoint:
|
||||
description: Identification to connect to a peer
|
||||
type: object
|
||||
properties:
|
||||
proto:
|
||||
description: Protocol used
|
||||
type: string
|
||||
enum:
|
||||
- 'tcp'
|
||||
- 'quic'
|
||||
example: tcp
|
||||
socketAddr:
|
||||
description: The socket address used
|
||||
type: string
|
||||
example: 192.0.2.6:9651
|
||||
|
||||
PeerStats:
|
||||
description: Info about a peer
|
||||
type: object
|
||||
properties:
|
||||
endpoint:
|
||||
$ref: '#/components/schemas/Endpoint'
|
||||
type:
|
||||
description: How we know about this peer
|
||||
type: string
|
||||
enum:
|
||||
- 'static'
|
||||
- 'inbound'
|
||||
- 'linkLocalDiscovery'
|
||||
example: static
|
||||
connectionState:
|
||||
description: The current state of the connection to the peer
|
||||
type: string
|
||||
enum:
|
||||
- 'alive'
|
||||
- 'connecting'
|
||||
- 'dead'
|
||||
example: alive
|
||||
txBytes:
|
||||
description: The amount of bytes transmitted to this peer
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 0
|
||||
example: 464531564
|
||||
rxBytes:
|
||||
description: The amount of bytes received from this peer
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 0
|
||||
example: 64645089
|
||||
|
||||
Route:
|
||||
description: Information about a route
|
||||
type: object
|
||||
properties:
|
||||
subnet:
|
||||
description: The overlay subnet for which this is the route
|
||||
type: string
|
||||
example: 469:1348:ab0c:a1d8::/64
|
||||
nextHop:
|
||||
description: A way to identify the next hop of the route, where forwarded packets will be sent
|
||||
type: string
|
||||
example: TCP 203.0.113.2:60128 <-> 198.51.100.27:9651
|
||||
metric:
|
||||
description: The metric of the route, an estimation of how long the packet will take to arrive at its final destination
|
||||
oneOf:
|
||||
- description: A finite metric value
|
||||
type: integer
|
||||
format: int32
|
||||
minimum: 0
|
||||
maximum: 65534
|
||||
example: 13
|
||||
- description: An infinite (unreachable) metric. This is always `infinite`
|
||||
type: string
|
||||
example: infinite
|
||||
seqno:
|
||||
description: the sequence number advertised with this route by the source
|
||||
type: integer
|
||||
format: int32
|
||||
minimum: 0
|
||||
maximum: 65535
|
||||
example: 1
|
||||
|
||||
InboundMessage:
|
||||
description: A message received by the system
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
description: Id of the message, hex encoded
|
||||
type: string
|
||||
format: hex
|
||||
minLength: 16
|
||||
maxLength: 16
|
||||
example: 0123456789abcdef
|
||||
srcIp:
|
||||
description: Sender overlay IP address
|
||||
type: string
|
||||
format: ipv6
|
||||
example: 449:abcd:0123:defa::1
|
||||
srcPk:
|
||||
description: Sender public key, hex encoded
|
||||
type: string
|
||||
format: hex
|
||||
minLength: 64
|
||||
maxLength: 64
|
||||
example: fedbca9876543210fedbca9876543210fedbca9876543210fedbca9876543210
|
||||
dstIp:
|
||||
description: Receiver overlay IP address
|
||||
type: string
|
||||
format: ipv6
|
||||
example: 34f:b680:ba6e:7ced:355f:346f:d97b:eecb
|
||||
dstPk:
|
||||
description: Receiver public key, hex encoded. This is the public key of the system
|
||||
type: string
|
||||
format: hex
|
||||
minLength: 64
|
||||
maxLength: 64
|
||||
example: 02468ace13579bdf02468ace13579bdf02468ace13579bdf02468ace13579bdf
|
||||
topic:
|
||||
description: An optional message topic
|
||||
type: string
|
||||
format: byte
|
||||
minLength: 0
|
||||
maxLength: 340
|
||||
example: hpV+
|
||||
payload:
|
||||
description: The message payload, encoded in standard alphabet base64
|
||||
type: string
|
||||
format: byte
|
||||
example: xuV+
|
||||
|
||||
PushMessageBody:
|
||||
description: A message to send to a given receiver
|
||||
type: object
|
||||
properties:
|
||||
dst:
|
||||
$ref: '#/components/schemas/MessageDestination'
|
||||
topic:
|
||||
description: An optional message topic
|
||||
type: string
|
||||
format: byte
|
||||
minLength: 0
|
||||
maxLength: 340
|
||||
example: hpV+
|
||||
payload:
|
||||
description: The message to send, base64 encoded
|
||||
type: string
|
||||
format: byte
|
||||
example: xuV+
|
||||
|
||||
MessageDestination:
|
||||
oneOf:
|
||||
- description: An IP in the subnet of the receiver node
|
||||
type: object
|
||||
properties:
|
||||
ip:
|
||||
description: The target IP of the message
|
||||
format: ipv6
|
||||
example: 449:abcd:0123:defa::1
|
||||
- description: The hex encoded public key of the receiver node
|
||||
type: object
|
||||
properties:
|
||||
pk:
|
||||
description: The hex encoded public key of the target node
|
||||
type: string
|
||||
minLength: 64
|
||||
maxLength: 64
|
||||
example: bb39b4a3a4efd70f3e05e37887677e02efbda14681d0acd3882bc0f754792c32
|
||||
|
||||
PushMessageResponseId:
|
||||
description: The ID generated for a message after pushing it to the system
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
description: Id of the message, hex encoded
|
||||
type: string
|
||||
format: hex
|
||||
minLength: 16
|
||||
maxLength: 16
|
||||
example: 0123456789abcdef
|
||||
|
||||
MessageStatusResponse:
|
||||
description: Information about an outbound message
|
||||
type: object
|
||||
properties:
|
||||
dst:
|
||||
description: IP address of the receiving node
|
||||
type: string
|
||||
format: ipv6
|
||||
example: 449:abcd:0123:defa::1
|
||||
state:
|
||||
$ref: '#/components/schemas/TransmissionState'
|
||||
created:
|
||||
description: Unix timestamp of when this message was created
|
||||
type: integer
|
||||
format: int64
|
||||
example: 1649512789
|
||||
deadline:
|
||||
description: Unix timestamp of when this message will expire. If the message is not received before this, the system will give up
|
||||
type: integer
|
||||
format: int64
|
||||
example: 1649513089
|
||||
msgLen:
|
||||
description: Length of the message in bytes
|
||||
type: integer
|
||||
minimum: 0
|
||||
example: 27
|
||||
|
||||
TransmissionState:
|
||||
description: The state of an outbound message in it's lifetime
|
||||
oneOf:
|
||||
- type: string
|
||||
enum: ['pending', 'received', 'read', 'aborted']
|
||||
example: 'received'
|
||||
- type: object
|
||||
properties:
|
||||
sending:
|
||||
type: object
|
||||
properties:
|
||||
pending:
|
||||
type: integer
|
||||
minimum: 0
|
||||
example: 5
|
||||
sent:
|
||||
type: integer
|
||||
minimum: 0
|
||||
example: 17
|
||||
acked:
|
||||
type: integer
|
||||
minimum: 0
|
||||
example: 3
|
||||
example: 'received'
|
||||
|
||||
PublicKeyResponse:
|
||||
description: Public key requested based on a node's IP
|
||||
type: object
|
||||
properties:
|
||||
NodePubKey:
|
||||
type: string
|
||||
format: hex
|
||||
minLength: 64
|
||||
maxLength: 64
|
||||
example: 02468ace13579bdf02468ace13579bdf02468ace13579bdf02468ace13579bdf
|
||||
@@ -1,6 +1,13 @@
|
||||
# Mycelium Client
|
||||
|
||||
A V client library for interacting with the Mycelium messaging system. This client provides functionality for sending, receiving, and managing messages through a Mycelium server.
|
||||
A V client library for interacting with the Mycelium messaging system. This client provides functionality for configuring and inspecting a Mycelium node.
|
||||
|
||||
## Components
|
||||
|
||||
The Mycelium integration consists of two main components:
|
||||
|
||||
1. **Mycelium Client** (this package) - For interacting with a running Mycelium node
|
||||
2. **Mycelium Installer** (in `installers/net/mycelium/`) - For installing and managing Mycelium nodes
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -11,131 +18,101 @@ The client can be configured either through V code or using heroscript.
|
||||
```v
|
||||
import freeflowuniverse.herolib.clients.mycelium
|
||||
|
||||
// Get default client instance
|
||||
mut client := mycelium.get()!
|
||||
|
||||
// By default connects to http://localhost:8989/api/v1/messages
|
||||
// To use a different server:
|
||||
mut client := mycelium.get(name: "custom", server_url: "http://myserver:8989/api/v1/messages")!
|
||||
// Get named client instance
|
||||
mut client := mycelium.get(name: "custom")!
|
||||
```
|
||||
|
||||
### Heroscript Configuration
|
||||
## Core Functions
|
||||
|
||||
```hero
|
||||
!!mycelium.configure
|
||||
name:'custom' # optional, defaults to 'default'
|
||||
server_url:'http://myserver:8989/api/v1/messages' # optional, defaults to localhost:8989
|
||||
```
|
||||
### Inspect Node
|
||||
|
||||
Note: Configuration is not needed if using a locally running Mycelium server with default settings.
|
||||
|
||||
## Example Script
|
||||
|
||||
Save as `mycelium_example.vsh`:
|
||||
Get information about the local Mycelium node:
|
||||
|
||||
```v
|
||||
import freeflowuniverse.herolib.clients.mycelium
|
||||
|
||||
// Get node info including public key and address
|
||||
result := mycelium.inspect()!
|
||||
println('Public Key: ${result.public_key}')
|
||||
println('Address: ${result.address}')
|
||||
|
||||
// Get just the IP address
|
||||
addr := mycelium.ipaddr()
|
||||
println('IP Address: ${addr}')
|
||||
```
|
||||
|
||||
### Check Node Status
|
||||
|
||||
Check if the Mycelium node is running and reachable:
|
||||
|
||||
```v
|
||||
import freeflowuniverse.herolib.clients.mycelium
|
||||
|
||||
is_running := mycelium.check()
|
||||
if is_running {
|
||||
println('Mycelium node is running and reachable')
|
||||
} else {
|
||||
println('Mycelium node is not running or unreachable')
|
||||
}
|
||||
```
|
||||
|
||||
### Sending and Receiving Messages
|
||||
|
||||
The client provides several functions for sending and receiving messages between nodes:
|
||||
|
||||
```v
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.clients.mycelium
|
||||
|
||||
// Initialize client
|
||||
mut client := mycelium.get()!
|
||||
|
||||
// Send a message and wait for reply
|
||||
// Send a message to a node by public key
|
||||
// Parameters: public_key, payload, topic, wait_for_reply
|
||||
msg := client.send_msg(
|
||||
pk: "recipient_public_key"
|
||||
payload: "Hello!"
|
||||
wait: true // wait for reply (timeout 120s)
|
||||
'abc123...', // destination public key
|
||||
'Hello World', // message payload
|
||||
'greetings', // optional topic
|
||||
true // wait for reply
|
||||
)!
|
||||
println('Sent message ID: ${msg.id}')
|
||||
|
||||
// Receive messages
|
||||
// Parameters: wait_for_message, peek_only, topic_filter
|
||||
received := client.receive_msg(true, false, 'greetings')!
|
||||
println('Received message from: ${received.src_pk}')
|
||||
println('Message payload: ${received.payload}')
|
||||
|
||||
// Reply to a message
|
||||
client.reply_msg(
|
||||
received.id, // original message ID
|
||||
received.src_pk, // sender's public key
|
||||
'Got your message!', // reply payload
|
||||
'greetings' // topic
|
||||
)!
|
||||
println('Message sent with ID: ${msg.id}')
|
||||
|
||||
// Check message status
|
||||
status := client.get_msg_status(msg.id)!
|
||||
println('Message status: ${status.state}')
|
||||
|
||||
// Receive messages with timeout
|
||||
if incoming := client.receive_msg_opt(wait: true) {
|
||||
println('Received message: ${incoming.payload}')
|
||||
println('From: ${incoming.src_pk}')
|
||||
|
||||
// Reply to the message
|
||||
client.reply_msg(
|
||||
id: incoming.id
|
||||
pk: incoming.src_pk
|
||||
payload: "Got your message!"
|
||||
)!
|
||||
}
|
||||
println('Created at: ${status.created}')
|
||||
println('Expires at: ${status.deadline}')
|
||||
```
|
||||
|
||||
## API Reference
|
||||
The messaging API supports:
|
||||
- Sending messages to nodes identified by public key
|
||||
- Optional message topics for filtering
|
||||
- Waiting for replies when sending messages
|
||||
- Peeking at messages without removing them from the queue
|
||||
- Tracking message delivery status
|
||||
- Base64 encoded message payloads for binary data
|
||||
|
||||
### Sending Messages
|
||||
## Installation and Management
|
||||
|
||||
```v
|
||||
// Send a message to a specific public key
|
||||
// wait=true means wait for reply (timeout 120s)
|
||||
msg := client.send_msg(pk: "recipient_public_key", payload: "Hello!", wait: true)!
|
||||
For installing and managing Mycelium nodes, use the Mycelium Installer package located in `installers/net/mycelium/`. The installer provides functionality for:
|
||||
|
||||
// Get status of a sent message
|
||||
status := client.get_msg_status(id: "message_id")!
|
||||
```
|
||||
|
||||
### Receiving Messages
|
||||
|
||||
```v
|
||||
// Receive a message (non-blocking)
|
||||
msg := client.receive_msg(wait: false)!
|
||||
|
||||
// Receive a message with timeout (blocking for 60s)
|
||||
msg := client.receive_msg(wait: true)!
|
||||
|
||||
// Receive a message (returns none if no message available)
|
||||
if msg := client.receive_msg_opt(wait: false) {
|
||||
println('Received: ${msg.payload}')
|
||||
}
|
||||
```
|
||||
|
||||
### Replying to Messages
|
||||
|
||||
```v
|
||||
// Reply to a specific message
|
||||
client.reply_msg(
|
||||
id: "original_message_id",
|
||||
pk: "sender_public_key",
|
||||
payload: "Reply message"
|
||||
)!
|
||||
```
|
||||
|
||||
## Message Types
|
||||
|
||||
### InboundMessage
|
||||
```v
|
||||
struct InboundMessage {
|
||||
id string
|
||||
src_ip string
|
||||
src_pk string
|
||||
dst_ip string
|
||||
dst_pk string
|
||||
payload string
|
||||
}
|
||||
```
|
||||
|
||||
### MessageStatusResponse
|
||||
```v
|
||||
struct MessageStatusResponse {
|
||||
id string
|
||||
dst string
|
||||
state string
|
||||
created string
|
||||
deadline string
|
||||
msg_len string
|
||||
}
|
||||
```
|
||||
|
||||
## Heroscript Complete Example
|
||||
|
||||
```hero
|
||||
!!mycelium.configure
|
||||
name:'mycelium'
|
||||
server_url:'http://localhost:8989/api/v1/messages'
|
||||
|
||||
# More heroscript commands can be added here as the API expands
|
||||
- Installing Mycelium nodes
|
||||
- Starting/stopping nodes
|
||||
- Managing node configuration
|
||||
- Setting up TUN interfaces
|
||||
- Configuring peer connections
|
||||
|
||||
@@ -152,11 +152,16 @@ fn build_() ! {
|
||||
// if core.platform()!= .ubuntu {
|
||||
// return error('only support ubuntu for now')
|
||||
// }
|
||||
// golang.install()!
|
||||
|
||||
//mut g:=golang.get()!
|
||||
//g.install()!
|
||||
|
||||
//console.print_header('build coredns')
|
||||
|
||||
//mut gs := gittools.new(coderoot: '~/code')!
|
||||
// console.print_header('build ${model.name}')
|
||||
|
||||
// gitpath := gittools.get_repo(coderoot: '/tmp/builder', url: url, reset: true, pull: true)!
|
||||
// gitpath := gittools.get_repo(url: url, reset: true, pull: true)!
|
||||
|
||||
// cmd := '
|
||||
// cd ??{gitpath}
|
||||
|
||||
@@ -144,7 +144,6 @@ pub fn (mut self Context) hero_config_delete(cat string, name string) ! {
|
||||
config_file.delete()!
|
||||
}
|
||||
|
||||
|
||||
pub fn (mut self Context) hero_config_exists(cat string, name string) bool {
|
||||
path := '${os.home_dir()}/hero/context/${self.config.name}/${cat}__${name}.yaml'
|
||||
return os.exists(path)
|
||||
|
||||
@@ -115,11 +115,11 @@ fn install() ! {
|
||||
console.print_header('install ${args.name}')
|
||||
//THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
|
||||
// mut url := ''
|
||||
// if osal.is_linux_arm() {
|
||||
// if core.is_linux_arm() {
|
||||
// url = 'https://github.com/${args.name}-dev/${args.name}/releases/download/v??{version}/${args.name}_??{version}_linux_arm64.tar.gz'
|
||||
// } else if osal.is_linux_intel() {
|
||||
// } else if core.is_linux_intel() {
|
||||
// url = 'https://github.com/${args.name}-dev/${args.name}/releases/download/v??{version}/${args.name}_??{version}_linux_amd64.tar.gz'
|
||||
// } else if osal.is_osx_arm() {
|
||||
// } else if core.is_osx_arm() {
|
||||
// url = 'https://github.com/${args.name}-dev/${args.name}/releases/download/v??{version}/${args.name}_??{version}_darwin_arm64.tar.gz'
|
||||
// } else if osal.is_osx_intel() {
|
||||
// url = 'https://github.com/${args.name}-dev/${args.name}/releases/download/v??{version}/${args.name}_??{version}_darwin_amd64.tar.gz'
|
||||
|
||||
@@ -42,8 +42,8 @@ pub fn get(args_ ArgsGet) !&${args.classname} {
|
||||
set(obj)!
|
||||
}else{
|
||||
heroscript := context.hero_config_get("${args.name}",args.name)!
|
||||
mut obj:=heroscript_loads(heroscript)!
|
||||
set_in_mem(obj)!
|
||||
mut obj_:=heroscript_loads(heroscript)!
|
||||
set_in_mem(obj_)!
|
||||
}
|
||||
}
|
||||
return ${args.name}_global[args.name] or {
|
||||
@@ -58,14 +58,14 @@ pub fn set(o ${args.classname})! {
|
||||
set_in_mem(o)!
|
||||
mut context := base.context()!
|
||||
heroscript := heroscript_dumps(o)!
|
||||
context.hero_config_set("gitea", o.name, heroscript)!
|
||||
context.hero_config_set("${args.name}", o.name, heroscript)!
|
||||
}
|
||||
|
||||
//does the config exists?
|
||||
pub fn exists(args_ ArgsGet)! {
|
||||
pub fn exists(args_ ArgsGet)! bool {
|
||||
mut context := base.context()!
|
||||
mut args := args_get(args_)
|
||||
return context.hero_config_exists("gitea", args.name)
|
||||
return context.hero_config_exists("${args.name}", args.name)
|
||||
}
|
||||
|
||||
pub fn delete(args_ ArgsGet)! {
|
||||
|
||||
@@ -29,19 +29,37 @@ pub fn cmd_docusaurus(mut cmdroot Command) {
|
||||
description: 'Url where docusaurus source is.'
|
||||
})
|
||||
|
||||
cmd_run.add_flag(Flag{
|
||||
flag: .string
|
||||
required: false
|
||||
name: 'deploykey'
|
||||
abbrev: 'dk'
|
||||
// default: ''
|
||||
description: 'Path of SSH Key used to deploy.'
|
||||
})
|
||||
|
||||
cmd_run.add_flag(Flag{
|
||||
flag: .string
|
||||
required: false
|
||||
name: 'publish'
|
||||
// default: ''
|
||||
description: 'Path where to publish.'
|
||||
})
|
||||
|
||||
|
||||
cmd_run.add_flag(Flag{
|
||||
flag: .bool
|
||||
required: false
|
||||
name: 'build'
|
||||
abbrev: 'b'
|
||||
name: 'buildpublish'
|
||||
abbrev: 'bp'
|
||||
description: 'build and publish.'
|
||||
})
|
||||
|
||||
cmd_run.add_flag(Flag{
|
||||
flag: .bool
|
||||
required: false
|
||||
name: 'builddev'
|
||||
abbrev: 'bd'
|
||||
name: 'builddevpublish'
|
||||
abbrev: 'bpd'
|
||||
description: 'build dev version and publish.'
|
||||
})
|
||||
|
||||
@@ -49,11 +67,9 @@ pub fn cmd_docusaurus(mut cmdroot Command) {
|
||||
flag: .bool
|
||||
required: false
|
||||
name: 'update'
|
||||
abbrev: 'p'
|
||||
description: 'update your environment the template and the repo you are working on (git pull).'
|
||||
})
|
||||
|
||||
|
||||
cmd_run.add_flag(Flag{
|
||||
flag: .bool
|
||||
required: false
|
||||
@@ -68,6 +84,8 @@ pub fn cmd_docusaurus(mut cmdroot Command) {
|
||||
fn cmd_docusaurus_execute(cmd Command) ! {
|
||||
mut update := cmd.flags.get_bool('update') or { false }
|
||||
mut url := cmd.flags.get_string('url') or { '' }
|
||||
mut publish_path := cmd.flags.get_string('publish') or { '' }
|
||||
mut deploykey := cmd.flags.get_string('deploykey') or { '' }
|
||||
|
||||
// mut path := cmd.flags.get_string('path') or { '' }
|
||||
// if path == '' {
|
||||
@@ -75,38 +93,51 @@ fn cmd_docusaurus_execute(cmd Command) ! {
|
||||
// }
|
||||
// path = path.replace('~', os.home_dir())
|
||||
|
||||
mut build := cmd.flags.get_bool('build') or { false }
|
||||
mut builddev := cmd.flags.get_bool('builddev') or { false }
|
||||
mut buildpublish := cmd.flags.get_bool('buildpublish') or { false }
|
||||
mut builddevpublish := cmd.flags.get_bool('builddevpublish') or { false }
|
||||
mut dev := cmd.flags.get_bool('dev') or { false }
|
||||
|
||||
// if build== false && build== false && build== false {
|
||||
// eprintln("specify build, builddev or dev")
|
||||
// exit(1)
|
||||
// }
|
||||
|
||||
mut docs := docusaurus.new(update: update)!
|
||||
|
||||
mut docs := docusaurus.new(update:update)!
|
||||
|
||||
if build {
|
||||
// Create a new docusaurus site
|
||||
if publish_path.len>0 {
|
||||
_ := docs.build(
|
||||
url: url
|
||||
update:update
|
||||
url: url
|
||||
update: update
|
||||
publish_path: publish_path
|
||||
deploykey:deploykey
|
||||
)!
|
||||
}
|
||||
|
||||
if builddev {
|
||||
|
||||
if buildpublish {
|
||||
// Create a new docusaurus site
|
||||
_ := docs.build_dev(
|
||||
url: url
|
||||
update:update
|
||||
_ := docs.build_publish(
|
||||
url: url
|
||||
update: update
|
||||
deploykey:deploykey
|
||||
)!
|
||||
}
|
||||
|
||||
if builddevpublish {
|
||||
// Create a new docusaurus site
|
||||
_ := docs.build_dev_publish(
|
||||
url: url
|
||||
update: update
|
||||
deploykey:deploykey
|
||||
)!
|
||||
}
|
||||
|
||||
if dev {
|
||||
// Create a new docusaurus site
|
||||
_ := docs.dev(
|
||||
url: url
|
||||
update:update
|
||||
url: url
|
||||
update: update
|
||||
deploykey:deploykey
|
||||
)!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +189,7 @@ pub fn (mut h HTTPConnection) get(req_ Request) !string {
|
||||
req.debug = true
|
||||
req.method = .get
|
||||
result := h.send(req)!
|
||||
println(result)
|
||||
return result.data
|
||||
}
|
||||
|
||||
|
||||
@@ -38,23 +38,33 @@ fn decode_struct[T](_ T, data string) !T {
|
||||
|
||||
// return t_
|
||||
$for field in T.fields {
|
||||
// $if fiel
|
||||
$if field.is_struct {
|
||||
$if field.typ !is time.Time {
|
||||
if !field.name[0].is_capital() {
|
||||
// skip embedded ones
|
||||
// Check if field has skip attribute
|
||||
mut should_skip := false
|
||||
|
||||
for attr in field.attrs {
|
||||
if attr.contains('skip') {
|
||||
should_skip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !should_skip {
|
||||
$if field.is_struct {
|
||||
$if field.typ !is time.Time {
|
||||
if !field.name[0].is_capital() {
|
||||
// skip embedded ones
|
||||
mut data_fmt := data.replace(action_str, '')
|
||||
data_fmt = data.replace('define.${obj_name}', 'define')
|
||||
typ.$(field.name) = decode_struct(typ.$(field.name), data_fmt)!
|
||||
}
|
||||
}
|
||||
} $else $if field.is_array {
|
||||
if is_struct_array(typ.$(field.name))! {
|
||||
mut data_fmt := data.replace(action_str, '')
|
||||
data_fmt = data.replace('define.${obj_name}', 'define')
|
||||
typ.$(field.name) = decode_struct(typ.$(field.name), data_fmt)!
|
||||
arr := decode_array(typ.$(field.name), data_fmt)!
|
||||
typ.$(field.name) = arr
|
||||
}
|
||||
}
|
||||
} $else $if field.is_array {
|
||||
if is_struct_array(typ.$(field.name))! {
|
||||
mut data_fmt := data.replace(action_str, '')
|
||||
data_fmt = data.replace('define.${obj_name}', 'define')
|
||||
arr := decode_array(typ.$(field.name), data_fmt)!
|
||||
typ.$(field.name) = arr
|
||||
}
|
||||
}
|
||||
}
|
||||
} $else {
|
||||
|
||||
@@ -28,6 +28,7 @@ pub fn encode[T](val T) !string {
|
||||
$if T is $struct {
|
||||
e.encode_struct[T](val)!
|
||||
} $else $if T is $array {
|
||||
// TODO: need to make comma separated list only works if int,u8,u16,i8... or string if string put all elements in \''...\'',...
|
||||
e.add_child_list[T](val, 'TODO')
|
||||
} $else {
|
||||
return error('can only add elements for struct or array of structs. \n${val}')
|
||||
@@ -128,18 +129,30 @@ pub fn (mut e Encoder) encode_struct[T](t T) ! {
|
||||
|
||||
// encode children structs and array of structs
|
||||
$for field in T.fields {
|
||||
val := t.$(field.name)
|
||||
// time is encoded in the above params encoding step so skip and dont treat as recursive struct
|
||||
$if val is time.Time || val is ourtime.OurTime {
|
||||
} $else $if val is $struct {
|
||||
if field.name[0].is_capital() {
|
||||
embedded_params := paramsparser.encode(val, recursive: false)!
|
||||
e.params.params << embedded_params.params
|
||||
} else {
|
||||
e.add(val)!
|
||||
// Check if field has skip attribute
|
||||
mut should_skip := false
|
||||
|
||||
for attr in field.attrs {
|
||||
if attr.contains('skip') {
|
||||
should_skip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !should_skip {
|
||||
val := t.$(field.name)
|
||||
// time is encoded in the above params encoding step so skip and dont treat as recursive struct
|
||||
$if val is time.Time || val is ourtime.OurTime {
|
||||
} $else $if val is $struct {
|
||||
if field.name[0].is_capital() {
|
||||
embedded_params := paramsparser.encode(val, recursive: false)!
|
||||
e.params.params << embedded_params.params
|
||||
} else {
|
||||
e.add(val)!
|
||||
}
|
||||
} $else $if val is $array {
|
||||
e.encode_array(val)!
|
||||
}
|
||||
} $else $if val is $array {
|
||||
e.encode_array(val)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
42
lib/data/encoderhero/encoder_ignorepropery_test.v
Normal file
42
lib/data/encoderhero/encoder_ignorepropery_test.v
Normal file
@@ -0,0 +1,42 @@
|
||||
module encoderhero
|
||||
|
||||
import freeflowuniverse.herolib.data.paramsparser
|
||||
import time
|
||||
import v.reflection
|
||||
|
||||
struct MyStruct {
|
||||
id int
|
||||
name string
|
||||
// skip attributes would be best way how to do the encoding but can't get it to work
|
||||
other ?&Remark @[skip; str: skip]
|
||||
}
|
||||
|
||||
// is the one we should skip
|
||||
struct Remark {
|
||||
id int
|
||||
}
|
||||
|
||||
fn test_encode() ! {
|
||||
mut o := MyStruct{
|
||||
id: 1
|
||||
name: 'test'
|
||||
other: &Remark{
|
||||
id: 123
|
||||
}
|
||||
}
|
||||
|
||||
script := encode[MyStruct](o)!
|
||||
|
||||
assert script.trim_space() == '!!define.my_struct id:1 name:test'
|
||||
|
||||
println(script)
|
||||
|
||||
o2 := decode[MyStruct](script)!
|
||||
|
||||
assert o2 == MyStruct{
|
||||
id: 1
|
||||
name: 'test'
|
||||
}
|
||||
|
||||
println(o2)
|
||||
}
|
||||
@@ -10,16 +10,16 @@ import freeflowuniverse.herolib.clients.postgresql_client
|
||||
// LocationDB handles all database operations for locations
|
||||
pub struct LocationDB {
|
||||
pub mut:
|
||||
db pg.DB
|
||||
db pg.DB
|
||||
db_client postgresql_client.PostgresClient
|
||||
tmp_dir pathlib.Path
|
||||
db_dir pathlib.Path
|
||||
tmp_dir pathlib.Path
|
||||
db_dir pathlib.Path
|
||||
}
|
||||
|
||||
// new_location_db creates a new LocationDB instance
|
||||
pub fn new_location_db(mut db_client postgresql_client.PostgresClient, reset bool) !LocationDB {
|
||||
mut db_dir := pathlib.get_dir(path:'${os.home_dir()}/hero/var/db/location.db',create: true)!
|
||||
|
||||
mut db_dir := pathlib.get_dir(path: '${os.home_dir()}/hero/var/db/location.db', create: true)!
|
||||
|
||||
// Create locations database if it doesn't exist
|
||||
if !db_client.db_exists('locations')! {
|
||||
db_client.db_create('locations')!
|
||||
@@ -27,15 +27,15 @@ pub fn new_location_db(mut db_client postgresql_client.PostgresClient, reset boo
|
||||
|
||||
// Switch to locations database
|
||||
db_client.dbname = 'locations'
|
||||
|
||||
|
||||
// Get the underlying pg.DB connection
|
||||
db := db_client.db()!
|
||||
|
||||
|
||||
mut loc_db := LocationDB{
|
||||
db: db
|
||||
db: db
|
||||
db_client: db_client
|
||||
tmp_dir: pathlib.get_dir(path: '/tmp/location/',create: true)!
|
||||
db_dir: db_dir
|
||||
tmp_dir: pathlib.get_dir(path: '/tmp/location/', create: true)!
|
||||
db_dir: db_dir
|
||||
}
|
||||
loc_db.init_tables(reset)!
|
||||
return loc_db
|
||||
@@ -50,7 +50,7 @@ fn (mut l LocationDB) init_tables(reset bool) ! {
|
||||
drop table Country
|
||||
}!
|
||||
}
|
||||
|
||||
|
||||
sql l.db {
|
||||
create table Country
|
||||
create table City
|
||||
|
||||
@@ -5,7 +5,7 @@ import freeflowuniverse.herolib.clients.postgresql_client
|
||||
// Location represents the main API for location operations
|
||||
pub struct Location {
|
||||
mut:
|
||||
db LocationDB
|
||||
db LocationDB
|
||||
db_client postgresql_client.PostgresClient
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ mut:
|
||||
pub fn new(mut db_client postgresql_client.PostgresClient, reset bool) !Location {
|
||||
db := new_location_db(mut db_client, reset)!
|
||||
return Location{
|
||||
db: db
|
||||
db: db
|
||||
db_client: db_client
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ pub fn (mut l Location) download_and_import(redownload bool) ! {
|
||||
fn main() ! {
|
||||
// Configure and get PostgreSQL client
|
||||
heroscript := "
|
||||
!!postgresql_client.configure
|
||||
!!postgresql_client.configure
|
||||
name:'test'
|
||||
user: 'postgres'
|
||||
port: 5432
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
module location
|
||||
|
||||
//https://www.geonames.org/export/codes.html
|
||||
|
||||
// https://www.geonames.org/export/codes.html
|
||||
|
||||
@@ -7,27 +7,24 @@ import freeflowuniverse.herolib.osal
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
|
||||
const (
|
||||
geonames_url = 'https://download.geonames.org/export/dump'
|
||||
)
|
||||
const geonames_url = 'https://download.geonames.org/export/dump'
|
||||
|
||||
// download_and_import_data downloads and imports GeoNames data
|
||||
pub fn (mut l LocationDB) download_and_import_data(redownload bool) ! {
|
||||
// Download country info
|
||||
|
||||
if redownload{
|
||||
if redownload {
|
||||
l.reset_import_dates()!
|
||||
}
|
||||
|
||||
country_file := osal.download(
|
||||
url: '${geonames_url}/countryInfo.txt'
|
||||
dest: '${l.tmp_dir.path}/country.txt'
|
||||
url: '${geonames_url}/countryInfo.txt'
|
||||
dest: '${l.tmp_dir.path}/country.txt'
|
||||
minsize_kb: 10
|
||||
)!
|
||||
l.import_country_data(country_file.path)!
|
||||
|
||||
l.import_cities()!
|
||||
|
||||
}
|
||||
|
||||
// reset_import_dates sets all country import_dates to 0
|
||||
@@ -41,9 +38,9 @@ pub fn (mut l LocationDB) reset_import_dates() ! {
|
||||
// should_import_cities checks if a city should be imported based on its last import date on country level
|
||||
fn (mut l LocationDB) should_import_cities(iso2 string) !bool {
|
||||
console.print_debug('Checking if should import country: ${iso2}')
|
||||
|
||||
|
||||
country := sql l.db {
|
||||
select from Country where iso2 == "${iso2}" limit 1
|
||||
select from Country where iso2 == '${iso2}' limit 1
|
||||
} or { []Country{} }
|
||||
|
||||
console.print_debug('SQL query result: ${country.len} records found')
|
||||
@@ -58,11 +55,11 @@ fn (mut l LocationDB) should_import_cities(iso2 string) !bool {
|
||||
one_month := i64(30 * 24 * 60 * 60) // 30 days in seconds
|
||||
last_import := country[0].import_date
|
||||
time_since_import := now - last_import
|
||||
|
||||
console.print_debug('Last import: ${last_import}, Time since import: ${time_since_import} seconds (${time_since_import/86400} days)')
|
||||
should_import := (time_since_import > one_month) || (last_import == 0)
|
||||
|
||||
console.print_debug('Last import: ${last_import}, Time since import: ${time_since_import} seconds (${time_since_import / 86400} days)')
|
||||
should_import := time_since_import > one_month || last_import == 0
|
||||
console.print_debug('Should import ${iso2}: ${should_import}')
|
||||
|
||||
|
||||
return should_import
|
||||
}
|
||||
|
||||
@@ -70,10 +67,10 @@ fn (mut l LocationDB) should_import_cities(iso2 string) !bool {
|
||||
fn (mut l LocationDB) import_country_data(filepath string) ! {
|
||||
console.print_header('Starting import from: ${filepath}')
|
||||
l.db.exec('BEGIN TRANSACTION')!
|
||||
|
||||
mut file := os.open(filepath) or {
|
||||
|
||||
mut file := os.open(filepath) or {
|
||||
console.print_stderr('Failed to open country file: ${err}')
|
||||
return err
|
||||
return err
|
||||
}
|
||||
defer { file.close() }
|
||||
|
||||
@@ -98,39 +95,34 @@ fn (mut l LocationDB) import_country_data(filepath string) ! {
|
||||
} or { []Country{} }
|
||||
|
||||
country := Country{
|
||||
iso2: iso2
|
||||
iso3: fields[1]
|
||||
name: fields[4]
|
||||
continent: fields[8]
|
||||
iso2: iso2
|
||||
iso3: fields[1]
|
||||
name: fields[4]
|
||||
continent: fields[8]
|
||||
population: fields[7].i64()
|
||||
timezone: fields[17]
|
||||
timezone: fields[17]
|
||||
}
|
||||
|
||||
|
||||
if existing_country.len > 0 {
|
||||
// Update existing country
|
||||
sql l.db {
|
||||
update Country set
|
||||
iso3 = country.iso3,
|
||||
name = country.name,
|
||||
continent = country.continent,
|
||||
population = country.population,
|
||||
timezone = country.timezone
|
||||
where iso2 == iso2
|
||||
update Country set iso3 = country.iso3, name = country.name, continent = country.continent,
|
||||
population = country.population, timezone = country.timezone where iso2 == iso2
|
||||
}!
|
||||
//console.print_debug("Updated country: ${country}")
|
||||
// console.print_debug("Updated country: ${country}")
|
||||
} else {
|
||||
// Insert new country
|
||||
sql l.db {
|
||||
insert country into Country
|
||||
}!
|
||||
//console.print_debug("Inserted country: ${country}")
|
||||
// console.print_debug("Inserted country: ${country}")
|
||||
}
|
||||
count++
|
||||
if count % 10 == 0 {
|
||||
console.print_header('Processed ${count} countries')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
l.db.exec('COMMIT')!
|
||||
console.print_header('Finished importing countries. Total records: ${count}')
|
||||
}
|
||||
@@ -158,13 +150,13 @@ fn (mut l LocationDB) import_cities() ! {
|
||||
|
||||
// Download and process cities for this country
|
||||
cities_file := osal.download(
|
||||
url: '${geonames_url}/${iso2}.zip'
|
||||
dest: '${l.tmp_dir.path}/${iso2}.zip'
|
||||
url: '${geonames_url}/${iso2}.zip'
|
||||
dest: '${l.tmp_dir.path}/${iso2}.zip'
|
||||
expand_file: '${l.tmp_dir.path}/${iso2}'
|
||||
minsize_kb: 2
|
||||
minsize_kb: 2
|
||||
)!
|
||||
|
||||
l.import_city_data("${l.tmp_dir.path}/${iso2}/${iso2}.txt")!
|
||||
l.import_city_data('${l.tmp_dir.path}/${iso2}/${iso2}.txt')!
|
||||
|
||||
// Update the country's import date after successful city import
|
||||
now := time.now().unix()
|
||||
@@ -193,36 +185,34 @@ fn (mut l LocationDB) import_city_data(filepath string) ! {
|
||||
// country code : ISO-3166 2-letter country code, 2 characters
|
||||
// cc2 : alternate country codes, comma separated, ISO-3166 2-letter country code, 200 characters
|
||||
// admin1 code : fipscode (subject to change to iso code), see exceptions below, see file admin1Codes.txt for display names of this code; varchar(20)
|
||||
// admin2 code : code for the second administrative division, a county in the US, see file admin2Codes.txt; varchar(80)
|
||||
// admin2 code : code for the second administrative division, a county in the US, see file admin2Codes.txt; varchar(80)
|
||||
// admin3 code : code for third level administrative division, varchar(20)
|
||||
// admin4 code : code for fourth level administrative division, varchar(20)
|
||||
// population : bigint (8 byte int)
|
||||
// population : bigint (8 byte int)
|
||||
// elevation : in meters, integer
|
||||
// dem : digital elevation model, srtm3 or gtopo30, average elevation of 3''x3'' (ca 90mx90m) or 30''x30'' (ca 900mx900m) area in meters, integer. srtm processed by cgiar/ciat.
|
||||
// timezone : the iana timezone id (see file timeZone.txt) varchar(40)
|
||||
// modification date : date of last modification in yyyy-MM-dd format
|
||||
|
||||
|
||||
|
||||
l.db.exec('BEGIN TRANSACTION')!
|
||||
|
||||
mut file := os.open(filepath) or {
|
||||
mut file := os.open(filepath) or {
|
||||
console.print_stderr('Failed to open city file: ${err}')
|
||||
return err
|
||||
return err
|
||||
}
|
||||
defer { file.close() }
|
||||
|
||||
mut reader := io.new_buffered_reader(reader:file)
|
||||
mut reader := io.new_buffered_reader(reader: file)
|
||||
defer { reader.free() }
|
||||
|
||||
mut count := 0
|
||||
console.print_header('Start import ${filepath}')
|
||||
for {
|
||||
line := reader.read_line() or {
|
||||
//console.print_debug('End of file reached')
|
||||
break
|
||||
line := reader.read_line() or {
|
||||
// console.print_debug('End of file reached')
|
||||
break
|
||||
}
|
||||
//console.print_debug(line)
|
||||
// console.print_debug(line)
|
||||
fields := line.split('\t')
|
||||
if fields.len < 12 { // Need at least 12 fields for required data
|
||||
console.print_stderr('fields < 12: ${line}')
|
||||
@@ -234,65 +224,52 @@ fn (mut l LocationDB) import_city_data(filepath string) ! {
|
||||
name := fields[1]
|
||||
ascii_name := texttools.name_fix(fields[2])
|
||||
country_iso2 := fields[8].to_upper()
|
||||
|
||||
|
||||
// Check if city exists
|
||||
existing_city := sql l.db {
|
||||
select from City where id == geoname_id
|
||||
} or { []City{} }
|
||||
|
||||
city := City{
|
||||
id: geoname_id
|
||||
name: name
|
||||
ascii_name: ascii_name
|
||||
country_iso2: country_iso2
|
||||
postal_code: '' // Not provided in this format
|
||||
state_name: '' // Will need separate admin codes file
|
||||
state_code: fields[10]
|
||||
county_name: ''
|
||||
county_code: fields[11]
|
||||
community_name: ''
|
||||
community_code: ''
|
||||
latitude: fields[4].f64()
|
||||
longitude: fields[5].f64()
|
||||
accuracy: 4 // Using geonameid, so accuracy is 4
|
||||
population: fields[14].i64()
|
||||
timezone: fields[17]
|
||||
feature_class: fields[6]
|
||||
feature_code: fields[7]
|
||||
id: geoname_id
|
||||
name: name
|
||||
ascii_name: ascii_name
|
||||
country_iso2: country_iso2
|
||||
postal_code: '' // Not provided in this format
|
||||
state_name: '' // Will need separate admin codes file
|
||||
state_code: fields[10]
|
||||
county_name: ''
|
||||
county_code: fields[11]
|
||||
community_name: ''
|
||||
community_code: ''
|
||||
latitude: fields[4].f64()
|
||||
longitude: fields[5].f64()
|
||||
accuracy: 4 // Using geonameid, so accuracy is 4
|
||||
population: fields[14].i64()
|
||||
timezone: fields[17]
|
||||
feature_class: fields[6]
|
||||
feature_code: fields[7]
|
||||
search_priority: 0 // Default priority
|
||||
}
|
||||
|
||||
if existing_city.len > 0 {
|
||||
// Update existing city
|
||||
sql l.db {
|
||||
update City set
|
||||
name = city.name,
|
||||
ascii_name = city.ascii_name,
|
||||
country_iso2 = city.country_iso2,
|
||||
postal_code = city.postal_code,
|
||||
state_name = city.state_name,
|
||||
state_code = city.state_code,
|
||||
county_name = city.county_name,
|
||||
county_code = city.county_code,
|
||||
community_name = city.community_name,
|
||||
community_code = city.community_code,
|
||||
latitude = city.latitude,
|
||||
longitude = city.longitude,
|
||||
accuracy = city.accuracy,
|
||||
population = city.population,
|
||||
timezone = city.timezone,
|
||||
feature_class = city.feature_class,
|
||||
feature_code = city.feature_code,
|
||||
search_priority = city.search_priority
|
||||
where id == geoname_id
|
||||
update City set name = city.name, ascii_name = city.ascii_name, country_iso2 = city.country_iso2,
|
||||
postal_code = city.postal_code, state_name = city.state_name, state_code = city.state_code,
|
||||
county_name = city.county_name, county_code = city.county_code, community_name = city.community_name,
|
||||
community_code = city.community_code, latitude = city.latitude, longitude = city.longitude,
|
||||
accuracy = city.accuracy, population = city.population, timezone = city.timezone,
|
||||
feature_class = city.feature_class, feature_code = city.feature_code,
|
||||
search_priority = city.search_priority where id == geoname_id
|
||||
}!
|
||||
//console.print_debug("Updated city: ${city}")
|
||||
// console.print_debug("Updated city: ${city}")
|
||||
} else {
|
||||
// Insert new city
|
||||
sql l.db {
|
||||
insert city into City
|
||||
}!
|
||||
//console.print_debug("Inserted city: ${city}")
|
||||
// console.print_debug("Inserted city: ${city}")
|
||||
}
|
||||
count++
|
||||
// if count % 1000 == 0 {
|
||||
@@ -300,8 +277,8 @@ fn (mut l LocationDB) import_city_data(filepath string) ! {
|
||||
// }
|
||||
}
|
||||
|
||||
console.print_debug( 'Processed ${count} cities')
|
||||
|
||||
console.print_debug('Processed ${count} cities')
|
||||
|
||||
l.db.exec('COMMIT')!
|
||||
console.print_header('Finished importing cities for ${filepath}. Total records: ${count}')
|
||||
}
|
||||
|
||||
@@ -2,44 +2,44 @@ module location
|
||||
|
||||
pub struct Country {
|
||||
pub:
|
||||
iso2 string @[primary; sql: 'iso2'; max_len: 2; unique; index]
|
||||
name string @[required; unique; index]
|
||||
iso3 string @[required; sql: 'iso3'; max_len: 3; unique; index]
|
||||
iso2 string @[index; max_len: 2; primary; sql: 'iso2'; unique]
|
||||
name string @[index; required; unique]
|
||||
iso3 string @[index; max_len: 3; required; sql: 'iso3'; unique]
|
||||
continent string @[max_len: 2]
|
||||
population i64
|
||||
timezone string @[max_len: 40]
|
||||
import_date i64 // Epoch timestamp of last import
|
||||
import_date i64 // Epoch timestamp of last import
|
||||
}
|
||||
|
||||
pub struct City {
|
||||
pub:
|
||||
id int @[unique; index]
|
||||
name string @[required; max_len: 200; index]
|
||||
ascii_name string @[required; max_len: 200; index] // Normalized name without special characters
|
||||
country_iso2 string @[required; fkey: 'Country.iso2']
|
||||
postal_code string @[max_len: 20; index ] //postal code
|
||||
state_name string @[max_len: 100] // State/Province name
|
||||
state_code string @[max_len: 20] // State/Province code
|
||||
county_name string @[max_len: 100]
|
||||
county_code string @[max_len: 20]
|
||||
community_name string @[max_len: 100]
|
||||
community_code string @[max_len: 20]
|
||||
latitude f64 @[index: 'idx_coords']
|
||||
longitude f64 @[index: 'idx_coords']
|
||||
population i64
|
||||
timezone string @[max_len: 40]
|
||||
feature_class string @[max_len: 1] // For filtering (P for populated places)
|
||||
feature_code string @[max_len: 10] // Detailed type (PPL, PPLA, etc.)
|
||||
id int @[index; unique]
|
||||
name string @[index; max_len: 200; required]
|
||||
ascii_name string @[index; max_len: 200; required] // Normalized name without special characters
|
||||
country_iso2 string @[fkey: 'Country.iso2'; required]
|
||||
postal_code string @[index; max_len: 20] // postal code
|
||||
state_name string @[max_len: 100] // State/Province name
|
||||
state_code string @[max_len: 20] // State/Province code
|
||||
county_name string @[max_len: 100]
|
||||
county_code string @[max_len: 20]
|
||||
community_name string @[max_len: 100]
|
||||
community_code string @[max_len: 20]
|
||||
latitude f64 @[index: 'idx_coords']
|
||||
longitude f64 @[index: 'idx_coords']
|
||||
population i64
|
||||
timezone string @[max_len: 40]
|
||||
feature_class string @[max_len: 1] // For filtering (P for populated places)
|
||||
feature_code string @[max_len: 10] // Detailed type (PPL, PPLA, etc.)
|
||||
search_priority int
|
||||
accuracy i16 = 1 //1=estimated, 4=geonameid, 6=centroid of addresses or shape
|
||||
accuracy i16 = 1 // 1=estimated, 4=geonameid, 6=centroid of addresses or shape
|
||||
}
|
||||
|
||||
pub struct AlternateName {
|
||||
pub:
|
||||
id int @[primary; sql: serial]
|
||||
city_id int @[required; fkey: 'City.id']
|
||||
name string @[required; max_len: 200; index]
|
||||
language_code string @[max_len: 2]
|
||||
id int @[primary; sql: serial]
|
||||
city_id int @[fkey: 'City.id'; required]
|
||||
name string @[index; max_len: 200; required]
|
||||
language_code string @[max_len: 2]
|
||||
is_preferred bool
|
||||
is_short bool
|
||||
}
|
||||
@@ -47,9 +47,9 @@ pub:
|
||||
// SearchResult represents a location search result with combined city and country info
|
||||
pub struct SearchResult {
|
||||
pub:
|
||||
city City
|
||||
country Country
|
||||
similarity f64 // Search similarity score
|
||||
city City
|
||||
country Country
|
||||
similarity f64 // Search similarity score
|
||||
}
|
||||
|
||||
// Coordinates represents a geographic point
|
||||
|
||||
@@ -51,11 +51,11 @@ import db.pg
|
||||
// where_clause := if query_conditions.len > 0 { 'WHERE ' + query_conditions.join(' AND ') } else { '' }
|
||||
|
||||
// query := '
|
||||
// SELECT c.*, co.*
|
||||
// SELECT c.*, co.*
|
||||
// FROM City c
|
||||
// JOIN Country co ON c.country_iso2 = co.iso2
|
||||
// ${where_clause}
|
||||
// ORDER BY c.search_priority DESC, c.population DESC
|
||||
// ORDER BY c.search_priority DESC, c.population DESC
|
||||
// LIMIT ${opts.limit}
|
||||
// '
|
||||
|
||||
@@ -111,8 +111,8 @@ import db.pg
|
||||
// query := "
|
||||
// WITH distances AS (
|
||||
// SELECT c.*, co.*,
|
||||
// (6371 * acos(cos(radians($1)) * cos(radians(latitude)) *
|
||||
// cos(radians(longitude) - radians($2)) + sin(radians($1)) *
|
||||
// (6371 * acos(cos(radians($1)) * cos(radians(latitude)) *
|
||||
// cos(radians(longitude) - radians($2)) + sin(radians($1)) *
|
||||
// sin(radians(latitude)))) AS distance
|
||||
// FROM City c
|
||||
// JOIN Country co ON c.country_iso2 = co.iso2
|
||||
@@ -122,7 +122,7 @@ import db.pg
|
||||
// ORDER BY distance
|
||||
// LIMIT $4
|
||||
// "
|
||||
|
||||
|
||||
// params := [
|
||||
// opts.coordinates.latitude.str(),
|
||||
// opts.coordinates.longitude.str(),
|
||||
|
||||
@@ -17,11 +17,16 @@ pub fn (params Params) decode_struct[T](_ T) !T {
|
||||
$if field.is_enum {
|
||||
t.$(field.name) = params.get_int(field.name) or { 0 }
|
||||
} $else {
|
||||
if field.name[0].is_capital() {
|
||||
// embed := params.decode_struct(t.$(field.name))!
|
||||
t.$(field.name) = params.decode_struct(t.$(field.name))!
|
||||
} else {
|
||||
t.$(field.name) = params.decode_value(t.$(field.name), field.name)!
|
||||
// super annoying didn't find other way, then to ignore options
|
||||
$if field.is_option {
|
||||
} $else {
|
||||
if field.name[0].is_capital() {
|
||||
// embed := params.decode_struct(t.$(field.name))!
|
||||
t.$(field.name) = params.decode_struct(t.$(field.name))!
|
||||
// panic("to implement")
|
||||
} else {
|
||||
t.$(field.name) = params.decode_value(t.$(field.name), field.name)!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,11 +35,7 @@ pub fn (params Params) decode_struct[T](_ T) !T {
|
||||
|
||||
pub fn (params Params) decode_value[T](_ T, key string) !T {
|
||||
// $if T is $option {
|
||||
// // unwrap and encode optionals
|
||||
// workaround := t
|
||||
// if workaround != none {
|
||||
// encode(t, args)!
|
||||
// }
|
||||
// return error("is option")
|
||||
// }
|
||||
// value := params.get(field.name)!
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ const test_child = TestChild{
|
||||
|
||||
const test_struct = TestStruct{
|
||||
name: 'test'
|
||||
nick: 'test_nick'
|
||||
birthday: time.new(
|
||||
day: 12
|
||||
month: 12
|
||||
@@ -104,6 +103,8 @@ fn test_decode() {
|
||||
decoded_child := test_child_params.decode[TestChild]()!
|
||||
assert decoded_child == test_child
|
||||
|
||||
// IMPORTANT OPTIONALS ARE NOT SUPPORTED AND WILL NOT BE ENCODED FOR NOW (unless we find ways how to deal with attributes to not encode skipped elements)
|
||||
|
||||
// test recursive decode struct with child
|
||||
decoded := test_params.decode[TestStruct]()!
|
||||
assert decoded == test_struct
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
# Git Tools Module
|
||||
|
||||
A comprehensive Git management module for V that provides high-level abstractions for Git operations, repository management, and automation of common Git workflows.
|
||||
### Get a specific path starting from url
|
||||
|
||||
## Features
|
||||
below is powerful command, will get the repo, put on right location, you can force a pull or even reset everything
|
||||
|
||||
- Repository management (clone, load, delete)
|
||||
- Branch operations (create, switch, checkout)
|
||||
- Tag management (create, switch, verify)
|
||||
- Change tracking and commits
|
||||
- Remote operations (push, pull)
|
||||
- SSH key integration
|
||||
- Submodule support
|
||||
- Repository status tracking
|
||||
- Light cloning option for large repositories
|
||||
```v
|
||||
import freeflowuniverse.herolib.develop.gittools
|
||||
mut gs := gittools.new()!
|
||||
mydocs_path:=gs.get_path(
|
||||
pull:true,
|
||||
reset:false,
|
||||
url:'https://git.ourworld.tf/tfgrid/info_docs_depin/src/branch/main/docs'
|
||||
)!
|
||||
|
||||
## Basic Usage
|
||||
println(mydocs_path)
|
||||
|
||||
```
|
||||
|
||||
### Repository Management
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ pub mut:
|
||||
log bool = true // If true, logs git commands/statements
|
||||
debug bool = true
|
||||
ssh_key_name string // name of ssh key to be used when loading the gitstructure
|
||||
ssh_key_path string
|
||||
reload bool
|
||||
}
|
||||
|
||||
@@ -35,6 +36,8 @@ pub fn new(args_ GitStructureArgsNew) !&GitStructure {
|
||||
log: args.log
|
||||
debug: args.debug
|
||||
ssh_key_name: args.ssh_key_name
|
||||
ssh_key_path: args.ssh_key_path
|
||||
|
||||
}
|
||||
|
||||
return get(coderoot: args.coderoot, reload: args.reload, cfg: cfg)
|
||||
@@ -77,7 +80,19 @@ pub fn get(args_ GitStructureArgGet) !&GitStructure {
|
||||
coderoot: pathlib.get_dir(path: args.coderoot, create: true)!
|
||||
}
|
||||
|
||||
mut cfg := args.cfg or {
|
||||
mut cfg_:=GitStructureConfig{coderoot:"SKIP"}
|
||||
cfg_
|
||||
}
|
||||
|
||||
if cfg.coderoot != "SKIP"{
|
||||
gs.config_ = cfg
|
||||
gs.config_save()!
|
||||
//println(gs.config()!)
|
||||
}
|
||||
|
||||
gs.config()! // will load the config, don't remove
|
||||
|
||||
gs.load(false)!
|
||||
|
||||
if gs.repos.keys().len == 0 || args.reload {
|
||||
|
||||
@@ -14,8 +14,10 @@ pub mut:
|
||||
log bool = true // If true, logs git commands/statements
|
||||
debug bool = true
|
||||
ssh_key_name string
|
||||
ssh_key_path string
|
||||
}
|
||||
|
||||
|
||||
// GitStructure holds information about repositories within a specific code root.
|
||||
// This structure keeps track of loaded repositories, their configurations, and their status.
|
||||
@[heap]
|
||||
@@ -233,6 +235,6 @@ pub fn (mut self GitStructure) config_reset() ! {
|
||||
pub fn (mut self GitStructure) config_save() ! {
|
||||
// Retrieve the configuration from Redis.
|
||||
mut redis := redis_get()
|
||||
datajson := json.encode(self.config)
|
||||
datajson := json.encode(self.config()!)
|
||||
redis.set('${self.cache_key()}:config', datajson)!
|
||||
}
|
||||
|
||||
@@ -88,7 +88,6 @@ pub fn (mut gitstructure GitStructure) get_repos(args_ ReposGetArgs) ![]&GitRepo
|
||||
// provider string // Git provider (e.g., GitHub).
|
||||
// pull bool // Pull the last changes.
|
||||
// reset bool // Reset the changes.
|
||||
// reload bool // Reload the repo into redis cache
|
||||
// url string // Repository URL, used if cloning is needed.
|
||||
//```
|
||||
//
|
||||
@@ -151,7 +150,6 @@ fn repo_match_check(repo GitRepo, args ReposGetArgs) !bool {
|
||||
// provider string // Git provider (e.g., GitHub).
|
||||
// pull bool // Pull the last changes.
|
||||
// reset bool // Reset the changes.
|
||||
// reload bool // Reload the repo into redis cache
|
||||
// url string // Repository URL, used if cloning is needed.
|
||||
//```
|
||||
//
|
||||
@@ -161,6 +159,10 @@ fn repo_match_check(repo GitRepo, args ReposGetArgs) !bool {
|
||||
// Raises:
|
||||
// - Error: If multiple repositories are found with similar names or if cloning fails.
|
||||
pub fn (mut gitstructure GitStructure) get_path(args_ ReposGetArgs) !string {
|
||||
mut args := args_
|
||||
if args.pull {
|
||||
args.status_clean = true
|
||||
}
|
||||
mut r := gitstructure.get_repo(args_)!
|
||||
mut mypath := r.get_path_of_url(args_.url)!
|
||||
return mypath
|
||||
|
||||
@@ -34,7 +34,17 @@ pub fn (mut gitstructure GitStructure) clone(args GitCloneArgs) !&GitRepo {
|
||||
extra = '--depth 1 --no-single-branch '
|
||||
}
|
||||
|
||||
cmd := 'cd ${parent_dir} && git clone ${extra} ${repo.get_http_url()!} ${repo.name}'
|
||||
cfg:=gitstructure.config()!
|
||||
|
||||
mut cmd := 'cd ${parent_dir} && git clone ${extra} ${repo.get_http_url()!} ${repo.name}'
|
||||
|
||||
mut sshkey_include := ""
|
||||
if cfg.ssh_key_path.len>0{
|
||||
sshkey_include="GIT_SSH_COMMAND=\"ssh -i ${cfg.ssh_key_path}\" "
|
||||
cmd = 'cd ${parent_dir} && ${sshkey_include}git clone ${extra} ${repo.get_ssh_url()!} ${repo.name}'
|
||||
}
|
||||
|
||||
console.print_debug(cmd)
|
||||
result := os.execute(cmd)
|
||||
if result.exit_code != 0 {
|
||||
return error('Cannot clone the repository due to: \n${result.output}')
|
||||
|
||||
@@ -97,13 +97,13 @@ fn (mut repo GitRepo) load_tags() ! {
|
||||
tags_result := repo.exec('git tag --list') or {
|
||||
return error('Failed to list tags: ${err}. Please ensure git is installed and repository is accessible.')
|
||||
}
|
||||
//println(tags_result)
|
||||
// println(tags_result)
|
||||
for line in tags_result.split('\n') {
|
||||
line_trimmed := line.trim_space()
|
||||
if line_trimmed != '' {
|
||||
parts := line_trimmed.split(' ')
|
||||
if parts.len < 2 {
|
||||
//console.print_debug('Skipping malformed tag line: ${line_trimmed}')
|
||||
// console.print_debug('Skipping malformed tag line: ${line_trimmed}')
|
||||
continue
|
||||
}
|
||||
commit_hash := parts[0].trim_space()
|
||||
|
||||
13
lib/installers/infra/coredns/.heroscript
Normal file
13
lib/installers/infra/coredns/.heroscript
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
!!hero_code.generate_installer
|
||||
name:'coredns'
|
||||
classname:'CoreDNS'
|
||||
singleton:1
|
||||
templates:1
|
||||
default:1
|
||||
title:'coredns'
|
||||
supported_platforms:''
|
||||
reset:0
|
||||
startupmanager:1
|
||||
hasconfig:1
|
||||
build:1
|
||||
@@ -1,138 +0,0 @@
|
||||
module coredns
|
||||
|
||||
import freeflowuniverse.herolib.osal
|
||||
import freeflowuniverse.herolib.osal.screen
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.core.httpconnection
|
||||
import os
|
||||
|
||||
@[params]
|
||||
pub struct InstallArgs {
|
||||
pub mut:
|
||||
reset bool // this means we re-install and forgot what we did before
|
||||
start bool = true
|
||||
stop bool
|
||||
restart bool // this means we stop if started, otherwise just start
|
||||
homedir string // not sure what this is?
|
||||
config_path string // path to Corefile, if empty will install default one
|
||||
config_url string // path to Corefile through e.g. git url, will pull it if it is not local yet
|
||||
dnszones_path string // path to where all the dns zones are
|
||||
dnszones_url string // path on git url pull if needed
|
||||
plugins []string // list of plugins to build CoreDNS with
|
||||
example bool // if true we will install examples
|
||||
}
|
||||
|
||||
pub fn install_(args_ InstallArgs) ! {
|
||||
mut args := args_
|
||||
version := '1.11.1'
|
||||
|
||||
res := os.execute('${osal.profile_path_source_and()!} coredns version')
|
||||
if res.exit_code == 0 {
|
||||
r := res.output.split_into_lines().filter(it.trim_space().starts_with('CoreDNS-'))
|
||||
if r.len != 1 {
|
||||
return error("couldn't parse coredns version.\n${res.output}")
|
||||
}
|
||||
if texttools.version(version) > texttools.version(r[0].all_after_first('CoreDNS-')) {
|
||||
args.reset = true
|
||||
}
|
||||
} else {
|
||||
args.reset = true
|
||||
}
|
||||
|
||||
if args.reset {
|
||||
console.print_header('install coredns')
|
||||
|
||||
mut url := ''
|
||||
if core.is_linux_arm()! {
|
||||
url = 'https://github.com/coredns/coredns/releases/download/v${version}/coredns_${version}_linux_arm64.tgz'
|
||||
} else if core.is_linux_intel()! {
|
||||
url = 'https://github.com/coredns/coredns/releases/download/v${version}/coredns_${version}_linux_amd64.tgz'
|
||||
} else if core.is_osx_arm()! {
|
||||
url = 'https://github.com/coredns/coredns/releases/download/v${version}/coredns_${version}_darwin_arm64.tgz'
|
||||
} else if core.is_osx_intel()! {
|
||||
url = 'https://github.com/coredns/coredns/releases/download/v${version}/coredns_${version}_darwin_amd64.tgz'
|
||||
} else {
|
||||
return error('unsported platform')
|
||||
}
|
||||
|
||||
mut dest := osal.download(
|
||||
url: url
|
||||
minsize_kb: 13000
|
||||
expand_dir: '/tmp/coredns'
|
||||
)!
|
||||
|
||||
mut binpath := dest.file_get('coredns')!
|
||||
osal.cmd_add(
|
||||
cmdname: 'coredns'
|
||||
source: binpath.path
|
||||
)!
|
||||
}
|
||||
|
||||
configure(args)!
|
||||
|
||||
if args.example {
|
||||
example_configure(args)!
|
||||
}
|
||||
|
||||
if args.restart {
|
||||
restart(args)!
|
||||
return
|
||||
}
|
||||
|
||||
if args.start {
|
||||
start(args)!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn restart(args_ InstallArgs) ! {
|
||||
stop(args_)!
|
||||
start(args_)!
|
||||
}
|
||||
|
||||
pub fn stop(args_ InstallArgs) ! {
|
||||
console.print_header('coredns stop')
|
||||
|
||||
name := 'coredns'
|
||||
|
||||
// use startup manager, see caddy
|
||||
mut scr := screen.new()!
|
||||
scr.kill(name)!
|
||||
}
|
||||
|
||||
pub fn start(args_ InstallArgs) ! {
|
||||
mut args := args_
|
||||
configure(args)!
|
||||
|
||||
if check()! {
|
||||
return
|
||||
}
|
||||
|
||||
console.print_header('coredns start')
|
||||
|
||||
name := 'coredns'
|
||||
|
||||
mut scr := screen.new()!
|
||||
|
||||
mut s := scr.add(name: name, reset: true)!
|
||||
|
||||
cmd2 := "coredns -conf '${args.config_path}'"
|
||||
|
||||
s.cmd_send(cmd2)!
|
||||
|
||||
if !check()! {
|
||||
return error("coredns did not install propertly, do: curl 'http://localhost:3334/health'")
|
||||
}
|
||||
|
||||
console.print_header('coredns running')
|
||||
}
|
||||
|
||||
pub fn check() !bool {
|
||||
// this checks health of coredns
|
||||
mut conn := httpconnection.new(name: 'coredns', url: 'http://localhost:3334')!
|
||||
r := conn.get(prefix: 'health')!
|
||||
if r.trim_space() == 'OK' {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
module coredns
|
||||
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.installers.base
|
||||
import os
|
||||
|
||||
pub fn play(mut plbook playbook.PlayBook) ! {
|
||||
base.play(playbook)!
|
||||
|
||||
coredns_actions := plbook.find(filter: 'coredns.')!
|
||||
if coredns_actions.len == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
mut install_actions := plbook.find(filter: 'coredns.install')!
|
||||
|
||||
if install_actions.len > 0 {
|
||||
for install_action in install_actions {
|
||||
mut p := install_action.params
|
||||
|
||||
// CoreDNS parameters
|
||||
reset := p.get_default_false('reset')
|
||||
start := p.get_default_true('start')
|
||||
stop := p.get_default_false('stop')
|
||||
restart := p.get_default_false('restart')
|
||||
homedir := p.get_default('homedir', '${os.home_dir()}/hero/var/coredns')!
|
||||
config_path := p.get_default('config_path', '${os.home_dir()}/hero/cfg/Corefile')!
|
||||
config_url := p.get_default('config_url', '')!
|
||||
dnszones_path := p.get_default('dnszones_path', '${os.home_dir()}/hero/var/coredns/zones')!
|
||||
dnszones_url := p.get_default('dnszones_url', '')!
|
||||
plugins := p.get_list_default('plugins', [])!
|
||||
example := p.get_default_false('example')
|
||||
|
||||
install(
|
||||
reset: reset
|
||||
start: start
|
||||
stop: stop
|
||||
restart: restart
|
||||
homedir: homedir
|
||||
config_path: config_path
|
||||
config_url: config_url
|
||||
dnszones_path: dnszones_path
|
||||
dnszones_url: dnszones_url
|
||||
plugins: plugins
|
||||
example: example
|
||||
)!
|
||||
}
|
||||
}
|
||||
}
|
||||
180
lib/installers/infra/coredns/coredns_actions.v
Normal file
180
lib/installers/infra/coredns/coredns_actions.v
Normal file
@@ -0,0 +1,180 @@
|
||||
module coredns
|
||||
|
||||
import freeflowuniverse.herolib.osal
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.core
|
||||
import freeflowuniverse.herolib.develop.gittools
|
||||
import freeflowuniverse.herolib.osal.zinit
|
||||
import freeflowuniverse.herolib.installers.ulist
|
||||
import freeflowuniverse.herolib.core.httpconnection
|
||||
import freeflowuniverse.herolib.installers.lang.golang
|
||||
import os
|
||||
|
||||
fn startupcmd() ![]zinit.ZProcessNewArgs {
|
||||
mut args := get()!
|
||||
mut res := []zinit.ZProcessNewArgs{}
|
||||
cmd := "coredns -conf '${args.config_path}'"
|
||||
res << zinit.ZProcessNewArgs{
|
||||
name: 'coredns'
|
||||
cmd: cmd
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
fn running() !bool {
|
||||
mut installer := get()!
|
||||
mut conn := httpconnection.new(name: 'coredns', url: 'http://localhost:3334')!
|
||||
r := conn.get(prefix: 'health')!
|
||||
if r.trim_space() == 'OK' {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn start_pre() ! {
|
||||
fix()!
|
||||
}
|
||||
|
||||
fn start_post() ! {
|
||||
set_local_dns()
|
||||
}
|
||||
|
||||
fn stop_pre() ! {
|
||||
}
|
||||
|
||||
fn stop_post() ! {
|
||||
}
|
||||
|
||||
//////////////////// following actions are not specific to instance of the object
|
||||
|
||||
// checks if a certain version or above is installed
|
||||
fn installed() !bool {
|
||||
res := os.execute('${osal.profile_path_source_and()!} coredns version')
|
||||
if res.exit_code != 0 {
|
||||
return false
|
||||
}
|
||||
r := res.output.split_into_lines().filter(it.trim_space().starts_with('CoreDNS-'))
|
||||
if r.len != 1 {
|
||||
return error("couldn't parse coredns version.\n${res.output}")
|
||||
}
|
||||
if texttools.version(version) == texttools.version(r[0].all_after_first('CoreDNS-')) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// get the Upload List of the files
|
||||
fn ulist_get() !ulist.UList {
|
||||
// optionally build a UList which is all paths which are result of building, is then used e.g. in upload
|
||||
return ulist.UList{}
|
||||
}
|
||||
|
||||
// uploads to S3 server if configured
|
||||
fn upload() ! {
|
||||
// installers.upload(
|
||||
// cmdname: 'coredns'
|
||||
// source: '${gitpath}/target/x86_64-unknown-linux-musl/release/coredns'
|
||||
// )!
|
||||
}
|
||||
|
||||
fn install() ! {
|
||||
console.print_header('install coredns')
|
||||
build()! // because we need the plugins
|
||||
// mut url := ''
|
||||
// if core.is_linux_arm()! {
|
||||
// url = 'https://github.com/coredns/coredns/releases/download/v${version}/coredns_${version}_linux_arm64.tgz'
|
||||
// } else if core.is_linux_intel()! {
|
||||
// url = 'https://github.com/coredns/coredns/releases/download/v${version}/coredns_${version}_linux_amd64.tgz'
|
||||
// } else if core.is_osx_arm()! {
|
||||
// url = 'https://github.com/coredns/coredns/releases/download/v${version}/coredns_${version}_darwin_arm64.tgz'
|
||||
// } else if core.is_osx_intel()! {
|
||||
// url = 'https://github.com/coredns/coredns/releases/download/v${version}/coredns_${version}_darwin_amd64.tgz'
|
||||
// } else {
|
||||
// return error('unsported platform')
|
||||
// }
|
||||
|
||||
// mut dest := osal.download(
|
||||
// url: url
|
||||
// minsize_kb: 13000
|
||||
// expand_dir: '/tmp/coredns'
|
||||
// )!
|
||||
|
||||
// mut binpath := dest.file_get('coredns')!
|
||||
// osal.cmd_add(
|
||||
// cmdname: 'coredns'
|
||||
// source: binpath.path
|
||||
// )!
|
||||
}
|
||||
|
||||
fn build() ! {
|
||||
url := 'https://github.com/coredns/coredns'
|
||||
|
||||
if core.platform()! != .ubuntu {
|
||||
return error('only support ubuntu for now')
|
||||
}
|
||||
mut g := golang.get()!
|
||||
g.install()!
|
||||
|
||||
console.print_header('build coredns')
|
||||
|
||||
mut gs := gittools.new()!
|
||||
|
||||
gitpath := gs.get_path(
|
||||
pull: true
|
||||
reset: true
|
||||
url: url
|
||||
)!
|
||||
|
||||
// set the plugins file on right location
|
||||
pluginsfile := $tmpl('templates/plugin.cfg')
|
||||
mut path := pathlib.get_file(path: '${gitpath}/plugin.cfg', create: true)!
|
||||
path.write(pluginsfile)!
|
||||
|
||||
cmd := '
|
||||
cd ${gitpath}
|
||||
make
|
||||
'
|
||||
osal.execute_stdout(cmd)!
|
||||
|
||||
// now copy to the default bin path
|
||||
mut codedir := pathlib.get_dir(path: '${gitpath}', create: false)!
|
||||
mut binpath := codedir.file_get('coredns')!
|
||||
osal.cmd_add(
|
||||
cmdname: 'coredns'
|
||||
source: binpath.path
|
||||
)!
|
||||
}
|
||||
|
||||
fn destroy() ! {
|
||||
// mut systemdfactory := systemd.new()!
|
||||
// systemdfactory.destroy("zinit")!
|
||||
|
||||
// osal.process_kill_recursive(name:'zinit')!
|
||||
// osal.cmd_delete('zinit')!
|
||||
|
||||
// osal.package_remove('
|
||||
// podman
|
||||
// conmon
|
||||
// buildah
|
||||
// skopeo
|
||||
// runc
|
||||
// ')!
|
||||
|
||||
// //will remove all paths where go/bin is found
|
||||
// osal.profile_path_add_remove(paths2delete:"go/bin")!
|
||||
|
||||
// osal.rm("
|
||||
// podman
|
||||
// conmon
|
||||
// buildah
|
||||
// skopeo
|
||||
// runc
|
||||
// /var/lib/containers
|
||||
// /var/lib/podman
|
||||
// /var/lib/buildah
|
||||
// /tmp/podman
|
||||
// /tmp/conmon
|
||||
// ")!
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
module coredns
|
||||
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.osal
|
||||
import freeflowuniverse.herolib.develop.gittools
|
||||
import os
|
||||
|
||||
pub fn configure(args_ InstallArgs) ! {
|
||||
mut args := args_
|
||||
pub fn configure() ! {
|
||||
mut args := get()!
|
||||
mut gs := gittools.get()!
|
||||
mut repo_path := ''
|
||||
|
||||
set_global_dns()
|
||||
|
||||
if args.config_url.len > 0 {
|
||||
mut repo := gs.get_repo(
|
||||
url: args.config_url
|
||||
@@ -37,15 +40,21 @@ pub fn configure(args_ InstallArgs) ! {
|
||||
mycorefile := $tmpl('templates/Corefile')
|
||||
mut path := pathlib.get_file(path: args.config_path, create: true)!
|
||||
path.write(mycorefile)!
|
||||
|
||||
if args.example {
|
||||
example_configure()!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn example_configure(args_ InstallArgs) ! {
|
||||
mut args := args_
|
||||
pub fn example_configure() ! {
|
||||
mut args := get()!
|
||||
|
||||
exampledbfile := $tmpl('templates/db.example.org')
|
||||
myipaddr := osal.ipaddr_pub_get_check()!
|
||||
|
||||
exampledbfile := $tmpl('templates/ourexample.org')
|
||||
|
||||
mut path_testzone := pathlib.get_file(
|
||||
path: '${args_.dnszones_path}/db.example.org'
|
||||
path: '${args.dnszones_path}/ourexample.org'
|
||||
create: true
|
||||
)!
|
||||
path_testzone.template_write(exampledbfile, true)!
|
||||
280
lib/installers/infra/coredns/coredns_factory_.v
Normal file
280
lib/installers/infra/coredns/coredns_factory_.v
Normal file
@@ -0,0 +1,280 @@
|
||||
module coredns
|
||||
|
||||
import freeflowuniverse.herolib.core.base
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.data.paramsparser
|
||||
import freeflowuniverse.herolib.sysadmin.startupmanager
|
||||
import freeflowuniverse.herolib.osal.zinit
|
||||
import time
|
||||
|
||||
__global (
|
||||
coredns_global map[string]&CoreDNS
|
||||
coredns_default string
|
||||
)
|
||||
|
||||
/////////FACTORY
|
||||
|
||||
@[params]
|
||||
pub struct ArgsGet {
|
||||
pub mut:
|
||||
name string
|
||||
}
|
||||
|
||||
fn args_get(args_ ArgsGet) ArgsGet {
|
||||
mut args := args_
|
||||
if args.name == '' {
|
||||
args.name = 'default'
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
pub fn get(args_ ArgsGet) !&CoreDNS {
|
||||
mut context := base.context()!
|
||||
mut args := args_get(args_)
|
||||
mut obj := CoreDNS{}
|
||||
if args.name !in coredns_global {
|
||||
if !exists(args)! {
|
||||
set(obj)!
|
||||
} else {
|
||||
heroscript := context.hero_config_get('coredns', args.name)!
|
||||
mut obj_ := heroscript_loads(heroscript)!
|
||||
set_in_mem(obj_)!
|
||||
}
|
||||
}
|
||||
return coredns_global[args.name] or {
|
||||
println(coredns_global)
|
||||
// bug if we get here because should be in globals
|
||||
panic('could not get config for coredns with name, is bug:${args.name}')
|
||||
}
|
||||
}
|
||||
|
||||
// register the config for the future
|
||||
pub fn set(o CoreDNS) ! {
|
||||
set_in_mem(o)!
|
||||
mut context := base.context()!
|
||||
heroscript := heroscript_dumps(o)!
|
||||
context.hero_config_set('coredns', o.name, heroscript)!
|
||||
}
|
||||
|
||||
// does the config exists?
|
||||
pub fn exists(args_ ArgsGet) !bool {
|
||||
mut context := base.context()!
|
||||
mut args := args_get(args_)
|
||||
return context.hero_config_exists('coredns', args.name)
|
||||
}
|
||||
|
||||
pub fn delete(args_ ArgsGet) ! {
|
||||
mut args := args_get(args_)
|
||||
mut context := base.context()!
|
||||
context.hero_config_delete('coredns', args.name)!
|
||||
if args.name in coredns_global {
|
||||
// del coredns_global[args.name]
|
||||
}
|
||||
}
|
||||
|
||||
// only sets in mem, does not set as config
|
||||
fn set_in_mem(o CoreDNS) ! {
|
||||
mut o2 := obj_init(o)!
|
||||
coredns_global[o.name] = &o2
|
||||
coredns_default = o.name
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct PlayArgs {
|
||||
pub mut:
|
||||
heroscript string // if filled in then plbook will be made out of it
|
||||
plbook ?playbook.PlayBook
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn play(args_ PlayArgs) ! {
|
||||
mut args := args_
|
||||
|
||||
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }
|
||||
|
||||
mut install_actions := plbook.find(filter: 'coredns.configure')!
|
||||
if install_actions.len > 0 {
|
||||
for install_action in install_actions {
|
||||
heroscript := install_action.heroscript()
|
||||
mut obj2 := heroscript_loads(heroscript)!
|
||||
set(obj2)!
|
||||
}
|
||||
}
|
||||
|
||||
mut other_actions := plbook.find(filter: 'coredns.')!
|
||||
for other_action in other_actions {
|
||||
if other_action.name in ['destroy', 'install', 'build'] {
|
||||
mut p := other_action.params
|
||||
reset := p.get_default_false('reset')
|
||||
if other_action.name == 'destroy' || reset {
|
||||
console.print_debug('install action coredns.destroy')
|
||||
destroy()!
|
||||
}
|
||||
if other_action.name == 'install' {
|
||||
console.print_debug('install action coredns.install')
|
||||
install()!
|
||||
}
|
||||
}
|
||||
if other_action.name in ['start', 'stop', 'restart'] {
|
||||
mut p := other_action.params
|
||||
name := p.get('name')!
|
||||
mut coredns_obj := get(name: name)!
|
||||
console.print_debug('action object:\n${coredns_obj}')
|
||||
if other_action.name == 'start' {
|
||||
console.print_debug('install action coredns.${other_action.name}')
|
||||
coredns_obj.start()!
|
||||
}
|
||||
|
||||
if other_action.name == 'stop' {
|
||||
console.print_debug('install action coredns.${other_action.name}')
|
||||
coredns_obj.stop()!
|
||||
}
|
||||
if other_action.name == 'restart' {
|
||||
console.print_debug('install action coredns.${other_action.name}')
|
||||
coredns_obj.restart()!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManager {
|
||||
// unknown
|
||||
// screen
|
||||
// zinit
|
||||
// tmux
|
||||
// systemd
|
||||
match cat {
|
||||
.zinit {
|
||||
console.print_debug('startupmanager: zinit')
|
||||
return startupmanager.get(cat: .zinit)!
|
||||
}
|
||||
.systemd {
|
||||
console.print_debug('startupmanager: systemd')
|
||||
return startupmanager.get(cat: .systemd)!
|
||||
}
|
||||
else {
|
||||
console.print_debug('startupmanager: auto')
|
||||
return startupmanager.get()!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load from disk and make sure is properly intialized
|
||||
pub fn (mut self CoreDNS) reload() ! {
|
||||
switch(self.name)
|
||||
self = obj_init(self)!
|
||||
}
|
||||
|
||||
pub fn (mut self CoreDNS) start() ! {
|
||||
switch(self.name)
|
||||
if self.running()! {
|
||||
return
|
||||
}
|
||||
|
||||
console.print_header('coredns start')
|
||||
|
||||
if !installed()! {
|
||||
install()!
|
||||
}
|
||||
|
||||
configure()!
|
||||
|
||||
start_pre()!
|
||||
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
|
||||
console.print_debug('starting coredns with ${zprocess.startuptype}...')
|
||||
|
||||
sm.new(zprocess)!
|
||||
|
||||
sm.start(zprocess.name)!
|
||||
}
|
||||
|
||||
start_post()!
|
||||
|
||||
for _ in 0 .. 50 {
|
||||
if self.running()! {
|
||||
return
|
||||
}
|
||||
time.sleep(100 * time.millisecond)
|
||||
}
|
||||
return error('coredns did not install properly.')
|
||||
}
|
||||
|
||||
pub fn (mut self CoreDNS) install_start(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
self.install(args)!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self CoreDNS) stop() ! {
|
||||
switch(self.name)
|
||||
stop_pre()!
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
sm.stop(zprocess.name)!
|
||||
}
|
||||
stop_post()!
|
||||
}
|
||||
|
||||
pub fn (mut self CoreDNS) restart() ! {
|
||||
switch(self.name)
|
||||
self.stop()!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self CoreDNS) running() !bool {
|
||||
switch(self.name)
|
||||
|
||||
// walk over the generic processes, if not running return
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
r := sm.running(zprocess.name)!
|
||||
if r == false {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return running()!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct InstallArgs {
|
||||
pub mut:
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn (mut self CoreDNS) install(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
if args.reset || (!installed()!) {
|
||||
install()!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut self CoreDNS) build() ! {
|
||||
switch(self.name)
|
||||
build()!
|
||||
}
|
||||
|
||||
pub fn (mut self CoreDNS) destroy() ! {
|
||||
switch(self.name)
|
||||
self.stop() or {}
|
||||
destroy()!
|
||||
}
|
||||
|
||||
// switch instance to be used for coredns
|
||||
pub fn switch(name string) {
|
||||
coredns_default = name
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
@[params]
|
||||
pub struct DefaultConfigArgs {
|
||||
instance string = 'default'
|
||||
}
|
||||
62
lib/installers/infra/coredns/coredns_fix.v
Normal file
62
lib/installers/infra/coredns/coredns_fix.v
Normal file
@@ -0,0 +1,62 @@
|
||||
module coredns
|
||||
|
||||
import os
|
||||
import net
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
fn is_systemd_resolved_active() bool {
|
||||
result := os.execute('systemctl is-active systemd-resolved')
|
||||
return result.exit_code == 0 && result.output.trim_space() == 'active'
|
||||
}
|
||||
|
||||
fn disable_systemd_resolved() {
|
||||
console.print_debug('Stopping and disabling systemd-resolved...')
|
||||
os.execute('sudo systemctl stop systemd-resolved')
|
||||
os.execute('sudo systemctl disable systemd-resolved')
|
||||
os.execute('sudo systemctl mask systemd-resolved')
|
||||
}
|
||||
|
||||
fn is_dns_port_free() bool {
|
||||
result := os.execute("sudo ss -tunlp | grep ':53 '")
|
||||
return result.exit_code != 0
|
||||
}
|
||||
|
||||
fn set_local_dns() {
|
||||
console.print_debug('Updating /etc/resolv.conf to use local DNS...')
|
||||
os.execute('sudo rm -f /etc/resolv.conf')
|
||||
os.write_file('/etc/resolv.conf', 'nameserver 127.0.0.1\n') or {
|
||||
console.print_debug('Failed to update /etc/resolv.conf')
|
||||
return
|
||||
}
|
||||
console.print_debug('/etc/resolv.conf updated successfully.')
|
||||
}
|
||||
|
||||
fn set_global_dns() {
|
||||
console.print_debug('Updating /etc/resolv.conf to use local DNS...')
|
||||
os.execute('sudo rm -f /etc/resolv.conf')
|
||||
os.write_file('/etc/resolv.conf', 'nameserver 8.8.8.8\n') or {
|
||||
console.print_debug('Failed to update /etc/resolv.conf')
|
||||
return
|
||||
}
|
||||
console.print_debug('/etc/resolv.conf updated successfully for global.')
|
||||
}
|
||||
|
||||
pub fn fix() ! {
|
||||
console.print_debug('Checking if systemd-resolved is active...')
|
||||
if is_systemd_resolved_active() {
|
||||
disable_systemd_resolved()
|
||||
} else {
|
||||
println('systemd-resolved is already disabled.')
|
||||
}
|
||||
|
||||
console.print_debug('Checking if DNS UDP port 53 is free...')
|
||||
if is_dns_port_free() {
|
||||
console.print_debug('UDP port 53 is free.')
|
||||
} else {
|
||||
console.print_debug('UDP port 53 is still in use. Ensure CoreDNS or another service is properly set up.')
|
||||
return
|
||||
}
|
||||
|
||||
set_global_dns()
|
||||
console.print_debug('Setup complete. Ensure CoreDNS is running.')
|
||||
}
|
||||
39
lib/installers/infra/coredns/coredns_model.v
Normal file
39
lib/installers/infra/coredns/coredns_model.v
Normal file
@@ -0,0 +1,39 @@
|
||||
module coredns
|
||||
|
||||
import freeflowuniverse.herolib.data.paramsparser
|
||||
import freeflowuniverse.herolib.data.encoderhero
|
||||
import os
|
||||
|
||||
pub const version = '1.12.0'
|
||||
const singleton = true
|
||||
const default = true
|
||||
|
||||
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
|
||||
@[heap]
|
||||
pub struct CoreDNS {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
config_path string
|
||||
config_url string // path to Corefile through e.g. git url, will pull it if it is not local yet
|
||||
dnszones_path string // path to where all the dns zones are
|
||||
dnszones_url string // path on git url pull if needed (is comma or \n separated list)
|
||||
plugins string // list of plugins to build CoreDNS with (is comma or \n separated list)
|
||||
example bool = true // if true we will install examples
|
||||
}
|
||||
|
||||
// your checking & initialization code if needed
|
||||
fn obj_init(mycfg_ CoreDNS) !CoreDNS {
|
||||
mut mycfg := mycfg_
|
||||
return mycfg
|
||||
}
|
||||
|
||||
/////////////NORMALLY NO NEED TO TOUCH
|
||||
|
||||
pub fn heroscript_dumps(obj CoreDNS) !string {
|
||||
return encoderhero.encode[CoreDNS](obj)!
|
||||
}
|
||||
|
||||
pub fn heroscript_loads(heroscript string) !CoreDNS {
|
||||
mut obj := encoderhero.decode[CoreDNS](heroscript)!
|
||||
return obj
|
||||
}
|
||||
43
lib/installers/infra/coredns/readme.md
Normal file
43
lib/installers/infra/coredns/readme.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# coredns
|
||||
|
||||
coredns
|
||||
|
||||
To get started
|
||||
|
||||
```vlang
|
||||
|
||||
|
||||
import freeflowuniverse.herolib.lib.installers.infra.coredns as coredns_installer
|
||||
|
||||
heroscript:="
|
||||
!!coredns.configure name:'test'
|
||||
config_path: '/etc/coredns/Corefile'
|
||||
dnszones_path: '/etc/coredns/zones'
|
||||
plugins: 'forward,cache'
|
||||
example: true
|
||||
|
||||
!!coredns.start name:'test' reset:1
|
||||
"
|
||||
|
||||
coredns_installer.play(heroscript=heroscript)!
|
||||
|
||||
//or we can call the default and do a start with reset
|
||||
//mut installer:= coredns_installer.get()!
|
||||
//installer.start(reset:true)!
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
## example heroscript
|
||||
|
||||
```hero
|
||||
!!coredns.configure
|
||||
name: 'custom'
|
||||
config_path: '/etc/coredns/Corefile'
|
||||
config_url: 'https://github.com/example/coredns-config'
|
||||
dnszones_path: '/etc/coredns/zones'
|
||||
dnszones_url: 'https://github.com/example/dns-zones'
|
||||
plugins: 'forward,cache'
|
||||
example: false
|
||||
```
|
||||
@@ -3,5 +3,11 @@
|
||||
log
|
||||
errors
|
||||
health :3334
|
||||
import '${args.dnszones_path}/*'
|
||||
redis {
|
||||
address localhost:6379
|
||||
connect_timeout 100
|
||||
read_timeout 100
|
||||
ttl 360
|
||||
prefix dns:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
??ORIGIN example.org.
|
||||
^^ 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. (
|
||||
2017042745 ; serial
|
||||
7200 ; refresh (2 hours)
|
||||
3600 ; retry (1 hour)
|
||||
1209600 ; expire (2 weeks)
|
||||
3600 ; minimum (1 hour)
|
||||
)
|
||||
|
||||
3600 IN NS a.iana-servers.net.
|
||||
3600 IN NS b.iana-servers.net.
|
||||
|
||||
www IN A 127.0.0.1
|
||||
IN AAAA ::1
|
||||
17
lib/installers/infra/coredns/templates/ourexample.org
Normal file
17
lib/installers/infra/coredns/templates/ourexample.org
Normal file
@@ -0,0 +1,17 @@
|
||||
??ORIGIN ourexample.org.
|
||||
^^ 3600 IN SOA ns1.ourexample.org. hostmaster.ourexample.org. (
|
||||
2017042745 ; serial
|
||||
7200 ; refresh (2 hours)
|
||||
3600 ; retry (1 hour)
|
||||
1209600 ; expire (2 weeks)
|
||||
3600 ; minimum (1 hour)
|
||||
)
|
||||
|
||||
3600 IN NS ns1.ourexample.org.
|
||||
3600 IN NS ns2.ourexample.org.
|
||||
|
||||
ns1 IN A ${myipaddr}
|
||||
ns2 IN A ${myipaddr}
|
||||
www IN A ${myipaddr}
|
||||
test IN A ${myipaddr}
|
||||
IN AAAA ::1
|
||||
68
lib/installers/infra/coredns/templates/plugin.cfg
Normal file
68
lib/installers/infra/coredns/templates/plugin.cfg
Normal file
@@ -0,0 +1,68 @@
|
||||
# Directives are registered in the order they should be executed.
|
||||
#
|
||||
# Ordering is VERY important. Every plugin will feel the effects of all other
|
||||
# plugin below (after) them during a request, but they must not care what plugin
|
||||
# above them are doing.
|
||||
|
||||
# How to rebuild with updated plugin configurations: Modify the list below and
|
||||
# run `go generate && go build`
|
||||
|
||||
# The parser takes the input format of:
|
||||
#
|
||||
# <plugin-name>:<package-name>
|
||||
# Or
|
||||
# <plugin-name>:<fully-qualified-package-name>
|
||||
#
|
||||
# External plugin example:
|
||||
#
|
||||
# log:github.com/coredns/coredns/plugin/log
|
||||
# Local plugin example:
|
||||
# log:log
|
||||
#etcd:etcd
|
||||
|
||||
root:root
|
||||
metadata:metadata
|
||||
geoip:geoip
|
||||
cancel:cancel
|
||||
tls:tls
|
||||
timeouts:timeouts
|
||||
multisocket:multisocket
|
||||
reload:reload
|
||||
nsid:nsid
|
||||
bufsize:bufsize
|
||||
bind:bind
|
||||
debug:debug
|
||||
trace:trace
|
||||
ready:ready
|
||||
health:health
|
||||
pprof:pprof
|
||||
prometheus:metrics
|
||||
errors:errors
|
||||
log:log
|
||||
dnstap:dnstap
|
||||
local:local
|
||||
dns64:dns64
|
||||
acl:acl
|
||||
any:any
|
||||
chaos:chaos
|
||||
loadbalance:loadbalance
|
||||
tsig:tsig
|
||||
cache:cache
|
||||
rewrite:rewrite
|
||||
header:header
|
||||
dnssec:dnssec
|
||||
autopath:autopath
|
||||
minimal:minimal
|
||||
template:template
|
||||
transfer:transfer
|
||||
hosts:hosts
|
||||
file:file
|
||||
secondary:secondary
|
||||
loop:loop
|
||||
forward:forward
|
||||
erratic:erratic
|
||||
whoami:whoami
|
||||
on:github.com/coredns/caddy/onevent
|
||||
sign:sign
|
||||
view:view
|
||||
redis:github.com/codysnider/coredns-redis
|
||||
@@ -25,22 +25,21 @@ fn installed() !bool {
|
||||
}
|
||||
|
||||
fn install() ! {
|
||||
|
||||
console.print_header('install gitea')
|
||||
baseurl:="https://github.com/go-gitea/gitea/releases/download/v${version}/gitea-${version}"
|
||||
baseurl := 'https://github.com/go-gitea/gitea/releases/download/v${version}/gitea-${version}'
|
||||
|
||||
mut url := ''
|
||||
if core.is_linux_arm()! {
|
||||
//https://github.com/go-gitea/gitea/releases/download/v1.23.2/gitea-1.23.2-linux-arm64.xz
|
||||
if core.is_linux_arm()! {
|
||||
// https://github.com/go-gitea/gitea/releases/download/v1.23.2/gitea-1.23.2-linux-arm64.xz
|
||||
url = '${baseurl}-linux-arm64.xz'
|
||||
} else if core.is_linux_intel()! {
|
||||
// https://github.com/go-gitea/gitea/releases/download/v1.23.2/gitea-1.23.2-linux-amd64.xz
|
||||
url = '${baseurl}-linux-amd64.xz'
|
||||
} else if core.is_osx_arm()! {
|
||||
//https://github.com/go-gitea/gitea/releases/download/v1.23.2/gitea-1.23.2-darwin-10.12-arm64.xz
|
||||
// https://github.com/go-gitea/gitea/releases/download/v1.23.2/gitea-1.23.2-darwin-10.12-arm64.xz
|
||||
url = '${baseurl}-darwin-10.12-arm64.xz'
|
||||
} else if core.is_osx_intel()! {
|
||||
//https://github.com/go-gitea/gitea/releases/download/v1.23.2/gitea-1.23.2-darwin-10.12-amd64.xz
|
||||
// https://github.com/go-gitea/gitea/releases/download/v1.23.2/gitea-1.23.2-darwin-10.12-amd64.xz
|
||||
url = '${baseurl}-darwin-10.12-amd64.xz'
|
||||
} else {
|
||||
return error('unsported platform')
|
||||
@@ -104,68 +103,66 @@ fn startupcmd() ![]zinit.ZProcessNewArgs {
|
||||
}
|
||||
return res
|
||||
|
||||
// mut res := []zinit.ZProcessNewArgs{}
|
||||
// cfg := get()!
|
||||
// res << zinit.ZProcessNewArgs{
|
||||
// name: 'gitea'
|
||||
// // cmd: 'GITEA_WORK_DIR=${cfg.path} sudo -u git /var/lib/git/gitea web -c /etc/gitea_app.ini'
|
||||
// cmd: '
|
||||
|
||||
// # Variables
|
||||
// GITEA_USER="${cfg.run_user}"
|
||||
// GITEA_HOME="${cfg.path}"
|
||||
// GITEA_BINARY="/usr/local/bin/gitea"
|
||||
// GITEA_CONFIG="/etc/gitea_app.ini"
|
||||
// GITEA_DATA_PATH="\$GITEA_HOME/data"
|
||||
// GITEA_CUSTOM_PATH="\$GITEA_HOME/custom"
|
||||
// GITEA_LOG_PATH="\$GITEA_HOME/log"
|
||||
|
||||
// mut res := []zinit.ZProcessNewArgs{}
|
||||
// cfg := get()!
|
||||
// res << zinit.ZProcessNewArgs{
|
||||
// name: 'gitea'
|
||||
// // cmd: 'GITEA_WORK_DIR=${cfg.path} sudo -u git /var/lib/git/gitea web -c /etc/gitea_app.ini'
|
||||
// cmd: '
|
||||
// # Ensure the script is run as root
|
||||
// if [[ \$EUID -ne 0 ]]; then
|
||||
// echo "This script must be run as root."
|
||||
// exit 1
|
||||
// fi
|
||||
|
||||
// # Variables
|
||||
// GITEA_USER="${cfg.run_user}"
|
||||
// GITEA_HOME="${cfg.path}"
|
||||
// GITEA_BINARY="/usr/local/bin/gitea"
|
||||
// GITEA_CONFIG="/etc/gitea_app.ini"
|
||||
// GITEA_DATA_PATH="\$GITEA_HOME/data"
|
||||
// GITEA_CUSTOM_PATH="\$GITEA_HOME/custom"
|
||||
// GITEA_LOG_PATH="\$GITEA_HOME/log"
|
||||
// echo "Setting up Gitea..."
|
||||
|
||||
// # Ensure the script is run as root
|
||||
// if [[ \$EUID -ne 0 ]]; then
|
||||
// echo "This script must be run as root."
|
||||
// exit 1
|
||||
// fi
|
||||
// # Create Gitea user if it doesn\'t exist
|
||||
// if id -u "\$GITEA_USER" &>/dev/null; then
|
||||
// echo "User \$GITEA_USER already exists."
|
||||
// else
|
||||
// echo "Creating Gitea user..."
|
||||
// if ! sudo adduser --system --shell /bin/bash --group --disabled-password --home "/var/lib/\$GITEA_USER" "\$GITEA_USER"; then
|
||||
// echo "Failed to create user \$GITEA_USER."
|
||||
// exit 1
|
||||
// fi
|
||||
// fi
|
||||
|
||||
// echo "Setting up Gitea..."
|
||||
// # Create necessary directories
|
||||
// echo "Creating directories..."
|
||||
// mkdir -p "\$GITEA_DATA_PATH" "\$GITEA_CUSTOM_PATH" "\$GITEA_LOG_PATH"
|
||||
// chown -R "\$GITEA_USER:\$GITEA_USER" "\$GITEA_HOME"
|
||||
// chmod -R 750 "\$GITEA_HOME"
|
||||
|
||||
// # Create Gitea user if it doesn\'t exist
|
||||
// if id -u "\$GITEA_USER" &>/dev/null; then
|
||||
// echo "User \$GITEA_USER already exists."
|
||||
// else
|
||||
// echo "Creating Gitea user..."
|
||||
// if ! sudo adduser --system --shell /bin/bash --group --disabled-password --home "/var/lib/\$GITEA_USER" "\$GITEA_USER"; then
|
||||
// echo "Failed to create user \$GITEA_USER."
|
||||
// exit 1
|
||||
// fi
|
||||
// fi
|
||||
// chown "\$GITEA_USER:\$GITEA_USER" "\$GITEA_CONFIG"
|
||||
// chmod 640 "\$GITEA_CONFIG"
|
||||
|
||||
// # Create necessary directories
|
||||
// echo "Creating directories..."
|
||||
// mkdir -p "\$GITEA_DATA_PATH" "\$GITEA_CUSTOM_PATH" "\$GITEA_LOG_PATH"
|
||||
// chown -R "\$GITEA_USER:\$GITEA_USER" "\$GITEA_HOME"
|
||||
// chmod -R 750 "\$GITEA_HOME"
|
||||
|
||||
// chown "\$GITEA_USER:\$GITEA_USER" "\$GITEA_CONFIG"
|
||||
// chmod 640 "\$GITEA_CONFIG"
|
||||
|
||||
// GITEA_WORK_DIR=\$GITEA_HOME sudo -u git gitea web -c \$GITEA_CONFIG
|
||||
// '
|
||||
// workdir: cfg.path
|
||||
// }
|
||||
// res << zinit.ZProcessNewArgs{
|
||||
// name: 'restart_gitea'
|
||||
// cmd: 'sleep 30 && zinit restart gitea && exit 1'
|
||||
// after: ['gitea']
|
||||
// oneshot: true
|
||||
// workdir: cfg.path
|
||||
// }
|
||||
// return res
|
||||
// GITEA_WORK_DIR=\$GITEA_HOME sudo -u git gitea web -c \$GITEA_CONFIG
|
||||
// '
|
||||
// workdir: cfg.path
|
||||
// }
|
||||
// res << zinit.ZProcessNewArgs{
|
||||
// name: 'restart_gitea'
|
||||
// cmd: 'sleep 30 && zinit restart gitea && exit 1'
|
||||
// after: ['gitea']
|
||||
// oneshot: true
|
||||
// workdir: cfg.path
|
||||
// }
|
||||
// return res
|
||||
}
|
||||
|
||||
fn running() !bool {
|
||||
//TODO: extend with proper gitea client
|
||||
// TODO: extend with proper gitea client
|
||||
res := os.execute('curl -fsSL http://localhost:3000 || exit 1')
|
||||
return res.exit_code == 0
|
||||
}
|
||||
|
||||
@@ -4,287 +4,277 @@ import freeflowuniverse.herolib.core.base
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.data.paramsparser
|
||||
|
||||
import freeflowuniverse.herolib.sysadmin.startupmanager
|
||||
import freeflowuniverse.herolib.osal.zinit
|
||||
import time
|
||||
|
||||
__global (
|
||||
gitea_global map[string]&GiteaServer
|
||||
gitea_default string
|
||||
gitea_global map[string]&GiteaServer
|
||||
gitea_default string
|
||||
)
|
||||
|
||||
/////////FACTORY
|
||||
|
||||
@[params]
|
||||
pub struct ArgsGet{
|
||||
pub struct ArgsGet {
|
||||
pub mut:
|
||||
name string
|
||||
name string
|
||||
}
|
||||
|
||||
fn args_get (args_ ArgsGet) ArgsGet {
|
||||
mut args:=args_
|
||||
if args.name == ""{
|
||||
args.name = "default"
|
||||
}
|
||||
return args
|
||||
fn args_get(args_ ArgsGet) ArgsGet {
|
||||
mut args := args_
|
||||
if args.name == '' {
|
||||
args.name = 'default'
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
pub fn get(args_ ArgsGet) !&GiteaServer {
|
||||
mut context:=base.context()!
|
||||
mut args := args_get(args_)
|
||||
mut obj := GiteaServer{}
|
||||
if !(args.name in gitea_global) {
|
||||
if ! exists(args)!{
|
||||
set(obj)!
|
||||
}else{
|
||||
heroscript := context.hero_config_get("gitea",args.name)!
|
||||
mut obj2:=heroscript_loads(heroscript)!
|
||||
set_in_mem(obj2)!
|
||||
}
|
||||
}
|
||||
return gitea_global[args.name] or {
|
||||
println(gitea_global)
|
||||
//bug if we get here because should be in globals
|
||||
panic("could not get config for gitea with name, is bug:${args.name}")
|
||||
}
|
||||
pub fn get(args_ ArgsGet) !&GiteaServer {
|
||||
mut context := base.context()!
|
||||
mut args := args_get(args_)
|
||||
mut obj := GiteaServer{}
|
||||
if args.name !in gitea_global {
|
||||
if !exists(args)! {
|
||||
set(obj)!
|
||||
} else {
|
||||
heroscript := context.hero_config_get('gitea', args.name)!
|
||||
mut obj2 := heroscript_loads(heroscript)!
|
||||
set_in_mem(obj2)!
|
||||
}
|
||||
}
|
||||
return gitea_global[args.name] or {
|
||||
println(gitea_global)
|
||||
// bug if we get here because should be in globals
|
||||
panic('could not get config for gitea with name, is bug:${args.name}')
|
||||
}
|
||||
}
|
||||
|
||||
//register the config for the future
|
||||
pub fn set(o GiteaServer)! {
|
||||
set_in_mem(o)!
|
||||
mut context := base.context()!
|
||||
heroscript := heroscript_dumps(o)!
|
||||
context.hero_config_set("gitea", o.name, heroscript)!
|
||||
// register the config for the future
|
||||
pub fn set(o GiteaServer) ! {
|
||||
set_in_mem(o)!
|
||||
mut context := base.context()!
|
||||
heroscript := heroscript_dumps(o)!
|
||||
context.hero_config_set('gitea', o.name, heroscript)!
|
||||
}
|
||||
|
||||
//does the config exists?
|
||||
// does the config exists?
|
||||
pub fn exists(args_ ArgsGet) !bool {
|
||||
mut context := base.context()!
|
||||
mut args := args_get(args_)
|
||||
return context.hero_config_exists("gitea", args.name)
|
||||
mut context := base.context()!
|
||||
mut args := args_get(args_)
|
||||
return context.hero_config_exists('gitea', args.name)
|
||||
}
|
||||
|
||||
pub fn delete(args_ ArgsGet)! {
|
||||
mut args := args_get(args_)
|
||||
mut context:=base.context()!
|
||||
context.hero_config_delete("gitea",args.name)!
|
||||
if args.name in gitea_global {
|
||||
//del gitea_global[args.name]
|
||||
}
|
||||
pub fn delete(args_ ArgsGet) ! {
|
||||
mut args := args_get(args_)
|
||||
mut context := base.context()!
|
||||
context.hero_config_delete('gitea', args.name)!
|
||||
if args.name in gitea_global {
|
||||
// del gitea_global[args.name]
|
||||
}
|
||||
}
|
||||
|
||||
//only sets in mem, does not set as config
|
||||
fn set_in_mem(o GiteaServer)! {
|
||||
mut o2:=obj_init(o)!
|
||||
gitea_global[o.name] = &o2
|
||||
gitea_default = o.name
|
||||
// only sets in mem, does not set as config
|
||||
fn set_in_mem(o GiteaServer) ! {
|
||||
mut o2 := obj_init(o)!
|
||||
gitea_global[o.name] = &o2
|
||||
gitea_default = o.name
|
||||
}
|
||||
|
||||
|
||||
@[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 args:=args_
|
||||
mut args := args_
|
||||
|
||||
mut plbook := args.plbook or {
|
||||
playbook.new(text: args.heroscript)!
|
||||
}
|
||||
|
||||
mut install_actions := plbook.find(filter: 'gitea.configure')!
|
||||
if install_actions.len > 0 {
|
||||
for install_action in install_actions {
|
||||
heroscript:=install_action.heroscript()
|
||||
mut obj:=heroscript_loads(heroscript)!
|
||||
set(obj)!
|
||||
}
|
||||
}
|
||||
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }
|
||||
|
||||
mut other_actions := plbook.find(filter: 'gitea.')!
|
||||
for other_action in other_actions {
|
||||
if other_action.name in ["destroy","install","build"]{
|
||||
mut p := other_action.params
|
||||
reset:=p.get_default_false("reset")
|
||||
if other_action.name == "destroy" || reset{
|
||||
console.print_debug("install action gitea.destroy")
|
||||
destroy()!
|
||||
}
|
||||
if other_action.name == "install"{
|
||||
console.print_debug("install action gitea.install")
|
||||
install()!
|
||||
}
|
||||
}
|
||||
if other_action.name in ["start","stop","restart"]{
|
||||
mut p := other_action.params
|
||||
name := p.get('name')!
|
||||
mut gitea_obj:=get(name:name)!
|
||||
console.print_debug("action object:\n${gitea_obj}")
|
||||
if other_action.name == "start"{
|
||||
console.print_debug("install action gitea.${other_action.name}")
|
||||
gitea_obj.start()!
|
||||
}
|
||||
mut install_actions := plbook.find(filter: 'gitea.configure')!
|
||||
if install_actions.len > 0 {
|
||||
for install_action in install_actions {
|
||||
heroscript := install_action.heroscript()
|
||||
mut obj := heroscript_loads(heroscript)!
|
||||
set(obj)!
|
||||
}
|
||||
}
|
||||
|
||||
if other_action.name == "stop"{
|
||||
console.print_debug("install action gitea.${other_action.name}")
|
||||
gitea_obj.stop()!
|
||||
}
|
||||
if other_action.name == "restart"{
|
||||
console.print_debug("install action gitea.${other_action.name}")
|
||||
gitea_obj.restart()!
|
||||
}
|
||||
}
|
||||
}
|
||||
mut other_actions := plbook.find(filter: 'gitea.')!
|
||||
for other_action in other_actions {
|
||||
if other_action.name in ['destroy', 'install', 'build'] {
|
||||
mut p := other_action.params
|
||||
reset := p.get_default_false('reset')
|
||||
if other_action.name == 'destroy' || reset {
|
||||
console.print_debug('install action gitea.destroy')
|
||||
destroy()!
|
||||
}
|
||||
if other_action.name == 'install' {
|
||||
console.print_debug('install action gitea.install')
|
||||
install()!
|
||||
}
|
||||
}
|
||||
if other_action.name in ['start', 'stop', 'restart'] {
|
||||
mut p := other_action.params
|
||||
name := p.get('name')!
|
||||
mut gitea_obj := get(name: name)!
|
||||
console.print_debug('action object:\n${gitea_obj}')
|
||||
if other_action.name == 'start' {
|
||||
console.print_debug('install action gitea.${other_action.name}')
|
||||
gitea_obj.start()!
|
||||
}
|
||||
|
||||
if other_action.name == 'stop' {
|
||||
console.print_debug('install action gitea.${other_action.name}')
|
||||
gitea_obj.stop()!
|
||||
}
|
||||
if other_action.name == 'restart' {
|
||||
console.print_debug('install action gitea.${other_action.name}')
|
||||
gitea_obj.restart()!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManager {
|
||||
// unknown
|
||||
// screen
|
||||
// zinit
|
||||
// tmux
|
||||
// systemd
|
||||
match cat{
|
||||
.zinit{
|
||||
console.print_debug("startupmanager: zinit")
|
||||
return startupmanager.get(cat:.zinit)!
|
||||
}
|
||||
.systemd{
|
||||
console.print_debug("startupmanager: systemd")
|
||||
return startupmanager.get(cat:.systemd)!
|
||||
}else{
|
||||
console.print_debug("startupmanager: auto")
|
||||
return startupmanager.get()!
|
||||
}
|
||||
}
|
||||
// unknown
|
||||
// screen
|
||||
// zinit
|
||||
// tmux
|
||||
// systemd
|
||||
match cat {
|
||||
.zinit {
|
||||
console.print_debug('startupmanager: zinit')
|
||||
return startupmanager.get(cat: .zinit)!
|
||||
}
|
||||
.systemd {
|
||||
console.print_debug('startupmanager: systemd')
|
||||
return startupmanager.get(cat: .systemd)!
|
||||
}
|
||||
else {
|
||||
console.print_debug('startupmanager: auto')
|
||||
return startupmanager.get()!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//load from disk and make sure is properly intialized
|
||||
// load from disk and make sure is properly intialized
|
||||
pub fn (mut self GiteaServer) reload() ! {
|
||||
switch(self.name)
|
||||
self=obj_init(self)!
|
||||
switch(self.name)
|
||||
self = obj_init(self)!
|
||||
}
|
||||
|
||||
pub fn (mut self GiteaServer) start() ! {
|
||||
switch(self.name)
|
||||
if self.running()!{
|
||||
return
|
||||
}
|
||||
switch(self.name)
|
||||
if self.running()! {
|
||||
return
|
||||
}
|
||||
|
||||
console.print_header('gitea start')
|
||||
console.print_header('gitea start')
|
||||
|
||||
if ! installed()!{
|
||||
install()!
|
||||
}
|
||||
if !installed()! {
|
||||
install()!
|
||||
}
|
||||
|
||||
configure()!
|
||||
configure()!
|
||||
|
||||
start_pre()!
|
||||
start_pre()!
|
||||
|
||||
for zprocess in startupcmd()!{
|
||||
mut sm:=startupmanager_get(zprocess.startuptype)!
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
|
||||
console.print_debug('starting gitea with ${zprocess.startuptype}...')
|
||||
console.print_debug('starting gitea with ${zprocess.startuptype}...')
|
||||
|
||||
sm.new(zprocess)!
|
||||
sm.new(zprocess)!
|
||||
|
||||
sm.start(zprocess.name)!
|
||||
}
|
||||
sm.start(zprocess.name)!
|
||||
}
|
||||
|
||||
start_post()!
|
||||
|
||||
for _ in 0 .. 50 {
|
||||
if self.running()! {
|
||||
return
|
||||
}
|
||||
time.sleep(100 * time.millisecond)
|
||||
}
|
||||
return error('gitea did not install properly.')
|
||||
start_post()!
|
||||
|
||||
for _ in 0 .. 50 {
|
||||
if self.running()! {
|
||||
return
|
||||
}
|
||||
time.sleep(100 * time.millisecond)
|
||||
}
|
||||
return error('gitea did not install properly.')
|
||||
}
|
||||
|
||||
pub fn (mut self GiteaServer) install_start(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
self.install(args)!
|
||||
self.start()!
|
||||
switch(self.name)
|
||||
self.install(args)!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self GiteaServer) stop() ! {
|
||||
switch(self.name)
|
||||
stop_pre()!
|
||||
for zprocess in startupcmd()!{
|
||||
mut sm:=startupmanager_get(zprocess.startuptype)!
|
||||
sm.stop(zprocess.name)!
|
||||
}
|
||||
stop_post()!
|
||||
switch(self.name)
|
||||
stop_pre()!
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
sm.stop(zprocess.name)!
|
||||
}
|
||||
stop_post()!
|
||||
}
|
||||
|
||||
pub fn (mut self GiteaServer) restart() ! {
|
||||
switch(self.name)
|
||||
self.stop()!
|
||||
self.start()!
|
||||
switch(self.name)
|
||||
self.stop()!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self GiteaServer) running() !bool {
|
||||
switch(self.name)
|
||||
switch(self.name)
|
||||
|
||||
//walk over the generic processes, if not running return
|
||||
for zprocess in startupcmd()!{
|
||||
mut sm:=startupmanager_get(zprocess.startuptype)!
|
||||
r:=sm.running(zprocess.name)!
|
||||
if r==false{
|
||||
return false
|
||||
}
|
||||
}
|
||||
return running()!
|
||||
// walk over the generic processes, if not running return
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
r := sm.running(zprocess.name)!
|
||||
if r == false {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return running()!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct InstallArgs{
|
||||
pub struct InstallArgs {
|
||||
pub mut:
|
||||
reset bool
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn (mut self GiteaServer) install(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
if args.reset || (!installed()!) {
|
||||
install()!
|
||||
}
|
||||
switch(self.name)
|
||||
if args.reset || (!installed()!) {
|
||||
install()!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut self GiteaServer) build() ! {
|
||||
switch(self.name)
|
||||
build()!
|
||||
switch(self.name)
|
||||
build()!
|
||||
}
|
||||
|
||||
pub fn (mut self GiteaServer) destroy() ! {
|
||||
switch(self.name)
|
||||
self.stop() or {}
|
||||
destroy()!
|
||||
switch(self.name)
|
||||
self.stop() or {}
|
||||
destroy()!
|
||||
}
|
||||
|
||||
|
||||
|
||||
//switch instance to be used for gitea
|
||||
// switch instance to be used for gitea
|
||||
pub fn switch(name string) {
|
||||
gitea_default = name
|
||||
gitea_default = name
|
||||
}
|
||||
|
||||
|
||||
//helpers
|
||||
// helpers
|
||||
|
||||
@[params]
|
||||
pub struct DefaultConfigArgs{
|
||||
instance string = 'default'
|
||||
pub struct DefaultConfigArgs {
|
||||
instance string = 'default'
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
module gitea
|
||||
|
||||
import freeflowuniverse.herolib.data.paramsparser
|
||||
import freeflowuniverse.herolib.data.encoderhero
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
@@ -16,30 +17,29 @@ const default = false
|
||||
@[heap]
|
||||
pub struct GiteaServer {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
path string = '${os.home_dir()}/hero/var/gitea'
|
||||
passwd string
|
||||
domain string = "git.test.com"
|
||||
jwt_secret string = rand.hex(12)
|
||||
lfs_jwt_secret string
|
||||
internal_token string
|
||||
secret_key string
|
||||
postgresql_client_name string = "default"
|
||||
mail_client_name string = "default"
|
||||
name string = 'default'
|
||||
path string = '${os.home_dir()}/hero/var/gitea'
|
||||
passwd string
|
||||
domain string = 'git.test.com'
|
||||
jwt_secret string = rand.hex(12)
|
||||
lfs_jwt_secret string
|
||||
internal_token string
|
||||
secret_key string
|
||||
postgresql_client_name string = 'default'
|
||||
mail_client_name string = 'default'
|
||||
}
|
||||
|
||||
|
||||
pub fn (obj GiteaServer) config_path() string {
|
||||
return '${obj.path}/config.ini'
|
||||
return '${obj.path}/config.ini'
|
||||
}
|
||||
|
||||
//your checking & initialization code if needed
|
||||
fn obj_init(mycfg_ GiteaServer)!GiteaServer{
|
||||
mut mycfg:=mycfg_
|
||||
return mycfg
|
||||
// your checking & initialization code if needed
|
||||
fn obj_init(mycfg_ GiteaServer) !GiteaServer {
|
||||
mut mycfg := mycfg_
|
||||
return mycfg
|
||||
}
|
||||
|
||||
//called before start if done
|
||||
// called before start if done
|
||||
fn configure() ! {
|
||||
mut server := get()!
|
||||
|
||||
@@ -68,7 +68,7 @@ fn configure() ! {
|
||||
return error('Failed to initialize mail client "${server.mail_client_name}": ${err}')
|
||||
}
|
||||
|
||||
//TODO: check database exists
|
||||
// TODO: check database exists
|
||||
if !db_client.db_exists('gitea_${server.name}')! {
|
||||
console.print_header('Creating database gitea_${server.name} for gitea.')
|
||||
db_client.db_create('gitea_${server.name}')!
|
||||
@@ -85,10 +85,10 @@ fn configure() ! {
|
||||
/////////////NORMALLY NO NEED TO TOUCH
|
||||
|
||||
pub fn heroscript_dumps(obj GiteaServer) !string {
|
||||
return encoderhero.encode[GiteaServer ](obj)!
|
||||
return encoderhero.encode[GiteaServer](obj)!
|
||||
}
|
||||
|
||||
pub fn heroscript_loads(heroscript string) !GiteaServer {
|
||||
mut obj := encoderhero.decode[GiteaServer](heroscript)!
|
||||
return obj
|
||||
mut obj := encoderhero.decode[GiteaServer](heroscript)!
|
||||
return obj
|
||||
}
|
||||
|
||||
@@ -54,9 +54,9 @@ pub fn v_analyzer_install(args_ InstallArgs) ! {
|
||||
// if pl == .ubuntu {
|
||||
// }else{
|
||||
// mut url := ''
|
||||
// if osal.is_linux_intel() {
|
||||
// if core.is_linux_intel() {
|
||||
// url = 'https://github.com/vlang/v-analyzer/releases/download/nightly/v-analyzer-linux-x86_64.zip'
|
||||
// } else if osal.is_osx_arm() {
|
||||
// } else if core.is_osx_arm() {
|
||||
// url = 'https://github.com/vlang/v-analyzer/releases/download/nightly/v-analyzer-darwin-arm64.zip'
|
||||
// } else if osal.is_osx_intel() {
|
||||
// url = 'https://github.com/vlang/v-analyzer/releases/download/nightly/v-analyzer-darwin-x86_64.zip'
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
!!hero_code.generate_installer
|
||||
name: "mycelium"
|
||||
classname: "MyceliumInstaller"
|
||||
hasconfig: false
|
||||
singleton: true
|
||||
hasconfig: true
|
||||
singleton: false
|
||||
default: true
|
||||
title: ""
|
||||
templates: false
|
||||
build: true
|
||||
startupmanager: true
|
||||
supported_platforms: ""
|
||||
|
||||
|
||||
@@ -1,227 +0,0 @@
|
||||
module mycelium
|
||||
|
||||
import freeflowuniverse.herolib.osal
|
||||
import freeflowuniverse.herolib.core
|
||||
import freeflowuniverse.herolib.installers.lang.rust
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.osal.screen
|
||||
import freeflowuniverse.herolib.ui
|
||||
import freeflowuniverse.herolib.sysadmin.startupmanager
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
|
||||
// install mycelium will return true if it was already installed
|
||||
pub fn installss(args_ InstallArgs) ! {
|
||||
mut args := args_
|
||||
|
||||
console.print_header('install mycelium.')
|
||||
|
||||
version := '0.5.6'
|
||||
|
||||
res := os.execute('${osal.profile_path_source_and()!} mycelium -V')
|
||||
if res.exit_code == 0 {
|
||||
r := res.output.split_into_lines().filter(it.trim_space().starts_with('mycelium'))
|
||||
if r.len != 1 {
|
||||
return error("couldn't parse mycelium version.\n${res.output}")
|
||||
}
|
||||
if texttools.version(version) > texttools.version(r[0].all_after_first('mycelium')) {
|
||||
args.reset = true
|
||||
}
|
||||
} else {
|
||||
args.reset = true
|
||||
}
|
||||
|
||||
if args.reset {
|
||||
console.print_header('install mycelium')
|
||||
|
||||
mut url := ''
|
||||
if core.is_linux_arm()! {
|
||||
url = 'https://github.com/threefoldtech/mycelium/releases/download/v${version}/mycelium-aarch64-unknown-linux-musl.tar.gz'
|
||||
} else if core.is_linux_intel()! {
|
||||
url = 'https://github.com/threefoldtech/mycelium/releases/download/v${version}/mycelium-x86_64-unknown-linux-musl.tar.gz'
|
||||
} else if core.is_osx_arm()! {
|
||||
url = 'https://github.com/threefoldtech/mycelium/releases/download/v${version}/mycelium-aarch64-apple-darwin.tar.gz'
|
||||
} else if core.is_osx_intel()! {
|
||||
url = 'https://github.com/threefoldtech/mycelium/releases/download/v${version}/mycelium-x86_64-apple-darwin.tar.gz'
|
||||
} else {
|
||||
return error('unsported platform')
|
||||
}
|
||||
// console.print_debug(url)
|
||||
mut dest := osal.download(
|
||||
url: url
|
||||
minsize_kb: 1000
|
||||
reset: true
|
||||
expand_dir: '/tmp/myceliumnet'
|
||||
)!
|
||||
|
||||
mut myceliumfile := dest.file_get('mycelium')! // file in the dest
|
||||
|
||||
// console.print_debug(myceliumfile.str())
|
||||
|
||||
osal.cmd_add(
|
||||
source: myceliumfile.path
|
||||
)!
|
||||
}
|
||||
|
||||
// if args.restart {
|
||||
// stop()!
|
||||
// }
|
||||
start()!
|
||||
|
||||
console.print_debug('install mycelium ok')
|
||||
}
|
||||
|
||||
pub fn restart() ! {
|
||||
stop()!
|
||||
start()!
|
||||
}
|
||||
|
||||
pub fn stop() ! {
|
||||
name := 'mycelium'
|
||||
console.print_debug('stop ${name}')
|
||||
if core.is_osx()! {
|
||||
mut scr := screen.new(reset: false)!
|
||||
scr.kill(name)!
|
||||
} else {
|
||||
mut sm := startupmanager.get()!
|
||||
sm.stop(name)!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(args InstallArgs) ! {
|
||||
if check() {
|
||||
console.print_header('mycelium was already running')
|
||||
return
|
||||
}
|
||||
myinitname := core.initname()!
|
||||
name := 'mycelium'
|
||||
console.print_debug('start ${name} (startupmanger:${myinitname})')
|
||||
|
||||
mut cmd := ''
|
||||
|
||||
if core.is_osx()! {
|
||||
cmd = 'sudo -s '
|
||||
}
|
||||
|
||||
cmd += 'mycelium --key-file ${osal.hero_path()!}/cfg/priv_key.bin --peers tcp://188.40.132.242:9651 quic://185.69.166.7:9651 tcp://65.21.231.58:9651 --tun-name utun9'
|
||||
console.print_debug(cmd)
|
||||
if core.is_osx()! {
|
||||
// do not change, because we need this on osx at least
|
||||
|
||||
mut scr := screen.new(reset: false)!
|
||||
|
||||
if scr.exists(name) {
|
||||
console.print_header('mycelium was already running')
|
||||
return
|
||||
}
|
||||
|
||||
mut s := scr.add(name: name, start: true, reset: args.reset)!
|
||||
s.cmd_send(cmd)!
|
||||
|
||||
mut myui := ui.new()!
|
||||
console.clear()
|
||||
|
||||
console.print_stderr("
|
||||
On the next screen you will be able to fill in your password.
|
||||
Once done and the server is started: do 'control a + d'
|
||||
|
||||
")
|
||||
|
||||
_ = myui.ask_yesno(question: 'Please confirm you understand?')!
|
||||
|
||||
s.attach()! // to allow filling in passwd
|
||||
} else {
|
||||
mut sm := startupmanager.get()!
|
||||
sm.new(
|
||||
name: name
|
||||
cmd: cmd
|
||||
start: true
|
||||
)!
|
||||
}
|
||||
|
||||
console.print_debug('startup manager started')
|
||||
|
||||
time.sleep(100 * time.millisecond)
|
||||
|
||||
if !check() {
|
||||
return error('cound not start mycelium')
|
||||
}
|
||||
|
||||
console.print_header('mycelium is running')
|
||||
}
|
||||
|
||||
pub fn check() bool {
|
||||
// if core.is_osx()! {
|
||||
// mut scr := screen.new(reset: false) or {return False}
|
||||
// name := 'mycelium'
|
||||
// if !scr.exists(name) {
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
|
||||
// if !(osal.process_exists_byname('mycelium') or {return False}) {
|
||||
// return false
|
||||
// }
|
||||
|
||||
// TODO: might be dangerous if that one goes out
|
||||
ping_result := osal.ping(address: '40a:152c:b85b:9646:5b71:d03a:eb27:2462', retry: 2) or {
|
||||
return false
|
||||
}
|
||||
if ping_result == .ok {
|
||||
console.print_debug('could reach 40a:152c:b85b:9646:5b71:d03a:eb27:2462')
|
||||
return true
|
||||
}
|
||||
console.print_stderr('could not reach 40a:152c:b85b:9646:5b71:d03a:eb27:2462')
|
||||
return false
|
||||
}
|
||||
|
||||
// install mycelium will return true if it was already installed
|
||||
pub fn build_() ! {
|
||||
rust.install()!
|
||||
console.print_header('build mycelium')
|
||||
if !osal.done_exists('build_mycelium') && !osal.cmd_exists('mycelium') {
|
||||
panic('implement')
|
||||
// USE OUR PRIMITIVES (TODO, needs to change, was from zola)
|
||||
cmd := '
|
||||
source ~/.cargo/env
|
||||
cd /tmp
|
||||
rm -rf mycelium
|
||||
git clone https://github.com/getmycelium/mycelium.git
|
||||
cd mycelium
|
||||
cargo install --path . --locked
|
||||
mycelium --version
|
||||
cargo build --release --locked --no-default-features --features=native-tls
|
||||
cp target/release/mycelium ~/.cargo/bin/mycelium
|
||||
'
|
||||
osal.execute_stdout(cmd)!
|
||||
osal.done_set('build_mycelium', 'OK')!
|
||||
console.print_header('mycelium installed')
|
||||
} else {
|
||||
console.print_header('mycelium already installed')
|
||||
}
|
||||
}
|
||||
|
||||
struct MyceliumInspectResult {
|
||||
public_key string @[json: publicKey]
|
||||
address string
|
||||
}
|
||||
|
||||
pub fn inspect() !MyceliumInspectResult {
|
||||
command := 'mycelium inspect --key-file /root/hero/cfg/priv_key.bin --json'
|
||||
result := os.execute(command)
|
||||
if result.exit_code != 0 {
|
||||
return error('Command failed: ${result.output}')
|
||||
}
|
||||
inspect_result := json.decode(MyceliumInspectResult, result.output) or {
|
||||
return error('Failed to parse JSON: ${err}')
|
||||
}
|
||||
return inspect_result
|
||||
}
|
||||
|
||||
// if returns empty then probably mycelium is not installed
|
||||
pub fn ipaddr() string {
|
||||
r := inspect() or { MyceliumInspectResult{} }
|
||||
return r.address
|
||||
}
|
||||
@@ -3,50 +3,37 @@ module mycelium
|
||||
import freeflowuniverse.herolib.osal
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.core
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.osal.systemd
|
||||
import freeflowuniverse.herolib.installers.sysadmintools.zinit as zinit_installer
|
||||
import freeflowuniverse.herolib.clients.mycelium
|
||||
import freeflowuniverse.herolib.develop.gittools
|
||||
import freeflowuniverse.herolib.osal.zinit
|
||||
import freeflowuniverse.herolib.installers.ulist
|
||||
import freeflowuniverse.herolib.installers.lang.golang
|
||||
import freeflowuniverse.herolib.installers.lang.rust
|
||||
import freeflowuniverse.herolib.installers.lang.python
|
||||
import os
|
||||
|
||||
fn startupcmd() ![]zinit.ZProcessNewArgs {
|
||||
mut installer := get()!
|
||||
mut res := []zinit.ZProcessNewArgs{}
|
||||
// THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
|
||||
// res << zinit.ZProcessNewArgs{
|
||||
// name: 'mycelium'
|
||||
// cmd: 'mycelium server'
|
||||
// env: {
|
||||
// 'HOME': '/root'
|
||||
// }
|
||||
// }
|
||||
|
||||
mut peers_str := installer.peers.join(' ')
|
||||
mut tun_name := 'tun${installer.tun_nr}'
|
||||
|
||||
res << zinit.ZProcessNewArgs{
|
||||
name: 'mycelium'
|
||||
cmd: 'mycelium --key-file ${osal.hero_path()!}/cfg/priv_key.bin --peers ${peers_str} --tun-name ${tun_name}'
|
||||
env: {
|
||||
'HOME': '/root'
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
fn running_() !bool {
|
||||
mut installer := get()!
|
||||
// THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
|
||||
// this checks health of mycelium
|
||||
// curl http://localhost:3333/api/v1/s --oauth2-bearer 1234 works
|
||||
// url:='http://127.0.0.1:${cfg.port}/api/v1'
|
||||
// mut conn := httpconnection.new(name: 'mycelium', url: url)!
|
||||
|
||||
// if cfg.secret.len > 0 {
|
||||
// conn.default_header.add(.authorization, 'Bearer ${cfg.secret}')
|
||||
// }
|
||||
// conn.default_header.add(.content_type, 'application/json')
|
||||
// console.print_debug("curl -X 'GET' '${url}'/tags --oauth2-bearer ${cfg.secret}")
|
||||
// r := conn.get_json_dict(prefix: 'tags', debug: false) or {return false}
|
||||
// println(r)
|
||||
// if true{panic("ssss")}
|
||||
// tags := r['Tags'] or { return false }
|
||||
// console.print_debug(tags)
|
||||
// console.print_debug('mycelium is answering.')
|
||||
return false
|
||||
fn running() !bool {
|
||||
mycelium.inspect() or { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
fn start_pre() ! {
|
||||
@@ -64,19 +51,21 @@ fn stop_post() ! {
|
||||
//////////////////// following actions are not specific to instance of the object
|
||||
|
||||
// checks if a certain version or above is installed
|
||||
fn installed_() !bool {
|
||||
// THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
|
||||
// res := os.execute('${osal.profile_path_source_and()!} mycelium version')
|
||||
// if res.exit_code != 0 {
|
||||
// return false
|
||||
// }
|
||||
// r := res.output.split_into_lines().filter(it.trim_space().len > 0)
|
||||
// if r.len != 1 {
|
||||
// return error("couldn't parse mycelium version.\n${res.output}")
|
||||
// }
|
||||
// if texttools.version(version) == texttools.version(r[0]) {
|
||||
// return true
|
||||
// }
|
||||
fn installed() !bool {
|
||||
cmd := '${osal.profile_path_source_and()!} mycelium -V'
|
||||
// println(cmd)
|
||||
res := os.execute(cmd)
|
||||
if res.exit_code != 0 {
|
||||
println(res)
|
||||
return false
|
||||
}
|
||||
r := res.output.split_into_lines().filter(it.trim_space().len > 0)
|
||||
if r.len != 1 {
|
||||
return error("couldn't parse mycelium version.\n${res.output}")
|
||||
}
|
||||
if texttools.version(version) == texttools.version(r[0].all_after_last('mycelium')) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -87,64 +76,78 @@ fn ulist_get() !ulist.UList {
|
||||
}
|
||||
|
||||
// uploads to S3 server if configured
|
||||
fn upload_() ! {
|
||||
fn upload() ! {
|
||||
// installers.upload(
|
||||
// cmdname: 'mycelium'
|
||||
// source: '${gitpath}/target/x86_64-unknown-linux-musl/release/mycelium'
|
||||
// )!
|
||||
}
|
||||
|
||||
fn install_() ! {
|
||||
fn install() ! {
|
||||
console.print_header('install mycelium')
|
||||
// THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
|
||||
// mut url := ''
|
||||
// if core.is_linux_arm()! {
|
||||
// url = 'https://github.com/mycelium-dev/mycelium/releases/download/v${version}/mycelium_${version}_linux_arm64.tar.gz'
|
||||
// } else if core.is_linux_intel()! {
|
||||
// url = 'https://github.com/mycelium-dev/mycelium/releases/download/v${version}/mycelium_${version}_linux_amd64.tar.gz'
|
||||
// } else if core.is_osx_arm()! {
|
||||
// url = 'https://github.com/mycelium-dev/mycelium/releases/download/v${version}/mycelium_${version}_darwin_arm64.tar.gz'
|
||||
// } else if core.is_osx_intel()! {
|
||||
// url = 'https://github.com/mycelium-dev/mycelium/releases/download/v${version}/mycelium_${version}_darwin_amd64.tar.gz'
|
||||
// } else {
|
||||
// return error('unsported platform')
|
||||
// }
|
||||
|
||||
// mut dest := osal.download(
|
||||
// url: url
|
||||
// minsize_kb: 9000
|
||||
// expand_dir: '/tmp/mycelium'
|
||||
// )!
|
||||
mut z_installer := zinit_installer.get()!
|
||||
z_installer.start()!
|
||||
|
||||
// //dest.moveup_single_subdir()!
|
||||
mut url := ''
|
||||
if core.is_linux_arm()! {
|
||||
url = 'https://github.com/threefoldtech/mycelium/releases/download/v${version}/mycelium-aarch64-unknown-linux-musl.tar.gz'
|
||||
} else if core.is_linux_intel()! {
|
||||
url = 'https://github.com/threefoldtech/mycelium/releases/download/v${version}/mycelium-x86_64-unknown-linux-musl.tar.gz'
|
||||
} else if core.is_osx_arm()! {
|
||||
url = 'https://github.com/threefoldtech/mycelium/releases/download/v${version}/mycelium-aarch64-apple-darwin.tar.gz'
|
||||
} else if core.is_osx_intel()! {
|
||||
url = 'https://github.com/threefoldtech/mycelium/releases/download/v${version}/mycelium-x86_64-apple-darwin.tar.gz'
|
||||
} else {
|
||||
return error('unsported platform')
|
||||
}
|
||||
|
||||
// mut binpath := dest.file_get('mycelium')!
|
||||
// osal.cmd_add(
|
||||
// cmdname: 'mycelium'
|
||||
// source: binpath.path
|
||||
// )!
|
||||
pathlib.get_dir(
|
||||
path: '${osal.hero_path()!}/cfg'
|
||||
create: true
|
||||
)!
|
||||
|
||||
mut dest := osal.download(
|
||||
url: url
|
||||
minsize_kb: 5000
|
||||
expand_dir: '/tmp/mycelium'
|
||||
)!
|
||||
mut binpath := dest.file_get('mycelium')!
|
||||
osal.cmd_add(
|
||||
cmdname: 'mycelium'
|
||||
source: binpath.path
|
||||
)!
|
||||
}
|
||||
|
||||
fn build_() ! {
|
||||
// url := 'https://github.com/threefoldtech/mycelium'
|
||||
fn build() ! {
|
||||
url := 'https://github.com/threefoldtech/mycelium'
|
||||
myplatform := core.platform()!
|
||||
if myplatform != .ubuntu {
|
||||
return error('only support ubuntu for now')
|
||||
}
|
||||
rust.install()!
|
||||
|
||||
// make sure we install base on the node
|
||||
// if core.platform()!= .ubuntu {
|
||||
// return error('only support ubuntu for now')
|
||||
// }
|
||||
// golang.install()!
|
||||
console.print_header('build mycelium')
|
||||
|
||||
// console.print_header('build mycelium')
|
||||
mut gs := gittools.new()!
|
||||
gitpath := gs.get_path(
|
||||
pull: true
|
||||
reset: false
|
||||
url: url
|
||||
)!
|
||||
|
||||
// gitpath := gittools.get_repo(coderoot: '/tmp/builder', url: url, reset: true, pull: true)!
|
||||
panic('implement')
|
||||
|
||||
cmd := '
|
||||
cd ${gitpath}
|
||||
source ~/.cargo/env
|
||||
cargo install --path . --locked
|
||||
cargo build --release --locked --no-default-features --features=native-tls
|
||||
cp target/release/mycelium ~/.cargo/bin/mycelium
|
||||
mycelium --version
|
||||
'
|
||||
osal.execute_stdout(cmd)!
|
||||
|
||||
// cmd := '
|
||||
// cd ${gitpath}
|
||||
// source ~/.cargo/env
|
||||
// exit 1 #todo
|
||||
// '
|
||||
// osal.execute_stdout(cmd)!
|
||||
//
|
||||
// //now copy to the default bin path
|
||||
// mut binpath := dest.file_get('...')!
|
||||
// adds it to path
|
||||
@@ -154,34 +157,11 @@ fn build_() ! {
|
||||
// )!
|
||||
}
|
||||
|
||||
fn destroy_() ! {
|
||||
// mut systemdfactory := systemd.new()!
|
||||
// systemdfactory.destroy("zinit")!
|
||||
fn destroy() ! {
|
||||
osal.process_kill_recursive(name: 'mycelium')!
|
||||
osal.cmd_delete('mycelium')!
|
||||
|
||||
// osal.process_kill_recursive(name:'zinit')!
|
||||
// osal.cmd_delete('zinit')!
|
||||
|
||||
// osal.package_remove('
|
||||
// podman
|
||||
// conmon
|
||||
// buildah
|
||||
// skopeo
|
||||
// runc
|
||||
// ')!
|
||||
|
||||
// //will remove all paths where go/bin is found
|
||||
// osal.profile_path_add_remove(paths2delete:"go/bin")!
|
||||
|
||||
// osal.rm("
|
||||
// podman
|
||||
// conmon
|
||||
// buildah
|
||||
// skopeo
|
||||
// runc
|
||||
// /var/lib/containers
|
||||
// /var/lib/podman
|
||||
// /var/lib/buildah
|
||||
// /tmp/podman
|
||||
// /tmp/conmon
|
||||
// ")!
|
||||
osal.rm('
|
||||
mycelium
|
||||
')!
|
||||
}
|
||||
|
||||
@@ -3,17 +3,141 @@ module mycelium
|
||||
import freeflowuniverse.herolib.core.base
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.data.paramsparser
|
||||
import freeflowuniverse.herolib.sysadmin.startupmanager
|
||||
import freeflowuniverse.herolib.osal.zinit
|
||||
import time
|
||||
|
||||
__global (
|
||||
mycelium_global map[string]&MyceliumInstaller
|
||||
mycelium_default string
|
||||
mycelium_installer_global map[string]&MyceliumInstaller
|
||||
mycelium_installer_default string
|
||||
)
|
||||
|
||||
/////////FACTORY
|
||||
|
||||
@[params]
|
||||
pub struct ArgsGet {
|
||||
pub mut:
|
||||
name string
|
||||
}
|
||||
|
||||
fn args_get(args_ ArgsGet) ArgsGet {
|
||||
mut args := args_
|
||||
if args.name == '' {
|
||||
args.name = 'default'
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
pub fn get(args_ ArgsGet) !&MyceliumInstaller {
|
||||
mut context := base.context()!
|
||||
mut args := args_get(args_)
|
||||
mut obj := MyceliumInstaller{}
|
||||
if args.name !in mycelium_installer_global {
|
||||
if !exists(args)! {
|
||||
set(obj)!
|
||||
} else {
|
||||
heroscript := context.hero_config_get('mycelium', args.name)!
|
||||
mut obj_ := heroscript_loads(heroscript)!
|
||||
set_in_mem(obj_)!
|
||||
}
|
||||
}
|
||||
return mycelium_installer_global[args.name] or {
|
||||
println(mycelium_installer_global)
|
||||
// bug if we get here because should be in globals
|
||||
panic('could not get config for mycelium with name, is bug:${args.name}')
|
||||
}
|
||||
}
|
||||
|
||||
// register the config for the future
|
||||
pub fn set(o MyceliumInstaller) ! {
|
||||
set_in_mem(o)!
|
||||
mut context := base.context()!
|
||||
heroscript := heroscript_dumps(o)!
|
||||
context.hero_config_set('mycelium', o.name, heroscript)!
|
||||
}
|
||||
|
||||
// does the config exists?
|
||||
pub fn exists(args_ ArgsGet) !bool {
|
||||
mut context := base.context()!
|
||||
mut args := args_get(args_)
|
||||
return context.hero_config_exists('mycelium', args.name)
|
||||
}
|
||||
|
||||
pub fn delete(args_ ArgsGet) ! {
|
||||
mut args := args_get(args_)
|
||||
mut context := base.context()!
|
||||
context.hero_config_delete('mycelium', args.name)!
|
||||
if args.name in mycelium_installer_global {
|
||||
// del mycelium_installer_global[args.name]
|
||||
}
|
||||
}
|
||||
|
||||
// only sets in mem, does not set as config
|
||||
fn set_in_mem(o MyceliumInstaller) ! {
|
||||
mut o2 := obj_init(o)!
|
||||
mycelium_installer_global[o.name] = &o2
|
||||
mycelium_installer_default = o.name
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct PlayArgs {
|
||||
pub mut:
|
||||
heroscript string // if filled in then plbook will be made out of it
|
||||
plbook ?playbook.PlayBook
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn play(args_ PlayArgs) ! {
|
||||
mut args := args_
|
||||
|
||||
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }
|
||||
|
||||
mut install_actions := plbook.find(filter: 'mycelium.configure')!
|
||||
if install_actions.len > 0 {
|
||||
for install_action in install_actions {
|
||||
heroscript := install_action.heroscript()
|
||||
mut obj2 := heroscript_loads(heroscript)!
|
||||
set(obj2)!
|
||||
}
|
||||
}
|
||||
|
||||
mut other_actions := plbook.find(filter: 'mycelium.')!
|
||||
for other_action in other_actions {
|
||||
if other_action.name in ['destroy', 'install', 'build'] {
|
||||
mut p := other_action.params
|
||||
reset := p.get_default_false('reset')
|
||||
if other_action.name == 'destroy' || reset {
|
||||
console.print_debug('install action mycelium.destroy')
|
||||
destroy()!
|
||||
}
|
||||
if other_action.name == 'install' {
|
||||
console.print_debug('install action mycelium.install')
|
||||
install()!
|
||||
}
|
||||
}
|
||||
if other_action.name in ['start', 'stop', 'restart'] {
|
||||
mut p := other_action.params
|
||||
name := p.get('name')!
|
||||
mut mycelium_obj := get(name: name)!
|
||||
console.print_debug('action object:\n${mycelium_obj}')
|
||||
if other_action.name == 'start' {
|
||||
console.print_debug('install action mycelium.${other_action.name}')
|
||||
mycelium_obj.start()!
|
||||
}
|
||||
|
||||
if other_action.name == 'stop' {
|
||||
console.print_debug('install action mycelium.${other_action.name}')
|
||||
mycelium_obj.stop()!
|
||||
}
|
||||
if other_action.name == 'restart' {
|
||||
console.print_debug('install action mycelium.${other_action.name}')
|
||||
mycelium_obj.restart()!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -40,6 +164,12 @@ fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManag
|
||||
}
|
||||
}
|
||||
|
||||
// load from disk and make sure is properly intialized
|
||||
pub fn (mut self MyceliumInstaller) reload() ! {
|
||||
switch(self.name)
|
||||
self = obj_init(self)!
|
||||
}
|
||||
|
||||
pub fn (mut self MyceliumInstaller) start() ! {
|
||||
switch(self.name)
|
||||
if self.running()! {
|
||||
@@ -48,8 +178,8 @@ pub fn (mut self MyceliumInstaller) start() ! {
|
||||
|
||||
console.print_header('mycelium start')
|
||||
|
||||
if !installed_()! {
|
||||
install_()!
|
||||
if !installed()! {
|
||||
install()!
|
||||
}
|
||||
|
||||
configure()!
|
||||
@@ -77,9 +207,9 @@ pub fn (mut self MyceliumInstaller) start() ! {
|
||||
return error('mycelium did not install properly.')
|
||||
}
|
||||
|
||||
pub fn (mut self MyceliumInstaller) install_start(model InstallArgs) ! {
|
||||
pub fn (mut self MyceliumInstaller) install_start(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
self.install(model)!
|
||||
self.install(args)!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
@@ -119,19 +249,32 @@ pub mut:
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn install(args InstallArgs) ! {
|
||||
if args.reset {
|
||||
destroy()!
|
||||
}
|
||||
if !(installed_()!) {
|
||||
install_()!
|
||||
pub fn (mut self MyceliumInstaller) install(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
if args.reset || (!installed()!) {
|
||||
install()!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy() ! {
|
||||
destroy_()!
|
||||
pub fn (mut self MyceliumInstaller) build() ! {
|
||||
switch(self.name)
|
||||
build()!
|
||||
}
|
||||
|
||||
pub fn build() ! {
|
||||
build_()!
|
||||
pub fn (mut self MyceliumInstaller) destroy() ! {
|
||||
switch(self.name)
|
||||
self.stop() or {}
|
||||
destroy()!
|
||||
}
|
||||
|
||||
// switch instance to be used for mycelium
|
||||
pub fn switch(name string) {
|
||||
mycelium_installer_default = name
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
@[params]
|
||||
pub struct DefaultConfigArgs {
|
||||
instance string = 'default'
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module mycelium
|
||||
|
||||
import freeflowuniverse.herolib.data.paramsparser
|
||||
import os
|
||||
import freeflowuniverse.herolib.data.encoderhero
|
||||
import freeflowuniverse.herolib.osal.tun
|
||||
|
||||
pub const version = '0.0.0'
|
||||
pub const version = '0.5.7'
|
||||
const singleton = true
|
||||
const default = true
|
||||
|
||||
@@ -11,17 +11,59 @@ const default = true
|
||||
@[heap]
|
||||
pub struct MyceliumInstaller {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
name string = 'default'
|
||||
peers []string = [
|
||||
'tcp://188.40.132.242:9651',
|
||||
'quic://[2a01:4f8:212:fa6::2]:9651',
|
||||
'tcp://185.69.166.7:9651',
|
||||
'quic://[2a02:1802:5e:0:ec4:7aff:fe51:e36b]:9651',
|
||||
'tcp://65.21.231.58:9651',
|
||||
'quic://[2a01:4f9:5a:1042::2]:9651',
|
||||
'tcp://[2604:a00:50:17b:9e6b:ff:fe1f:e054]:9651',
|
||||
'quic://5.78.122.16:9651',
|
||||
'tcp://[2a01:4ff:2f0:3621::1]:9651',
|
||||
'quic://142.93.217.194:9651',
|
||||
]
|
||||
tun_nr int
|
||||
}
|
||||
|
||||
fn obj_init(obj_ MyceliumInstaller) !MyceliumInstaller {
|
||||
// never call get here, only thing we can do here is work on object itself
|
||||
mut obj := obj_
|
||||
panic('implement')
|
||||
return obj
|
||||
// your checking & initialization code if needed
|
||||
fn obj_init(mycfg_ MyceliumInstaller) !MyceliumInstaller {
|
||||
mut mycfg := mycfg_
|
||||
return mycfg
|
||||
}
|
||||
|
||||
// called before start if done
|
||||
fn configure() ! {
|
||||
// mut installer := get()!
|
||||
mut installer := get()!
|
||||
if installer.tun_nr == 0 {
|
||||
// Check if TUN is available first
|
||||
if available := tun.available() {
|
||||
if !available {
|
||||
return error('TUN is not available on this system')
|
||||
}
|
||||
// Get free TUN interface name
|
||||
if interface_name := tun.free() {
|
||||
// Parse the interface number from the name (e.g. "tun0" -> 0)
|
||||
nr := interface_name.trim_string_left('tun').int()
|
||||
installer.tun_nr = nr
|
||||
} else {
|
||||
return error('Failed to get free TUN interface: ${err}')
|
||||
}
|
||||
} else {
|
||||
return error('Failed to check TUN availability: ${err}')
|
||||
}
|
||||
set(installer)!
|
||||
}
|
||||
}
|
||||
|
||||
/////////////NORMALLY NO NEED TO TOUCH
|
||||
|
||||
pub fn heroscript_dumps(obj MyceliumInstaller) !string {
|
||||
return encoderhero.encode[MyceliumInstaller](obj)!
|
||||
}
|
||||
|
||||
pub fn heroscript_loads(heroscript string) !MyceliumInstaller {
|
||||
mut obj := encoderhero.decode[MyceliumInstaller](heroscript)!
|
||||
return obj
|
||||
}
|
||||
|
||||
1
lib/installers/net/mycelium/tun.v
Normal file
1
lib/installers/net/mycelium/tun.v
Normal file
@@ -0,0 +1 @@
|
||||
module mycelium
|
||||
@@ -1,34 +1,17 @@
|
||||
# zinit
|
||||
|
||||
Zinit is threefold startup manager, in linux will be launched inside systemd
|
||||
|
||||
```v
|
||||
|
||||
|
||||
To get started
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
```vlang
|
||||
|
||||
|
||||
import freeflowuniverse.herolib.installers.something. zinit
|
||||
|
||||
mut installer:= zinit.get()!
|
||||
import freeflowuniverse.herolib.installers.sysadmintools.zinit as zinit_installer
|
||||
|
||||
mut installer:=zinit_installer.get()!
|
||||
installer.start()!
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
## example heroscript
|
||||
|
||||
```hero
|
||||
!!zinit.install
|
||||
homedir: '/home/user/zinit'
|
||||
username: 'admin'
|
||||
password: 'secretpassword'
|
||||
title: 'Some Title'
|
||||
host: 'localhost'
|
||||
port: 8888
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ module zinit
|
||||
import freeflowuniverse.herolib.osal
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.core
|
||||
import freeflowuniverse.herolib.osal.zinit
|
||||
import freeflowuniverse.herolib.installers.ulist
|
||||
import freeflowuniverse.herolib.installers.lang.rust
|
||||
@@ -11,7 +12,7 @@ import freeflowuniverse.herolib.osal.systemd
|
||||
import os
|
||||
|
||||
// checks if a certain version or above is installed
|
||||
fn installed_() !bool {
|
||||
fn installed() !bool {
|
||||
cmd := 'zinit --version'
|
||||
// console.print_debug(cmd)
|
||||
res := os.execute(cmd)
|
||||
@@ -28,7 +29,7 @@ fn installed_() !bool {
|
||||
return false
|
||||
}
|
||||
|
||||
fn install_() ! {
|
||||
fn install() ! {
|
||||
console.print_header('install zinit')
|
||||
if !core.is_linux()! {
|
||||
return error('only support linux for now')
|
||||
@@ -52,7 +53,7 @@ fn install_() ! {
|
||||
console.print_header('install zinit done')
|
||||
}
|
||||
|
||||
fn build_() ! {
|
||||
fn build() ! {
|
||||
if !core.is_linux()! {
|
||||
return error('only support linux for now')
|
||||
}
|
||||
@@ -91,12 +92,12 @@ fn ulist_get() !ulist.UList {
|
||||
}
|
||||
|
||||
// uploads to S3 server if configured
|
||||
fn upload_() ! {
|
||||
fn upload() ! {
|
||||
}
|
||||
|
||||
fn startupcmd() ![]ZProcessNewArgs {
|
||||
fn startupcmd() ![]zinit.ZProcessNewArgs {
|
||||
mut res := []zinit.ZProcessNewArgs{}
|
||||
res << ZProcessNewArgs{
|
||||
res << zinit.ZProcessNewArgs{
|
||||
name: 'zinit'
|
||||
cmd: '/usr/local/bin/zinit init'
|
||||
startuptype: .systemd
|
||||
@@ -106,7 +107,7 @@ fn startupcmd() ![]ZProcessNewArgs {
|
||||
return res
|
||||
}
|
||||
|
||||
fn running_() !bool {
|
||||
fn running() !bool {
|
||||
cmd := 'zinit list'
|
||||
return osal.execute_ok(cmd)
|
||||
}
|
||||
@@ -123,7 +124,7 @@ fn stop_pre() ! {
|
||||
fn stop_post() ! {
|
||||
}
|
||||
|
||||
fn destroy_() ! {
|
||||
fn destroy() ! {
|
||||
mut systemdfactory := systemd.new()!
|
||||
systemdfactory.destroy('zinit')!
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ module zinit
|
||||
import freeflowuniverse.herolib.core.base
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.data.paramsparser
|
||||
import freeflowuniverse.herolib.sysadmin.startupmanager
|
||||
import freeflowuniverse.herolib.osal.zinit
|
||||
import time
|
||||
@@ -14,6 +15,65 @@ __global (
|
||||
|
||||
/////////FACTORY
|
||||
|
||||
@[params]
|
||||
pub struct ArgsGet {
|
||||
pub mut:
|
||||
name string
|
||||
}
|
||||
|
||||
pub fn get(args_ ArgsGet) !&Zinit {
|
||||
return &Zinit{}
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct PlayArgs {
|
||||
pub mut:
|
||||
heroscript string // if filled in then plbook will be made out of it
|
||||
plbook ?playbook.PlayBook
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn play(args_ PlayArgs) ! {
|
||||
mut args := args_
|
||||
|
||||
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }
|
||||
|
||||
mut other_actions := plbook.find(filter: 'zinit.')!
|
||||
for other_action in other_actions {
|
||||
if other_action.name in ['destroy', 'install', 'build'] {
|
||||
mut p := other_action.params
|
||||
reset := p.get_default_false('reset')
|
||||
if other_action.name == 'destroy' || reset {
|
||||
console.print_debug('install action zinit.destroy')
|
||||
destroy()!
|
||||
}
|
||||
if other_action.name == 'install' {
|
||||
console.print_debug('install action zinit.install')
|
||||
install()!
|
||||
}
|
||||
}
|
||||
if other_action.name in ['start', 'stop', 'restart'] {
|
||||
mut p := other_action.params
|
||||
name := p.get('name')!
|
||||
mut zinit_obj := get(name: name)!
|
||||
console.print_debug('action object:\n${zinit_obj}')
|
||||
if other_action.name == 'start' {
|
||||
console.print_debug('install action zinit.${other_action.name}')
|
||||
zinit_obj.start()!
|
||||
}
|
||||
|
||||
if other_action.name == 'stop' {
|
||||
console.print_debug('install action zinit.${other_action.name}')
|
||||
zinit_obj.stop()!
|
||||
}
|
||||
if other_action.name == 'restart' {
|
||||
console.print_debug('install action zinit.${other_action.name}')
|
||||
zinit_obj.restart()!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -48,8 +108,8 @@ pub fn (mut self Zinit) start() ! {
|
||||
|
||||
console.print_header('zinit start')
|
||||
|
||||
if !installed_()! {
|
||||
install_()!
|
||||
if !installed()! {
|
||||
install()!
|
||||
}
|
||||
|
||||
configure()!
|
||||
@@ -77,9 +137,9 @@ pub fn (mut self Zinit) start() ! {
|
||||
return error('zinit did not install properly.')
|
||||
}
|
||||
|
||||
pub fn (mut self Zinit) install_start(model InstallArgs) ! {
|
||||
pub fn (mut self Zinit) install_start(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
self.install(model)!
|
||||
self.install(args)!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
@@ -119,19 +179,32 @@ pub mut:
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn install(args InstallArgs) ! {
|
||||
if args.reset {
|
||||
destroy()!
|
||||
}
|
||||
if !(installed_()!) {
|
||||
install_()!
|
||||
pub fn (mut self Zinit) install(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
if args.reset || (!installed()!) {
|
||||
install()!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy() ! {
|
||||
destroy_()!
|
||||
pub fn (mut self Zinit) build() ! {
|
||||
switch(self.name)
|
||||
build()!
|
||||
}
|
||||
|
||||
pub fn build() ! {
|
||||
build_()!
|
||||
pub fn (mut self Zinit) destroy() ! {
|
||||
switch(self.name)
|
||||
self.stop() or {}
|
||||
destroy()!
|
||||
}
|
||||
|
||||
// switch instance to be used for zinit
|
||||
pub fn switch(name string) {
|
||||
zinit_default = name
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
@[params]
|
||||
pub struct DefaultConfigArgs {
|
||||
instance string = 'default'
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
!!hero_code.generate_installer
|
||||
name:'buildah'
|
||||
classname:'BuildahInstaller'
|
||||
singleton:1
|
||||
templates:0
|
||||
default:1
|
||||
title:''
|
||||
supported_platforms:''
|
||||
reset:0
|
||||
startupmanager:0
|
||||
hasconfig:0
|
||||
build:0
|
||||
@@ -1,36 +0,0 @@
|
||||
module buildah
|
||||
|
||||
import freeflowuniverse.herolib.osal
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.installers.ulist
|
||||
import freeflowuniverse.herolib.core
|
||||
|
||||
// checks if a certain version or above is installed
|
||||
fn installed() !bool {
|
||||
osal.execute_silent('buildah -v') or { return false }
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fn install() ! {
|
||||
console.print_header('install buildah')
|
||||
if core.platform()! != .ubuntu {
|
||||
return error('Only ubuntu is supported for now')
|
||||
}
|
||||
|
||||
cmd := 'sudo apt-get -y update && sudo apt-get -y install buildah'
|
||||
osal.execute_stdout(cmd)!
|
||||
|
||||
console.print_header('Buildah Installed Successfuly')
|
||||
}
|
||||
|
||||
// get the Upload List of the files
|
||||
fn ulist_get() !ulist.UList {
|
||||
// mut installer := get()!
|
||||
// optionally build a UList which is all paths which are result of building, is then used e.g. in upload
|
||||
return ulist.UList{}
|
||||
}
|
||||
|
||||
fn destroy() ! {
|
||||
osal.execute_stdout('sudo apt remove --purge -y buildah')!
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
module buildah
|
||||
|
||||
import freeflowuniverse.herolib.core.base
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.sysadmin.startupmanager
|
||||
import freeflowuniverse.herolib.osal.zinit
|
||||
import time
|
||||
|
||||
__global (
|
||||
buildah_global map[string]&BuildahInstaller
|
||||
buildah_default string
|
||||
)
|
||||
|
||||
/////////FACTORY
|
||||
|
||||
@[params]
|
||||
pub struct ArgsGet {
|
||||
pub mut:
|
||||
name string
|
||||
}
|
||||
|
||||
pub fn get(args_ ArgsGet) !&BuildahInstaller {
|
||||
return &BuildahInstaller{}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManager {
|
||||
// unknown
|
||||
// screen
|
||||
// zinit
|
||||
// tmux
|
||||
// systemd
|
||||
match cat {
|
||||
.zinit {
|
||||
console.print_debug('startupmanager: zinit')
|
||||
return startupmanager.get(cat: .zinit)!
|
||||
}
|
||||
.systemd {
|
||||
console.print_debug('startupmanager: systemd')
|
||||
return startupmanager.get(cat: .systemd)!
|
||||
}
|
||||
else {
|
||||
console.print_debug('startupmanager: auto')
|
||||
return startupmanager.get()!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct InstallArgs {
|
||||
pub mut:
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn (mut self BuildahInstaller) install(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
if args.reset || (!installed()!) {
|
||||
install()!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut self BuildahInstaller) destroy() ! {
|
||||
switch(self.name)
|
||||
destroy()!
|
||||
}
|
||||
|
||||
// switch instance to be used for buildah
|
||||
pub fn switch(name string) {
|
||||
buildah_default = name
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
module buildah
|
||||
|
||||
const singleton = true
|
||||
const default = true
|
||||
|
||||
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
|
||||
@[heap]
|
||||
pub struct BuildahInstaller {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
}
|
||||
|
||||
fn obj_init(obj_ BuildahInstaller) !BuildahInstaller {
|
||||
// never call get here, only thing we can do here is work on object itself
|
||||
mut obj := obj_
|
||||
return obj
|
||||
}
|
||||
|
||||
// called before start if done
|
||||
fn configure() ! {
|
||||
// mut installer := get()!
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
# buildah
|
||||
|
||||
|
||||
|
||||
To get started
|
||||
|
||||
```vlang
|
||||
|
||||
|
||||
import freeflowuniverse.herolib.installers.something. buildah
|
||||
|
||||
mut installer:= buildah.get()!
|
||||
|
||||
installer.start()!
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
## example heroscript
|
||||
|
||||
```hero
|
||||
!!buildah.install
|
||||
homedir: '/home/user/buildah'
|
||||
username: 'admin'
|
||||
password: 'secretpassword'
|
||||
title: 'Some Title'
|
||||
host: 'localhost'
|
||||
port: 8888
|
||||
|
||||
```
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user