mirror of
https://github.com/penpot/penpot.git
synced 2025-05-10 19:46:37 +02:00
132 lines
4 KiB
Clojure
132 lines
4 KiB
Clojure
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
;;
|
|
;; Copyright (c) UXBOX Labs SL
|
|
|
|
(ns app.http.errors
|
|
"A errors handling for the http server."
|
|
(:require
|
|
[app.common.exceptions :as ex]
|
|
[app.common.logging :as l]
|
|
[app.common.uuid :as uuid]
|
|
[clojure.pprint]
|
|
[clojure.spec.alpha :as s]
|
|
[cuerdas.core :as str]))
|
|
|
|
(defn- parse-client-ip
|
|
[{:keys [headers] :as request}]
|
|
(or (some-> (get headers "x-forwarded-for") (str/split ",") first)
|
|
(get headers "x-real-ip")
|
|
(get request :remote-addr)))
|
|
|
|
(defn get-error-context
|
|
[request error]
|
|
(let [data (ex-data error)]
|
|
(merge
|
|
{:id (uuid/next)
|
|
:path (:uri request)
|
|
:method (:request-method request)
|
|
:hint (or (:hint data) (ex-message error))
|
|
:params (l/stringify-data (:params request))
|
|
:spec-problems (some-> data ::s/problems)
|
|
:ip-addr (parse-client-ip request)
|
|
:profile-id (:profile-id request)}
|
|
|
|
(let [headers (:headers request)]
|
|
{:user-agent (get headers "user-agent")
|
|
:frontend-version (get headers "x-frontend-version" "unknown")})
|
|
|
|
(dissoc data ::s/problems))))
|
|
|
|
(defmulti handle-exception
|
|
(fn [err & _rest]
|
|
(let [edata (ex-data err)]
|
|
(or (:type edata)
|
|
(class err)))))
|
|
|
|
(defmethod handle-exception :authentication
|
|
[err _]
|
|
{:status 401 :body (ex-data err)})
|
|
|
|
(defmethod handle-exception :restriction
|
|
[err _]
|
|
{:status 400 :body (ex-data err)})
|
|
|
|
(defmethod handle-exception :validation
|
|
[err req]
|
|
(let [edata (ex-data err)]
|
|
{:status 400
|
|
:body (dissoc edata ::s/problems)}))
|
|
|
|
(defmethod handle-exception :assertion
|
|
[error request]
|
|
(let [edata (ex-data error)]
|
|
(l/with-context (get-error-context request error)
|
|
(l/error :hint (ex-message error) :cause error))
|
|
{:status 500
|
|
:body {:type :server-error
|
|
:code :assertion
|
|
:data (dissoc edata ::s/problems)}}))
|
|
|
|
(defmethod handle-exception :not-found
|
|
[err _]
|
|
{:status 404 :body (ex-data err)})
|
|
|
|
(defmethod handle-exception :default
|
|
[error request]
|
|
(let [edata (ex-data error)]
|
|
;; NOTE: this is a special case for the idle-in-transaction error;
|
|
;; when it happens, the connection is automatically closed and
|
|
;; next-jdbc combines the two errors in a single ex-info. We only
|
|
;; need the :handling error, because the :rollback error will be
|
|
;; always "connection closed".
|
|
(if (and (ex/exception? (:rollback edata))
|
|
(ex/exception? (:handling edata)))
|
|
(handle-exception (:handling edata) request)
|
|
(do
|
|
(l/with-context (get-error-context request error)
|
|
(l/error :hint (ex-message error) :cause error))
|
|
|
|
{:status 500
|
|
:body {:type :server-error
|
|
:code :unexpected
|
|
:hint (ex-message error)
|
|
:data edata}}))))
|
|
|
|
(defmethod handle-exception org.postgresql.util.PSQLException
|
|
[error request]
|
|
(let [state (.getSQLState ^java.sql.SQLException error)]
|
|
|
|
(l/with-context (get-error-context request error)
|
|
(l/error :hint "psql exception"
|
|
:error-message (ex-message error)
|
|
:state state
|
|
:cause error))
|
|
|
|
(cond
|
|
(= state "57014")
|
|
{:status 504
|
|
:body {:type :server-timeout
|
|
:code :statement-timeout
|
|
:hint (ex-message error)}}
|
|
|
|
(= state "25P03")
|
|
{:status 504
|
|
:body {:type :server-timeout
|
|
:code :idle-in-transaction-timeout
|
|
:hint (ex-message error)}}
|
|
|
|
:else
|
|
{:status 500
|
|
:body {:type :server-error
|
|
:code :psql-exception
|
|
:hint (ex-message error)
|
|
:state state}})))
|
|
|
|
(defn handle
|
|
[error req]
|
|
(if (or (instance? java.util.concurrent.CompletionException error)
|
|
(instance? java.util.concurrent.ExecutionException error))
|
|
(handle-exception (.getCause ^Throwable error) req)
|
|
(handle-exception error req)))
|