\q
--
-- PostgreSQL database dump
--

-- Dumped from database version 15.13
-- Dumped by pg_dump version 15.13

SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;

--
-- Name: dataimpulse; Type: SCHEMA; Schema: -; Owner: postgres
--

CREATE SCHEMA dataimpulse;


ALTER SCHEMA dataimpulse OWNER TO postgres;

--
-- Name: add_allowed_ip(integer, inet); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.add_allowed_ip(subuser_id integer, ip inet) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httppost(
	'/reseller/sub-user/allowed-ips/add',
        jsonb_build_object(
            'subuser_id', subuser_id,
            'ip', ip
        )::TEXT);
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse.add_allowed_ip(subuser_id integer, ip inet) OWNER TO postgres;

--
-- Name: all_countries_all_pools(); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.all_countries_all_pools() RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    pool_variant DATAIMPULSEPOOL;
    pool_variants DATAIMPULSEPOOL[] := ARRAY['datacenter', 'residential', 'mobile'];
    countries_json JSONB;
    all_countries JSONB := '[]'::JSONB;
BEGIN
    FOREACH pool_variant IN ARRAY pool_variants
    LOOP
        countries_json := dataimpulse.get_proxy_countries(pool_variant);
        all_countries := all_countries || (
	  SELECT jsonb_agg(value->>'name')
	  FROM jsonb_array_elements(countries_json));
    END LOOP;
    RETURN all_countries;
END
$$;


ALTER FUNCTION dataimpulse.all_countries_all_pools() OWNER TO postgres;

--
-- Name: all_proxy_cities_all_pools(); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.all_proxy_cities_all_pools() RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    pool_variant DATAIMPULSEPOOL;
    pool_variants DATAIMPULSEPOOL[] := ARRAY['datacenter', 'residential', 'mobile'];
    countries_json JSONB;
    countries TEXT[];
    city_result JSONB;
    all_cities JSONB := '[]'::JSONB;
    country_array JSONB;
BEGIN
    FOREACH pool_variant IN ARRAY pool_variants
    LOOP
        countries_json := dataimpulse.get_proxy_countries(pool_variant);
        country_array := COALESCE(countries_json, '[]'::JSONB);
        SELECT array_agg(value->>'code') FROM jsonb_array_elements(country_array) INTO countries;
        city_result := dataimpulse.get_proxy_cities(pool_variant, countries);
        all_cities := all_cities || (
	  SELECT jsonb_agg(value->>'name')
	  FROM jsonb_array_elements(city_result));
    END LOOP;

    RETURN all_cities;
END
$$;


ALTER FUNCTION dataimpulse.all_proxy_cities_all_pools() OWNER TO postgres;

--
-- Name: all_proxy_states_all_pools(); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.all_proxy_states_all_pools() RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    pool_variant DATAIMPULSEPOOL;
    pool_variants DATAIMPULSEPOOL[] := ARRAY['datacenter', 'residential', 'mobile'];
    countries_json JSONB;
    countries TEXT[];
    state_result JSONB;
    all_states JSONB := '[]'::JSONB;
    country_array JSONB;
BEGIN
    FOREACH pool_variant IN ARRAY pool_variants
    LOOP
        countries_json := dataimpulse.get_proxy_countries(pool_variant);
        country_array := COALESCE(countries_json, '[]'::JSONB);
        SELECT array_agg(value->>'code') FROM jsonb_array_elements(country_array) INTO countries;
        state_result := dataimpulse.get_proxy_states(pool_variant, countries);
        all_states := all_states || (
	  SELECT jsonb_agg(value->>'name')
	  FROM jsonb_array_elements(state_result));
    END LOOP;

    RETURN all_states;
END
$$;


ALTER FUNCTION dataimpulse.all_proxy_states_all_pools() OWNER TO postgres;

--
-- Name: all_subusers(); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.all_subusers() RETURNS json
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_offset INT := 0;
    v_limit INT := 1000;
    v_partial_response RECORD;
    v_content JSONB;
    v_all_subusers JSONB := '[]'::JSONB;
BEGIN
    LOOP
        v_partial_response := dataimpulse.httpget(
	  format('/reseller/sub-user/list?limit=%s&offset=%s', v_limit, v_offset));

        v_content := dataimpulse.handle_api_response(
	    v_partial_response.status,
            v_partial_response.content);

        v_all_subusers := jsonb_set(
            v_all_subusers::jsonb, 
            '{}', 
            (v_all_subusers::jsonb || v_content::jsonb)::jsonb
        )::json;

	 IF v_content IS NOT NULL THEN
            v_all_subusers := v_all_subusers || (v_content->'subusers')::JSONB;
        END IF;

        IF jsonb_array_length((v_content->'subusers')::JSONB) < v_limit THEN
            EXIT;
        END IF;

        v_offset := v_offset + v_limit;
    END LOOP;

    RETURN v_all_subusers;
END $$;


ALTER FUNCTION dataimpulse.all_subusers() OWNER TO postgres;

--
-- Name: common_locations(common.dataimpulsepool); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.common_locations(pool common.dataimpulsepool) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    _token TEXT := get_current_token();
    response RECORD := dataimpulse.httpget(format('/reseller/common/locations?pool_type=%s', pool));
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse.common_locations(pool common.dataimpulsepool) OWNER TO postgres;

--
-- Name: create_subuser(text, jsonb, integer, inet[], common.dataimpulsepool, jsonb); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.create_subuser(label text DEFAULT NULL::text, sticky_range jsonb DEFAULT NULL::jsonb, threads integer DEFAULT NULL::integer, allowed_ips inet[] DEFAULT NULL::inet[], pool_type common.dataimpulsepool DEFAULT NULL::common.dataimpulsepool, default_pool_parameters jsonb DEFAULT NULL::jsonb) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD;
    data JSONB := '{}';
BEGIN
    IF label IS NOT NULL THEN
        data := data || jsonb_build_object('label', label);
    ELSE 
        RAISE WARNING 'CREATE SUBUSER: Label not provided';
    END IF;
    IF sticky_range IS NOT NULL THEN
        data := data || jsonb_build_object('sticky_range', sticky_range);
    END IF;
    IF threads IS NOT NULL THEN
        data := data || jsonb_build_object('threads', threads);
    END IF;
    IF allowed_ips IS NOT NULL THEN
        data := data || jsonb_build_object('allowed_ips', to_jsonb(allowed_ips));
    END IF;
    IF pool_type IS NOT NULL THEN
        data := data || jsonb_build_object('pool_type', pool_type);
    END IF;
    IF default_pool_parameters IS NOT NULL THEN
        data := data || jsonb_build_object('default_pool_parameters', default_pool_parameters);
    END IF;

    RAISE NOTICE 'Creating subuser with lable: %', label;

    response := dataimpulse.httppost('/reseller/sub-user/create', data::TEXT);
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END $$;


ALTER FUNCTION dataimpulse.create_subuser(label text, sticky_range jsonb, threads integer, allowed_ips inet[], pool_type common.dataimpulsepool, default_pool_parameters jsonb) OWNER TO postgres;

--
-- Name: delete_subuser(integer); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.delete_subuser(subuser_id integer) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httppost(
	'/reseller/sub-user/delete', 
        jsonb_build_object(
            'subuser_id', subuser_id
        )::TEXT);
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END $$;


ALTER FUNCTION dataimpulse.delete_subuser(subuser_id integer) OWNER TO postgres;

--
-- Name: get_proxy(text, text, text, text, integer, integer, common.dataimpulsepool, text, text[], text[], text[], text[], text[], text[], inet); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.get_proxy(anonymous text, type text, protocol text, format text, quantity integer, sessionttl integer, pool common.dataimpulsepool, subuser_id text, countries text[] DEFAULT NULL::text[], cities text[] DEFAULT NULL::text[], states text[] DEFAULT NULL::text[], excluded_asns text[] DEFAULT NULL::text[], included_asns text[] DEFAULT NULL::text[], zipcodes text[] DEFAULT NULL::text[], host inet DEFAULT '148.251.5.30'::inet) RETURNS text
    LANGUAGE plpgsql
    AS $$
DECLARE
    url TEXT;
    response TEXT;
    _anonymous INT;
    response_status INT;
    user_info JSONB;
    username TEXT;
    password TEXT;
    error_type TEXT;
    additional_locations TEXT := '';
BEGIN
    IF anonymous = 'true' THEN
          _anonymous :=  1;
    ELSE
          _anonymous :=  0;
    END IF;

    IF cities IS NOT NULL THEN
        additional_locations = additional_locations || format('&cities=%s', array_to_string(cities, ','));
    END IF;
    IF states IS NOT NULL THEN
        additional_locations = additional_locations || format('&states=%s', array_to_string(states, ','));
    END IF;
    IF included_asns IS NOT NULL THEN
        additional_locations = additional_locations || format('&asns=%s', array_to_string(included_asns, ','));
    END IF;
    IF zipcodes IS NOT NULL THEN
        additional_locations = additional_locations || format('&zipcodes=%s', array_to_string(zipcodes, ','));
    END IF;
    IF excluded_asns IS NOT NULL THEN
        additional_locations = additional_locations || format('&exclude_asns=%s', array_to_string(excluded_asns, ','));
    END IF;
    IF countries IS NOT NULL THEN
        additional_locations = additional_locations || format('&countries=%s', array_to_string(countries, ','));
    END IF;

    IF subuser_id IS NULL THEN
          RAISE sqlstate 'PT422' USING 
            message = 'No pool for this user';
    END IF;

    user_info := dataimpulse.get_subuser(subuser_id::INT);
    password := user_info->>'password';
    username := user_info->>'login';

    RAISE LOG 'password %, username %', password, username;

    url := 'https://gw.dataimpulse.com:777/api/list?'
           || '&anonymous=' || _anonymous::TEXT
           || '&type=' || type
           || '&protocol=' || protocol
           || '&format=' || format
           || '&quantity=' || quantity::TEXT;

    IF additional_locations != '' THEN 
        url := url || additional_locations;
    END IF;

    IF type = 'sticky' THEN
      IF sessionttl IS NOT NULL THEN
        url := url || '&session_ttl=' || sessionttl::TEXT;
      END IF;
    END IF;

    RAISE LOG 'url %', url;

    SELECT status, content FROM http((
	'GET',
        url,
        ARRAY[ http_header('Authorization', 'Basic ' || encode(convert_to(username || ':' || password, 'UTF-8'), 'base64')) ],
        null,
	null
    )::http_request) INTO response_status, response;

    -- RAISE LOG 'response %', response;

    IF response_status != 200 THEN
	error_type := format('PT%s', response_status);
        RAISE EXCEPTION '%', response USING ERRCODE = error_type;
    END IF;

    RETURN REPLACE(response, 'gw.dataimpulse.com', host(host));
END;
$$;


ALTER FUNCTION dataimpulse.get_proxy(anonymous text, type text, protocol text, format text, quantity integer, sessionttl integer, pool common.dataimpulsepool, subuser_id text, countries text[], cities text[], states text[], excluded_asns text[], included_asns text[], zipcodes text[], host inet) OWNER TO postgres;

--
-- Name: get_proxy_asns(common.dataimpulsepool, text[], text[], text[], text[], text); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.get_proxy_asns(pool common.dataimpulsepool, countries text[], states text[] DEFAULT '{}'::text[], cities text[] DEFAULT '{}'::text[], zipcodes text[] DEFAULT '{}'::text[], order_by text DEFAULT 'name,asc'::text) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httppost(
	'/reseller/common/locations/asns', 
        jsonb_build_object(
            'pool_type', pool,
            'countries', countries,
            'states', states,
            'cities', cities,
            'zipcodes', zipcodes,
            'order_by', order_by
        )::TEXT);
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END $$;


ALTER FUNCTION dataimpulse.get_proxy_asns(pool common.dataimpulsepool, countries text[], states text[], cities text[], zipcodes text[], order_by text) OWNER TO postgres;

--
-- Name: get_proxy_cities(common.dataimpulsepool, text[], text[], text[], text[], text); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.get_proxy_cities(pool common.dataimpulsepool, countries text[], states text[] DEFAULT '{}'::text[], zipcodes text[] DEFAULT '{}'::text[], asns text[] DEFAULT '{}'::text[], order_by text DEFAULT 'name,asc'::text) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httppost(
	'/reseller/common/locations/cities', 
        jsonb_build_object(
            'pool_type', pool,
            'countries', countries,
            'states', states,
            'zipcodes', zipcodes,
            'asns', asns,
            'order_by', order_by
        )::TEXT);
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END $$;


ALTER FUNCTION dataimpulse.get_proxy_cities(pool common.dataimpulsepool, countries text[], states text[], zipcodes text[], asns text[], order_by text) OWNER TO postgres;

--
-- Name: get_proxy_countries(common.dataimpulsepool, text); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.get_proxy_countries(pool common.dataimpulsepool, order_by text DEFAULT 'name,asc'::text) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httppost(
	'/reseller/common/locations/countries', 
        jsonb_build_object(
            'pool_type', pool,
            'order_by', order_by
        )::TEXT);
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END $$;


ALTER FUNCTION dataimpulse.get_proxy_countries(pool common.dataimpulsepool, order_by text) OWNER TO postgres;

--
-- Name: get_proxy_states(common.dataimpulsepool, text[], text[], text[], text[], text); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.get_proxy_states(pool common.dataimpulsepool, countries text[], cities text[] DEFAULT '{}'::text[], zipcodes text[] DEFAULT '{}'::text[], asns text[] DEFAULT '{}'::text[], order_by text DEFAULT 'name,asc'::text) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httppost(
	'/reseller/common/locations/states', 
        jsonb_build_object(
            'pool_type', pool,
            'countries', countries,
            'cities', cities,
            'zipcodes', zipcodes,
            'asns', asns,
            'order_by', order_by
        )::TEXT);
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END $$;


ALTER FUNCTION dataimpulse.get_proxy_states(pool common.dataimpulsepool, countries text[], cities text[], zipcodes text[], asns text[], order_by text) OWNER TO postgres;

--
-- Name: get_proxy_zipcodes(common.dataimpulsepool, text[], text[], text[], text); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.get_proxy_zipcodes(pool common.dataimpulsepool, countries text[], states text[] DEFAULT '{}'::text[], asns text[] DEFAULT '{}'::text[], order_by text DEFAULT 'name,asc'::text) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httppost(
	'/reseller/common/locations/zipcodes', 
        jsonb_build_object(
            'pool_type', pool,
            'countries', countries,
            'states', states,
            'asns', asns,
            'order_by', order_by
        )::TEXT);
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END $$;


ALTER FUNCTION dataimpulse.get_proxy_zipcodes(pool common.dataimpulsepool, countries text[], states text[], asns text[], order_by text) OWNER TO postgres;

--
-- Name: get_subuser(integer); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.get_subuser(subuser_id integer) RETURNS json
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httpget(
      format('/reseller/sub-user/get?subuser_id=%s', subuser_id));
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END $$;


ALTER FUNCTION dataimpulse.get_subuser(subuser_id integer) OWNER TO postgres;

--
-- Name: get_token(); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.get_token(OUT token text) RETURNS text
    LANGUAGE plpgsql
    AS $$
DECLARE
    _dataimpulse_username TEXT := current_setting(format('%s.dataimpulse_username', CURRENT_SCHEMA));
    _dataimpulse_password TEXT := current_setting(format('%s.dataimpulse_password', CURRENT_SCHEMA));
    response RECORD;
BEGIN
  response := dataimpulse.httppost(
      '/reseller/user/token/get', 
      jsonb_build_object(
      	'login', _dataimpulse_username,
      	'password', _dataimpulse_password
      ));

  get_token.token=dataimpulse.handle_api_response(response.status, response.content)->>'token';
END $$;


ALTER FUNCTION dataimpulse.get_token(OUT token text) OWNER TO postgres;

--
-- Name: get_user_balance(); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.get_user_balance() RETURNS double precision
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httpget('/reseller/user/balance');
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content)->>'balance';
END $$;


ALTER FUNCTION dataimpulse.get_user_balance() OWNER TO postgres;

--
-- Name: handle_api_response(integer, text); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.handle_api_response(response_status integer, response text) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
  response_json JSONB;
BEGIN
  RAISE NOTICE 'response status: %s', response_status;
  BEGIN
    response_json := response::jsonb;
  EXCEPTION WHEN invalid_text_representation THEN
    RAISE EXCEPTION '%', response USING
      ERRCODE = format('PT%s', response_status);
  END;

  IF response_status >= 200 AND response_status < 300 THEN
    IF response_json ? 'success' 
      AND (response_json->>'success')::boolean = false 
    THEN
      PERFORM dataimpulse.raise_api_exception(400, response_json);
    END IF;
    RETURN response_json;
  ELSE
    PERFORM dataimpulse.raise_api_exception(response_status, response_json);
  END IF;
END;
$$;


ALTER FUNCTION dataimpulse.handle_api_response(response_status integer, response text) OWNER TO postgres;

--
-- Name: httpget(text, public.http_header[]); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.httpget(relative_path text, additional_headers public.http_header[] DEFAULT '{}'::public.http_header[]) RETURNS TABLE(status integer, content text)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_token TEXT := get_current_token();
    headers http_header[];
    retry_count INTEGER := 0;
    max_retries INTEGER := 3;
    retry_delay INTEGER := 1; -- seconds
    request_status INTEGER;
    request_content TEXT;
BEGIN
    headers := ARRAY[http_header('Authorization', v_token)];
    IF additional_headers IS NOT NULL THEN
        headers := headers || additional_headers;
    END IF;
    
    LOOP
        BEGIN
            SELECT t.status, t.content::TEXT INTO request_status, request_content
            FROM http((
                'GET',
                format('%s%s', current_setting('app.api_dataimpulse_url'), relative_path),
                headers,
                NULL,
                NULL
            )::http_request) AS t;
            
            -- If we get here, the request succeeded
            status := request_status;
            content := request_content;
            RETURN NEXT;
            RETURN;
            
        EXCEPTION WHEN OTHERS THEN
            -- Check if this is a timeout error
            IF SQLERRM LIKE '%28%' OR SQLERRM LIKE '%timed out%' OR SQLERRM LIKE '%timeout%' THEN
                retry_count := retry_count + 1;
                
                RAISE LOG 'HTTP GET timeout on attempt % for path %, retrying in % seconds', 
                    retry_count, relative_path, retry_delay;
                
                IF retry_count <= max_retries THEN
                    PERFORM pg_sleep(retry_delay);
                    retry_delay := retry_delay * 2; -- Exponential backoff
                    CONTINUE;
                END IF;
            END IF;
            
            -- Re-raise the exception if it's not a timeout or we've exceeded max retries
            RAISE;
        END;
    END LOOP;
END;
$$;


ALTER FUNCTION dataimpulse.httpget(relative_path text, additional_headers public.http_header[]) OWNER TO postgres;

--
-- Name: httppost(text, jsonb, public.http_header[]); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.httppost(relative_path text, body jsonb, additional_headers public.http_header[] DEFAULT '{}'::public.http_header[]) RETURNS TABLE(status integer, content text)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_token TEXT := get_current_token();
    headers http_header[];
    retry_count INTEGER := 0;
    max_retries INTEGER := 3;
    retry_delay INTEGER := 1; -- seconds
    request_status INTEGER;
    request_content TEXT;
BEGIN
    headers := ARRAY[http_header('Authorization', v_token)];
    IF additional_headers IS NOT NULL THEN
        headers := headers || additional_headers;
    END IF;
    
    LOOP
        BEGIN
            SELECT t.status, t.content::TEXT INTO request_status, request_content
            FROM http((
                'POST',
                format('%s%s', current_setting('app.api_dataimpulse_url'), relative_path),
                headers,
                'application/json',
                body
            )::http_request) AS t;
            
            -- If we get here, the request succeeded
            status := request_status;
            content := request_content;
            RETURN NEXT;
            RETURN;
            
        EXCEPTION WHEN OTHERS THEN
            -- Check if this is a timeout error
            IF SQLERRM LIKE '%28%' OR SQLERRM LIKE '%timed out%' OR SQLERRM LIKE '%timeout%' THEN
                retry_count := retry_count + 1;
                
                RAISE LOG 'HTTP POST timeout on attempt % for path %, retrying in % seconds', 
                    retry_count, relative_path, retry_delay;
                
                IF retry_count <= max_retries THEN
                    PERFORM pg_sleep(retry_delay);
                    retry_delay := retry_delay * 2; -- Exponential backoff
                    CONTINUE;
                END IF;
            END IF;
            
            -- Re-raise the exception if it's not a timeout or we've exceeded max retries
            RAISE;
        END;
    END LOOP;
END;
$$;


ALTER FUNCTION dataimpulse.httppost(relative_path text, body jsonb, additional_headers public.http_header[]) OWNER TO postgres;

--
-- Name: httppost(text, text, public.http_header[]); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.httppost(relative_path text, body text, additional_headers public.http_header[] DEFAULT '{}'::public.http_header[]) RETURNS TABLE(status integer, content text)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_token TEXT := get_current_token();
    headers http_header[];
    retry_count INTEGER := 0;
    max_retries INTEGER := 3;
    retry_delay INTEGER := 1; -- seconds
    request_status INTEGER;
    request_content TEXT;
BEGIN
    headers := ARRAY[http_header('Authorization', v_token)];
    IF additional_headers IS NOT NULL THEN
        headers := headers || additional_headers;
    END IF;
    
    LOOP
        BEGIN
            SELECT t.status, t.content::TEXT INTO request_status, request_content
            FROM http((
                'POST',
                format('%s%s', current_setting('app.api_dataimpulse_url'), relative_path),
                headers,
                'application/json',
                body
            )::http_request) AS t;
            
            -- If we get here, the request succeeded
            status := request_status;
            content := request_content;
            RETURN NEXT;
            RETURN;
            
        EXCEPTION WHEN OTHERS THEN
            -- Check if this is a timeout error
            IF SQLERRM LIKE '%28%' OR SQLERRM LIKE '%timed out%' OR SQLERRM LIKE '%timeout%' THEN
                retry_count := retry_count + 1;
                
                RAISE LOG 'HTTP POST timeout on attempt % for path %, retrying in % seconds', 
                    retry_count, relative_path, retry_delay;
                
                IF retry_count <= max_retries THEN
                    PERFORM pg_sleep(retry_delay);
                    retry_delay := retry_delay * 2; -- Exponential backoff
                    CONTINUE;
                END IF;
            END IF;
            
            -- Re-raise the exception if it's not a timeout or we've exceeded max retries
            RAISE;
        END;
    END LOOP;
END;
$$;


ALTER FUNCTION dataimpulse.httppost(relative_path text, body text, additional_headers public.http_header[]) OWNER TO postgres;

--
-- Name: pool_stats(common.dataimpulsepool); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.pool_stats(pool common.dataimpulsepool) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httpget(format('/reseller/common/pool_stats?pool_type=%s', pool));
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse.pool_stats(pool common.dataimpulsepool) OWNER TO postgres;

--
-- Name: raise_api_exception(integer, jsonb); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.raise_api_exception(status integer, response_json jsonb) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
  error_type TEXT := format('PT%s', status);
  message TEXT;
BEGIN
  RAISE NOTICE 'response_json: %', response_json;
  IF response_json ? 'message' THEN
    message := response_json->>'message';
  ELSIF response_json ? 'msg' THEN
    message := response_json->>'msg';
  ELSE
    message := response_json::text;
  END IF;
  RAISE NOTICE 'message: %', message;
  RAISE EXCEPTION '%', message USING ERRCODE = error_type;
END;
$$;


ALTER FUNCTION dataimpulse.raise_api_exception(status integer, response_json jsonb) OWNER TO postgres;

--
-- Name: remove_allowed_ip(integer, inet); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.remove_allowed_ip(subuser_id integer, ip inet) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httppost(
	'/reseller/sub-user/allowed-ips/remove', 
        jsonb_build_object(
            'subuser_id', subuser_id,
            'ip', ip
        )::TEXT);
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse.remove_allowed_ip(subuser_id integer, ip inet) OWNER TO postgres;

--
-- Name: reset_subuser_password(integer); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.reset_subuser_password(subuser_id integer) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httppost(
	'/reseller/sub-user/reset-password', 
        jsonb_build_object(
            'subuser_id', subuser_id
        )::TEXT);
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse.reset_subuser_password(subuser_id integer) OWNER TO postgres;

--
-- Name: set_subuser_blocked(integer, boolean); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.set_subuser_blocked(subuser_id integer, blocked boolean) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httppost(
	'/reseller/sub-user/set-blocked', 
        jsonb_build_object(
            'subuser_id', subuser_id,
            'blocked', blocked
        )::TEXT);
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse.set_subuser_blocked(subuser_id integer, blocked boolean) OWNER TO postgres;

--
-- Name: set_subuser_blocked_hosts(integer, text[]); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.set_subuser_blocked_hosts(subuser_id integer, blocked_hosts text[]) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httppost(
	'/reseller/sub-user/set-blocked-hosts', 
        jsonb_build_object(
            'subuser_id', subuser_id,
            'blocked_hosts', to_jsonb(blocked_hosts)
        )::TEXT);
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse.set_subuser_blocked_hosts(subuser_id integer, blocked_hosts text[]) OWNER TO postgres;

--
-- Name: sub-user.usage-stat.detail(integer, common.period, integer, integer, common.datetime_aggregate); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse."sub-user.usage-stat.detail"(subuser_id integer, period common.period DEFAULT 'week'::common.period, "limit" integer DEFAULT 100, "offset" integer DEFAULT 0, datetime_aggregate common.datetime_aggregate DEFAULT 'minute'::common.datetime_aggregate) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httpget(
      format(
	'/reseller/sub-user/usage-stat/detail?subuser_id=%s&period=%s&limit=%s&offset=%s&datetime_aggregate=%s',
        subuser_id,
        period,
        "limit",
        "offset",
        datetime_aggregate));
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse."sub-user.usage-stat.detail"(subuser_id integer, period common.period, "limit" integer, "offset" integer, datetime_aggregate common.datetime_aggregate) OWNER TO postgres;

--
-- Name: subuser_balance_add(integer, integer); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.subuser_balance_add(subuser_id integer, traffic integer) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httppost(
	'/reseller/sub-user/balance/add', 
        jsonb_build_object(
            'subuser_id', subuser_id,
            'traffic', traffic
        )::TEXT);
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse.subuser_balance_add(subuser_id integer, traffic integer) OWNER TO postgres;

--
-- Name: subuser_balance_drop(integer); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.subuser_balance_drop(subuser_id integer) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httppost(
	'/reseller/sub-user/balance/drop', 
        jsonb_build_object(
            'subuser_id', subuser_id
        )::TEXT);
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse.subuser_balance_drop(subuser_id integer) OWNER TO postgres;

--
-- Name: subuser_balance_get(bigint); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.subuser_balance_get(subuser_id bigint) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
   response RECORD := dataimpulse.httpget(
      format('/reseller/sub-user/balance/get?subuser_id=%s',
        subuser_id));
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse.subuser_balance_get(subuser_id bigint) OWNER TO postgres;

--
-- Name: subuser_balance_get_addition_history(integer); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.subuser_balance_get_addition_history(subuser_id integer) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httpget(
           format(
                '/reseller/sub-user/balance/addition-history?subuser_id=%s',
                subuser_id
            ));
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse.subuser_balance_get_addition_history(subuser_id integer) OWNER TO postgres;

--
-- Name: subuser_get_supported_protocols(integer); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.subuser_get_supported_protocols(subuser_id integer) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httpget(
        format(
            '/reseller/sub-user/supported-protocols/get?subuser_id=%s',
            subuser_id));
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse.subuser_get_supported_protocols(subuser_id integer) OWNER TO postgres;

--
-- Name: subuser_get_usage_stat(integer, common.period); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.subuser_get_usage_stat(subuser_id integer, period common.period DEFAULT 'week'::common.period) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
     response RECORD := dataimpulse.httpget(format(
         '/reseller/sub-user/usage-stat/get?subuser_id=%s&period=%s',
         subuser_id,
         period
     ));
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse.subuser_get_usage_stat(subuser_id integer, period common.period) OWNER TO postgres;

--
-- Name: subuser_list(bigint, bigint); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.subuser_list("limit" bigint DEFAULT 1000, "offset" bigint DEFAULT 0) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httpget(
      format('/reseller/sub-user/list?limit=%s&offset=%s', "limit", "offset"));
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END $$;


ALTER FUNCTION dataimpulse.subuser_list("limit" bigint, "offset" bigint) OWNER TO postgres;

--
-- Name: subuser_set_default_pool_parameters(integer, jsonb); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.subuser_set_default_pool_parameters(subuser_id integer, parameters jsonb) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    cleaned_params jsonb := parameters - 'pool';
    final_params jsonb := jsonb_build_object(
        'subuser_id', subuser_id,
        'default_pool_parameters', cleaned_params
    );
    response RECORD := dataimpulse.httppost(
        '/reseller/sub-user/set-default-pool-parameters',
        final_params::TEXT);
BEGIN
    RAISE LOG 'Final params: %', jsonb_pretty(final_params);
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END $$;


ALTER FUNCTION dataimpulse.subuser_set_default_pool_parameters(subuser_id integer, parameters jsonb) OWNER TO postgres;

--
-- Name: subuser_set_supported_protocols(integer, common.protocols[]); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.subuser_set_supported_protocols(subuser_id integer, protocols common.protocols[]) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httppost(
	'/reseller/sub-user/supported-protocols/set', 
        jsonb_build_object(
            'subuser_id', subuser_id,
            'supported_protocols', protocols
        )::TEXT);
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse.subuser_set_supported_protocols(subuser_id integer, protocols common.protocols[]) OWNER TO postgres;

--
-- Name: subuser_usage_stat_errors(integer, common.errors_period, integer, integer, common.datetime_aggregate); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.subuser_usage_stat_errors(subuser_id integer, period common.errors_period DEFAULT 'week'::common.errors_period, "limit" integer DEFAULT 100, "offset" integer DEFAULT 0, datetime_aggregate common.datetime_aggregate DEFAULT 'minute'::common.datetime_aggregate) RETURNS jsonb
    LANGUAGE plpgsql
    AS $$
DECLARE
    response RECORD := dataimpulse.httpget(format(
      '/reseller/sub-user/usage-stat/errors?subuser_id=%s&period=%s&limit=%s&offset=%s&datetime_aggregate=%s',
      subuser_id,
      period,
      "limit",
      "offset",
      datetime_aggregate));
BEGIN
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse.subuser_usage_stat_errors(subuser_id integer, period common.errors_period, "limit" integer, "offset" integer, datetime_aggregate common.datetime_aggregate) OWNER TO postgres;

--
-- Name: update_balance(); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.update_balance() RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN

END
$$;


ALTER FUNCTION dataimpulse.update_balance() OWNER TO postgres;

--
-- Name: update_subuser(integer, text, jsonb, integer); Type: FUNCTION; Schema: dataimpulse; Owner: postgres
--

CREATE FUNCTION dataimpulse.update_subuser(subuser_id integer, label text DEFAULT NULL::text, sticky_range jsonb DEFAULT NULL::jsonb, threads integer DEFAULT NULL::integer) RETURNS json
    LANGUAGE plpgsql
    AS $$
DECLARE
    data JSONB := '{}';
    response RECORD;
BEGIN
    data := data || jsonb_build_object('subuser_id', subuser_id);

    IF label IS NOT NULL THEN
        data := data || jsonb_build_object('label', label);
    END IF;
    IF sticky_range IS NOT NULL THEN
        data := data || jsonb_build_object('sticky_range', sticky_range);
    END IF;
    IF threads IS NOT NULL THEN
        data := data || jsonb_build_object('threads', threads);
    END IF;

    response := dataimpulse.httppost('/reseller/sub-user/update', data::TEXT);
    RETURN dataimpulse.handle_api_response(response.status, response.content);
END
$$;


ALTER FUNCTION dataimpulse.update_subuser(subuser_id integer, label text, sticky_range jsonb, threads integer) OWNER TO postgres;

SET default_tablespace = '';

SET default_table_access_method = heap;

--
-- Name: host; Type: TABLE; Schema: dataimpulse; Owner: postgres
--

CREATE TABLE dataimpulse.host (
    id integer NOT NULL,
    region common.proxyregion,
    name text,
    ip inet
)
INHERITS (common.created_at, common.updated_at);


ALTER TABLE dataimpulse.host OWNER TO postgres;

--
-- Name: host_id_seq; Type: SEQUENCE; Schema: dataimpulse; Owner: postgres
--

CREATE SEQUENCE dataimpulse.host_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


ALTER TABLE dataimpulse.host_id_seq OWNER TO postgres;

--
-- Name: host_id_seq; Type: SEQUENCE OWNED BY; Schema: dataimpulse; Owner: postgres
--

ALTER SEQUENCE dataimpulse.host_id_seq OWNED BY dataimpulse.host.id;


--
-- Name: host created_at; Type: DEFAULT; Schema: dataimpulse; Owner: postgres
--

ALTER TABLE ONLY dataimpulse.host ALTER COLUMN created_at SET DEFAULT now();


--
-- Name: host updated_at; Type: DEFAULT; Schema: dataimpulse; Owner: postgres
--

ALTER TABLE ONLY dataimpulse.host ALTER COLUMN updated_at SET DEFAULT now();


--
-- Name: host id; Type: DEFAULT; Schema: dataimpulse; Owner: postgres
--

ALTER TABLE ONLY dataimpulse.host ALTER COLUMN id SET DEFAULT nextval('dataimpulse.host_id_seq'::regclass);


--
-- Name: host host_pkey; Type: CONSTRAINT; Schema: dataimpulse; Owner: postgres
--

ALTER TABLE ONLY dataimpulse.host
    ADD CONSTRAINT host_pkey PRIMARY KEY (id);


--
-- Name: SCHEMA dataimpulse; Type: ACL; Schema: -; Owner: postgres
--

GRANT USAGE ON SCHEMA dataimpulse TO vgate_provider;
GRANT USAGE ON SCHEMA dataimpulse TO anon;


--
-- Name: FUNCTION get_subuser(subuser_id integer); Type: ACL; Schema: dataimpulse; Owner: postgres
--

GRANT ALL ON FUNCTION dataimpulse.get_subuser(subuser_id integer) TO vgate_provider;


--
-- PostgreSQL database dump complete
--

