Files
herolib/lib/data/dbfs/db.v
Mahmoud Emad ccfc7c4656 fix: improve package management and screen status
- Fix issues in package installation and removal across
different platforms (Ubuntu, macOS, Alpine, Arch).
- Improve error handling and add sudo support where
necessary.
- Enhance screen status check to accurately reflect
process activity.
- Address minor bugs in `db.v`, `done.v`, and
`net_test.v`.
- Correct minor inconsistencies in package names.
2024-12-25 17:12:08 +02:00

313 lines
7.5 KiB
V

module dbfs
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.crypt.aes_symmetric
import encoding.base64
// import freeflowuniverse.herolib.ui.console
@[heap]
pub struct DB {
mut:
config DBConfig
pub mut:
path pathlib.Path
parent &DBCollection @[skip; str: skip]
namedb ?NameDB // optional namedb which is for hashed keys
}
pub struct DBConfig {
mut:
encrypted bool
pub:
name string
withkeys bool // if set means we will use keys in stead of only u32
keyshashed bool // if its ok to hash the keys, which will generate id out of these keys and its more scalable
ext string // extension if we want to use it in DB e.g. 'json'
// base64 bool //if binary data will be base encoded, not used now
}
@[params]
pub struct GetArgs {
pub mut:
key string
id u32
}
// get the value, if it doesn't exist then return empty string
pub fn (mut db DB) get(args_ GetArgs) !string {
mut args := args_
args.key = texttools.name_fix(args.key)
mut pathsrc := pathlib.Path{}
if args.key.len > 0 {
if args.id > 0 {
return error("cann't specify id and key")
}
if db.config.withkeys {
if db.config.keyshashed {
// means we use a namedb
mut ndb := db.namedb or { panic('namedb should be available') }
args.id, _ = ndb.get(args.key)!
pathsrc = db.path_get(args.id)!
} else {
// now we need to use the link as set
mut datapath0 := '${db.path.path}/${args.key}'
if db.config.ext.len > 0 {
datapath0 += '.${db.config.ext}'
}
pathsrc = pathlib.get_link(path: datapath0, create: false)!
}
} else {
pathsrc = db.path_get(args.id)!
}
} else if args.id > 0 {
pathsrc = db.path_get(args.id)!
} else {
return error('either id or key has to be specified')
}
mut data := pathsrc.read()!
if data.len == 0 {
panic('data cannot be empty for get:${args}')
}
if db.is_encrypted() {
data = aes_symmetric.decrypt_str(data, db.secret()!)
}
return data
}
@[params]
pub struct SetArgs {
pub mut:
key string
id u32
value string
valueb []u8 // as bytes
}
// set the key/value will go to filesystem, is organzed per context and each db has a name
pub fn (mut db DB) set(args_ SetArgs) !u32 {
// console.print_debug(args_)
mut args := args_
if args.value.len == 0 && args.valueb.len == 0 {
return error('specify for value or valueb, now both empty')
}
if args.key.len > 0 {
args.key = texttools.name_fix(args.key)
}
if args.value.len > 0 {
args.valueb = args.value.bytes()
args.value = ''
}
mut pathsrc := pathlib.Path{}
// lets deal with key
if args.key.len > 0 {
if args.id > 0 {
return error('cant have id and key at same time')
}
if !db.config.withkeys {
return error('db needs to be with keys')
}
if db.config.keyshashed {
// means we use a namedb
mut ndb := db.namedb or { panic('namedb should be available') }
args.id = ndb.set(args.key, '')!
pathsrc = db.path_get(args.id)!
} else {
mut datapath0 := '${db.path.path}/${args.key}'
if db.config.ext.len > 0 {
datapath0 += '.${db.config.ext}'
}
pathsrc = pathlib.get_link(path: datapath0, create: false)!
if !pathsrc.exists() {
args.id = db.parent.incr()!
mut destname := '${db.path.path}/${args.key}'
if db.config.ext.len > 0 {
destname += '.${db.config.ext}'
}
pathsrc = db.path_get(args.id)!
pathsrc.write('')!
pathsrc.link(destname, true)! // link the key to the right source info
} else {
mut p3 := pathsrc.getlink()!
p3_name := p3.name()
args.id = p3_name.u32()
}
}
} else if args.id > 0 {
pathsrc = db.path_get(args.id)!
} else {
args.id = db.parent.incr()!
pathsrc = db.path_get(args.id)!
}
console.print_debug('keydb ${pathsrc}')
if db.config.encrypted {
args.valueb = aes_symmetric.encrypt(args.valueb, db.secret()!)
pathsrc.write(base64.encode(args.valueb))!
} else {
pathsrc.writeb(args.valueb)!
}
assert args.id > 0
return args.id
}
// get path based on int id in the DB
fn (mut db DB) path_get(myid u32) !pathlib.Path {
a, b, c := namedb_dbid(myid)
mut destname := c.str()
if db.config.ext.len > 0 {
destname += '.${db.config.ext}'
}
mut mydatafile := pathlib.get_file(
path: '${db.path.path}/${a}/${b}/${destname}'
create: false
)!
return mydatafile
}
// check if entry exists based on keyname
pub fn (mut db DB) exists(args_ GetArgs) !bool {
mut args := args_
args.key = texttools.name_fix(args.key)
mut pathsrc := pathlib.Path{}
if args.key.len > 0 {
if args.id > 0 {
return error("cann't specify id and key")
}
if !db.config.withkeys {
return error('db needs to be with keys')
}
if db.config.keyshashed {
// means we use a namedb
mut ndb := db.namedb or { panic('namedb should be available') }
return ndb.exists(args.key)!
} else {
mut datapath0 := '${db.path.path}/${args.key}'
if db.config.ext.len > 0 {
datapath0 += '.${db.config.ext}'
}
pathsrc = pathlib.get_link(path: datapath0, create: false)!
}
} else {
pathsrc = db.path_get(args.id)!
}
return pathsrc.exists()
}
// delete an entry
pub fn (mut db DB) delete(args_ GetArgs) ! {
mut args := args_
if args.key.len > 0 {
args.key = texttools.name_fix(args.key)
}
mut pathsrc := pathlib.Path{}
if args.key.len > 0 {
if args.id > 0 {
return error('cant have id and key at same time')
}
if !db.config.withkeys {
return error('db needs to be with keys')
}
if db.config.keyshashed {
// means we use a namedb
mut ndb := db.namedb or { panic('namedb should be available') }
args.id, _ = ndb.get(args.key)!
pathsrc = db.path_get(args.id)!
ndb.delete(args.key)!
} else {
mut datapath0 := '${db.path.path}/${args.key}'
if db.config.ext.len > 0 {
datapath0 += '.${db.config.ext}'
}
pathsrc = pathlib.get_link(path: datapath0, create: false)!
if pathsrc.exists() {
mut p3 := pathsrc.getlink()!
p3.delete()!
}
}
} else {
pathsrc = db.path_get(args.id)!
}
pathsrc.delete()!
}
// delete the db, will not be able to use it any longer
pub fn (mut db DB) destroy() ! {
db.path.delete()!
}
// get all keys of the db (e.g. per session) can be with a prefix
pub fn (mut db DB) keys(prefix_ string) ![]string {
// TODO: see get, to fix this one
if prefix_.len == 0 {
return []
}
mut files := db.path.list()!
panic('implement ${files}')
prefix := texttools.name_fix(prefix_)
mut r := db.path.list(recursive: false)!
mut res := []string{}
for item in r.paths {
name := item.name()
if prefix == '' || name.starts_with(prefix) {
res << name
}
}
return res
}
// get all keys of the db (e.g. per session) can be with a prefix
pub fn (mut db DB) ids() ![]u32 {
// TODO: see get, to fix this one
mut files := db.path.list(files_only: true)!
mut res := []u32{}
for file in files.paths {
res << u32(file.name().int())
}
return res
}
// delete all data
pub fn (mut db DB) empty() ! {
db.path.empty()!
}
fn (mut db DB) secret() !string {
if db.is_encrypted() {
return db.parent.secret
}
return ''
}
// will mark db for encryption .
// will go over all existing keys and encrypt
pub fn (mut db DB) encrypt() ! {
// TODO: see get, to fix this one
if db.config.encrypted {
return
}
db.secret()! // just to check if ok
for key in db.keys('')! {
v := db.get(key: key)!
encrypted_v := aes_symmetric.encrypt(v.bytes(), db.secret()!)
db.set(key: key, valueb: encrypted_v)!
}
db.config.encrypted = true
db.path.file_get_new('encrypted')!
}
pub fn (db DB) is_encrypted() bool {
return db.config.encrypted
}