Punishments.vue 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. <script setup lang="ts">
  2. import { ref } from "vue";
  3. import { useStore } from "vuex";
  4. import Toast from "toasters";
  5. import AdvancedTable from "@/components/AdvancedTable.vue";
  6. const store = useStore();
  7. const { socket } = store.state.websockets;
  8. const ipBan = ref({
  9. expiresAt: "1h"
  10. });
  11. const columnDefault = ref({
  12. sortable: true,
  13. hidable: true,
  14. defaultVisibility: "shown",
  15. draggable: true,
  16. resizable: true,
  17. minWidth: 150,
  18. maxWidth: 600
  19. });
  20. const columns = ref([
  21. {
  22. name: "options",
  23. displayName: "Options",
  24. properties: ["_id"],
  25. sortable: false,
  26. hidable: false,
  27. resizable: false,
  28. minWidth: 76,
  29. defaultWidth: 76
  30. },
  31. {
  32. name: "status",
  33. displayName: "Status",
  34. properties: ["status"],
  35. sortable: false,
  36. defaultWidth: 150
  37. },
  38. {
  39. name: "type",
  40. displayName: "Type",
  41. properties: ["type"],
  42. sortProperty: "type"
  43. },
  44. {
  45. name: "value",
  46. displayName: "Value",
  47. properties: ["value"],
  48. sortProperty: "value",
  49. defaultWidth: 150
  50. },
  51. {
  52. name: "reason",
  53. displayName: "Reason",
  54. properties: ["reason"],
  55. sortProperty: "reason"
  56. },
  57. {
  58. name: "punishedBy",
  59. displayName: "Punished By",
  60. properties: ["punishedBy"],
  61. sortProperty: "punishedBy",
  62. defaultWidth: 200,
  63. defaultVisibility: "hidden"
  64. },
  65. {
  66. name: "punishedAt",
  67. displayName: "Punished At",
  68. properties: ["punishedAt"],
  69. sortProperty: "punishedAt",
  70. defaultWidth: 200,
  71. defaultVisibility: "hidden"
  72. },
  73. {
  74. name: "expiresAt",
  75. displayName: "Expires At",
  76. properties: ["expiresAt"],
  77. sortProperty: "verifiedAt",
  78. defaultWidth: 200,
  79. defaultVisibility: "hidden"
  80. }
  81. ]);
  82. const filters = ref([
  83. {
  84. name: "status",
  85. displayName: "Status",
  86. property: "status",
  87. filterTypes: ["exact"],
  88. defaultFilterType: "exact",
  89. dropdown: [
  90. ["Active", "Active"],
  91. ["Inactive", "Inactive"]
  92. ]
  93. },
  94. {
  95. name: "type",
  96. displayName: "Type",
  97. property: "type",
  98. filterTypes: ["exact"],
  99. defaultFilterType: "exact",
  100. dropdown: [
  101. ["banUserId", "User ID"],
  102. ["banUserIp", "IP Address"]
  103. ]
  104. },
  105. {
  106. name: "value",
  107. displayName: "Value",
  108. property: "value",
  109. filterTypes: ["contains", "exact", "regex"],
  110. defaultFilterType: "contains"
  111. },
  112. {
  113. name: "reason",
  114. displayName: "Reason",
  115. property: "reason",
  116. filterTypes: ["contains", "exact", "regex"],
  117. defaultFilterType: "contains"
  118. },
  119. {
  120. name: "punishedBy",
  121. displayName: "Punished By",
  122. property: "punishedBy",
  123. filterTypes: ["contains", "exact", "regex"],
  124. defaultFilterType: "contains"
  125. },
  126. {
  127. name: "punishedAt",
  128. displayName: "Punished At",
  129. property: "punishedAt",
  130. filterTypes: ["datetimeBefore", "datetimeAfter"],
  131. defaultFilterType: "datetimeBefore"
  132. },
  133. {
  134. name: "expiresAt",
  135. displayName: "Expires At",
  136. property: "expiresAt",
  137. filterTypes: ["datetimeBefore", "datetimeAfter"],
  138. defaultFilterType: "datetimeBefore"
  139. }
  140. ]);
  141. const openModal = payload =>
  142. store.dispatch("modalVisibility/openModal", payload);
  143. const banIP = () => {
  144. socket.dispatch(
  145. "punishments.banIP",
  146. ipBan.value.ip,
  147. ipBan.value.reason,
  148. ipBan.value.expiresAt,
  149. res => {
  150. new Toast(res.message);
  151. }
  152. );
  153. };
  154. const getDateFormatted = createdAt => {
  155. const date = new Date(createdAt);
  156. const year = date.getFullYear();
  157. const month = `${date.getMonth() + 1}`.padStart(2, 0);
  158. const day = `${date.getDate()}`.padStart(2, 0);
  159. const hour = `${date.getHours()}`.padStart(2, 0);
  160. const minute = `${date.getMinutes()}`.padStart(2, 0);
  161. return `${year}-${month}-${day} ${hour}:${minute}`;
  162. };
  163. </script>
  164. <template>
  165. <div class="admin-tab container">
  166. <page-metadata title="Admin | Users | Punishments" />
  167. <div class="card tab-info">
  168. <div class="info-row">
  169. <h1>Punishments</h1>
  170. <p>Manage punishments or ban an IP</p>
  171. </div>
  172. </div>
  173. <advanced-table
  174. :column-default="columnDefault"
  175. :columns="columns"
  176. :filters="filters"
  177. data-action="punishments.getData"
  178. name="admin-punishments"
  179. :max-width="1200"
  180. >
  181. <template #column-options="slotProps">
  182. <div class="row-options">
  183. <button
  184. class="button is-primary icon-with-button material-icons"
  185. @click="
  186. openModal({
  187. modal: 'viewPunishment',
  188. data: { punishmentId: slotProps.item._id }
  189. })
  190. "
  191. :disabled="slotProps.item.removed"
  192. content="View Punishment"
  193. v-tippy
  194. >
  195. open_in_full
  196. </button>
  197. </div>
  198. </template>
  199. <template #column-status="slotProps">
  200. <span>{{ slotProps.item.status }}</span>
  201. </template>
  202. <template #column-type="slotProps">
  203. <span
  204. :title="
  205. slotProps.item.type === 'banUserId'
  206. ? 'User ID'
  207. : 'IP Address'
  208. "
  209. >{{
  210. slotProps.item.type === "banUserId"
  211. ? "User ID"
  212. : "IP Address"
  213. }}</span
  214. >
  215. </template>
  216. <template #column-value="slotProps">
  217. <user-link
  218. v-if="slotProps.item.type === 'banUserId'"
  219. :user-id="slotProps.item.value"
  220. :alt="slotProps.item.value"
  221. />
  222. <span v-else :title="slotProps.item.value">{{
  223. slotProps.item.value
  224. }}</span>
  225. </template>
  226. <template #column-reason="slotProps">
  227. <span :title="slotProps.item.reason">{{
  228. slotProps.item.reason
  229. }}</span>
  230. </template>
  231. <template #column-punishedBy="slotProps">
  232. <user-link :user-id="slotProps.item.punishedBy" />
  233. </template>
  234. <template #column-punishedAt="slotProps">
  235. <span :title="new Date(slotProps.item.punishedAt)">{{
  236. getDateFormatted(slotProps.item.punishedAt)
  237. }}</span>
  238. </template>
  239. <template #column-expiresAt="slotProps">
  240. <span :title="new Date(slotProps.item.expiresAt)">{{
  241. getDateFormatted(slotProps.item.expiresAt)
  242. }}</span>
  243. </template>
  244. </advanced-table>
  245. <div class="card">
  246. <h4>Ban an IP</h4>
  247. <hr class="section-horizontal-rule" />
  248. <div class="card-content">
  249. <label class="label">Expires In</label>
  250. <p class="control is-expanded select">
  251. <select v-model="ipBan.expiresAt">
  252. <option value="1h">1 Hour</option>
  253. <option value="12h">12 Hours</option>
  254. <option value="1d">1 Day</option>
  255. <option value="1w">1 Week</option>
  256. <option value="1m">1 Month</option>
  257. <option value="3m">3 Months</option>
  258. <option value="6m">6 Months</option>
  259. <option value="1y">1 Year</option>
  260. </select>
  261. </p>
  262. <label class="label">IP</label>
  263. <p class="control is-expanded">
  264. <input
  265. v-model="ipBan.ip"
  266. class="input"
  267. type="text"
  268. placeholder="IP address (xxx.xxx.xxx.xxx)"
  269. />
  270. </p>
  271. <label class="label">Reason</label>
  272. <p class="control is-expanded">
  273. <input
  274. v-model="ipBan.reason"
  275. class="input"
  276. type="text"
  277. placeholder="Reason"
  278. />
  279. </p>
  280. <button class="button is-primary" @click="banIP()">
  281. Ban IP
  282. </button>
  283. </div>
  284. </div>
  285. </div>
  286. </template>
  287. <style lang="less" scoped>
  288. .card .button.is-primary {
  289. width: 100%;
  290. }
  291. </style>