This commit is contained in:
2025-10-29 09:35:46 +04:00
parent fbed626771
commit 9c8bcbff0c
69 changed files with 203 additions and 271 deletions

View File

@@ -0,0 +1,8 @@
!!hero_code.generate_client
name:'rcloneclient'
classname:'RCloneClient'
singleton:1
default:1
hasconfig:1
reset:0

View File

@@ -0,0 +1,44 @@
module rcloneclient
import incubaid.herolib.core.playbook
import incubaid.herolib.core.texttools
import os
// const configfile = '${os.home_dir()}/.config/rclone/rclone.conf'
// // will look for personal configuration file in ~/hero/config .
// // this file is in heroscript format and will have all required info to configure rclone
// //```
// // !!config.s3server_define
// // name:'tf_write_1'
// // description:'ThreeFold Read Write Repo 1
// // keyid:'003e2a7be6357fb0000000001'
// // keyname:'tfrw'
// // appkey:'K003UsdrYOZou2ulBHA8p4KLa/dL2n4'
// // url:''
// //```
// pub fn configure() ! {
// mut plbook := playbook.new(
// path: configfile
// // actor_filter: ['config']
// // action_filter: [
// // 's3server_define',
// // ]
// )!
// actions := plbook.find(filter: 'config.s3server_define')!
// mut out := ''
// for action in actions {
// mut name := action.params.get_default('name', '')!
// mut descr := action.params.get_default('descr', '')!
// mut config := '
// [${name}]
// type = b2
// account = e2a7be6357fb
// hard_delete = true
// key = 003b79a6ae62cd7cb04477834a24e007e7afc601ba
// '
// config = texttools.dedent(config)
// out += '\n${config}\n'
// }
// }

View File

@@ -0,0 +1,39 @@
module rcloneclient
fn test_rclone_new() {
rclone := new('test_remote') or { panic(err) }
assert rclone.name == 'test_remote'
}
fn test_check_installed() {
installed := check_installed()
// This test will pass or fail depending on whether rclone is installed
// on the system. It's mainly for documentation purposes.
println('RCloneClient installed: ${installed}')
}
// Note: The following tests are commented out as they require an actual rclone
// configuration and remote to work with. They serve as examples of how to use
// the RCloneClient module.
/*
fn test_rclone_operations() ! {
mut rclone := new('my_remote')!
// Test upload
rclone.upload('./testdata', 'backup/testdata')!
// Test download
rclone.download('backup/testdata', './testdata_download')!
// Test mount
rclone.mount('backup', './mounted_backup')!
// Test list
content := rclone.list('backup')!
println(content)
// Test unmount
rclone.unmount('./mounted_backup')!
}
*/

View File

@@ -0,0 +1,75 @@
module rcloneclient
import os
// // RCloneClient represents a configured rclone instance
// pub struct RCloneClient {
// pub mut:
// name string // name of the remote
// }
// mount mounts a remote at the specified path
pub fn (mut r RCloneClient) mount(remote_path string, local_path string) ! {
if !os.exists(local_path) {
os.mkdir_all(local_path) or { return error('Failed to create mount directory: ${err}') }
}
cmd := 'rclone mount ${r.name}:${remote_path} ${local_path} --daemon'
res := os.execute(cmd)
if res.exit_code != 0 {
return error('Failed to mount remote: ${res.output}')
}
}
// unmount unmounts a mounted remote
pub fn (mut r RCloneClient) unmount(local_path string) ! {
if os.user_os() == 'macos' {
os.execute_opt('umount ${local_path}') or { return error('Failed to unmount: ${err}') }
} else {
os.execute_opt('fusermount -u ${local_path}') or {
return error('Failed to unmount: ${err}')
}
}
}
// upload uploads a file or directory to the remote
pub fn (mut r RCloneClient) upload(local_path string, remote_path string) ! {
if !os.exists(local_path) {
return error('Local path does not exist: ${local_path}')
}
cmd := 'rclone copy ${local_path} ${r.name}:${remote_path}'
res := os.execute(cmd)
if res.exit_code != 0 {
return error('Failed to upload: ${res.output}')
}
}
// download downloads a file or directory from the remote
pub fn (mut r RCloneClient) download(remote_path string, local_path string) ! {
if !os.exists(local_path) {
os.mkdir_all(local_path) or { return error('Failed to create local directory: ${err}') }
}
cmd := 'rclone copy ${r.name}:${remote_path} ${local_path}'
res := os.execute(cmd)
if res.exit_code != 0 {
return error('Failed to download: ${res.output}')
}
}
// list lists contents of a remote path
pub fn (mut r RCloneClient) list(remote_path string) !string {
cmd := 'rclone ls ${r.name}:${remote_path}'
res := os.execute(cmd)
if res.exit_code != 0 {
return error('Failed to list remote contents: ${res.output}')
}
return res.output
}
// check_installed checks if rclone is installed
pub fn check_installed() bool {
res := os.execute('which rclone')
return res.exit_code == 0
}

View File

@@ -0,0 +1,139 @@
module rcloneclient
import incubaid.herolib.core.base
import incubaid.herolib.core.playbook { PlayBook }
import incubaid.herolib.ui.console
import json
__global (
rcloneclient_global map[string]&RCloneClient
rcloneclient_default string
)
/////////FACTORY
@[params]
pub struct ArgsGet {
pub mut:
name string = 'default'
fromdb bool // will load from filesystem
create bool // default will not create if not exist
}
pub fn new(args ArgsGet) !&RCloneClient {
mut obj := RCloneClient{
name: args.name
}
set(obj)!
return get(name: args.name)!
}
pub fn get(args ArgsGet) !&RCloneClient {
mut context := base.context()!
rcloneclient_default = args.name
if args.fromdb || args.name !in rcloneclient_global {
mut r := context.redis()!
if r.hexists('context:rcloneclient', args.name)! {
data := r.hget('context:rcloneclient', args.name)!
if data.len == 0 {
print_backtrace()
return error('RCloneClient with name: rcloneclient does not exist, prob bug.')
}
mut obj := json.decode(RCloneClient, data)!
set_in_mem(obj)!
} else {
if args.create {
new(args)!
} else {
print_backtrace()
return error("RCloneClient with name 'rcloneclient' does not exist")
}
}
return get(name: args.name)! // no longer from db nor create
}
return rcloneclient_global[args.name] or {
print_backtrace()
return error('could not get config for rcloneclient with name:rcloneclient')
}
}
// register the config for the future
pub fn set(o RCloneClient) ! {
mut o2 := set_in_mem(o)!
rcloneclient_default = o2.name
mut context := base.context()!
mut r := context.redis()!
r.hset('context:rcloneclient', o2.name, json.encode(o2))!
}
// does the config exists?
pub fn exists(args ArgsGet) !bool {
mut context := base.context()!
mut r := context.redis()!
return r.hexists('context:rcloneclient', args.name)!
}
pub fn delete(args ArgsGet) ! {
mut context := base.context()!
mut r := context.redis()!
r.hdel('context:rcloneclient', args.name)!
}
@[params]
pub struct ArgsList {
pub mut:
fromdb bool // will load from filesystem
}
// if fromdb set: load from filesystem, and not from mem, will also reset what is in mem
pub fn list(args ArgsList) ![]&RCloneClient {
mut res := []&RCloneClient{}
mut context := base.context()!
if args.fromdb {
// reset what is in mem
rcloneclient_global = map[string]&RCloneClient{}
rcloneclient_default = ''
}
if args.fromdb {
mut r := context.redis()!
mut l := r.hkeys('context:rcloneclient')!
for name in l {
res << get(name: name, fromdb: true)!
}
return res
} else {
// load from memory
for _, client in rcloneclient_global {
res << client
}
}
return res
}
// only sets in mem, does not set as config
fn set_in_mem(o RCloneClient) !RCloneClient {
mut o2 := obj_init(o)!
rcloneclient_global[o2.name] = &o2
rcloneclient_default = o2.name
return o2
}
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'rcloneclient.') {
return
}
mut install_actions := plbook.find(filter: 'rcloneclient.configure')!
if install_actions.len > 0 {
for mut install_action in install_actions {
heroscript := install_action.heroscript()
mut obj2 := heroscript_loads(heroscript)!
set(obj2)!
install_action.done = true
}
}
}
// switch instance to be used for rcloneclient
pub fn switch(name string) {
}

View File

@@ -0,0 +1,74 @@
module rcloneclient
import incubaid.herolib.data.paramsparser
import incubaid.herolib.data.encoderhero
import os
pub const version = '0.0.0'
const singleton = true
const default = true
pub fn heroscript_default() !string {
name := os.getenv_opt('RCLONE_NAME') or { 'default' }
remote_type := os.getenv_opt('RCLONE_TYPE') or { 's3' }
provider := os.getenv_opt('RCLONE_PROVIDER') or { 'aws' }
access_key := os.getenv_opt('RCLONE_ACCESS_KEY') or { '' }
secret_key := os.getenv_opt('RCLONE_SECRET_KEY') or { '' }
region := os.getenv_opt('RCLONE_REGION') or { 'us-east-1' }
endpoint := os.getenv_opt('RCLONE_ENDPOINT') or { '' }
heroscript := "
!!rclone.configure
name: '${name}'
type: '${remote_type}'
provider: '${provider}'
access_key: '${access_key}'
secret_key: '${secret_key}'
region: '${region}'
endpoint: '${endpoint}'
"
return heroscript
}
@[heap]
pub struct RCloneClient {
pub mut:
name string = 'default'
type_ string = 's3' // remote type (s3, sftp, etc)
provider string = 'aws' // provider for s3 (aws, minio, etc)
access_key string // access key for authentication
secret_key string // secret key for authentication
region string = 'us-east-1' // region for s3
endpoint string // custom endpoint URL if needed
}
fn cfg_play(p paramsparser.Params) ! {
mut mycfg := RCloneClient{
name: p.get_default('name', 'default')!
type_: p.get_default('type', 's3')!
provider: p.get_default('provider', 'aws')!
access_key: p.get('access_key')!
secret_key: p.get('secret_key')!
region: p.get_default('region', 'us-east-1')!
endpoint: p.get_default('endpoint', '')!
}
set(mycfg)!
}
fn obj_init(obj_ RCloneClient) !RCloneClient {
// never call get here, only thing we can do here is work on object itself
mut obj := obj_
return obj
}
/////////////NORMALLY NO NEED TO TOUCH
pub fn heroscript_dumps(obj RCloneClient) !string {
return encoderhero.encode[RCloneClient](obj)!
}
pub fn heroscript_loads(heroscript string) !RCloneClient {
mut obj := encoderhero.decode[RCloneClient](heroscript)!
return obj
}

View File

@@ -0,0 +1,38 @@
# a sal to work with rclone
Rclone is this incredible swiss army knive to deal with S3 storage servers.
## Example
```golang
import incubaid.herolib.osal.core.rclone
fn main() {
do() or { panic(err) }
}
fn do() ! {
mut z:=rclone.new()!
// name string @[required]
// cmd string @[required]
// cmd_file bool //if we wanna force to run it as a file which is given to bash -c (not just a cmd in rclone)
// test string
// test_file bool
// after []string
// env map[string]string
// oneshot bool
p:=z.new(
name:"test"
cmd:'/bin/bash'
)!
}
```
## protocol defined in
sal on top of <https://github.com/threefoldtech/rclone/tree/master>
<https://github.com/threefoldtech/rclone/blob/master/docs/protocol.md>