فهرست منبع

404 hosts polish

Jamie Curnow 2 ماه پیش
والد
کامیت
da68fe29ac

+ 0 - 8
README.md

@@ -88,14 +88,6 @@ Sometimes this can take a little bit because of the entropy of keys.
 
 [http://127.0.0.1:81](http://127.0.0.1:81)
 
-Default Admin User:
-```
-Email:    [email protected]
-Password: changeme
-```
-
-Immediately after logging in with this default user you will be asked to modify your details and change your password.
-
 
 ## Contributing
 

+ 3 - 1
backend/internal/dead-host.js

@@ -63,7 +63,7 @@ const internalDeadHost = {
 			action: "created",
 			object_type: "dead-host",
 			object_id: row.id,
-			meta: _.assign({}, data.meta || {}, row.meta),
+			meta: thisData,
 		});
 
 		if (createCertificate) {
@@ -240,6 +240,7 @@ const internalDeadHost = {
 		// Delete Nginx Config
 		await internalNginx.deleteConfig("dead_host", row);
 		await internalNginx.reload();
+
 		// Add to audit log
 		await internalAuditLog.add(access, {
 			action: "deleted",
@@ -247,6 +248,7 @@ const internalDeadHost = {
 			object_id: row.id,
 			meta: _.omit(row, omissions()),
 		});
+		return true;
 	},
 
 	/**

+ 4 - 1
backend/internal/nginx.js

@@ -301,8 +301,11 @@ const internalNginx = {
 	 * @param   {String}  filename
 	 */
 	deleteFile: (filename) => {
-		logger.debug(`Deleting file: ${filename}`);
+		if (!fs.existsSync(filename)) {
+			return;
+		}
 		try {
+			logger.debug(`Deleting file: ${filename}`);
 			fs.unlinkSync(filename);
 		} catch (err) {
 			logger.debug("Could not delete file:", JSON.stringify(err, null, 2));

+ 2 - 2
backend/routes/nginx/dead_hosts.js

@@ -121,7 +121,7 @@ router
 	/**
 	 * PUT /api/nginx/dead-hosts/123
 	 *
-	 * Update and existing dead-host
+	 * Update an existing dead-host
 	 */
 	.put(async (req, res, next) => {
 		try {
@@ -138,7 +138,7 @@ router
 	/**
 	 * DELETE /api/nginx/dead-hosts/123
 	 *
-	 * Update and existing dead-host
+	 * Delete a dead-host
 	 */
 	.delete(async (req, res, next) => {
 		try {

+ 21 - 3
frontend/src/components/Table/Formatter/DomainsFormatter.tsx

@@ -4,14 +4,32 @@ interface Props {
 	domains: string[];
 	createdOn?: string;
 }
+
+const DomainLink = ({ domain }: { domain: string }) => {
+	// when domain contains a wildcard, make the link go nowhere.
+	let onClick: ((e: React.MouseEvent) => void) | undefined;
+	if (domain.includes("*")) {
+		onClick = (e: React.MouseEvent) => e.preventDefault();
+	}
+	return (
+		<a
+			key={domain}
+			href={`http://${domain}`}
+			target="_blank"
+			onClick={onClick}
+			className="badge bg-yellow-lt domain-name me-2"
+		>
+			{domain}
+		</a>
+	);
+};
+
 export function DomainsFormatter({ domains, createdOn }: Props) {
 	return (
 		<div className="flex-fill">
 			<div className="font-weight-medium">
 				{domains.map((domain: string) => (
-					<a key={domain} href={`http://${domain}`} className="badge bg-yellow-lt domain-name">
-						{domain}
-					</a>
+					<DomainLink key={domain} domain={domain} />
 				))}
 			</div>
 			{createdOn ? (

+ 2 - 0
frontend/src/components/Table/Formatter/EventFormatter.tsx

@@ -10,6 +10,8 @@ const getEventValue = (event: AuditLog) => {
 	switch (event.objectType) {
 		case "user":
 			return event.meta?.name;
+		case "dead-host":
+			return event.meta?.domainNames?.join(", ") || "N/A";
 		default:
 			return `UNKNOWN EVENT TYPE: ${event.objectType}`;
 	}

+ 8 - 0
frontend/src/locale/lang/en.json

@@ -41,6 +41,8 @@
   "column.status": "Status",
   "created-on": "Created: {date}",
   "dashboard.title": "Dashboard",
+  "dead-host.delete.content": "Are you sure you want to delete this 404 host?",
+  "dead-host.delete.title": "Delete 404 Host",
   "dead-host.edit": "Edit 404 Host",
   "dead-host.new": "New 404 Host",
   "dead-hosts.actions-title": "404 Host #{id}",
@@ -67,8 +69,11 @@
   "error.max-domains": "Too many domains, max is {max}",
   "error.passwords-must-match": "Passwords must match",
   "error.required": "This is required",
+  "event.created-dead-host": "Created 404 Host",
   "event.created-user": "Created User",
   "event.deleted-user": "Deleted User",
+  "event.disabled-dead-host": "Disabled 404 Host",
+  "event.enabled-dead-host": "Enabled 404 Host",
   "event.updated-user": "Updated User",
   "footer.github-fork": "Fork me on Github",
   "hosts.title": "Hosts",
@@ -84,6 +89,9 @@
   "notfound.title": "Oops… You just found an error page",
   "notification.dead-host-saved": "404 Host has been saved",
   "notification.error": "Error",
+  "notification.host-deleted": "Host has been deleted",
+  "notification.host-disabled": "Host has been disabled",
+  "notification.host-enabled": "Host has been enabled",
   "notification.success": "Success",
   "notification.user-deleted": "User has been deleted",
   "notification.user-saved": "User has been saved",

+ 27 - 3
frontend/src/locale/src/en.json

@@ -134,12 +134,18 @@
 	"dead-hosts.count": {
 		"defaultMessage": "{count} 404 Hosts"
 	},
-	"dead-host.edit": {
-		"defaultMessage": "Edit 404 Host"
-	},
 	"dead-hosts.empty": {
 		"defaultMessage": "There are no 404 Hosts"
 	},
+	"dead-host.delete.content": {
+		"defaultMessage": "Are you sure you want to delete this 404 host?"
+	},
+	"dead-host.delete.title": {
+		"defaultMessage": "Delete 404 Host"
+	},
+	"dead-host.edit": {
+		"defaultMessage": "Edit 404 Host"
+	},
 	"dead-host.new": {
 		"defaultMessage": "New 404 Host"
 	},
@@ -200,12 +206,21 @@
 	"error.required": {
 		"defaultMessage": "This is required"
 	},
+	"event.created-dead-host": {
+		"defaultMessage": "Created 404 Host"
+	},
 	"event.created-user": {
 		"defaultMessage": "Created User"
 	},
 	"event.deleted-user": {
 		"defaultMessage": "Deleted User"
 	},
+	"event.disabled-dead-host": {
+		"defaultMessage": "Disabled 404 Host"
+	},
+	"event.enabled-dead-host": {
+		"defaultMessage": "Enabled 404 Host"
+	},
 	"event.updated-user": {
 		"defaultMessage": "Updated User"
 	},
@@ -254,9 +269,18 @@
 	"notification.error": {
 		"defaultMessage": "Error"
 	},
+	"notification.host-deleted": {
+		"defaultMessage": "Host has been deleted"
+	},
 	"notification.user-deleted": {
 		"defaultMessage": "User has been deleted"
 	},
+	"notification.host-disabled": {
+		"defaultMessage": "Host has been disabled"
+	},
+	"notification.host-enabled": {
+		"defaultMessage": "Host has been enabled"
+	},
 	"notification.user-saved": {
 		"defaultMessage": "User has been saved"
 	},

+ 14 - 4
frontend/src/pages/Nginx/DeadHosts/Table.tsx

@@ -12,9 +12,10 @@ interface Props {
 	isFetching?: boolean;
 	onEdit?: (id: number) => void;
 	onDelete?: (id: number) => void;
+	onDisableToggle?: (id: number, enabled: boolean) => void;
 	onNew?: () => void;
 }
-export default function Table({ data, isFetching, onEdit, onDelete, onNew }: Props) {
+export default function Table({ data, isFetching, onEdit, onDelete, onDisableToggle, onNew }: Props) {
 	const columnHelper = createColumnHelper<DeadHost>();
 	const columns = useMemo(
 		() => [
@@ -83,9 +84,18 @@ export default function Table({ data, isFetching, onEdit, onDelete, onNew }: Pro
 									<IconEdit size={16} />
 									{intl.formatMessage({ id: "action.edit" })}
 								</a>
-								<a className="dropdown-item" href="#">
+								<a
+									className="dropdown-item"
+									href="#"
+									onClick={(e) => {
+										e.preventDefault();
+										onDisableToggle?.(info.row.original.id, !info.row.original.enabled);
+									}}
+								>
 									<IconPower size={16} />
-									{intl.formatMessage({ id: "action.disable" })}
+									{intl.formatMessage({
+										id: info.row.original.enabled ? "action.disable" : "action.enable",
+									})}
 								</a>
 								<div className="dropdown-divider" />
 								<a
@@ -108,7 +118,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onNew }: Pro
 				},
 			}),
 		],
-		[columnHelper, onDelete, onEdit],
+		[columnHelper, onDelete, onEdit, onDisableToggle],
 	);
 
 	const tableInstance = useReactTable<DeadHost>({

+ 14 - 3
frontend/src/pages/Nginx/DeadHosts/TableWrapper.tsx

@@ -1,6 +1,8 @@
 import { IconSearch } from "@tabler/icons-react";
+import { useQueryClient } from "@tanstack/react-query";
 import { useState } from "react";
 import Alert from "react-bootstrap/Alert";
+import { deleteDeadHost, toggleDeadHost } from "src/api/backend";
 import { Button, LoadingPage } from "src/components";
 import { useDeadHosts } from "src/hooks";
 import { intl } from "src/locale";
@@ -9,6 +11,7 @@ import { showSuccess } from "src/notifications";
 import Table from "./Table";
 
 export default function TableWrapper() {
+	const queryClient = useQueryClient();
 	const [deleteId, setDeleteId] = useState(0);
 	const [editId, setEditId] = useState(0 as number | "new");
 	const { isFetching, isLoading, isError, error, data } = useDeadHosts(["owner", "certificate"]);
@@ -22,10 +25,17 @@ export default function TableWrapper() {
 	}
 
 	const handleDelete = async () => {
-		// await deleteUser(deleteId);
+		await deleteDeadHost(deleteId);
 		showSuccess(intl.formatMessage({ id: "notification.host-deleted" }));
 	};
 
+	const handleDisableToggle = async (id: number, enabled: boolean) => {
+		await toggleDeadHost(id, enabled);
+		queryClient.invalidateQueries({ queryKey: ["dead-hosts"] });
+		queryClient.invalidateQueries({ queryKey: ["dead-host", id] });
+		showSuccess(intl.formatMessage({ id: enabled ? "notification.host-enabled" : "notification.host-disabled" }));
+	};
+
 	return (
 		<div className="card mt-4">
 			<div className="card-status-top bg-red" />
@@ -60,17 +70,18 @@ export default function TableWrapper() {
 					isFetching={isFetching}
 					onEdit={(id: number) => setEditId(id)}
 					onDelete={(id: number) => setDeleteId(id)}
+					onDisableToggle={handleDisableToggle}
 					onNew={() => setEditId("new")}
 				/>
 				{editId ? <DeadHostModal id={editId} onClose={() => setEditId(0)} /> : null}
 				{deleteId ? (
 					<DeleteConfirmModal
-						title={intl.formatMessage({ id: "user.delete.title" })}
+						title={intl.formatMessage({ id: "dead-host.delete.title" })}
 						onConfirm={handleDelete}
 						onClose={() => setDeleteId(0)}
 						invalidations={[["dead-hosts"], ["dead-host", deleteId]]}
 					>
-						{intl.formatMessage({ id: "user.delete.content" })}
+						{intl.formatMessage({ id: "dead-host.delete.content" })}
 					</DeleteConfirmModal>
 				) : null}
 			</div>