\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: public_api; Type: SCHEMA; Schema: -; Owner: postgres
--

CREATE SCHEMA public_api;


ALTER SCHEMA public_api OWNER TO postgres;

--
-- Name: asns(text, text[], text[], text[], text[]); Type: FUNCTION; Schema: public_api; Owner: postgres
--

CREATE FUNCTION public_api.asns(pool text, countries text[] DEFAULT NULL::text[], cities text[] DEFAULT NULL::text[], states text[] DEFAULT NULL::text[], zipcodes text[] DEFAULT NULL::text[]) RETURNS jsonb
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
DECLARE
    _pool common.dataimpulsepool;
BEGIN
    BEGIN
      _pool := pool::common.dataimpulsepool;
    EXCEPTION
      WHEN OTHERS THEN
        RAISE sqlstate 'PT400' USING
            message = 'Invalid pool, possible values: residential, datacenter, mobile';
    END;

    RETURN dataimpulse.get_proxy_asns(_pool, countries, states, cities, zipcodes);
EXCEPTION
    WHEN OTHERS THEN
        RAISE WARNING 'Unexpected exception: %', SQLERRM;
        RAISE sqlstate 'PT500' USING message = 'Unexpected exception';
END
$$;


ALTER FUNCTION public_api.asns(pool text, countries text[], cities text[], states text[], zipcodes text[]) OWNER TO postgres;

--
-- Name: balance(text); Type: FUNCTION; Schema: public_api; Owner: postgres
--

CREATE FUNCTION public_api.balance(pool text) RETURNS jsonb
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
DECLARE
    balance JSON;
    _pool common.dataimpulsepool;
    _user_id TEXT := (current_setting('request.jwt.claims')::JSONB->>'sub')::TEXT;
    _sub_id INTEGER;
    _record RECORD;
BEGIN
    IF _user_id IS NULL THEN
      RAISE WARNING 'jwt claims is %', current_setting('request.jwt.claims');
      RAISE sqlstate 'PT500' USING
          message = 'Internal error';
    END IF;

    BEGIN
      _pool := "pool"::common.dataimpulsepool;
    EXCEPTION
      WHEN OTHERS THEN
        RAISE sqlstate 'PT400' USING
            message = 'Invalid pool, possible values: residential, datacenter, mobile';
    END;

    RAISE NOTICE 'pool: %, user_id: %', _pool, _user_id;

    FOR _record IN (SELECT 
      "user"."sub_id"
    FROM 
      "legacy"."user" 
    WHERE 
      "user"."user_id" = _user_id) LOOP
      RAISE NOTICE 'record: %', _record;
      RAISE NOTICE 'record.sub_id: %', _record.sub_id;
    END LOOP;

    SELECT 
      "user"."sub_id"
    FROM 
      "legacy"."user" 
    WHERE 
      "user"."user_id" = _user_id
      AND "user"."pool" = _pool
    INTO _sub_id;

    RAISE NOTICE 'sub_id: %', _sub_id;
    
    if _sub_id is null then
        RAISE sqlstate 'PT404' USING
            message = 'pool not found';
    end if;

    balance := dataimpulse.get_subuser(_sub_id::INTEGER);

    IF balance IS NULL THEN
        RAISE sqlstate 'PT500' USING
            message = 'Failed to get balance';
    END IF;

    RETURN jsonb_build_object('pool', _pool, 'balance', balance->'balance', 'balance_format', balance->'balance_format');
END
$$;


ALTER FUNCTION public_api.balance(pool text) OWNER TO postgres;

--
-- Name: FUNCTION balance(pool text); Type: COMMENT; Schema: public_api; Owner: postgres
--

COMMENT ON FUNCTION public_api.balance(pool text) IS 'Получение баланса для указанного пула прокси. Параметр pool - тип пула (residential, datacenter, mobile). Требует JWT-авторизацию.';


--
-- Name: cities(text, text[], text[], text[], text[]); Type: FUNCTION; Schema: public_api; Owner: postgres
--

CREATE FUNCTION public_api.cities(pool text, countries text[] DEFAULT NULL::text[], states text[] DEFAULT NULL::text[], asns text[] DEFAULT NULL::text[], zipcodes text[] DEFAULT NULL::text[]) RETURNS jsonb
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
DECLARE
    _pool common.dataimpulsepool;
BEGIN
    BEGIN
      _pool := pool::common.dataimpulsepool;
    EXCEPTION
      WHEN OTHERS THEN
        RAISE sqlstate 'PT400' USING
            message = 'Invalid pool, possible values: residential, datacenter, mobile';
    END;

    RETURN dataimpulse.get_proxy_cities(_pool, countries, states, zipcodes, asns);
EXCEPTION
    WHEN OTHERS THEN
        RAISE WARNING 'Unexpected exception: %', SQLERRM;
        RAISE sqlstate 'PT500' USING message = 'Unexpected exception';
END
$$;


ALTER FUNCTION public_api.cities(pool text, countries text[], states text[], asns text[], zipcodes text[]) OWNER TO postgres;

--
-- Name: countries(text); Type: FUNCTION; Schema: public_api; Owner: postgres
--

CREATE FUNCTION public_api.countries(pool text) RETURNS jsonb
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
DECLARE
    _pool common.dataimpulsepool;
BEGIN
    BEGIN
      _pool := pool::common.dataimpulsepool;
    EXCEPTION
      WHEN OTHERS THEN
        RAISE sqlstate 'PT400' USING
            message = 'Invalid pool, possible values: residential, datacenter, mobile';
    END;

    RETURN dataimpulse.get_proxy_countries(_pool);
EXCEPTION
    WHEN OTHERS THEN
        RAISE WARNING 'Unexpected exception: %', SQLERRM;
        RAISE sqlstate 'PT500' USING message = 'Unexpected exception';
END
$$;


ALTER FUNCTION public_api.countries(pool text) OWNER TO postgres;

--
-- Name: FUNCTION countries(pool text); Type: COMMENT; Schema: public_api; Owner: postgres
--

COMMENT ON FUNCTION public_api.countries(pool text) IS 'Получение списка доступных стран для указанного пула прокси. Требует JWT-авторизацию.';


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

CREATE FUNCTION public_api.proxy(pool text, countries text[] DEFAULT NULL::text[], cities text[] DEFAULT NULL::text[], states text[] DEFAULT NULL::text[], asns text[] DEFAULT NULL::text[], zipcodes text[] DEFAULT NULL::text[], anonymous text DEFAULT 'true'::text, type text DEFAULT 'rotating'::text, protocol text DEFAULT 'http'::text, format text DEFAULT 'login:password@hostname:port'::text, quantity integer DEFAULT 1, sessionttl integer DEFAULT 600, username text DEFAULT NULL::text, password text DEFAULT NULL::text, connection_host text DEFAULT NULL::text) RETURNS vgate."text/plain"
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
DECLARE
    _user_id TEXT;
    sub_id TEXT;
    _pool common.dataimpulsepool;
    result TEXT;
    _auth_user_id TEXT;
    host_ip INET;
BEGIN
    -- Authentication priority:
    -- 1. Use provided username/password parameters (authenticate and get user_id)
    -- 2. Fall back to JWT header

    IF proxy.username IS NOT NULL AND proxy.password IS NOT NULL THEN
        -- Authenticate with username/password
        SELECT au.user_id
        FROM legacy.all_user au
        WHERE au.email = proxy.username OR au.username = proxy.username OR au.user_id = proxy.username
        INTO _auth_user_id;

        IF _auth_user_id IS NULL THEN
            RAISE sqlstate 'PT401' USING message = 'Invalid credentials: user not found';
        END IF;

        -- Verify password using the same method as public_api.token()
        SELECT basic_auth.check_user_pass(_auth_user_id, proxy.password) INTO _user_id;

        IF _user_id IS NULL THEN
            RAISE sqlstate 'PT401' USING message = 'Invalid credentials: incorrect password';
        END IF;

    ELSE
        -- Fall back to JWT header authentication
        BEGIN
            _user_id := (current_setting('request.jwt.claims')::JSONB->>'sub')::TEXT;

            IF _user_id IS NULL THEN
                RAISE sqlstate 'PT401' USING message = 'Authentication required: provide username/password or valid JWT header';
            END IF;
        EXCEPTION
            WHEN OTHERS THEN
                RAISE sqlstate 'PT401' USING message = 'Authentication required: provide username/password or valid JWT header';
        END;
    END IF;
    BEGIN
        _pool := pool::common.dataimpulsepool;
    EXCEPTION
        WHEN OTHERS THEN
            RAISE sqlstate 'PT400' USING
                message = 'Invalid pool type';
    END;

    SELECT "user".sub_id::INTEGER
    FROM "legacy"."user"
    WHERE "user".user_id = _user_id
    AND "user".pool = _pool
    INTO sub_id;

    IF sub_id IS NULL THEN
        RAISE sqlstate 'PT404' USING
            message = 'Pool not found or user does not have access to this pool';
    END IF;

    IF connection_host IS NOT NULL THEN
        SELECT ip
        FROM dataimpulse.host
        WHERE region::TEXT = connection_host
        LIMIT 1
        INTO host_ip;

        IF NOT FOUND THEN
            RAISE sqlstate 'PT404' USING
                message = 'Such connection host does not exist';
        END IF;
    ELSE
        SELECT ip
        FROM dataimpulse.host
        WHERE region::TEXT = 'auto'
        LIMIT 1
        INTO host_ip;
    END IF;

    result := dataimpulse.get_proxy(
        anonymous,
        "type",
        protocol,
        format,
        quantity,
        sessionttl,
        _pool,
        sub_id::TEXT,
        countries,
        cities,
        states,
        NULL,  -- excluded_asns
        asns,
        zipcodes,
        host_ip
    );

    IF result IS NULL THEN
        RAISE sqlstate 'PT500' USING
            message = 'Failed to generate proxy';
    END IF;

    RETURN result;
EXCEPTION
    WHEN sqlstate 'PT401' THEN
        RAISE;
    WHEN sqlstate 'PT404' THEN
        RAISE;
    WHEN sqlstate 'PT400' THEN
        RAISE;
    WHEN OTHERS THEN
        RAISE WARNING 'public_api.proxy error: %', SQLERRM;
        RAISE sqlstate 'PT500' USING message = 'Unexpected exception';
END
$$;


ALTER FUNCTION public_api.proxy(pool text, countries text[], cities text[], states text[], asns text[], zipcodes text[], anonymous text, type text, protocol text, format text, quantity integer, sessionttl integer, username text, password text, connection_host text) OWNER TO postgres;

--
-- Name: states(text, text[], text[], text[], text[]); Type: FUNCTION; Schema: public_api; Owner: postgres
--

CREATE FUNCTION public_api.states(pool text, countries text[] DEFAULT NULL::text[], cities text[] DEFAULT NULL::text[], zipcodes text[] DEFAULT NULL::text[], asns text[] DEFAULT NULL::text[]) RETURNS jsonb
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
DECLARE
    _pool common.dataimpulsepool;
BEGIN
    BEGIN
      _pool := pool::common.dataimpulsepool;
    EXCEPTION
      WHEN OTHERS THEN
        RAISE sqlstate 'PT400' USING
            message = 'Invalid pool, possible values: residential, datacenter, mobile';
    END;

    RETURN dataimpulse.get_proxy_states(_pool, countries, cities, zipcodes, asns);
EXCEPTION
    WHEN OTHERS THEN
        RAISE WARNING 'Unexpected exception: %', SQLERRM;
        RAISE sqlstate 'PT500' USING message = 'Unexpected exception';
END
$$;


ALTER FUNCTION public_api.states(pool text, countries text[], cities text[], zipcodes text[], asns text[]) OWNER TO postgres;

--
-- Name: token(text, text); Type: FUNCTION; Schema: public_api; Owner: postgres
--

CREATE FUNCTION public_api.token(username text, password text) RETURNS vgate."text/plain"
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
DECLARE
  _role NAME;
  _confirmed INT;
  rolename TEXT;
  actual_username TEXT;
  schema_prefix TEXT := CURRENT_SCHEMA || '_';
  user_id TEXT;
BEGIN
  -- Check if the user is trying to login with email
  SELECT au.user_id, au.role INTO user_id, _role
  FROM legacy.all_user au
  WHERE au.email = token.username OR au.username = token.username OR au.user_id = token.username;

  IF user_id IS NULL THEN
    RAISE DEBUG 'user_id is null';
    RAISE invalid_password USING message = 'invalid user or password';
  END IF;

  SELECT basic_auth.check_user_pass(user_id, password) INTO user_id;
  
  IF user_id IS NULL THEN
    RAISE DEBUG 'user_id auth failed';
    RAISE invalid_password USING message = 'invalid user or password';
  END IF;

  RETURN (SELECT public.sign(
         row_to_json(r), common.get_jwt_secret()
       ) AS token
       FROM (SELECT _role AS role, user_id as sub) r);
END;
$$;


ALTER FUNCTION public_api.token(username text, password text) OWNER TO postgres;

--
-- Name: FUNCTION token(username text, password text); Type: COMMENT; Schema: public_api; Owner: postgres
--

COMMENT ON FUNCTION public_api.token(username text, password text) IS 'Получение JWT-токена для авторизации в API. Принимает имя пользователя (или email) и пароль.';


--
-- Name: zipcodes(text, text[], text[], text[]); Type: FUNCTION; Schema: public_api; Owner: postgres
--

CREATE FUNCTION public_api.zipcodes(pool text, countries text[] DEFAULT NULL::text[], states text[] DEFAULT NULL::text[], asns text[] DEFAULT NULL::text[]) RETURNS jsonb
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
DECLARE
    _pool common.dataimpulsepool;
BEGIN
    BEGIN
      _pool := pool::common.dataimpulsepool;
    EXCEPTION
      WHEN OTHERS THEN
        RAISE sqlstate 'PT400' USING
            message = 'Invalid pool, possible values: residential, datacenter, mobile';
    END;

    RETURN dataimpulse.get_proxy_zipcodes(_pool, countries, states, asns);
EXCEPTION
    WHEN OTHERS THEN
        RAISE WARNING 'Unexpected exception: %', SQLERRM;
        RAISE sqlstate 'PT500' USING message = 'Unexpected exception';
END
$$;


ALTER FUNCTION public_api.zipcodes(pool text, countries text[], states text[], asns text[]) OWNER TO postgres;

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

GRANT ALL ON SCHEMA public_api TO vgate_user;
GRANT ALL ON SCHEMA public_api TO anon;


--
-- Name: FUNCTION balance(pool text); Type: ACL; Schema: public_api; Owner: postgres
--

GRANT ALL ON FUNCTION public_api.balance(pool text) TO vgate_user;


--
-- Name: FUNCTION countries(pool text); Type: ACL; Schema: public_api; Owner: postgres
--

GRANT ALL ON FUNCTION public_api.countries(pool text) TO vgate_user;


--
-- Name: FUNCTION token(username text, password text); Type: ACL; Schema: public_api; Owner: postgres
--

GRANT ALL ON FUNCTION public_api.token(username text, password text) TO vgate_user;
GRANT ALL ON FUNCTION public_api.token(username text, password text) TO anon;


--
-- PostgreSQL database dump complete
--

