static_resources:
  listeners:
    - name: ingress_http
      address:
        socket_address:
          address: 0.0.0.0
          port_value: 8080
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                stat_prefix: ingress_http
                codec_type: AUTO
                generate_request_id: true
                use_remote_address: true
                xff_num_trusted_hops: 1
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: juwan_services
                      domains: ["*"]
                      routes:
                        - match:
                            path: /healthz
                          direct_response:
                            status: 200
                            body:
                              inline_string: ok
                          typed_per_filter_config:
                            envoy.filters.http.ext_authz:
                              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
                              disabled: true

                        - match:
                            path: /api/v1/auth/login
                          route:
                            cluster: user_api_cluster
                            timeout: 30s
                            rate_limits:
                              - actions:
                                  - generic_key:
                                      descriptor_value: login
                                  - remote_address: {}
                          typed_per_filter_config:
                            envoy.filters.http.ext_authz:
                              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
                              disabled: true

                        - match:
                            path: /api/v1/auth/register
                          route:
                            cluster: user_api_cluster
                            timeout: 30s
                            rate_limits:
                              - actions:
                                  - generic_key:
                                      descriptor_value: register
                                  - remote_address: {}
                          typed_per_filter_config:
                            envoy.filters.http.ext_authz:
                              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
                              disabled: true

                        - match:
                            path: /api/v1/auth/forgot-password
                          route:
                            cluster: user_api_cluster
                            timeout: 30s
                          typed_per_filter_config:
                            envoy.filters.http.ext_authz:
                              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
                              disabled: true

                        - match:
                            path: /api/v1/auth/reset-password
                          route:
                            cluster: user_api_cluster
                            timeout: 30s
                          typed_per_filter_config:
                            envoy.filters.http.ext_authz:
                              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
                              disabled: true

                        - match:
                            path: /api/v1/auth/forgot-password/send
                          route:
                            cluster: email_api_cluster
                            timeout: 30s
                            rate_limits:
                              - actions:
                                  - generic_key:
                                      descriptor_value: forgot_password_send
                                  - remote_address: {}
                          typed_per_filter_config:
                            envoy.filters.http.ext_authz:
                              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
                              disabled: true

                        - match:
                            path: /api/v1/email/verification-code/send
                          route:
                            cluster: email_api_cluster
                            timeout: 30s
                            rate_limits:
                              - actions:
                                  - generic_key:
                                      descriptor_value: verify_code_send
                                  - remote_address: {}
                          typed_per_filter_config:
                            envoy.filters.http.ext_authz:
                              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
                              disabled: true

                        - match:
                            prefix: /api/v1/games
                            headers:
                              - name: ":method"
                                exact_match: GET
                          route:
                            cluster: game_api_cluster
                            timeout: 30s
                          typed_per_filter_config:
                            envoy.filters.http.ext_authz:
                              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
                              disabled: true

                        - match:
                            prefix: /api/v1/players
                            headers:
                              - name: ":method"
                                exact_match: GET
                          route:
                            cluster: player_api_cluster
                            timeout: 30s
                          typed_per_filter_config:
                            envoy.filters.http.ext_authz:
                              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
                              disabled: true

                        - match:
                            prefix: /api/v1/services
                            headers:
                              - name: ":method"
                                exact_match: GET
                          route:
                            cluster: player_api_cluster
                            timeout: 30s
                          typed_per_filter_config:
                            envoy.filters.http.ext_authz:
                              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
                              disabled: true

                        - match:
                            path: /api/v1/shops
                            headers:
                              - name: ":method"
                                exact_match: GET
                          route:
                            cluster: shop_api_cluster
                            timeout: 30s
                          typed_per_filter_config:
                            envoy.filters.http.ext_authz:
                              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
                              disabled: true

                        - match:
                            safe_regex:
                              google_re2: {}
                              regex: "^/api/v1/shops/[0-9]+$"
                            headers:
                              - name: ":method"
                                exact_match: GET
                          route:
                            cluster: shop_api_cluster
                            timeout: 30s
                          typed_per_filter_config:
                            envoy.filters.http.ext_authz:
                              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
                              disabled: true

                        - match:
                            safe_regex:
                              google_re2: {}
                              regex: "^/api/v1/users/[0-9]+$"
                            headers:
                              - name: ":method"
                                exact_match: GET
                          route:
                            cluster: user_api_cluster
                            timeout: 30s
                          typed_per_filter_config:
                            envoy.filters.http.ext_authz:
                              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
                              disabled: true

                        - match:
                            safe_regex:
                              google_re2: {}
                              regex: "^/api/v1/users/[0-9]+/posts$"
                            headers:
                              - name: ":method"
                                exact_match: GET
                          route:
                            cluster: community_api_cluster
                            timeout: 30s
                          typed_per_filter_config:
                            envoy.filters.http.ext_authz:
                              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
                              disabled: true

                        - match:
                            safe_regex:
                              google_re2: {}
                              regex: "^/api/v1/users/[0-9]+/shop$"
                            headers:
                              - name: ":method"
                                exact_match: GET
                          route:
                            cluster: shop_api_cluster
                            timeout: 30s
                          typed_per_filter_config:
                            envoy.filters.http.ext_authz:
                              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
                              disabled: true

                        - match:
                            prefix: /api/v1/posts
                            headers:
                              - name: ":method"
                                exact_match: GET
                          route:
                            cluster: community_api_cluster
                            timeout: 30s
                          typed_per_filter_config:
                            envoy.filters.http.ext_authz:
                              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
                              disabled: true

                        - match:
                            prefix: /api/v1/auth
                          route:
                            cluster: user_api_cluster
                            timeout: 30s

                        - match:
                            prefix: /api/v1/admin
                          route:
                            cluster: user_api_cluster
                            timeout: 30s

                        - match:
                            prefix: /api/v1/users
                          route:
                            cluster: user_api_cluster
                            timeout: 30s

                        - match:
                            prefix: /api/v1/email
                          route:
                            cluster: email_api_cluster
                            timeout: 30s

                        - match:
                            prefix: /api/v1/games
                          route:
                            cluster: game_api_cluster
                            timeout: 30s

                        - match:
                            prefix: /api/v1/players
                          route:
                            cluster: player_api_cluster
                            timeout: 30s

                        - match:
                            prefix: /api/v1/services
                          route:
                            cluster: player_api_cluster
                            timeout: 30s

                        - match:
                            prefix: /api/v1/shops
                          route:
                            cluster: shop_api_cluster
                            timeout: 30s

                        - match:
                            prefix: /api/v1/orders
                          route:
                            cluster: order_api_cluster
                            timeout: 30s

                        - match:
                            prefix: /api/v1/wallet
                          route:
                            cluster: wallet_api_cluster
                            timeout: 30s

                        - match:
                            prefix: /api/v1/posts
                          route:
                            cluster: community_api_cluster
                            timeout: 30s

                        - match:
                            prefix: /api/v1/comments
                          route:
                            cluster: community_api_cluster
                            timeout: 30s

                        - match:
                            path: /api/v1/upload
                          route:
                            cluster: objectstory_api_cluster
                            timeout: 30s

                        - match:
                            prefix: /api/v1/files
                          route:
                            cluster: objectstory_api_cluster
                            timeout: 30s

                        - match:
                            prefix: /
                          direct_response:
                            status: 404
                            body:
                              inline_string: gateway route not found

                access_log:
                  - name: envoy.access_loggers.stdout
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
                      log_format:
                        json_format:
                          start_time: "%START_TIME%"
                          method: "%REQ(:METHOD)%"
                          path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
                          protocol: "%PROTOCOL%"
                          authority: "%REQ(:AUTHORITY)%"
                          user_agent: "%REQ(USER-AGENT)%"
                          request_id: "%REQ(X-REQUEST-ID)%"
                          response_code: "%RESPONSE_CODE%"
                          response_flags: "%RESPONSE_FLAGS%"
                          bytes_received: "%BYTES_RECEIVED%"
                          bytes_sent: "%BYTES_SENT%"
                          duration_ms: "%DURATION%"
                          upstream_cluster: "%UPSTREAM_CLUSTER%"
                          upstream_host: "%UPSTREAM_HOST%"
                          upstream_service_time_ms: "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%"
                          route_name: "%ROUTE_NAME%"

                http_filters:
                  - name: envoy.filters.http.lua
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
                      inline_code: |
                        local TOKEN_HEADER = "xsrf-token"
                        local TOKEN_COOKIE = "__Host-XSRF-TOKEN"
                        local GUARD_COOKIE = "__Host-XSRF-GUARD"

                        local seeded = false

                        local function seed_random()
                          if seeded then
                            return
                          end
                          seeded = true
                          math.randomseed(os.time())
                        end

                        local function split_cookie(header)
                          local out = {}
                          if not header then
                            return out
                          end
                          for pair in string.gmatch(header, "([^;]+)") do
                            local key, value = string.match(pair, "^%s*([^=]+)=?(.*)$")
                            if key ~= nil and value ~= nil then
                              out[string.lower(key)] = value
                            end
                          end
                          return out
                        end

                        local function is_safe_method(method)
                          return method == "GET" or method == "HEAD" or method == "OPTIONS"
                        end

                        local function build_token(request_id)
                          seed_random()
                          local rnd = tostring(math.random(100000, 999999))
                          local rid = request_id or "rid"
                          return tostring(os.time()) .. "-" .. rid .. "-" .. rnd
                        end

                        function envoy_on_request(request_handle)
                          local headers = request_handle:headers()
                          local method = headers:get(":method")

                          local cookie_header = headers:get("cookie")
                          local cookies = split_cookie(cookie_header)
                          local token_cookie = cookies[string.lower(TOKEN_COOKIE)]
                          local guard_cookie = cookies[string.lower(GUARD_COOKIE)]

                          request_handle:streamInfo():dynamicMetadata():set("csrf", "need_set_token_cookie", token_cookie == nil or token_cookie == "")
                          request_handle:streamInfo():dynamicMetadata():set("csrf", "need_set_guard_cookie", guard_cookie == nil or guard_cookie == "")

                          if token_cookie == nil or token_cookie == "" then
                            token_cookie = build_token(headers:get("x-request-id"))
                            request_handle:streamInfo():dynamicMetadata():set("csrf", "token_value", token_cookie)
                          else
                            request_handle:streamInfo():dynamicMetadata():set("csrf", "token_value", token_cookie)
                          end

                          if guard_cookie == nil or guard_cookie == "" then
                            guard_cookie = build_token(headers:get("x-request-id"))
                            request_handle:streamInfo():dynamicMetadata():set("csrf", "guard_value", guard_cookie)
                          else
                            request_handle:streamInfo():dynamicMetadata():set("csrf", "guard_value", guard_cookie)
                          end

                          if is_safe_method(method) then
                            return
                          end

                          local token_header = headers:get(TOKEN_HEADER)

                          if token_header == nil or token_header == "" then
                            request_handle:respond(
                              {[":status"] = "403", ["content-type"] = "application/json"},
                              '{"code":403,"message":"missing XSRF-TOKEN header"}'
                            )
                            return
                          end

                          if token_cookie == nil or token_cookie == "" or guard_cookie == nil or guard_cookie == "" then
                            request_handle:respond(
                              {[":status"] = "403", ["content-type"] = "application/json"},
                              '{"code":403,"message":"missing csrf cookies"}'
                            )
                            return
                          end

                          if token_header ~= token_cookie then
                            request_handle:respond(
                              {[":status"] = "403", ["content-type"] = "application/json"},
                              '{"code":403,"message":"xsrf token mismatch"}'
                            )
                            return
                          end
                        end

                        function envoy_on_response(response_handle)
                          local metadata = response_handle:streamInfo():dynamicMetadata():get("csrf")
                          if metadata == nil then
                            return
                          end

                          local token_value = metadata["token_value"]
                          local guard_value = metadata["guard_value"]

                          if metadata["need_set_token_cookie"] == true and token_value ~= nil and token_value ~= "" then
                            response_handle:headers():add(
                              "set-cookie",
                              TOKEN_COOKIE .. "=" .. token_value .. "; Path=/; Max-Age=7200; SameSite=Strict; Secure"
                            )
                          end

                          if metadata["need_set_guard_cookie"] == true and guard_value ~= nil and guard_value ~= "" then
                            response_handle:headers():add(
                              "set-cookie",
                              GUARD_COOKIE .. "=" .. guard_value .. "; Path=/; Max-Age=7200; SameSite=Strict; Secure; HttpOnly"
                            )
                          end
                        end

                  - name: envoy.filters.http.jwt_authn
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication
                      providers:
                        juwan_user_jwt:
                          issuer: juwan-user-rpc
                          from_cookies:
                            - JToken
                          local_jwks:
                            inline_string: '{"keys":[{"kty":"oct","k":"TUdVeU1XRTNaRGhqTVRRNVpEZzFNV1ZpT1dVME1HTTNPVEUyTldWa1lUQmxPVEU1WldSa1pEVTFZall6T0dKak9XUmlOek0wTlRjNE5ESXlNamxrWlE","alg":"HS256","use":"sig","kid":"juwan-hs256-1"}]}'
                          forward: false
                          claim_to_headers:
                            - header_name: x-auth-user-id
                              claim_name: UserId
                            - header_name: x-auth-is-admin
                              claim_name: IsAdmin
                      rules:
                        - match:
                            path: /healthz
                        - match:
                            prefix: /api/v1
                            headers:
                              - name: ":method"
                                exact_match: OPTIONS
                        - match:
                            path: /api/v1/auth/login
                        - match:
                            path: /api/v1/auth/register
                        - match:
                            path: /api/v1/auth/forgot-password
                        - match:
                            path: /api/v1/auth/reset-password
                        - match:
                            path: /api/v1/auth/forgot-password/send
                        - match:
                            path: /api/v1/email/verification-code/send
                        - match:
                            prefix: /api/v1/games
                            headers:
                              - name: ":method"
                                exact_match: GET
                        - match:
                            prefix: /api/v1/players
                            headers:
                              - name: ":method"
                                exact_match: GET
                        - match:
                            prefix: /api/v1/services
                            headers:
                              - name: ":method"
                                exact_match: GET
                        - match:
                            path: /api/v1/shops
                            headers:
                              - name: ":method"
                                exact_match: GET
                        - match:
                            safe_regex:
                              google_re2: {}
                              regex: "^/api/v1/shops/[0-9]+$"
                            headers:
                              - name: ":method"
                                exact_match: GET
                        - match:
                            safe_regex:
                              google_re2: {}
                              regex: "^/api/v1/users/[0-9]+$"
                            headers:
                              - name: ":method"
                                exact_match: GET
                        - match:
                            safe_regex:
                              google_re2: {}
                              regex: "^/api/v1/users/[0-9]+/posts$"
                            headers:
                              - name: ":method"
                                exact_match: GET
                        - match:
                            safe_regex:
                              google_re2: {}
                              regex: "^/api/v1/users/[0-9]+/shop$"
                            headers:
                              - name: ":method"
                                exact_match: GET
                        - match:
                            prefix: /api/v1/posts
                            headers:
                              - name: ":method"
                                exact_match: GET
                        - match:
                            prefix: /api/v1
                          requires:
                            provider_name: juwan_user_jwt

                  - name: envoy.filters.http.ext_authz
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
                      transport_api_version: V3
                      failure_mode_allow: false
                      with_request_body:
                        max_request_bytes: 8192
                        allow_partial_message: true
                      grpc_service:
                        envoy_grpc:
                          cluster_name: authz_adapter_cluster
                        timeout: 0.5s

                  - name: envoy.filters.http.ratelimit
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit
                      domain: api
                      failure_mode_deny: false
                      rate_limited_as_resource_exhausted: true
                      enable_x_ratelimit_headers: DRAFT_VERSION_03
                      rate_limit_service:
                        transport_api_version: V3
                        grpc_service:
                          envoy_grpc:
                            cluster_name: ratelimit_cluster
                          timeout: 0.2s

                  - name: envoy.filters.http.router
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

  clusters:
    - name: user_api_cluster
      connect_timeout: 2s
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: user_api_cluster
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: users-api
                      port_value: 8888

    - name: email_api_cluster
      connect_timeout: 2s
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: email_api_cluster
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: email-api
                      port_value: 8888

    - name: player_api_cluster
      connect_timeout: 2s
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: player_api_cluster
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: player-api
                      port_value: 8888

    - name: game_api_cluster
      connect_timeout: 2s
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: game_api_cluster
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: game-api
                      port_value: 8888

    - name: shop_api_cluster
      connect_timeout: 2s
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: shop_api_cluster
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: shop-api
                      port_value: 8888

    - name: order_api_cluster
      connect_timeout: 2s
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: order_api_cluster
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: order-api
                      port_value: 8888

    - name: wallet_api_cluster
      connect_timeout: 2s
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: wallet_api_cluster
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: wallet-api
                      port_value: 8888

    - name: community_api_cluster
      connect_timeout: 2s
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: community_api_cluster
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: community-api
                      port_value: 8888

    - name: objectstory_api_cluster
      connect_timeout: 2s
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: objectstory_api_cluster
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: objectstory-api
                      port_value: 8888

    - name: authz_adapter_cluster
      connect_timeout: 0.5s
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      http2_protocol_options: {}
      load_assignment:
        cluster_name: authz_adapter_cluster
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: authz-adapter
                      port_value: 9002

    - name: ratelimit_cluster
      connect_timeout: 0.25s
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      http2_protocol_options: {}
      load_assignment:
        cluster_name: ratelimit_cluster
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: ratelimit
                      port_value: 8081

admin:
  access_log_path: /tmp/admin.log
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901
