Punishments.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. <template>
  2. <div>
  3. <page-metadata title="Admin | Punishments" />
  4. <div class="container">
  5. <table class="table is-striped">
  6. <thead>
  7. <tr>
  8. <td>Status</td>
  9. <td>Type</td>
  10. <td>Value</td>
  11. <td>Reason</td>
  12. <td>Options</td>
  13. </tr>
  14. </thead>
  15. <tbody>
  16. <tr
  17. v-for="punishment in sortedPunishments"
  18. :key="punishment._id"
  19. >
  20. <td>
  21. {{
  22. punishment.active &&
  23. new Date(punishment.expiresAt).getTime() >
  24. Date.now()
  25. ? "Active"
  26. : "Inactive"
  27. }}
  28. </td>
  29. <td v-if="punishment.type === 'banUserId'">User ID</td>
  30. <td v-else>IP Address</td>
  31. <td v-if="punishment.type === 'banUserId'">
  32. <user-id-to-username
  33. :user-id="punishment.value"
  34. :alt="punishment.value"
  35. :link="true"
  36. />
  37. ({{ punishment.value }})
  38. </td>
  39. <td v-else>
  40. {{ punishment.value }}
  41. </td>
  42. <td>{{ punishment.reason }}</td>
  43. <td>
  44. <a
  45. class="button is-primary"
  46. @click="view(punishment)"
  47. content="Expand"
  48. v-tippy
  49. >
  50. <i class="material-icons icon-with-button">
  51. open_in_full
  52. </i>
  53. Expand
  54. </a>
  55. </td>
  56. </tr>
  57. </tbody>
  58. </table>
  59. <div class="card is-fullwidth">
  60. <header class="card-header">
  61. <p class="card-header-title">Ban an IP</p>
  62. </header>
  63. <div class="card-content">
  64. <div class="content">
  65. <label class="label">Expires In</label>
  66. <select v-model="ipBan.expiresAt">
  67. <option value="1h">1 Hour</option>
  68. <option value="12h">12 Hours</option>
  69. <option value="1d">1 Day</option>
  70. <option value="1w">1 Week</option>
  71. <option value="1m">1 Month</option>
  72. <option value="3m">3 Months</option>
  73. <option value="6m">6 Months</option>
  74. <option value="1y">1 Year</option>
  75. </select>
  76. <label class="label">IP</label>
  77. <p class="control is-expanded">
  78. <input
  79. v-model="ipBan.ip"
  80. class="input"
  81. type="text"
  82. placeholder="IP address (xxx.xxx.xxx.xxx)"
  83. />
  84. </p>
  85. <label class="label">Reason</label>
  86. <p class="control is-expanded">
  87. <input
  88. v-model="ipBan.reason"
  89. class="input"
  90. type="text"
  91. placeholder="Reason"
  92. />
  93. </p>
  94. </div>
  95. </div>
  96. <footer class="card-footer">
  97. <a class="card-footer-item" @click="banIP()" href="#"
  98. >Ban IP</a
  99. >
  100. </footer>
  101. </div>
  102. </div>
  103. <view-punishment
  104. v-if="modals.viewPunishment"
  105. :punishment-id="viewingPunishmentId"
  106. sector="admin"
  107. />
  108. </div>
  109. </template>
  110. <script>
  111. import { mapState, mapGetters, mapActions } from "vuex";
  112. import Toast from "toasters";
  113. import { defineAsyncComponent } from "vue";
  114. import ws from "@/ws";
  115. import UserIdToUsername from "@/components/UserIdToUsername.vue";
  116. export default {
  117. components: {
  118. ViewPunishment: defineAsyncComponent(() =>
  119. import("@/components/modals/ViewPunishment.vue")
  120. ),
  121. UserIdToUsername
  122. },
  123. data() {
  124. return {
  125. viewingPunishmentId: "",
  126. punishments: [],
  127. ipBan: {
  128. expiresAt: "1h"
  129. }
  130. };
  131. },
  132. computed: {
  133. sortedPunishments() {
  134. return this.punishments;
  135. },
  136. ...mapState("modalVisibility", {
  137. modals: state => state.modals
  138. }),
  139. ...mapGetters({
  140. socket: "websockets/getSocket"
  141. })
  142. },
  143. mounted() {
  144. ws.onConnect(this.init);
  145. this.socket.on("event:admin.punishment.created", res =>
  146. this.punishments.push(res.data.punishment)
  147. );
  148. },
  149. methods: {
  150. view(punishment) {
  151. this.viewingPunishmentId = punishment._id;
  152. this.openModal("viewPunishment");
  153. },
  154. banIP() {
  155. this.socket.dispatch(
  156. "punishments.banIP",
  157. this.ipBan.ip,
  158. this.ipBan.reason,
  159. this.ipBan.expiresAt,
  160. res => {
  161. new Toast(res.message);
  162. }
  163. );
  164. },
  165. init() {
  166. this.socket.dispatch("punishments.index", res => {
  167. if (res.status === "success")
  168. this.punishments = res.data.punishments;
  169. });
  170. this.socket.dispatch("apis.joinAdminRoom", "punishments", () => {});
  171. },
  172. ...mapActions("modalVisibility", ["openModal"]),
  173. ...mapActions("admin/punishments", ["viewPunishment"])
  174. }
  175. };
  176. </script>
  177. <style lang="scss" scoped>
  178. .night-mode {
  179. .table {
  180. color: var(--light-grey-2);
  181. background-color: var(--dark-grey-3);
  182. thead tr {
  183. background: var(--dark-grey-3);
  184. td {
  185. color: var(--white);
  186. }
  187. }
  188. tbody tr:hover {
  189. background-color: var(--dark-grey-4) !important;
  190. }
  191. tbody tr:nth-child(even) {
  192. background-color: var(--dark-grey-2);
  193. }
  194. strong {
  195. color: var(--light-grey-2);
  196. }
  197. }
  198. .card {
  199. background: var(--dark-grey-3);
  200. .card-header {
  201. box-shadow: 0 1px 2px rgba(10, 10, 10, 0.8);
  202. }
  203. p,
  204. .label {
  205. color: var(--light-grey-2);
  206. }
  207. }
  208. }
  209. body {
  210. font-family: "Hind", sans-serif;
  211. }
  212. td {
  213. vertical-align: middle;
  214. }
  215. select {
  216. margin-bottom: 10px;
  217. }
  218. </style>