this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Admin dashboard updates for adding new PDSs

+422 -60
+1 -1
ts/bgs-dash/index.html
··· 4 4 <meta charset="UTF-8" /> 5 5 <!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> --> 6 6 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 7 - <title>BGS Dashboard</title> 7 + <title>Relay Dashboard</title> 8 8 </head> 9 9 <body> 10 10 <div id="root"></div>
+19 -3
ts/bgs-dash/src/App.tsx
··· 14 14 import Domains from "./components/Domains/Domains"; 15 15 import Repos from "./components/Repos/Repos"; 16 16 import Consumers from "./components/Consumers/Consumers"; 17 + import NewPDS from "./components/NewPDS/NewPDS"; 17 18 18 19 function classNames(...classes: string[]) { 19 20 return classes.filter(Boolean).join(" "); ··· 57 58 requrieAuth: true, 58 59 }, 59 60 { 61 + path: "/new_pds", 62 + name: "New PDS", 63 + element: ( 64 + <RequireAuth> 65 + <Nav /> 66 + <main> 67 + <div className="mx-auto max-w-7xl px-2 py-6 sm:px-6 lg:px-8"> 68 + <NewPDS /> 69 + </div> 70 + </main> 71 + </RequireAuth> 72 + ), 73 + }, 74 + { 60 75 path: "/consumers", 61 76 name: "Consumers", 62 77 element: ( ··· 101 116 ), 102 117 requrieAuth: true, 103 118 }, 119 + 104 120 { 105 121 path: "/login", 106 122 name: "Login", ··· 151 167 <img 152 168 className="h-8 w-8" 153 169 src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=500" 154 - alt="BGS Admin Dashboard" 170 + alt="Relay Admin Dashboard" 155 171 /> 156 172 </div> 157 173 <div className="hidden md:block"> 158 174 <div className="ml-10 flex items-baseline space-x-4"> 159 175 {routes.map((item) => 160 176 (isAuthed && item.hideIfAuth) || 161 - (!isAuthed && item.requrieAuth) ? null : ( 177 + (!isAuthed && item.requrieAuth) ? null : ( 162 178 <NavLink 163 179 key={item.path} 164 180 to={item.path || "/"} ··· 204 220 <div className="space-y-1 px-2 pb-3 pt-2 sm:px-3"> 205 221 {routes.map((item) => 206 222 (isAuthed && item.hideIfAuth) || 207 - (!isAuthed && item.requrieAuth) ? null : ( 223 + (!isAuthed && item.requrieAuth) ? null : ( 208 224 <Disclosure.Button 209 225 key={item.path} 210 226 className={classNames(
+23 -28
ts/bgs-dash/src/components/Consumers/Consumers.tsx
··· 5 5 NotificationType, 6 6 } from "../Notification/Notification"; 7 7 8 - import { BGS_HOST } from "../../constants"; 8 + import { RELAY_HOST } from "../../constants"; 9 9 10 10 import { useNavigate } from "react-router-dom"; 11 11 import { Consumer, ConsumerKey, ConsumerResponse } from "../../models/consumer"; ··· 51 51 }, []); 52 52 53 53 const refreshPDSList = () => { 54 - fetch(`${BGS_HOST}/admin/consumers/list`, { 54 + fetch(`${RELAY_HOST}/admin/consumers/list`, { 55 55 method: "GET", 56 56 headers: { 57 57 "Content-Type": "application/json", ··· 152 152 Consumer Connections 153 153 </h1> 154 154 <p className="mt-2 text-sm text-gray-700"> 155 - A list of all websocket consumers actively connected to the BGS 155 + A list of all websocket consumers actively connected to the Relay 156 156 </p> 157 157 </div> 158 158 </div> ··· 175 175 > 176 176 ID 177 177 <span 178 - className={`ml-2 flex-none rounded text-gray-400 ${ 179 - sortField === "ID" 180 - ? "group-hover:bg-gray-200" 181 - : "invisible group-hover:visible group-focus:visible" 182 - }`} 178 + className={`ml-2 flex-none rounded text-gray-400 ${sortField === "ID" 179 + ? "group-hover:bg-gray-200" 180 + : "invisible group-hover:visible group-focus:visible" 181 + }`} 183 182 > 184 183 {sortField === "ID" && sortOrder === "asc" ? ( 185 184 <ChevronUpIcon className="h-5 w-5" aria-hidden="true" /> ··· 206 205 > 207 206 Remote Address 208 207 <span 209 - className={`ml-2 flex-none rounded text-gray-400 ${ 210 - sortField === "RemoteAddr" 211 - ? "group-hover:bg-gray-200" 212 - : "invisible group-hover:visible group-focus:visible" 213 - }`} 208 + className={`ml-2 flex-none rounded text-gray-400 ${sortField === "RemoteAddr" 209 + ? "group-hover:bg-gray-200" 210 + : "invisible group-hover:visible group-focus:visible" 211 + }`} 214 212 > 215 213 {sortField === "RemoteAddr" && sortOrder === "asc" ? ( 216 214 <ChevronUpIcon className="h-5 w-5" aria-hidden="true" /> ··· 237 235 > 238 236 User Agent 239 237 <span 240 - className={`ml-2 flex-none rounded text-gray-400 ${ 241 - sortField === "UserAgent" 242 - ? "group-hover:bg-gray-200" 243 - : "invisible group-hover:visible group-focus:visible" 244 - }`} 238 + className={`ml-2 flex-none rounded text-gray-400 ${sortField === "UserAgent" 239 + ? "group-hover:bg-gray-200" 240 + : "invisible group-hover:visible group-focus:visible" 241 + }`} 245 242 > 246 243 {sortField === "UserAgent" && sortOrder === "asc" ? ( 247 244 <ChevronUpIcon className="h-5 w-5" aria-hidden="true" /> ··· 268 265 > 269 266 Events Consumed 270 267 <span 271 - className={`ml-2 flex-none rounded text-gray-400 ${ 272 - sortField === "EventsConsumed" 273 - ? "group-hover:bg-gray-200" 274 - : "invisible group-hover:visible group-focus:visible" 275 - }`} 268 + className={`ml-2 flex-none rounded text-gray-400 ${sortField === "EventsConsumed" 269 + ? "group-hover:bg-gray-200" 270 + : "invisible group-hover:visible group-focus:visible" 271 + }`} 276 272 > 277 273 {sortField === "EventsConsumed" && sortOrder === "asc" ? ( 278 274 <ChevronUpIcon className="h-5 w-5" aria-hidden="true" /> ··· 299 295 > 300 296 Connected At 301 297 <span 302 - className={`ml-2 flex-none rounded text-gray-400 ${ 303 - sortField === "ConnectedAt" 304 - ? "group-hover:bg-gray-200" 305 - : "invisible group-hover:visible group-focus:visible" 306 - }`} 298 + className={`ml-2 flex-none rounded text-gray-400 ${sortField === "ConnectedAt" 299 + ? "group-hover:bg-gray-200" 300 + : "invisible group-hover:visible group-focus:visible" 301 + }`} 307 302 > 308 303 {sortField === "ConnectedAt" && sortOrder === "asc" ? ( 309 304 <ChevronUpIcon className="h-5 w-5" aria-hidden="true" />
+10 -10
ts/bgs-dash/src/components/Dash/Dash.tsx
··· 18 18 NotificationType, 19 19 } from "../Notification/Notification"; 20 20 21 - import { BGS_HOST } from "../../constants"; 21 + import { RELAY_HOST } from "../../constants"; 22 22 import { PDS, PDSKey } from "../../models/pds"; 23 23 24 24 import { Switch } from "@headlessui/react"; ··· 93 93 }, []); 94 94 95 95 useEffect(() => { 96 - document.title = "BGS Admin Dashboard"; 96 + document.title = "Relay Admin Dashboard"; 97 97 }, []); 98 98 99 99 const refreshPDSList = () => { 100 - fetch(`${BGS_HOST}/admin/pds/list`, { 100 + fetch(`${RELAY_HOST}/admin/pds/list`, { 101 101 method: "GET", 102 102 headers: { 103 103 "Content-Type": "application/json", ··· 127 127 }; 128 128 129 129 const getSlurpsEnabled = () => { 130 - fetch(`${BGS_HOST}/admin/subs/getEnabled`, { 130 + fetch(`${RELAY_HOST}/admin/subs/getEnabled`, { 131 131 method: "GET", 132 132 headers: { 133 133 "Content-Type": "application/json", ··· 157 157 158 158 const requestSlurpsEnabledStateChange = (state: boolean) => { 159 159 setCanToggleSlurps(false); 160 - fetch(`${BGS_HOST}/admin/subs/setEnabled?enabled=${state}`, { 160 + fetch(`${RELAY_HOST}/admin/subs/setEnabled?enabled=${state}`, { 161 161 method: "POST", 162 162 headers: { 163 163 "Content-Type": "application/json", ··· 192 192 }; 193 193 194 194 const requestCrawlHost = (host: string) => { 195 - fetch(`${BGS_HOST}/xrpc/com.atproto.sync.requestCrawl`, { 195 + fetch(`${RELAY_HOST}/xrpc/com.atproto.sync.requestCrawl`, { 196 196 method: "POST", 197 197 headers: { 198 198 "Content-Type": "application/json", ··· 216 216 217 217 const requestDisconnectHost = (host: string, shouldBlock: boolean) => { 218 218 fetch( 219 - `${BGS_HOST}/admin/subs/killUpstream?host=${host}&block=${shouldBlock}`, 219 + `${RELAY_HOST}/admin/subs/killUpstream?host=${host}&block=${shouldBlock}`, 220 220 { 221 221 method: "POST", 222 222 headers: { ··· 244 244 }; 245 245 246 246 const requestBlockHost = (host: string) => { 247 - fetch(`${BGS_HOST}/admin/pds/block?host=${host}`, { 247 + fetch(`${RELAY_HOST}/admin/pds/block?host=${host}`, { 248 248 method: "POST", 249 249 headers: { 250 250 "Content-Type": "application/json", ··· 264 264 }); 265 265 }; 266 266 const requestUnblockHost = (host: string) => { 267 - fetch(`${BGS_HOST}/admin/pds/unblock?host=${host}`, { 267 + fetch(`${RELAY_HOST}/admin/pds/unblock?host=${host}`, { 268 268 method: "POST", 269 269 headers: { 270 270 "Content-Type": "application/json", ··· 286 286 287 287 const updateRateLimits = (pds: PDS) => { 288 288 fetch( 289 - `${BGS_HOST}/admin/pds/changeLimits`, 289 + `${RELAY_HOST}/admin/pds/changeLimits`, 290 290 { 291 291 method: "POST", 292 292 headers: {
+7 -7
ts/bgs-dash/src/components/Domains/Domains.tsx
··· 9 9 NotificationType, 10 10 } from "../Notification/Notification"; 11 11 12 - import { BGS_HOST } from "../../constants"; 12 + import { RELAY_HOST } from "../../constants"; 13 13 14 14 import { useNavigate } from "react-router-dom"; 15 15 import ConfirmDomainBanModal from "./ConfirmDomainBanModal"; ··· 37 37 domain: string; 38 38 type: "ban" | "unban"; 39 39 } | null>(null); 40 - const [modalConfirm, setModalConfirm] = useState<() => void>(() => {}); 41 - const [modalCancel, setModalCancel] = useState<() => void>(() => {}); 40 + const [modalConfirm, setModalConfirm] = useState<() => void>(() => { }); 41 + const [modalCancel, setModalCancel] = useState<() => void>(() => { }); 42 42 43 43 const [adminToken, setAdminToken] = useState<string>( 44 44 localStorage.getItem("admin_route_token") || "" ··· 68 68 }, []); 69 69 70 70 useEffect(() => { 71 - document.title = "BGS Admin Dashboard"; 71 + document.title = "Relay Admin Dashboard"; 72 72 }, []); 73 73 74 74 const refreshDomainBanList = () => { 75 - fetch(`${BGS_HOST}/admin/subs/listDomainBans`, { 75 + fetch(`${RELAY_HOST}/admin/subs/listDomainBans`, { 76 76 method: "GET", 77 77 headers: { 78 78 "Content-Type": "application/json", ··· 102 102 }; 103 103 104 104 const requestBanDomain = (domain: string) => { 105 - fetch(`${BGS_HOST}/admin/subs/banDomain`, { 105 + fetch(`${RELAY_HOST}/admin/subs/banDomain`, { 106 106 method: "POST", 107 107 headers: { 108 108 "Content-Type": "application/json", ··· 135 135 }; 136 136 137 137 const requestUnbanDomain = (domain: string) => { 138 - fetch(`${BGS_HOST}/admin/subs/unbanDomain`, { 138 + fetch(`${RELAY_HOST}/admin/subs/unbanDomain`, { 139 139 method: "POST", 140 140 headers: { 141 141 "Content-Type": "application/json",
+3 -3
ts/bgs-dash/src/components/Login/Login.tsx
··· 1 1 import React, { useState } from "react"; 2 2 import { useNavigate } from "react-router-dom"; 3 - import { BGS_HOST } from "../../constants"; 3 + import { RELAY_HOST } from "../../constants"; 4 4 import Notification, { NotificationMeta } from "../Notification/Notification"; 5 5 6 6 export default function Login() { ··· 20 20 21 21 if (token) { 22 22 // Try to make a request to the Admin API to verify the token 23 - fetch(`${BGS_HOST}/admin/pds/list`, { 23 + fetch(`${RELAY_HOST}/admin/pds/list`, { 24 24 method: "GET", 25 25 headers: { 26 26 "Content-Type": "application/json", ··· 74 74 )} 75 75 </div> 76 76 <h2 className=" text-center text-2xl font-bold leading-9 tracking-tight text-gray-900"> 77 - Login to the BGS Admin Dashboard 77 + Login to the Relay Admin Dashboard 78 78 </h2> 79 79 </div> 80 80
+107
ts/bgs-dash/src/components/NewPDS/ConfirmNewPDSModal.tsx
··· 1 + import { Fragment, useState } from "react"; 2 + import { Dialog, Transition } from "@headlessui/react"; 3 + import { XCircleIcon } from "@heroicons/react/24/outline"; 4 + 5 + interface ConfirmModalProps { 6 + action: { 7 + type: "add" | "remove"; 8 + pds: string; 9 + }; 10 + onConfirm: () => void; 11 + onCancel: () => void; 12 + } 13 + 14 + const ConfirmNewPDSModal = ({ 15 + action, 16 + onConfirm, 17 + onCancel, 18 + }: ConfirmModalProps) => { 19 + const [open, setOpen] = useState(true); 20 + 21 + const handleConfirm = () => { 22 + onConfirm(); 23 + setOpen(false); 24 + }; 25 + 26 + const handleCancel = () => { 27 + onCancel(); 28 + setOpen(false); 29 + }; 30 + 31 + return ( 32 + <Transition.Root show={open} as={Fragment}> 33 + <Dialog as="div" className="relative z-10" onClose={setOpen}> 34 + <Transition.Child 35 + as={Fragment} 36 + enter="ease-out duration-300" 37 + enterFrom="opacity-0" 38 + enterTo="opacity-100" 39 + leave="ease-in duration-200" 40 + leaveFrom="opacity-100" 41 + leaveTo="opacity-0" 42 + > 43 + <div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" /> 44 + </Transition.Child> 45 + 46 + <div className="fixed inset-0 z-10 overflow-y-auto"> 47 + <div className="flex min-h-full items-center justify-center p-4 text-center sm:items-center sm:p-0"> 48 + <Transition.Child 49 + as={Fragment} 50 + enter="ease-out duration-300" 51 + enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" 52 + enterTo="opacity-100 translate-y-0 sm:scale-100" 53 + leave="ease-in duration-200" 54 + leaveFrom="opacity-100 translate-y-0 sm:scale-100" 55 + leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" 56 + > 57 + <Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6"> 58 + <div> 59 + <div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-yellow-100"> 60 + <XCircleIcon 61 + className="h-6 w-6 text-yellow-600" 62 + aria-hidden="true" 63 + /> 64 + </div> 65 + <div className="mt-3 text-center sm:mt-5"> 66 + <Dialog.Title 67 + as="h3" 68 + className="text-lg font-medium leading-6 text-gray-900" 69 + > 70 + {`${action.type[0].toLocaleUpperCase()}${action.type.substring( 71 + 1 72 + )}`}{" "} 73 + PDS 74 + </Dialog.Title> 75 + <div className="mt-2"> 76 + <p className="text-sm text-gray-500"> 77 + Are you sure you want to {action.type} {action.pds}? 78 + </p> 79 + </div> 80 + </div> 81 + </div> 82 + <div className="mt-5 sm:mt-6 sm:grid sm:grid-cols-2 sm:gap-3 sm:grid-flow-row-dense"> 83 + <button 84 + type="button" 85 + className="inline-flex w-full justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:col-start-2" 86 + onClick={handleConfirm} 87 + > 88 + Confirm 89 + </button> 90 + <button 91 + type="button" 92 + className="mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-medium text-gray-700 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 sm:col-start-1 sm:mt-0" 93 + onClick={handleCancel} 94 + > 95 + Cancel 96 + </button> 97 + </div> 98 + </Dialog.Panel> 99 + </Transition.Child> 100 + </div> 101 + </div> 102 + </Dialog> 103 + </Transition.Root> 104 + ); 105 + }; 106 + 107 + export default ConfirmNewPDSModal;
+244
ts/bgs-dash/src/components/NewPDS/NewPDS.tsx
··· 1 + import { FC, useEffect, useState } from "react"; 2 + import Notification, { 3 + NotificationMeta, 4 + NotificationType, 5 + } from "../Notification/Notification"; 6 + 7 + import { RELAY_HOST } from "../../constants"; 8 + 9 + import { useNavigate } from "react-router-dom"; 10 + import ConfirmNewPDSModal from "./ConfirmNewPDSModal"; 11 + import { 12 + ShieldCheckIcon, 13 + ShieldExclamationIcon, 14 + } from "@heroicons/react/24/outline"; 15 + 16 + const NewPDS: FC<{}> = () => { 17 + const [pdsHost, setPDSHost] = useState<string>(""); 18 + 19 + // Notification Management 20 + const [shouldShowNotification, setShouldShowNotification] = 21 + useState<boolean>(false); 22 + const [notification, setNotification] = useState<NotificationMeta>({ 23 + message: "", 24 + alertType: "", 25 + }); 26 + 27 + // Modal state management 28 + const [modalAction, setModalAction] = useState<{ 29 + pds: string; 30 + type: "add" | "remove"; 31 + } | null>(null); 32 + const [modalConfirm, setModalConfirm] = useState<() => void>(() => { }); 33 + const [modalCancel, setModalCancel] = useState<() => void>(() => { }); 34 + 35 + const [adminToken, setAdminToken] = useState<string>( 36 + localStorage.getItem("admin_route_token") || "" 37 + ); 38 + const navigate = useNavigate(); 39 + 40 + const setAlertWithTimeout = ( 41 + type: NotificationType, 42 + message: string, 43 + dismiss: boolean 44 + ) => { 45 + setNotification({ 46 + message, 47 + alertType: type, 48 + autodismiss: dismiss, 49 + }); 50 + setShouldShowNotification(true); 51 + }; 52 + 53 + useEffect(() => { 54 + const token = localStorage.getItem("admin_route_token"); 55 + if (token) { 56 + setAdminToken(token); 57 + } else { 58 + navigate("/login"); 59 + } 60 + }, []); 61 + 62 + const requestAddPDS = (pds: string) => { 63 + fetch(`${RELAY_HOST}/admin/pds/requestCrawl`, { 64 + method: "POST", 65 + headers: { 66 + "Content-Type": "application/json", 67 + Authorization: `Bearer ${adminToken}`, 68 + }, 69 + body: JSON.stringify({ 70 + hostname: pds, 71 + }), 72 + }) 73 + .then((res) => { 74 + if (res.status !== 200) { 75 + try { 76 + res.json().then((data) => { 77 + if (data.error) { 78 + setAlertWithTimeout( 79 + "failure", 80 + `Failed to add PDS: ${data.error}`, 81 + true 82 + ); 83 + } else { 84 + setAlertWithTimeout( 85 + "failure", 86 + `Failed to add PDS: ${res.statusText}`, 87 + true 88 + ); 89 + } 90 + }); 91 + } 92 + catch (err) { 93 + setAlertWithTimeout( 94 + "failure", 95 + `Failed to add PDS: ${err}`, 96 + true 97 + ); 98 + } 99 + } else { 100 + try { 101 + res.json().then((data) => { 102 + if (data.error) { 103 + setAlertWithTimeout( 104 + "failure", 105 + `Failed to add PDS: ${data.error}`, 106 + true 107 + ); 108 + } else { 109 + setAlertWithTimeout( 110 + "success", 111 + `Successfully added PDS ${pds}`, 112 + true 113 + ); 114 + } 115 + }); 116 + } catch (err) { 117 + setAlertWithTimeout( 118 + "failure", 119 + `Failed to add PDS: ${err}`, 120 + true 121 + ); 122 + } 123 + } 124 + }) 125 + .catch((err) => { 126 + setAlertWithTimeout("failure", `Failed to add PDS: ${err}`, true); 127 + }); 128 + }; 129 + 130 + const requestRemovePDS = (pds: string) => { 131 + setAlertWithTimeout( 132 + "failure", 133 + `Failed to remove PDS: ${pds} - Not implemented`, 134 + true 135 + ); 136 + }; 137 + 138 + const handleAddPDS = ( 139 + pds: string, 140 + type: "add" | "remove" 141 + ) => { 142 + if (pds === "") { 143 + setAlertWithTimeout("failure", "PDS Hostname cannot be empty", true); 144 + return; 145 + } 146 + 147 + // Strip the protocol from the hostname 148 + pds = pds.replace(/^https?:\/\//, ""); 149 + 150 + setModalAction({ pds: pds, type }); 151 + 152 + setModalConfirm(() => { 153 + return () => { 154 + if (type === "add") requestAddPDS(pds); 155 + else requestRemovePDS(pds); 156 + 157 + setModalAction(null); 158 + }; 159 + }); 160 + 161 + setModalCancel(() => { 162 + return () => { 163 + setModalAction(null); 164 + }; 165 + }); 166 + }; 167 + 168 + return ( 169 + <div className="mx-auto max-w-full"> 170 + {shouldShowNotification ? ( 171 + <Notification 172 + message={notification.message} 173 + alertType={notification.alertType} 174 + subMessage={notification.subMessage} 175 + autodismiss={notification.autodismiss} 176 + unshow={() => { 177 + setShouldShowNotification(false); 178 + setNotification({ message: "", alertType: "" }); 179 + }} 180 + show={shouldShowNotification} 181 + ></Notification> 182 + ) : ( 183 + <></> 184 + )} 185 + <div className="sm:flex sm:items-center"> 186 + <div className="sm:flex-auto"> 187 + <h1 className="text-2xl font-semibold leading-6 text-gray-900"> 188 + Add a PDS 189 + </h1> 190 + <p className="mt-2 text-sm text-gray-700"> 191 + Add a PDS to the Relay and trigger crawling. 192 + </p> 193 + </div> 194 + </div> 195 + <div className="flex-grow mt-5"> 196 + <div className="max-w-3xl w-full"> 197 + <label 198 + htmlFor="email" 199 + className="block text-sm font-medium leading-6 text-gray-900" 200 + > 201 + PDS Hostname 202 + </label> 203 + <div className="mt-2 inline-flex flex-col sm:flex-row"> 204 + <input 205 + type="text" 206 + name="pds" 207 + id="pds" 208 + className="block w-72 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" 209 + placeholder="hydnum.us-west.host.bsky.network" 210 + value={pdsHost} 211 + onChange={(e) => { 212 + setPDSHost(e.target.value); 213 + }} 214 + /> 215 + <div className="inline-flex mt-4 sm:mt-0"> 216 + <button 217 + type="button" 218 + onClick={() => { 219 + handleAddPDS(pdsHost.trim(), "add"); 220 + }} 221 + className="ml-0 sm:ml-2 inline-flex whitespace-nowrap items-center gap-x-1.5 rounded-md bg-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600" 222 + > 223 + <ShieldCheckIcon 224 + className="-ml-0.5 h-5 w-5" 225 + aria-hidden="true" 226 + /> 227 + Add PDS 228 + </button> 229 + </div> 230 + </div> 231 + </div> 232 + </div> 233 + {modalAction && ( 234 + <ConfirmNewPDSModal 235 + action={modalAction} 236 + onConfirm={modalConfirm} 237 + onCancel={modalCancel} 238 + /> 239 + )} 240 + </div> 241 + ); 242 + }; 243 + 244 + export default NewPDS;
+6 -6
ts/bgs-dash/src/components/Repos/Repos.tsx
··· 4 4 NotificationType, 5 5 } from "../Notification/Notification"; 6 6 7 - import { BGS_HOST } from "../../constants"; 7 + import { RELAY_HOST } from "../../constants"; 8 8 9 9 import { useNavigate } from "react-router-dom"; 10 10 import ConfirmRepoTakedownModal from "./ConfirmRepoTakedownModal"; ··· 29 29 repo: string; 30 30 type: "takedown" | "untakedown"; 31 31 } | null>(null); 32 - const [modalConfirm, setModalConfirm] = useState<() => void>(() => {}); 33 - const [modalCancel, setModalCancel] = useState<() => void>(() => {}); 32 + const [modalConfirm, setModalConfirm] = useState<() => void>(() => { }); 33 + const [modalCancel, setModalCancel] = useState<() => void>(() => { }); 34 34 35 35 const [adminToken, setAdminToken] = useState<string>( 36 36 localStorage.getItem("admin_route_token") || "" ··· 60 60 }, []); 61 61 62 62 const requestTakedownRepo = (repo: string) => { 63 - fetch(`${BGS_HOST}/admin/repo/takeDown`, { 63 + fetch(`${RELAY_HOST}/admin/repo/takeDown`, { 64 64 method: "POST", 65 65 headers: { 66 66 "Content-Type": "application/json", ··· 92 92 }; 93 93 94 94 const requestUntakedownRepo = (repo: string) => { 95 - fetch(`${BGS_HOST}/admin/repo/reverseTakedown`, { 95 + fetch(`${RELAY_HOST}/admin/repo/reverseTakedown`, { 96 96 method: "POST", 97 97 headers: { 98 98 "Content-Type": "application/json", ··· 173 173 Repo Takedowns 174 174 </h1> 175 175 <p className="mt-2 text-sm text-gray-700"> 176 - Takedown a repo to purge it from the BGS history and reject all 176 + Takedown a repo to purge it from the Relay history and reject all 177 177 future events for it. 178 178 </p> 179 179 </div>
+2 -2
ts/bgs-dash/src/constants.ts
··· 2 2 window.location.hostname === "localhost" || 3 3 window.location.hostname === "jaz1"; 4 4 5 - const BGS_HOST = `${window.location.protocol}//${window.location.hostname}:${ 5 + const RELAY_HOST = `${window.location.protocol}//${window.location.hostname}:${ 6 6 isDev ? "2470" : window.location.port 7 7 }`; 8 8 9 - export { BGS_HOST }; 9 + export { RELAY_HOST };