api.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import { $fetch } from 'ofetch'
  2. import { useLogger } from '@/composables/useLogger'
  3. import { useUser } from '~/store/useUser'
  4. interface TUserData {
  5. user_id: number
  6. access_token: string
  7. }
  8. interface Detail {
  9. type: string
  10. loc: string[]
  11. msg: string
  12. }
  13. interface TException {
  14. error?: string
  15. code?: number
  16. detail?: Detail[]
  17. }
  18. export class API {
  19. private log
  20. private module: string = 'API'
  21. private API_URL: string = useRuntimeConfig().public.API_URL
  22. private user = useUser()
  23. private snackbar = useSnackbar()
  24. constructor(module: string) {
  25. this.module = module
  26. this.log = useLogger(this.module)
  27. }
  28. private throwError(request: string, data: TException, body?: unknown) {
  29. const err = {
  30. info: '',
  31. request,
  32. body,
  33. }
  34. if (Object.prototype.hasOwnProperty.call(data, 'code') && Object.prototype.hasOwnProperty.call(data, 'error')) {
  35. err.info = `${data.error} (${data.code})`
  36. }
  37. else if (Object.prototype.hasOwnProperty.call(data, 'detail')) {
  38. const details = data.detail[0]
  39. err.info = `${details.type} ${details.loc.join(', ')} (${details.msg})`
  40. }
  41. else {
  42. err.info = data
  43. }
  44. this.log.error(err)
  45. this.snackbar.add({
  46. type: 'error',
  47. text: 'Возникла ошибка на сервере. Не переживайте, мы уже знаем о проблеме 👌'
  48. })
  49. }
  50. private instance = $fetch.create({
  51. baseURL: this.API_URL,
  52. onRequest: (ctx) => {
  53. if (!this.user.data.access_token && ctx.request !== '/login')
  54. navigateTo('/auth')
  55. },
  56. onResponse: (ctx) => {
  57. const statusCode = ctx.response.status
  58. if (statusCode > 300 && statusCode !== 400) {
  59. this.throwError(ctx.request.toString(), ctx.response._data, ctx.options.body)
  60. }
  61. },
  62. onResponseError: async (ctx) => {
  63. const statusCode = ctx.response.status
  64. if (statusCode === 401) {
  65. const newToken: TUserData = await this.get('/user/refresh', {
  66. access_token: ctx.request.toString().split('?')[1].split('=')[1],
  67. })
  68. this.user.data.access_token = newToken.access_token
  69. this.log.log('Access token обновлен:', this.user.data.access_token)
  70. return this.instance(ctx.request, {
  71. method: ctx.request.method,
  72. body: ctx.request.body,
  73. params: ctx.request.params,
  74. })
  75. }
  76. else if (statusCode === 400 && ctx.request !== `${this.API_URL}/user/login`) {
  77. this.user.logout()
  78. }
  79. else if (statusCode !== 400) {
  80. this.throwError(ctx.request.toString(), ctx.response._data, ctx.options.body)
  81. }
  82. },
  83. })
  84. get = <T>(url: string, params?: object): Promise<T> => {
  85. return this.instance(url, {
  86. method: 'GET',
  87. params: {
  88. access_token: this.user.data.access_token,
  89. ...params,
  90. },
  91. })
  92. }
  93. post = <T>(url: string, body?: object, params?: object): Promise<T> => {
  94. return this.instance(url, {
  95. method: 'POST',
  96. body,
  97. params: {
  98. access_token: this.user.data.access_token,
  99. ...params,
  100. },
  101. })
  102. }
  103. put = <T>(url: string, body?: object, params?: object): Promise<T> => {
  104. return this.instance(url, {
  105. method: 'PUT',
  106. body,
  107. params: {
  108. access_token: this.user.data.access_token,
  109. ...params,
  110. },
  111. })
  112. }
  113. delete = <T>(url: string, body?: object, params?: object): Promise<T> => {
  114. return this.instance(url, {
  115. method: 'DELETE',
  116. body,
  117. params: {
  118. access_token: this.user.data.access_token,
  119. ...params,
  120. },
  121. })
  122. }
  123. }