1.0.0-beta9.ts 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. import db from "@server/db";
  2. import {
  3. emailVerificationCodes,
  4. passwordResetTokens,
  5. resourceOtp,
  6. resources,
  7. resourceWhitelist,
  8. targets,
  9. userInvites,
  10. users
  11. } from "@server/db/schema";
  12. import { APP_PATH, configFilePath1, configFilePath2 } from "@server/lib/consts";
  13. import { eq, sql } from "drizzle-orm";
  14. import fs from "fs";
  15. import yaml from "js-yaml";
  16. import path from "path";
  17. import { z } from "zod";
  18. import { fromZodError } from "zod-validation-error";
  19. export default async function migration() {
  20. console.log("Running setup script 1.0.0-beta.9...");
  21. await db.transaction(async (trx) => {
  22. try {
  23. // Determine which config file exists
  24. const filePaths = [configFilePath1, configFilePath2];
  25. let filePath = "";
  26. for (const path of filePaths) {
  27. if (fs.existsSync(path)) {
  28. filePath = path;
  29. break;
  30. }
  31. }
  32. if (!filePath) {
  33. throw new Error(
  34. `No config file found (expected config.yml or config.yaml).`
  35. );
  36. }
  37. // Read and parse the YAML file
  38. let rawConfig: any;
  39. const fileContents = fs.readFileSync(filePath, "utf8");
  40. rawConfig = yaml.load(fileContents);
  41. rawConfig.server.resource_session_request_param =
  42. "p_session_request";
  43. rawConfig.server.session_cookie_name = "p_session_token"; // rename to prevent conflicts
  44. delete rawConfig.server.resource_session_cookie_name;
  45. // Write the updated YAML back to the file
  46. const updatedYaml = yaml.dump(rawConfig);
  47. fs.writeFileSync(filePath, updatedYaml, "utf8");
  48. } catch (e) {
  49. console.log(
  50. `Failed to add resource_session_request_param to config. Please add it manually. https://docs.fossorial.io/Pangolin/Configuration/config`
  51. );
  52. trx.rollback();
  53. return;
  54. }
  55. try {
  56. const traefikPath = path.join(
  57. APP_PATH,
  58. "traefik",
  59. "traefik_config.yml"
  60. );
  61. // Define schema for traefik config validation
  62. const schema = z.object({
  63. entryPoints: z
  64. .object({
  65. websecure: z
  66. .object({
  67. address: z.string(),
  68. transport: z
  69. .object({
  70. respondingTimeouts: z.object({
  71. readTimeout: z.string()
  72. })
  73. })
  74. .optional()
  75. })
  76. .optional()
  77. })
  78. .optional(),
  79. experimental: z.object({
  80. plugins: z.object({
  81. badger: z.object({
  82. moduleName: z.string(),
  83. version: z.string()
  84. })
  85. })
  86. })
  87. });
  88. const traefikFileContents = fs.readFileSync(traefikPath, "utf8");
  89. const traefikConfig = yaml.load(traefikFileContents) as any;
  90. const parsedConfig = schema.safeParse(traefikConfig);
  91. if (parsedConfig.success) {
  92. // Ensure websecure entrypoint exists
  93. if (traefikConfig.entryPoints?.websecure) {
  94. // Add transport configuration
  95. traefikConfig.entryPoints.websecure.transport = {
  96. respondingTimeouts: {
  97. readTimeout: "30m"
  98. }
  99. };
  100. }
  101. traefikConfig.experimental.plugins.badger.version =
  102. "v1.0.0-beta.3";
  103. const updatedTraefikYaml = yaml.dump(traefikConfig);
  104. fs.writeFileSync(traefikPath, updatedTraefikYaml, "utf8");
  105. console.log(
  106. "Updated the version of Badger in your Traefik configuration to v1.0.0-beta.3 and added readTimeout to websecure entrypoint in your Traefik configuration.."
  107. );
  108. } else {
  109. console.log(fromZodError(parsedConfig.error));
  110. console.log(
  111. "We were unable to update the version of Badger in your Traefik configuration. Please update it manually to at least v1.0.0-beta.3. https://github.com/fosrl/badger"
  112. );
  113. }
  114. } catch (e) {
  115. console.log(
  116. "We were unable to update the version of Badger in your Traefik configuration. Please update it manually to at least v1.0.0-beta.3. https://github.com/fosrl/badger"
  117. );
  118. trx.rollback();
  119. return;
  120. }
  121. trx.run(sql`UPDATE ${users} SET email = LOWER(email);`);
  122. trx.run(
  123. sql`UPDATE ${emailVerificationCodes} SET email = LOWER(email);`
  124. );
  125. trx.run(sql`UPDATE ${passwordResetTokens} SET email = LOWER(email);`);
  126. trx.run(sql`UPDATE ${userInvites} SET email = LOWER(email);`);
  127. trx.run(sql`UPDATE ${resourceWhitelist} SET email = LOWER(email);`);
  128. trx.run(sql`UPDATE ${resourceOtp} SET email = LOWER(email);`);
  129. const resourcesAll = await trx
  130. .select({
  131. resourceId: resources.resourceId,
  132. fullDomain: resources.fullDomain,
  133. subdomain: resources.subdomain
  134. })
  135. .from(resources);
  136. trx.run(`DROP INDEX resources_fullDomain_unique;`);
  137. trx.run(`ALTER TABLE resources
  138. DROP COLUMN fullDomain;
  139. `);
  140. trx.run(`ALTER TABLE resources
  141. DROP COLUMN subdomain;
  142. `);
  143. trx.run(sql`ALTER TABLE resources
  144. ADD COLUMN fullDomain TEXT;
  145. `);
  146. trx.run(sql`ALTER TABLE resources
  147. ADD COLUMN subdomain TEXT;
  148. `);
  149. trx.run(sql`ALTER TABLE resources
  150. ADD COLUMN http INTEGER DEFAULT true NOT NULL;
  151. `);
  152. trx.run(sql`ALTER TABLE resources
  153. ADD COLUMN protocol TEXT DEFAULT 'tcp' NOT NULL;
  154. `);
  155. trx.run(sql`ALTER TABLE resources
  156. ADD COLUMN proxyPort INTEGER;
  157. `);
  158. // write the new fullDomain and subdomain values back to the database
  159. for (const resource of resourcesAll) {
  160. await trx
  161. .update(resources)
  162. .set({
  163. fullDomain: resource.fullDomain,
  164. subdomain: resource.subdomain
  165. })
  166. .where(eq(resources.resourceId, resource.resourceId));
  167. }
  168. const targetsAll = await trx
  169. .select({
  170. targetId: targets.targetId,
  171. method: targets.method
  172. })
  173. .from(targets);
  174. trx.run(`ALTER TABLE targets
  175. DROP COLUMN method;
  176. `);
  177. trx.run(`ALTER TABLE targets
  178. DROP COLUMN protocol;
  179. `);
  180. trx.run(sql`ALTER TABLE targets
  181. ADD COLUMN method TEXT;
  182. `);
  183. // write the new method and protocol values back to the database
  184. for (const target of targetsAll) {
  185. await trx
  186. .update(targets)
  187. .set({
  188. method: target.method
  189. })
  190. .where(eq(targets.targetId, target.targetId));
  191. }
  192. trx.run(
  193. sql`ALTER TABLE 'resourceSessions' ADD 'isRequestToken' integer;`
  194. );
  195. trx.run(
  196. sql`ALTER TABLE 'resourceSessions' ADD 'userSessionId' text REFERENCES session(id);`
  197. );
  198. });
  199. console.log("Done.");
  200. }