localpersistence.ts 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /* eslint-disable no-restricted-globals, no-restricted-syntax */
  2. import clone from "lodash/clone"
  3. import assign from "lodash/assign"
  4. import isEmpty from "lodash/isEmpty"
  5. import * as O from "fp-ts/Option"
  6. import { pipe } from "fp-ts/function"
  7. import {
  8. safelyExtractRESTRequest,
  9. translateToNewRequest,
  10. translateToNewRESTCollection,
  11. translateToNewGQLCollection,
  12. Environment,
  13. } from "@hoppscotch/data"
  14. import { cloneDeep } from "lodash"
  15. import {
  16. settingsStore,
  17. bulkApplySettings,
  18. defaultSettings,
  19. applySetting,
  20. HoppAccentColor,
  21. HoppBgColor,
  22. } from "./settings"
  23. import {
  24. restHistoryStore,
  25. graphqlHistoryStore,
  26. setRESTHistoryEntries,
  27. setGraphqlHistoryEntries,
  28. translateToNewRESTHistory,
  29. translateToNewGQLHistory,
  30. } from "./history"
  31. import {
  32. restCollectionStore,
  33. graphqlCollectionStore,
  34. setGraphqlCollections,
  35. setRESTCollections,
  36. } from "./collections"
  37. import {
  38. replaceEnvironments,
  39. environments$,
  40. addGlobalEnvVariable,
  41. setGlobalEnvVariables,
  42. globalEnv$,
  43. selectedEnvIndex$,
  44. setCurrentEnvironment,
  45. } from "./environments"
  46. import {
  47. getDefaultRESTRequest,
  48. restRequest$,
  49. setRESTRequest,
  50. } from "./RESTSession"
  51. import { WSRequest$, setWSRequest } from "./WebSocketSession"
  52. import { SIORequest$, setSIORequest } from "./SocketIOSession"
  53. import { SSERequest$, setSSERequest } from "./SSESession"
  54. import { MQTTRequest$, setMQTTRequest } from "./MQTTSession"
  55. import { bulkApplyLocalState, localStateStore } from "./localstate"
  56. function checkAndMigrateOldSettings() {
  57. const vuexData = JSON.parse(window.localStorage.getItem("vuex") || "{}")
  58. if (isEmpty(vuexData)) return
  59. const { postwoman } = vuexData
  60. if (!isEmpty(postwoman?.settings)) {
  61. const settingsData = assign(clone(defaultSettings), postwoman.settings)
  62. window.localStorage.setItem("settings", JSON.stringify(settingsData))
  63. delete postwoman.settings
  64. window.localStorage.setItem("vuex", JSON.stringify(vuexData))
  65. }
  66. if (postwoman?.collections) {
  67. window.localStorage.setItem(
  68. "collections",
  69. JSON.stringify(postwoman.collections)
  70. )
  71. delete postwoman.collections
  72. window.localStorage.setItem("vuex", JSON.stringify(vuexData))
  73. }
  74. if (postwoman?.collectionsGraphql) {
  75. window.localStorage.setItem(
  76. "collectionsGraphql",
  77. JSON.stringify(postwoman.collectionsGraphql)
  78. )
  79. delete postwoman.collectionsGraphql
  80. window.localStorage.setItem("vuex", JSON.stringify(vuexData))
  81. }
  82. if (postwoman?.environments) {
  83. window.localStorage.setItem(
  84. "environments",
  85. JSON.stringify(postwoman.environments)
  86. )
  87. delete postwoman.environments
  88. window.localStorage.setItem("vuex", JSON.stringify(vuexData))
  89. }
  90. if (window.localStorage.getItem("THEME_COLOR")) {
  91. const themeColor = window.localStorage.getItem("THEME_COLOR")
  92. applySetting("THEME_COLOR", themeColor as HoppAccentColor)
  93. window.localStorage.removeItem("THEME_COLOR")
  94. }
  95. if (window.localStorage.getItem("nuxt-color-mode")) {
  96. const color = window.localStorage.getItem("nuxt-color-mode") as HoppBgColor
  97. applySetting("BG_COLOR", color)
  98. window.localStorage.removeItem("nuxt-color-mode")
  99. }
  100. }
  101. function setupLocalStatePersistence() {
  102. const localStateData = JSON.parse(
  103. window.localStorage.getItem("localState") ?? "{}"
  104. )
  105. if (localStateData) bulkApplyLocalState(localStateData)
  106. localStateStore.subject$.subscribe((state) => {
  107. window.localStorage.setItem("localState", JSON.stringify(state))
  108. })
  109. }
  110. function setupSettingsPersistence() {
  111. const settingsData = JSON.parse(
  112. window.localStorage.getItem("settings") || "{}"
  113. )
  114. if (settingsData) {
  115. bulkApplySettings(settingsData)
  116. }
  117. settingsStore.subject$.subscribe((settings) => {
  118. window.localStorage.setItem("settings", JSON.stringify(settings))
  119. })
  120. }
  121. function setupHistoryPersistence() {
  122. const restHistoryData = JSON.parse(
  123. window.localStorage.getItem("history") || "[]"
  124. ).map(translateToNewRESTHistory)
  125. const graphqlHistoryData = JSON.parse(
  126. window.localStorage.getItem("graphqlHistory") || "[]"
  127. ).map(translateToNewGQLHistory)
  128. setRESTHistoryEntries(restHistoryData)
  129. setGraphqlHistoryEntries(graphqlHistoryData)
  130. restHistoryStore.subject$.subscribe(({ state }) => {
  131. window.localStorage.setItem("history", JSON.stringify(state))
  132. })
  133. graphqlHistoryStore.subject$.subscribe(({ state }) => {
  134. window.localStorage.setItem("graphqlHistory", JSON.stringify(state))
  135. })
  136. }
  137. function setupCollectionsPersistence() {
  138. const restCollectionData = JSON.parse(
  139. window.localStorage.getItem("collections") || "[]"
  140. ).map(translateToNewRESTCollection)
  141. const graphqlCollectionData = JSON.parse(
  142. window.localStorage.getItem("collectionsGraphql") || "[]"
  143. ).map(translateToNewGQLCollection)
  144. setRESTCollections(restCollectionData)
  145. setGraphqlCollections(graphqlCollectionData)
  146. restCollectionStore.subject$.subscribe(({ state }) => {
  147. window.localStorage.setItem("collections", JSON.stringify(state))
  148. })
  149. graphqlCollectionStore.subject$.subscribe(({ state }) => {
  150. window.localStorage.setItem("collectionsGraphql", JSON.stringify(state))
  151. })
  152. }
  153. function setupEnvironmentsPersistence() {
  154. const environmentsData: Environment[] = JSON.parse(
  155. window.localStorage.getItem("environments") || "[]"
  156. )
  157. // Check if a global env is defined and if so move that to globals
  158. const globalIndex = environmentsData.findIndex(
  159. (x) => x.name.toLowerCase() === "globals"
  160. )
  161. if (globalIndex !== -1) {
  162. const globalEnv = environmentsData[globalIndex]
  163. globalEnv.variables.forEach((variable) => addGlobalEnvVariable(variable))
  164. // Remove global from environments
  165. environmentsData.splice(globalIndex, 1)
  166. // Just sync the changes manually
  167. window.localStorage.setItem(
  168. "environments",
  169. JSON.stringify(environmentsData)
  170. )
  171. }
  172. replaceEnvironments(environmentsData)
  173. environments$.subscribe((envs) => {
  174. window.localStorage.setItem("environments", JSON.stringify(envs))
  175. })
  176. }
  177. function setupSelectedEnvPersistence() {
  178. const selectedEnvIndex = pipe(
  179. // Value from local storage can be nullable
  180. O.fromNullable(window.localStorage.getItem("selectedEnvIndex")),
  181. O.map(parseInt), // If not null, parse to integer
  182. O.chain(
  183. O.fromPredicate(
  184. Number.isInteger // Check if the number is proper int (not NaN)
  185. )
  186. ),
  187. O.getOrElse(() => -1) // If all the above conditions pass, we are good, else set default value (-1)
  188. )
  189. setCurrentEnvironment(selectedEnvIndex)
  190. selectedEnvIndex$.subscribe((index) => {
  191. window.localStorage.setItem("selectedEnvIndex", index.toString())
  192. })
  193. }
  194. function setupWebsocketPersistence() {
  195. const request = JSON.parse(
  196. window.localStorage.getItem("WebsocketRequest") || "null"
  197. )
  198. setWSRequest(request)
  199. WSRequest$.subscribe((req) => {
  200. window.localStorage.setItem("WebsocketRequest", JSON.stringify(req))
  201. })
  202. }
  203. function setupSocketIOPersistence() {
  204. const request = JSON.parse(
  205. window.localStorage.getItem("SocketIORequest") || "null"
  206. )
  207. setSIORequest(request)
  208. SIORequest$.subscribe((req) => {
  209. window.localStorage.setItem("SocketIORequest", JSON.stringify(req))
  210. })
  211. }
  212. function setupSSEPersistence() {
  213. const request = JSON.parse(
  214. window.localStorage.getItem("SSERequest") || "null"
  215. )
  216. setSSERequest(request)
  217. SSERequest$.subscribe((req) => {
  218. window.localStorage.setItem("SSERequest", JSON.stringify(req))
  219. })
  220. }
  221. function setupMQTTPersistence() {
  222. const request = JSON.parse(
  223. window.localStorage.getItem("MQTTRequest") || "null"
  224. )
  225. setMQTTRequest(request)
  226. MQTTRequest$.subscribe((req) => {
  227. window.localStorage.setItem("MQTTRequest", JSON.stringify(req))
  228. })
  229. }
  230. function setupGlobalEnvsPersistence() {
  231. const globals: Environment["variables"] = JSON.parse(
  232. window.localStorage.getItem("globalEnv") || "[]"
  233. )
  234. setGlobalEnvVariables(globals)
  235. globalEnv$.subscribe((vars) => {
  236. window.localStorage.setItem("globalEnv", JSON.stringify(vars))
  237. })
  238. }
  239. function setupRequestPersistence() {
  240. const localRequest = JSON.parse(
  241. window.localStorage.getItem("restRequest") || "null"
  242. )
  243. if (localRequest) {
  244. const parsedLocal = translateToNewRequest(localRequest)
  245. setRESTRequest(
  246. safelyExtractRESTRequest(parsedLocal, getDefaultRESTRequest())
  247. )
  248. }
  249. restRequest$.subscribe((req) => {
  250. const reqClone = cloneDeep(req)
  251. if (reqClone.body.contentType === "multipart/form-data") {
  252. reqClone.body.body = reqClone.body.body.map((x) => {
  253. if (x.isFile)
  254. return {
  255. ...x,
  256. isFile: false,
  257. value: "",
  258. }
  259. else return x
  260. })
  261. }
  262. window.localStorage.setItem("restRequest", JSON.stringify(reqClone))
  263. })
  264. }
  265. export function setupLocalPersistence() {
  266. checkAndMigrateOldSettings()
  267. setupLocalStatePersistence()
  268. setupSettingsPersistence()
  269. setupRequestPersistence()
  270. setupHistoryPersistence()
  271. setupCollectionsPersistence()
  272. setupGlobalEnvsPersistence()
  273. setupEnvironmentsPersistence()
  274. setupSelectedEnvPersistence()
  275. setupWebsocketPersistence()
  276. setupSocketIOPersistence()
  277. setupSSEPersistence()
  278. setupMQTTPersistence()
  279. }
  280. /**
  281. * Gets a value in LocalStorage.
  282. *
  283. * NOTE: Use LocalStorage to only store non-reactive simple data
  284. * For more complex data, use stores and connect it to localpersistence
  285. */
  286. export function getLocalConfig(name: string) {
  287. return window.localStorage.getItem(name)
  288. }
  289. /**
  290. * Sets a value in LocalStorage.
  291. *
  292. * NOTE: Use LocalStorage to only store non-reactive simple data
  293. * For more complex data, use stores and connect it to localpersistence
  294. */
  295. export function setLocalConfig(key: string, value: string) {
  296. window.localStorage.setItem(key, value)
  297. }
  298. /**
  299. * Clear config value in LocalStorage.
  300. * @param key Key to be cleared
  301. */
  302. export function removeLocalConfig(key: string) {
  303. window.localStorage.removeItem(key)
  304. }