userAuth.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import { defineStore } from "pinia";
  2. import Toast from "toasters";
  3. import validation from "@/validation";
  4. import ws from "@/ws";
  5. export const useUserAuthStore = defineStore("userAuth", {
  6. state: () => ({
  7. userIdMap: {},
  8. userIdRequested: {},
  9. pendingUserIdCallbacks: {},
  10. loggedIn: false,
  11. role: "",
  12. username: "",
  13. email: "",
  14. userId: "",
  15. banned: false,
  16. ban: {
  17. reason: null,
  18. expiresAt: null
  19. },
  20. gotData: false,
  21. permissions: {}
  22. }),
  23. actions: {
  24. register(user) {
  25. return new Promise((resolve, reject) => {
  26. const { username, email, password, recaptchaToken } = user;
  27. if (!email || !username || !password)
  28. reject(new Error("Please fill in all fields"));
  29. else if (!validation.isLength(email, 3, 254))
  30. reject(
  31. new Error(
  32. "Email must have between 3 and 254 characters."
  33. )
  34. );
  35. else if (
  36. email.indexOf("@") !== email.lastIndexOf("@") ||
  37. !validation.regex.emailSimple.test(email)
  38. )
  39. reject(new Error("Invalid email format."));
  40. else if (!validation.isLength(username, 2, 32))
  41. reject(
  42. new Error(
  43. "Username must have between 2 and 32 characters."
  44. )
  45. );
  46. else if (!validation.regex.azAZ09_.test(username))
  47. reject(
  48. new Error(
  49. "Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _."
  50. )
  51. );
  52. else if (username.replaceAll(/[_]/g, "").length === 0)
  53. reject(
  54. new Error(
  55. "Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _, and there has to be at least one letter or number."
  56. )
  57. );
  58. else if (!validation.isLength(password, 6, 200))
  59. reject(
  60. new Error(
  61. "Password must have between 6 and 200 characters."
  62. )
  63. );
  64. else if (!validation.regex.password.test(password))
  65. reject(
  66. new Error(
  67. "Invalid password format. Must have one lowercase letter, one uppercase letter, one number and one special character."
  68. )
  69. );
  70. else
  71. ws.socket.dispatch(
  72. "users.register",
  73. username,
  74. email,
  75. password,
  76. recaptchaToken,
  77. res => {
  78. if (res.status === "success") {
  79. if (res.SID) {
  80. return lofig.get("cookie").then(cookie => {
  81. const date = new Date();
  82. date.setTime(
  83. new Date().getTime() +
  84. 2 * 365 * 24 * 60 * 60 * 1000
  85. );
  86. const secure = cookie.secure
  87. ? "secure=true; "
  88. : "";
  89. let domain = "";
  90. if (cookie.domain !== "localhost")
  91. domain = ` domain=${cookie.domain};`;
  92. document.cookie = `${cookie.SIDname}=${
  93. res.SID
  94. }; expires=${date.toUTCString()}; ${domain}${secure}path=/`;
  95. return resolve({
  96. status: "success",
  97. message: "Account registered!"
  98. });
  99. });
  100. }
  101. return reject(new Error("You must login"));
  102. }
  103. return reject(new Error(res.message));
  104. }
  105. );
  106. });
  107. },
  108. login(user) {
  109. return new Promise((resolve, reject) => {
  110. const { email, password } = user;
  111. ws.socket.dispatch("users.login", email, password, res => {
  112. if (res.status === "success") {
  113. return lofig.get("cookie").then(cookie => {
  114. const date = new Date();
  115. date.setTime(
  116. new Date().getTime() +
  117. 2 * 365 * 24 * 60 * 60 * 1000
  118. );
  119. const secure = cookie.secure ? "secure=true; " : "";
  120. let domain = "";
  121. if (cookie.domain !== "localhost")
  122. domain = ` domain=${cookie.domain};`;
  123. document.cookie = `${cookie.SIDname}=${
  124. res.data.SID
  125. }; expires=${date.toUTCString()}; ${domain}${secure}path=/`;
  126. const bc = new BroadcastChannel(
  127. `${cookie.SIDname}.user_login`
  128. );
  129. bc.postMessage(true);
  130. bc.close();
  131. return resolve({
  132. status: "success",
  133. message: "Logged in!"
  134. });
  135. });
  136. }
  137. return reject(new Error(res.message));
  138. });
  139. });
  140. },
  141. logout() {
  142. return new Promise((resolve, reject) => {
  143. ws.socket.dispatch("users.logout", res => {
  144. if (res.status === "success") {
  145. return resolve(
  146. lofig.get("cookie").then(cookie => {
  147. document.cookie = `${cookie.SIDname}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
  148. return window.location.reload();
  149. })
  150. );
  151. }
  152. new Toast(res.message);
  153. return reject(new Error(res.message));
  154. });
  155. });
  156. },
  157. getBasicUser(userId) {
  158. return new Promise(resolve => {
  159. if (typeof this.userIdMap[`Z${userId}`] !== "string") {
  160. if (this.userIdRequested[`Z${userId}`] !== true) {
  161. this.requestingUserId(userId);
  162. ws.socket.dispatch(
  163. "users.getBasicUser",
  164. userId,
  165. res => {
  166. if (res.status === "success") {
  167. const user = res.data;
  168. this.mapUserId({
  169. userId,
  170. user: {
  171. name: user.name,
  172. username: user.username
  173. }
  174. });
  175. this.pendingUserIdCallbacks[
  176. `Z${userId}`
  177. ].forEach(cb => cb(user));
  178. this.clearPendingCallbacks(userId);
  179. return resolve(user);
  180. }
  181. return resolve(null);
  182. }
  183. );
  184. } else {
  185. this.pendingUser({
  186. userId,
  187. callback: user => resolve(user)
  188. });
  189. }
  190. } else {
  191. resolve(this.userIdMap[`Z${userId}`]);
  192. }
  193. });
  194. },
  195. mapUserId(data) {
  196. this.userIdMap[`Z${data.userId}`] = data.user;
  197. this.userIdRequested[`Z${data.userId}`] = false;
  198. },
  199. requestingUserId(userId) {
  200. this.userIdRequested[`Z${userId}`] = true;
  201. if (!this.pendingUserIdCallbacks[`Z${userId}`])
  202. this.pendingUserIdCallbacks[`Z${userId}`] = [];
  203. },
  204. pendingUser(data) {
  205. this.pendingUserIdCallbacks[`Z${data.userId}`].push(data.callback);
  206. },
  207. clearPendingCallbacks(userId) {
  208. this.pendingUserIdCallbacks[`Z${userId}`] = [];
  209. },
  210. authData(data) {
  211. this.loggedIn = data.loggedIn;
  212. this.role = data.role;
  213. this.username = data.username;
  214. this.email = data.email;
  215. this.userId = data.userId;
  216. this.gotData = true;
  217. },
  218. banUser(ban) {
  219. this.banned = true;
  220. this.ban = ban;
  221. },
  222. updateUsername(username) {
  223. this.username = username;
  224. },
  225. updateRole(role) {
  226. this.role = role;
  227. this.updatePermissions();
  228. },
  229. hasPermission(permission) {
  230. return !!(this.permissions && this.permissions[permission]);
  231. },
  232. updatePermissions() {
  233. return new Promise(resolve => {
  234. ws.socket.dispatch("utils.getPermissions", res => {
  235. this.permissions = res.data.permissions;
  236. resolve(this.permissions);
  237. });
  238. });
  239. }
  240. }
  241. });