mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-08-05 12:56:48 +02:00
209 lines
4.8 KiB
Go
209 lines
4.8 KiB
Go
//go:build mipsle || riscv64
|
|
// +build mipsle riscv64
|
|
|
|
package database
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
func newDatabase(dbfile string, readOnlyMode bool) (*Database, error) {
|
|
dbRootPath := filepath.ToSlash(filepath.Clean(dbfile))
|
|
dbRootPath = "fsdb/" + dbRootPath
|
|
err := os.MkdirAll(dbRootPath, 0755)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tableMap := sync.Map{}
|
|
//build the table list from file system
|
|
files, err := filepath.Glob(filepath.Join(dbRootPath, "/*"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, file := range files {
|
|
if isDirectory(file) {
|
|
tableMap.Store(filepath.Base(file), "")
|
|
}
|
|
}
|
|
|
|
log.Println("Filesystem Emulated Key-value Database Service Started: " + dbRootPath)
|
|
return &Database{
|
|
Db: dbRootPath,
|
|
Tables: tableMap,
|
|
ReadOnly: readOnlyMode,
|
|
}, nil
|
|
}
|
|
|
|
func (d *Database) dump(filename string) ([]string, error) {
|
|
//Get all file objects from root
|
|
rootfiles, err := filepath.Glob(filepath.Join(d.Db.(string), "/*"))
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
|
|
//Filter out the folders
|
|
rootFolders := []string{}
|
|
for _, file := range rootfiles {
|
|
if !isDirectory(file) {
|
|
rootFolders = append(rootFolders, filepath.Base(file))
|
|
}
|
|
}
|
|
|
|
return rootFolders, nil
|
|
}
|
|
|
|
func (d *Database) newTable(tableName string) error {
|
|
if d.ReadOnly {
|
|
return errors.New("Operation rejected in ReadOnly mode")
|
|
}
|
|
tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
|
|
if !fileExists(tablePath) {
|
|
return os.MkdirAll(tablePath, 0755)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *Database) tableExists(tableName string) bool {
|
|
tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
|
|
if _, err := os.Stat(tablePath); errors.Is(err, os.ErrNotExist) {
|
|
return false
|
|
}
|
|
|
|
if !isDirectory(tablePath) {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (d *Database) dropTable(tableName string) error {
|
|
if d.ReadOnly {
|
|
return errors.New("Operation rejected in ReadOnly mode")
|
|
}
|
|
tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
|
|
if d.tableExists(tableName) {
|
|
return os.RemoveAll(tablePath)
|
|
} else {
|
|
return errors.New("table not exists")
|
|
}
|
|
|
|
}
|
|
|
|
func (d *Database) write(tableName string, key string, value interface{}) error {
|
|
if d.ReadOnly {
|
|
return errors.New("Operation rejected in ReadOnly mode")
|
|
}
|
|
tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
|
|
js, err := json.Marshal(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
|
|
|
|
return os.WriteFile(filepath.Join(tablePath, key+".entry"), js, 0755)
|
|
}
|
|
|
|
func (d *Database) read(tableName string, key string, assignee interface{}) error {
|
|
if !d.keyExists(tableName, key) {
|
|
return errors.New("key not exists")
|
|
}
|
|
|
|
key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
|
|
|
|
tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
|
|
entryPath := filepath.Join(tablePath, key+".entry")
|
|
content, err := os.ReadFile(entryPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = json.Unmarshal(content, &assignee)
|
|
return err
|
|
}
|
|
|
|
func (d *Database) keyExists(tableName string, key string) bool {
|
|
key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
|
|
tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
|
|
entryPath := filepath.Join(tablePath, key+".entry")
|
|
return fileExists(entryPath)
|
|
}
|
|
|
|
func (d *Database) delete(tableName string, key string) error {
|
|
if d.ReadOnly {
|
|
return errors.New("Operation rejected in ReadOnly mode")
|
|
}
|
|
if !d.keyExists(tableName, key) {
|
|
return errors.New("key not exists")
|
|
}
|
|
key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
|
|
tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
|
|
entryPath := filepath.Join(tablePath, key+".entry")
|
|
|
|
return os.Remove(entryPath)
|
|
}
|
|
|
|
func (d *Database) listTable(tableName string) ([][][]byte, error) {
|
|
if !d.tableExists(tableName) {
|
|
return [][][]byte{}, errors.New("table not exists")
|
|
}
|
|
tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
|
|
entries, err := filepath.Glob(filepath.Join(tablePath, "/*.entry"))
|
|
if err != nil {
|
|
return [][][]byte{}, err
|
|
}
|
|
|
|
var results [][][]byte = [][][]byte{}
|
|
for _, entry := range entries {
|
|
if !isDirectory(entry) {
|
|
//Read it
|
|
key := filepath.Base(entry)
|
|
key = strings.TrimSuffix(key, filepath.Ext(key))
|
|
key = strings.ReplaceAll(key, "-SLASH_SIGN-", "/")
|
|
|
|
bkey := []byte(key)
|
|
bval := []byte("")
|
|
c, err := os.ReadFile(entry)
|
|
if err != nil {
|
|
break
|
|
}
|
|
|
|
bval = c
|
|
results = append(results, [][]byte{bkey, bval})
|
|
}
|
|
}
|
|
return results, nil
|
|
}
|
|
|
|
func (d *Database) close() {
|
|
//Nothing to close as it is file system
|
|
}
|
|
|
|
func isDirectory(path string) bool {
|
|
fileInfo, err := os.Stat(path)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
return fileInfo.IsDir()
|
|
}
|
|
|
|
func fileExists(name string) bool {
|
|
_, err := os.Stat(name)
|
|
if err == nil {
|
|
return true
|
|
}
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return false
|
|
}
|
|
return false
|
|
}
|