# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
+import json
import sys
import time
+import requests
+import validators
+
+from fba import blacklist
+from fba import cache
from fba import fba
+from fba import federation
# Found info from node, such as nodeinfo URL, detection mode that needs to be
# written to database. Both arrays must be filled at the same time or else
-# update_instance_data() will fail
+# update_data() will fail
_pending = {
# Detection mode: 'AUTO_DISCOVERY', 'STATIC_CHECKS' or 'GENERATOR'
# NULL means all detection methods have failed (maybe still reachable instance)
"last_error_details" : {},
}
-def set(key: str, domain: str, value: any):
+def set_data(key: str, domain: str, value: any):
# DEBUG: print(f"DEBUG: key='{key}',domain='{domain}',value[]='{type(value)}' - CALLED!")
- if type(key) != str:
+ if not isinstance(key, str):
raise ValueError("Parameter key[]='{type(key)}' is not 'str'")
elif key == "":
- raise ValueError(f"Parameter 'key' cannot be empty")
- elif type(domain) != str:
- raise ValueError("Parameter domain[]='{type(domain)}' is not 'str'")
+ raise ValueError("Parameter 'key' is empty")
+ elif not isinstance(domain, str):
+ raise ValueError(f"Parameter domain[]='{type(domain)}' is not 'str'")
elif domain == "":
- raise ValueError(f"Parameter 'domain' cannot be empty")
+ raise ValueError("Parameter 'domain' is empty")
elif not key in _pending:
raise ValueError(f"key='{key}' not found in _pending")
elif not fba.is_primitive(value):
def has_pending_instance_data(domain: str) -> bool:
# DEBUG: print(f"DEBUG: domain='{domain}' - CALLED!")
- if type(domain) != str:
+ if not isinstance(domain, str):
raise ValueError(f"Parameter domain[]={type(domain)} is not 'str'")
elif domain == "":
- raise ValueError(f"Parameter 'domain' is empty")
+ raise ValueError("Parameter 'domain' is empty")
has_pending = False
for key in _pending:
# DEBUG: print(f"DEBUG: has_pending='{has_pending}' - EXIT!")
return has_pending
-def update_instance_data(domain: str):
+def update_data(domain: str):
# DEBUG: print(f"DEBUG: domain='{domain}' - CALLED!")
- if type(domain) != str:
+ if not isinstance(domain, str):
raise ValueError(f"Parameter domain[]={type(domain)} is not 'str'")
elif domain == "":
- raise ValueError(f"Parameter 'domain' is empty")
+ raise ValueError("Parameter 'domain' is empty")
elif not has_pending_instance_data(domain):
raise Exception(f"Domain '{domain}' has no pending instance data, but function invoked")
for key in _pending:
# DEBUG: print("DEBUG: key:", key)
if domain in _pending[key]:
- # DEBUG: print(f"DEBUG: Adding '{_pending[key][domain]}' for key='{key}' ...")
- fields.append(_pending[key][domain])
- sql_string += f" {key} = ?,"
+ # DEBUG: print(f"DEBUG: Adding '{_pending[key][domain]}' for key='{key}' ...")
+ fields.append(_pending[key][domain])
+ sql_string += f" {key} = ?,"
fields.append(time.time())
fields.append(domain)
except:
pass
- except BaseException as e:
- print(f"ERROR: failed SQL query: domain='{domain}',sql_string='{sql_string}',exception[{type(e)}]:'{str(e)}'")
+ except BaseException as exception:
+ print(f"ERROR: failed SQL query: domain='{domain}',sql_string='{sql_string}',exception[{type(exception)}]:'{str(exception)}'")
sys.exit(255)
# DEBUG: print("DEBUG: EXIT!")
def update_last_instance_fetch(domain: str):
# DEBUG: print(f"DEBUG: domain='{domain}' - CALLED!")
- if type(domain) != str:
+ if not isinstance(domain, str):
raise ValueError(f"Parameter domain[]={type(domain)} is not 'str'")
elif domain == "":
- raise ValueError(f"Parameter 'domain' is empty")
+ raise ValueError("Parameter 'domain' is empty")
# DEBUG: print("DEBUG: Updating last_instance_fetch for domain:", domain)
- set("last_instance_fetch", domain, time.time())
+ set_data("last_instance_fetch", domain, time.time())
# Running pending updated
- # DEBUG: print(f"DEBUG: Invoking update_instance_data({domain}) ...")
- update_instance_data(domain)
+ # DEBUG: print(f"DEBUG: Invoking update_data({domain}) ...")
+ update_data(domain)
# DEBUG: print("DEBUG: EXIT!")
def update_last_blocked(domain: str):
- if type(domain) != str:
+ if not isinstance(domain, str):
raise ValueError(f"Parameter domain[]={type(domain)} is not 'str'")
elif domain == "":
- raise ValueError(f"Parameter 'domain' is empty")
+ raise ValueError("Parameter 'domain' is empty")
# DEBUG: print("DEBUG: Updating last_blocked for domain", domain)
- set("last_blocked", domain, time.time())
+ set_data("last_blocked", domain, time.time())
# Running pending updated
- # DEBUG: print(f"DEBUG: Invoking update_instance_data({domain}) ...")
- update_instance_data(domain)
+ # DEBUG: print(f"DEBUG: Invoking update_data({domain}) ...")
+ update_data(domain)
# DEBUG: print("DEBUG: EXIT!")
-def add(domain: str, origin: str, originator: str, path: str = None):
- # DEBUG: print(f"DEBUG: domain='{domain}',origin='{origin}',originator='{originator}',path='{path}' - CALLED!")
- if type(domain) != str:
+def add(domain: str, origin: str, command: str, path: str = None):
+ # DEBUG: print(f"DEBUG: domain='{domain}',origin='{origin}',command='{command}',path='{path}' - CALLED!")
+ if not isinstance(domain, str):
raise ValueError(f"Parameter domain[]={type(domain)} is not 'str'")
elif domain == "":
- raise ValueError(f"Parameter 'domain' is empty")
- elif type(origin) != str and origin != None:
+ raise ValueError("Parameter 'domain' is empty")
+ elif not isinstance(origin, str) and origin is not None:
raise ValueError(f"origin[]={type(origin)} is not 'str'")
- elif type(originator) != str:
- raise ValueError(f"originator[]={type(originator)} is not 'str'")
- elif originator == "":
- raise ValueError(f"originator cannot be empty")
+ elif origin == "":
+ raise ValueError("Parameter 'origin' is empty")
+ elif not isinstance(command, str):
+ raise ValueError(f"command[]={type(command)} is not 'str'")
+ elif command == "":
+ raise ValueError("Parameter 'command' is empty")
elif not validators.domain(domain.split("/")[0]):
raise ValueError(f"Bad domain name='{domain}'")
elif origin is not None and not validators.domain(origin.split("/")[0]):
raise ValueError(f"Bad origin name='{origin}'")
elif blacklist.is_blacklisted(domain):
raise Exception(f"domain='{domain}' is blacklisted, but method invoked")
+ elif domain.find("/profile/") > 0 or domain.find("/users/") > 0:
+ raise Exception(f"domain='{domain}' is a single user")
- # DEBUG: print("DEBUG: domain,origin,originator,path:", domain, origin, originator, path)
- if domain.find("/profile") > 0:
- # Need to cut off /profile/* part
- domain = domain.split("/profile/")[0]
- if instances.is_registered(domain):
- raise Exception(f"WARNING: After removing /profile/ domain='{domain}' is already registered - EXIT!")
+ # DEBUG: print("DEBUG: domain,origin,command,path:", domain, origin, command, path)
+ software = federation.determine_software(domain, path)
- software = determine_software(domain, path)
# DEBUG: print("DEBUG: Determined software:", software)
+ if software == "lemmy" and domain.find("/c/") > 0:
+ domain = domain.split("/c/")[0]
+ if is_registered(domain):
+ print(f"WARNING: domain='{domain}' already registered after cutting off user part. - EXIT!")
+ return
print(f"INFO: Adding instance domain='{domain}' (origin='{origin}',software='{software}')")
try:
- cursor.execute(
- "INSERT INTO instances (domain, origin, originator, hash, software, first_seen) VALUES (?, ?, ?, ?, ?, ?)",
+ fba.cursor.execute(
+ "INSERT INTO instances (domain, origin, command, hash, software, first_seen) VALUES (?, ?, ?, ?, ?, ?)",
(
domain,
origin,
- originator,
- get_hash(domain),
+ command,
+ fba.get_hash(domain),
software,
time.time()
),
cache.set_sub_key("is_registered", domain, True)
- if instances.has_pending_instance_data(domain):
+ if has_pending_instance_data(domain):
# DEBUG: print(f"DEBUG: domain='{domain}' has pending nodeinfo being updated ...")
- instances.set("last_status_code" , domain, None)
- instances.set("last_error_details", domain, None)
- instances.update_instance_data(domain)
- remove_pending_error(domain)
-
- if domain in pending_errors:
- # DEBUG: print("DEBUG: domain has pending error being updated:", domain)
- update_last_error(domain, pending_errors[domain])
- remove_pending_error(domain)
-
- except BaseException as e:
- print(f"ERROR: failed SQL query: domain='{domain}',exception[{type(e)}]:'{str(e)}'")
- sys.exit(255)
+ set_data("last_status_code" , domain, None)
+ set_data("last_error_details", domain, None)
+ update_data(domain)
+
+ except BaseException as exception:
+ update_last_error(domain, exception)
+ raise Exception(f"ERROR: failed SQL query: domain='{domain}',exception[{type(exception)}]:'{str(exception)}'") from exception
else:
# DEBUG: print("DEBUG: Updating nodeinfo for domain:", domain)
update_last_nodeinfo(domain)
# DEBUG: print("DEBUG: EXIT!")
+
+def update_last_nodeinfo(domain: str):
+ # DEBUG: print(f"DEBUG: domain='{domain}' - CALLED!")
+ if not isinstance(domain, str):
+ raise ValueError(f"Parameter domain[]={type(domain)} is not 'str'")
+ elif domain == "":
+ raise ValueError("Parameter 'domain' is empty")
+
+ # DEBUG: print("DEBUG: Updating last_nodeinfo for domain:", domain)
+ set_data("last_nodeinfo", domain, time.time())
+ set_data("last_updated" , domain, time.time())
+
+ # Running pending updated
+ # DEBUG: print(f"DEBUG: Invoking update_data({domain}) ...")
+ update_data(domain)
+
+ # DEBUG: print("DEBUG: EXIT!")
+
+def update_last_error(domain: str, error: dict):
+ # DEBUG: print("DEBUG: domain,error[]:", domain, type(error))
+ if not isinstance(domain, str):
+ raise ValueError(f"Parameter domain[]={type(domain)} is not 'str'")
+ elif domain == "":
+ raise ValueError("Parameter 'domain' is empty")
+
+ # DEBUG: print("DEBUG: BEFORE error[]:", type(error))
+ if isinstance(error, BaseException) or isinstance(error, json.decoder.JSONDecodeError):
+ error = f"error[{type(error)}]='{str(error)}'"
+ # DEBUG: print("DEBUG: AFTER error[]:", type(error))
+
+ if isinstance(error, str):
+ # DEBUG: print(f"DEBUG: Setting last_error_details='{error}'")
+ set_data("last_status_code" , domain, 999)
+ set_data("last_error_details", domain, error)
+ elif isinstance(error, requests.models.Response):
+ # DEBUG: print(f"DEBUG: Setting last_error_details='{error.reason}'")
+ set_data("last_status_code" , domain, error.status_code)
+ set_data("last_error_details", domain, error.reason)
+ else:
+ # DEBUG: print(f"DEBUG: Setting last_error_details='{error['error_message']}'")
+ set_data("last_status_code" , domain, error["status_code"])
+ set_data("last_error_details", domain, error["error_message"])
+
+ # Running pending updated
+ # DEBUG: print(f"DEBUG: Invoking update_data({domain}) ...")
+ update_data(domain)
+
+ fba.log_error(domain, error)
+
+ # DEBUG: print("DEBUG: EXIT!")
+
+def is_registered(domain: str) -> bool:
+ # DEBUG: print(f"DEBUG: domain='{domain}' - CALLED!")
+ if not isinstance(domain, str):
+ raise ValueError(f"Parameter domain[]={type(domain)} is not 'str'")
+ elif domain == "":
+ raise ValueError("Parameter 'domain' is empty")
+
+ # DEBUG: print(f"DEBUG: domain='{domain}' - CALLED!")
+ if not cache.key_exists("is_registered"):
+ # DEBUG: print("DEBUG: Cache for 'is_registered' not initialized, fetching all rows ...")
+ try:
+ fba.cursor.execute("SELECT domain FROM instances")
+
+ # Check Set all
+ cache.set_all("is_registered", fba.cursor.fetchall(), True)
+ except BaseException as exception:
+ update_last_error(domain, exception)
+ raise Exception(f"ERROR: failed SQL query: domain='{domain}',exception[{type(exception)}]:'{str(exception)}'") from exception
+
+ # Is cache found?
+ registered = cache.sub_key_exists("is_registered", domain)
+
+ # DEBUG: print(f"DEBUG: registered='{registered}' - EXIT!")
+ return registered