diff --git a/src/pages/log.tsx b/src/pages/log.tsx
index 6b5c9e689969119de793fb1f69de28467bb8b131..cf4cd6a71d638cedb7beab80c86539e00f5caeeb 100644
--- a/src/pages/log.tsx
+++ b/src/pages/log.tsx
@@ -1,5 +1,23 @@
+import { useEffect } from "react";
+import { Box, Typography } from "@mui/material";
+import services from "../services";
+
 const LogPage = () => {
-  return <h1>Log</h1>;
+  useEffect(() => {
+    const sourcePromise = services.getLogs(console.log);
+
+    return () => {
+      sourcePromise.then((src) => src.cancel());
+    };
+  }, []);
+
+  return (
+    <Box sx={{ width: 0.9, maxWidth: "850px", mx: "auto", mb: 2 }}>
+      <Typography variant="h4" component="h1" sx={{ py: 2 }}>
+        Logs
+      </Typography>
+    </Box>
+  );
 };
 
 export default LogPage;
diff --git a/src/services/common.ts b/src/services/common.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5240f482501265d96fe043f9d34320bd08a34308
--- /dev/null
+++ b/src/services/common.ts
@@ -0,0 +1,56 @@
+import axios from "axios";
+import axiosIns from "./base";
+
+/// Get Version
+export async function getVersion() {
+  return axiosIns.get("/version") as Promise<{
+    premium: boolean;
+    version: string;
+  }>;
+}
+
+export interface ConfigType {
+  port: number;
+  mode: string;
+  "socket-port": number;
+  "allow-lan": boolean;
+  "log-level": string;
+  "mixed-port": number;
+}
+
+/// Get current base configs
+export async function getConfigs() {
+  return axiosIns.get("/configs") as Promise<ConfigType>;
+}
+
+/// Update current configs
+export async function updateConfigs(config: Partial<ConfigType>) {
+  return axiosIns.patch("/configs", config);
+}
+
+interface RuleItem {
+  type: string;
+  payload: string;
+  proxy: string;
+}
+
+/// Get current rules
+export async function getRules() {
+  return axiosIns.get("/rules") as Promise<RuleItem[]>;
+}
+
+/// Get logs stream
+export async function getLogs(callback: (t: any) => void) {
+  const source = axios.CancelToken.source();
+
+  axiosIns.get("/logs", {
+    cancelToken: source.token,
+    onDownloadProgress: (progressEvent) => {
+      const data = progressEvent.currentTarget.response || "";
+      const lastData = data.slice(data.trim().lastIndexOf("\n") + 1);
+      callback(JSON.parse(lastData));
+    },
+  });
+
+  return source;
+}
diff --git a/src/services/index.ts b/src/services/index.ts
index 6832043ac008cbb3f4e8b0089939f31dc03f9c89..434a9d35911b0c18e79bc03cb094b96b325d7324 100644
--- a/src/services/index.ts
+++ b/src/services/index.ts
@@ -1,7 +1,9 @@
+import * as common from "./common";
 import * as proxy from "./proxy";
 import * as traffic from "./traffic";
 
 export default {
+  ...common,
   ...proxy,
   ...traffic,
 };