When a profile is deleted, it now automatically respawns the ticket if it's a ticket profile. Also fixed some bugs with fetching the server's public key with the APIResponse object being used, when it expected a regular dictionary. This is a left-over from the prior transition to API objects.
This commit is contained in:
parent
829feb5939
commit
5ab57ab287
7 changed files with 96 additions and 13 deletions
|
|
@ -1,5 +1,10 @@
|
|||
# Major Change Log:
|
||||
|
||||
# Respawn on Delete
|
||||
### Aug 15, 2026
|
||||
When a profile is deleted, it now automatically respawns the ticket if it's a ticket profile. Also fixed some bugs with fetching the server's public key with the APIResponse object being used, when it expected a regular dictionary. This is a left-over from the prior transition to API objects.
|
||||
<br/>
|
||||
|
||||
# Codes/Tickets
|
||||
### Aug 15, 2026
|
||||
The profile object now has a ticket field. It's now none by default. Also the profile object is passed in, for the use of tickets, both for GUI and core.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@ from core.services.networking.systemwide import killswitch
|
|||
from core.services.subscriptions import subscriptions
|
||||
from core.errors.exceptions import FirewallError
|
||||
from core.models.Result import Result, ResultError
|
||||
from core.observers.TicketObserver import TicketObserver
|
||||
from core.controllers.tickets.TicketPrepController import respawn_billing_code_into_ticket
|
||||
from core.services.prepare_tickets.ticket_tracker import get_tickets_with
|
||||
from core.errors.logger import logger
|
||||
|
||||
from core.Errors import InvalidSubscriptionError, MissingSubscriptionError, ConnectionTerminationError, ProfileActivationError, ProfileDeactivationError, MissingLocationError, ConnectionUnprotectedError, EndpointVerificationError, ProfileStateConflictError
|
||||
from core.controllers.ApplicationController import ApplicationController
|
||||
|
|
@ -182,8 +186,47 @@ class ProfileController:
|
|||
time.sleep(1.0)
|
||||
|
||||
@staticmethod
|
||||
def destroy(profile: Union[SessionProfile, SystemProfile], profile_observer: ProfileObserver = None):
|
||||
def destroy(profile: Union[SessionProfile, SystemProfile], profile_observer: ProfileObserver = None, ticket_observer: TicketObserver = None, connection_observer: ConnectionObserver = None):
|
||||
|
||||
####################################
|
||||
# DESTROY TICKET
|
||||
####################################
|
||||
which_ticket = profile.ticket
|
||||
|
||||
# try via lookup:
|
||||
if not which_ticket:
|
||||
logger.info(f"Unable to find the ticket # in the profile's native config for {profile.id}. Checking the ticket tracker JSON")
|
||||
target_subscription = profile.subscription.billing_code
|
||||
ticket_list = get_tickets_with(target_subscription)
|
||||
if len(ticket_list) >= 1:
|
||||
which_ticket = ticket_list[0]
|
||||
logger.info(f"We got the ticket {which_ticket} for profile {profile.id} from the ticket tracker JSON")
|
||||
else:
|
||||
logger.info(f"We were UNABLE to find a ticket with the subscription for profile {profile.id} from the ticket tracker JSON. Proceeding with delete regardless..")
|
||||
|
||||
# Regardless of how it was acquired,
|
||||
if which_ticket:
|
||||
notification = f"Respawn Started for Ticket {which_ticket}"
|
||||
logger.info(notification)
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
respawn_result = respawn_billing_code_into_ticket(
|
||||
profile=profile,
|
||||
which_ticket=which_ticket,
|
||||
ticket_observer=ticket_observer,
|
||||
connection_observer=connection_observer
|
||||
)
|
||||
if respawn_result.valid:
|
||||
notification = f"Ticket Respawned!"
|
||||
logger.info(notification)
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
else:
|
||||
notification = f"Error with Ticket Respawn!"
|
||||
logger.error(f"{notification} {respawn_result.error_type} with message: {respawn_result.message}")
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
|
||||
####################################
|
||||
# DESTROY PROFILE
|
||||
####################################
|
||||
ProfileController.disable(profile)
|
||||
profile.delete()
|
||||
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ def respawn_billing_code_into_ticket(
|
|||
which_ticket: int,
|
||||
ticket_observer: TicketObserver,
|
||||
connection_observer: ConnectionObserver
|
||||
) -> bool:
|
||||
) -> Result:
|
||||
"""
|
||||
Converts a valid VPN billing code, into a valid unused ticket.
|
||||
At the cost of the server expiring the billing code immediately.
|
||||
|
|
@ -128,7 +128,10 @@ def respawn_billing_code_into_ticket(
|
|||
return Result(valid=False, error_type=ResultError.MISSING_DATA, message="Missing which ticket slot is being respawned")
|
||||
|
||||
if not isinstance(which_ticket, int):
|
||||
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message="Ticket slot must be a number.")
|
||||
try:
|
||||
which_ticket = int(which_ticket)
|
||||
except:
|
||||
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message="Ticket slot must be a number.")
|
||||
|
||||
ticket_result = prepare_tickets(
|
||||
how_many_profiles=1,
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ def get_from_server_and_save(
|
|||
|
||||
url = make_url(string_form_of_key_name)
|
||||
|
||||
# public_key_results = get_data_from_server(url, connection_observer)
|
||||
public_key_results = connect.single_endpoint(
|
||||
method="get",
|
||||
url=url,
|
||||
|
|
@ -39,14 +38,22 @@ def get_from_server_and_save(
|
|||
payload=None
|
||||
)
|
||||
|
||||
if public_key_results.valid:
|
||||
# extract:
|
||||
public_key = public_key_results.data
|
||||
if not public_key_results.valid:
|
||||
error_msg = f"Error with API getting the Public Key: {public_key_results.error_type} and {public_key_results.message}"
|
||||
logger.error(error_msg)
|
||||
return {"valid": False, "message": error_msg}
|
||||
|
||||
# save it:
|
||||
did_it_save = write_string_to_text_file(public_key, file_path)
|
||||
# extract:
|
||||
public_key = public_key_results.data
|
||||
|
||||
return public_key_results
|
||||
# save it:
|
||||
did_it_save = write_string_to_text_file(public_key, file_path)
|
||||
|
||||
if not did_it_save:
|
||||
logger.error(f"Error with saving the public key {public_key} with write_string_to_text_file, but still returning data..")
|
||||
|
||||
# regardless of saving:
|
||||
return {"valid": True, "data": public_key}
|
||||
|
||||
|
||||
def get_pub_key(
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ from core.services.helpers.get_which_billing_key import get_which_billing_key
|
|||
from core.models.session.SessionProfile import SessionProfile
|
||||
from core.models.system.SystemProfile import SystemProfile
|
||||
from core.models.Subscription import Subscription # not sure if needed
|
||||
from core.controllers.ProfileController import ProfileController
|
||||
# from core.controllers.ProfileController import ProfileController
|
||||
|
||||
# utils
|
||||
from core.utils.basic_operations.write_or_read_from_json import get_value_from_json_file
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
from core.utils.basic_operations.write_or_read_from_json import read_entire_json, update_json
|
||||
from core.utils.basic_operations.does_file_exist import does_file_exist
|
||||
from core.errors.exceptions import *
|
||||
from core.errors.logger import logger
|
||||
from core.Constants import Constants
|
||||
|
||||
# generic
|
||||
import json
|
||||
import os
|
||||
|
||||
|
|
@ -89,3 +91,22 @@ def wipe_one_ticket_sub(which_ticket: int) -> bool:
|
|||
key_to_add=which_ticket,
|
||||
value_to_update=wipe_payload
|
||||
)
|
||||
|
||||
|
||||
def get_tickets_with(target_subscription: str) -> list:
|
||||
|
||||
if not does_file_exist(Constants.TICKET_TRACKER_PATH):
|
||||
return []
|
||||
|
||||
whole_json = read_entire_json(Constants.TICKET_TRACKER_PATH)
|
||||
if not whole_json:
|
||||
return []
|
||||
|
||||
list_of_matches = []
|
||||
|
||||
for each_number in whole_json:
|
||||
if whole_json[each_number]['subscription'] == target_subscription:
|
||||
list_of_matches.append(each_number)
|
||||
|
||||
return list_of_matches
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import os
|
||||
|
||||
"""
|
||||
Note: this function will accept EITHER raw dicts or string, WITHOUT converting to JSON,
|
||||
and literally convert to str to save. At a first glance, this seems ridiculous.
|
||||
|
|
@ -13,16 +15,18 @@ Therefore, we opted for this data storage format for some data types.
|
|||
"""
|
||||
|
||||
|
||||
|
||||
def write_string_to_text_file(content_to_write: str | dict, file_path: str) -> bool:
|
||||
if content_to_write is None:
|
||||
return False
|
||||
|
||||
try:
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
with open(file_path, "w") as file:
|
||||
file.write(str(content_to_write))
|
||||
return True
|
||||
except FileNotFoundError:
|
||||
print(f"Error: The file '{file_path}' was not found.")
|
||||
print(f"Error inside of write_string_to_text: The file '{file_path}' was not found.")
|
||||
return False
|
||||
except IOError:
|
||||
print(
|
||||
|
|
|
|||
Loading…
Reference in a new issue