|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import ast |
|
import os |
|
import sys |
|
|
|
from .. import run_as_module |
|
from . import * |
|
|
|
if run_as_module: |
|
from ..configs import Var |
|
|
|
|
|
Redis = MongoClient = psycopg2 = Database = None |
|
if Var.REDIS_URI or Var.REDISHOST: |
|
try: |
|
from redis import Redis |
|
except ImportError: |
|
LOGS.info("Installing 'redis' for database.") |
|
os.system(f"{sys.executable} -m pip install -q redis hiredis") |
|
from redis import Redis |
|
elif Var.MONGO_URI: |
|
try: |
|
from pymongo import MongoClient |
|
except ImportError: |
|
LOGS.info("Installing 'pymongo' for database.") |
|
os.system(f"{sys.executable} -m pip install -q pymongo[srv]") |
|
from pymongo import MongoClient |
|
elif Var.DATABASE_URL: |
|
try: |
|
import psycopg2 |
|
except ImportError: |
|
LOGS.info("Installing 'pyscopg2' for database.") |
|
os.system(f"{sys.executable} -m pip install -q psycopg2-binary") |
|
import psycopg2 |
|
else: |
|
try: |
|
from localdb import Database |
|
except ImportError: |
|
LOGS.info("Using local file as database.") |
|
os.system(f"{sys.executable} -m pip install -q localdb.json") |
|
from localdb import Database |
|
|
|
|
|
|
|
|
|
class _BaseDatabase: |
|
def __init__(self, *args, **kwargs): |
|
self._cache = {} |
|
|
|
def get_key(self, key): |
|
if key in self._cache: |
|
return self._cache[key] |
|
value = self._get_data(key) |
|
self._cache.update({key: value}) |
|
return value |
|
|
|
def re_cache(self): |
|
self._cache.clear() |
|
for key in self.keys(): |
|
self._cache.update({key: self.get_key(key)}) |
|
|
|
def ping(self): |
|
return 1 |
|
|
|
@property |
|
def usage(self): |
|
return 0 |
|
|
|
def keys(self): |
|
return [] |
|
|
|
def del_key(self, key): |
|
if key in self._cache: |
|
del self._cache[key] |
|
self.delete(key) |
|
return True |
|
|
|
def _get_data(self, key=None, data=None): |
|
if key: |
|
data = self.get(str(key)) |
|
if data and isinstance(data, str): |
|
try: |
|
data = ast.literal_eval(data) |
|
except BaseException: |
|
pass |
|
return data |
|
|
|
def set_key(self, key, value, cache_only=False): |
|
value = self._get_data(data=value) |
|
self._cache[key] = value |
|
if cache_only: |
|
return |
|
return self.set(str(key), str(value)) |
|
|
|
def rename(self, key1, key2): |
|
_ = self.get_key(key1) |
|
if _: |
|
self.del_key(key1) |
|
self.set_key(key2, _) |
|
return 0 |
|
return 1 |
|
|
|
|
|
class MongoDB(_BaseDatabase): |
|
def __init__(self, key, dbname="UltroidDB"): |
|
self.dB = MongoClient(key, serverSelectionTimeoutMS=5000) |
|
self.db = self.dB[dbname] |
|
super().__init__() |
|
|
|
def __repr__(self): |
|
return f"<Ultroid.MonGoDB\n -total_keys: {len(self.keys())}\n>" |
|
|
|
@property |
|
def name(self): |
|
return "Mongo" |
|
|
|
@property |
|
def usage(self): |
|
return self.db.command("dbstats")["dataSize"] |
|
|
|
def ping(self): |
|
if self.dB.server_info(): |
|
return True |
|
|
|
def keys(self): |
|
return self.db.list_collection_names() |
|
|
|
def set(self, key, value): |
|
if key in self.keys(): |
|
self.db[key].replace_one({"_id": key}, {"value": str(value)}) |
|
else: |
|
self.db[key].insert_one({"_id": key, "value": str(value)}) |
|
return True |
|
|
|
def delete(self, key): |
|
self.db.drop_collection(key) |
|
|
|
def get(self, key): |
|
if x := self.db[key].find_one({"_id": key}): |
|
return x["value"] |
|
|
|
def flushall(self): |
|
self.dB.drop_database("UltroidDB") |
|
self._cache.clear() |
|
return True |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SqlDB(_BaseDatabase): |
|
def __init__(self, url): |
|
self._url = url |
|
self._connection = None |
|
self._cursor = None |
|
try: |
|
self._connection = psycopg2.connect(dsn=url) |
|
self._connection.autocommit = True |
|
self._cursor = self._connection.cursor() |
|
self._cursor.execute( |
|
"CREATE TABLE IF NOT EXISTS Ultroid (ultroidCli varchar(70))" |
|
) |
|
except Exception as error: |
|
LOGS.exception(error) |
|
LOGS.info("Invaid SQL Database") |
|
if self._connection: |
|
self._connection.close() |
|
sys.exit() |
|
super().__init__() |
|
|
|
@property |
|
def name(self): |
|
return "SQL" |
|
|
|
@property |
|
def usage(self): |
|
self._cursor.execute( |
|
"SELECT pg_size_pretty(pg_relation_size('Ultroid')) AS size" |
|
) |
|
data = self._cursor.fetchall() |
|
return int(data[0][0].split()[0]) |
|
|
|
def keys(self): |
|
self._cursor.execute( |
|
"SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'ultroid'" |
|
) |
|
data = self._cursor.fetchall() |
|
return [_[0] for _ in data] |
|
|
|
def get(self, variable): |
|
try: |
|
self._cursor.execute(f"SELECT {variable} FROM Ultroid") |
|
except psycopg2.errors.UndefinedColumn: |
|
return None |
|
data = self._cursor.fetchall() |
|
if not data: |
|
return None |
|
if len(data) >= 1: |
|
for i in data: |
|
if i[0]: |
|
return i[0] |
|
|
|
def set(self, key, value): |
|
try: |
|
self._cursor.execute(f"ALTER TABLE Ultroid DROP COLUMN IF EXISTS {key}") |
|
except (psycopg2.errors.UndefinedColumn, psycopg2.errors.SyntaxError): |
|
pass |
|
except BaseException as er: |
|
LOGS.exception(er) |
|
self._cache.update({key: value}) |
|
self._cursor.execute(f"ALTER TABLE Ultroid ADD {key} TEXT") |
|
self._cursor.execute(f"INSERT INTO Ultroid ({key}) values (%s)", (str(value),)) |
|
return True |
|
|
|
def delete(self, key): |
|
try: |
|
self._cursor.execute(f"ALTER TABLE Ultroid DROP COLUMN {key}") |
|
except psycopg2.errors.UndefinedColumn: |
|
return False |
|
return True |
|
|
|
def flushall(self): |
|
self._cache.clear() |
|
self._cursor.execute("DROP TABLE Ultroid") |
|
self._cursor.execute( |
|
"CREATE TABLE IF NOT EXISTS Ultroid (ultroidCli varchar(70))" |
|
) |
|
return True |
|
|
|
|
|
|
|
|
|
|
|
class RedisDB(_BaseDatabase): |
|
def __init__( |
|
self, |
|
host, |
|
port, |
|
password, |
|
platform="", |
|
logger=LOGS, |
|
*args, |
|
**kwargs, |
|
): |
|
if host and ":" in host: |
|
spli_ = host.split(":") |
|
host = spli_[0] |
|
port = int(spli_[-1]) |
|
if host.startswith("http"): |
|
logger.error("Your REDIS_URI should not start with http !") |
|
import sys |
|
|
|
sys.exit() |
|
elif not host or not port: |
|
logger.error("Port Number not found") |
|
import sys |
|
|
|
sys.exit() |
|
kwargs["host"] = host |
|
kwargs["password"] = password |
|
kwargs["port"] = port |
|
|
|
if platform.lower() == "qovery" and not host: |
|
var, hash_, host, password = "", "", "", "" |
|
for vars_ in os.environ: |
|
if vars_.startswith("QOVERY_REDIS_") and vars.endswith("_HOST"): |
|
var = vars_ |
|
if var: |
|
hash_ = var.split("_", maxsplit=2)[1].split("_")[0] |
|
if hash: |
|
kwargs["host"] = os.environ.get(f"QOVERY_REDIS_{hash_}_HOST") |
|
kwargs["port"] = os.environ.get(f"QOVERY_REDIS_{hash_}_PORT") |
|
kwargs["password"] = os.environ.get(f"QOVERY_REDIS_{hash_}_PASSWORD") |
|
self.db = Redis(**kwargs) |
|
self.set = self.db.set |
|
self.get = self.db.get |
|
self.keys = self.db.keys |
|
self.delete = self.db.delete |
|
super().__init__() |
|
|
|
@property |
|
def name(self): |
|
return "Redis" |
|
|
|
@property |
|
def usage(self): |
|
return sum(self.db.memory_usage(x) for x in self.keys()) |
|
|
|
|
|
|
|
|
|
|
|
class LocalDB(_BaseDatabase): |
|
def __init__(self): |
|
self.db = Database("ultroid") |
|
self.get = self.db.get |
|
self.set = self.db.set |
|
self.delete = self.db.delete |
|
super().__init__() |
|
|
|
@property |
|
def name(self): |
|
return "LocalDB" |
|
|
|
def keys(self): |
|
return self._cache.keys() |
|
|
|
def __repr__(self): |
|
return f"<Ultroid.LocalDB\n -total_keys: {len(self.keys())}\n>" |
|
|
|
|
|
def UltroidDB(): |
|
_er = False |
|
from .. import HOSTED_ON |
|
|
|
try: |
|
if Redis: |
|
return RedisDB( |
|
host=Var.REDIS_URI or Var.REDISHOST, |
|
password=Var.REDIS_PASSWORD or Var.REDISPASSWORD, |
|
port=Var.REDISPORT, |
|
platform=HOSTED_ON, |
|
decode_responses=True, |
|
socket_timeout=5, |
|
retry_on_timeout=True, |
|
) |
|
elif MongoClient: |
|
return MongoDB(Var.MONGO_URI) |
|
elif psycopg2: |
|
return SqlDB(Var.DATABASE_URL) |
|
else: |
|
LOGS.critical( |
|
"No DB requirement fullfilled!\nPlease install redis, mongo or sql dependencies...\nTill then using local file as database." |
|
) |
|
return LocalDB() |
|
except BaseException as err: |
|
LOGS.exception(err) |
|
exit() |
|
|
|
|
|
|
|
|