EditUser.vue 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. <template>
  2. <div>
  3. <modal title="Edit User">
  4. <template #body v-if="user && user._id">
  5. <div class="section">
  6. <label class="label"> Change username </label>
  7. <p class="control has-addons">
  8. <input
  9. v-model="user.username"
  10. class="input is-expanded"
  11. type="text"
  12. placeholder="Username"
  13. autofocus
  14. />
  15. <a class="button is-info" @click="updateUsername()"
  16. >Update Username</a
  17. >
  18. </p>
  19. <label class="label"> Change email address </label>
  20. <p class="control has-addons">
  21. <input
  22. v-model="user.email.address"
  23. class="input is-expanded"
  24. type="text"
  25. placeholder="Email Address"
  26. autofocus
  27. />
  28. <a class="button is-info" @click="updateEmail()"
  29. >Update Email Address</a
  30. >
  31. </p>
  32. <label class="label"> Change user role </label>
  33. <div class="control is-grouped input-with-button">
  34. <div class="control is-expanded select">
  35. <select v-model="user.role">
  36. <option>default</option>
  37. <option>admin</option>
  38. </select>
  39. </div>
  40. <p class="control">
  41. <a class="button is-info" @click="updateRole()"
  42. >Update Role</a
  43. >
  44. </p>
  45. </div>
  46. </div>
  47. <div class="section">
  48. <label class="label"> Punish/Ban User </label>
  49. <p class="control has-addons">
  50. <span class="select">
  51. <select v-model="ban.expiresAt">
  52. <option value="1h">1 Hour</option>
  53. <option value="12h">12 Hours</option>
  54. <option value="1d">1 Day</option>
  55. <option value="1w">1 Week</option>
  56. <option value="1m">1 Month</option>
  57. <option value="3m">3 Months</option>
  58. <option value="6m">6 Months</option>
  59. <option value="1y">1 Year</option>
  60. </select>
  61. </span>
  62. <input
  63. v-model="ban.reason"
  64. class="input is-expanded"
  65. type="text"
  66. placeholder="Ban reason"
  67. autofocus
  68. />
  69. <a class="button is-danger" @click="banUser()">
  70. Ban user
  71. </a>
  72. </p>
  73. </div>
  74. </template>
  75. <template #footer>
  76. <button class="button is-warning" @click="removeSessions()">
  77. <span>&nbsp;Remove all sessions</span>
  78. </button>
  79. </template>
  80. </modal>
  81. </div>
  82. </template>
  83. <script>
  84. import { mapState, mapGetters, mapActions } from "vuex";
  85. import Toast from "toasters";
  86. import validation from "@/validation";
  87. import ws from "@/ws";
  88. import Modal from "../Modal.vue";
  89. export default {
  90. components: { Modal },
  91. props: {
  92. userId: { type: String, default: "" },
  93. sector: { type: String, default: "admin" }
  94. },
  95. data() {
  96. return {
  97. ban: {
  98. expiresAt: "1h"
  99. }
  100. };
  101. },
  102. computed: {
  103. ...mapState("modals/editUser", {
  104. user: state => state.user
  105. }),
  106. ...mapGetters({
  107. socket: "websockets/getSocket"
  108. })
  109. },
  110. mounted() {
  111. ws.onConnect(this.init);
  112. },
  113. methods: {
  114. init() {
  115. this.socket.dispatch(`users.getUserFromId`, this.userId, res => {
  116. if (res.status === "success") {
  117. const user = res.data;
  118. this.editUser(user);
  119. } else {
  120. new Toast("User with that ID not found");
  121. this.closeModal("editUser");
  122. }
  123. });
  124. },
  125. updateUsername() {
  126. const { username } = this.user;
  127. if (!validation.isLength(username, 2, 32))
  128. return new Toast(
  129. "Username must have between 2 and 32 characters."
  130. );
  131. if (!validation.regex.custom("a-zA-Z0-9_-").test(username))
  132. return new Toast(
  133. "Invalid username format. Allowed characters: a-z, A-Z, 0-9, _ and -."
  134. );
  135. return this.socket.dispatch(
  136. `users.updateUsername`,
  137. this.user._id,
  138. username,
  139. res => {
  140. new Toast(res.message);
  141. }
  142. );
  143. },
  144. updateEmail() {
  145. const email = this.user.email.address;
  146. if (!validation.isLength(email, 3, 254))
  147. return new Toast(
  148. "Email must have between 3 and 254 characters."
  149. );
  150. if (
  151. email.indexOf("@") !== email.lastIndexOf("@") ||
  152. !validation.regex.emailSimple.test(email) ||
  153. !validation.regex.ascii.test(email)
  154. )
  155. return new Toast("Invalid email format.");
  156. return this.socket.dispatch(
  157. `users.updateEmail`,
  158. this.user._id,
  159. email,
  160. res => {
  161. new Toast(res.message);
  162. }
  163. );
  164. },
  165. updateRole() {
  166. this.socket.dispatch(
  167. `users.updateRole`,
  168. this.user._id,
  169. this.user.role,
  170. res => {
  171. new Toast(res.message);
  172. }
  173. );
  174. },
  175. banUser() {
  176. const { reason } = this.ban;
  177. if (!validation.isLength(reason, 1, 64))
  178. return new Toast(
  179. "Reason must have between 1 and 64 characters."
  180. );
  181. if (!validation.regex.ascii.test(reason))
  182. return new Toast(
  183. "Invalid reason format. Only ascii characters are allowed."
  184. );
  185. return this.socket.dispatch(
  186. `users.banUserById`,
  187. this.user._id,
  188. this.ban.reason,
  189. this.ban.expiresAt,
  190. res => {
  191. new Toast(res.message);
  192. }
  193. );
  194. },
  195. removeSessions() {
  196. this.socket.dispatch(`users.removeSessions`, this.user._id, res => {
  197. new Toast(res.message);
  198. });
  199. },
  200. ...mapActions("modals/editUser", ["editUser"]),
  201. ...mapActions("modalVisibility", ["closeModal"])
  202. }
  203. };
  204. </script>
  205. <style lang="scss" scoped>
  206. .section {
  207. padding: 15px 0 !important;
  208. }
  209. .save-changes {
  210. color: var(--white);
  211. }
  212. .tag:not(:last-child) {
  213. margin-right: 5px;
  214. }
  215. .select:after {
  216. border-color: var(--primary-color);
  217. }
  218. </style>