瀏覽代碼

Beginning of HTML+Bootstrap+React UI for new desktop client -- looking like it will be easier than retrofitting the old Qt client for the new API.

Adam Ierymenko 10 年之前
父節點
當前提交
b2b32e5969
共有 12 個文件被更改,包括 2777 次插入31 次删除
  1. 32 29
      service/ControlPlane.cpp
  2. 2 1
      service/ControlPlane.hpp
  3. 1 1
      service/OneService.cpp
  4. 2606 0
      ui/JSXTransformer.js
  5. 51 0
      ui/ZeroTierNode.jsx
  6. 4 0
      ui/bootstrap-theme.min.css
  7. 4 0
      ui/bootstrap.min.css
  8. 22 0
      ui/index.html
  9. 4 0
      ui/main.jsx
  10. 11 0
      ui/react.min.js
  11. 1 0
      ui/simpleajax.min.js
  12. 39 0
      ui/zerotier.css

+ 32 - 29
service/ControlPlane.cpp

@@ -37,8 +37,7 @@
 #include "../node/InetAddress.hpp"
 #include "../node/Node.hpp"
 #include "../node/Utils.hpp"
-
-#define ZT_BUILD_IN_WEB_UI
+#include "../osdep/OSUtils.hpp"
 
 namespace ZeroTier {
 
@@ -242,9 +241,10 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT1_Peer *peer
 	buf.append(json);
 }
 
-ControlPlane::ControlPlane(OneService *svc,Node *n) :
+ControlPlane::ControlPlane(OneService *svc,Node *n,const char *uiStaticPath) :
 	_svc(svc),
-	_node(n)
+	_node(n),
+	_uiStaticPath((uiStaticPath) ? uiStaticPath : "")
 {
 }
 
@@ -316,31 +316,34 @@ unsigned int ControlPlane::handleRequest(
 			 * dot in the first path element (e.g. foo.html) is considered a static page,
 			 * as nothing in the API is so named. */
 
-#ifdef ZT_BUILD_IN_WEB_UI
-			if (ext == ".html")
-				responseContentType = "text/html";
-			else if (ext == ".js")
-				responseContentType = "application/javascript";
-			else if (ext == ".json")
-				responseContentType = "application/json";
-			else if (ext == ".css")
-				responseContentType = "text/css";
-			else if (ext == ".png")
-				responseContentType = "image/png";
-			else if (ext == ".jpg")
-				responseContentType = "image/jpeg";
-			else if (ext == ".gif")
-				responseContentType = "image/gif";
-			else if (ext == ".txt")
-				responseContentType = "text/plain";
-			else if (ext == ".xml")
-				responseContentType = "text/xml";
-			else if (ext == ".svg")
-				responseContentType = "image/svg+xml";
-			else responseContentType = "application/octet-stream";
-			responseBody = "<html><body>Hello World!</body></html>";
-			scode = 200;
-#endif // ZT_BUILD_IN_WEB_UI
+			if (_uiStaticPath.length() > 0) {
+				if (ext == ".html")
+					responseContentType = "text/html";
+				else if (ext == ".js")
+					responseContentType = "application/javascript";
+				else if (ext == ".jsx")
+					responseContentType = "text/jsx";
+				else if (ext == ".json")
+					responseContentType = "application/json";
+				else if (ext == ".css")
+					responseContentType = "text/css";
+				else if (ext == ".png")
+					responseContentType = "image/png";
+				else if (ext == ".jpg")
+					responseContentType = "image/jpeg";
+				else if (ext == ".gif")
+					responseContentType = "image/gif";
+				else if (ext == ".txt")
+					responseContentType = "text/plain";
+				else if (ext == ".xml")
+					responseContentType = "text/xml";
+				else if (ext == ".svg")
+					responseContentType = "image/svg+xml";
+				else responseContentType = "application/octet-stream";
+				scode = OSUtils::readFile((_uiStaticPath + ZT_PATH_SEPARATOR_S + ps[0]).c_str(),responseBody) ? 200 : 404;
+			} else {
+				scode = 404;
+			}
 
 		} else if (isAuth) {
 			/* Things that require authentication -- a.k.a. everything but static web app pages. */

+ 2 - 1
service/ControlPlane.hpp

@@ -49,7 +49,7 @@ struct InetAddress;
 class ControlPlane
 {
 public:
-	ControlPlane(OneService *svc,Node *n);
+	ControlPlane(OneService *svc,Node *n,const char *uiStaticPath);
 	~ControlPlane();
 
 	/**
@@ -102,6 +102,7 @@ public:
 private:
 	OneService *const _svc;
 	Node *const _node;
+	std::string _uiStaticPath;
 	std::set<std::string> _authTokens;
 	std::map<std::string,ControlPlaneSubsystem *> _subsystems;
 	Mutex _lock;

+ 1 - 1
service/OneService.cpp

@@ -226,7 +226,7 @@ public:
 			if (_master)
 				_node->setNetconfMaster((void *)_master);
 
-			_controlPlane = new ControlPlane(this,_node);
+			_controlPlane = new ControlPlane(this,_node,(_homePath + ZT_PATH_SEPARATOR_S + "ui").c_str());
 			_controlPlane->addAuthToken(authToken.c_str());
 			if (_master)
 				_controlPlane->mount("controller",reinterpret_cast<ControlPlaneSubsystem *>(_master));

文件差異過大導致無法顯示
+ 2606 - 0
ui/JSXTransformer.js


+ 51 - 0
ui/ZeroTierNode.jsx

@@ -0,0 +1,51 @@
+var ZeroTierNode = React.createClass({
+	getInitialState: function() {
+		return {
+			address: '----------',
+			online: false,
+			version: '_._._'
+		};
+	},
+
+	updateAll: function() {
+		Ajax.call({
+			url: 'status?auth='+this.props.authToken,
+			cache: false,
+			type: 'GET',
+			success: function(data) {
+				if (data)
+					this.setState(JSON.parse(data));
+			}.bind(this),
+			error: function() {
+				this.setState(this.getInitialState());
+			}.bind(this)
+		})
+	},
+
+	componentDidMount: function() {
+		this.updateAll();
+//		this.updateIntervalId = setInterval(this.updateAll,2500);
+	},
+	componentWillUnmount: function() {
+//		clearInterval(this.updateIntervalId);
+	},
+	render: function() {
+		return (
+			<div className="container-fluid zeroTierNode">
+				<div className="row">
+				</div>
+				<div className="row">
+					<div className="col-xs-8">
+						<span className="zerotier-address">{this.state.address}</span>
+						<span className="zerotier-node-statusline">{this.state.online ? 'ONLINE' : 'OFFLINE'}&nbsp;&nbsp;{this.state.version}</span>
+					</div>
+					<div className="col-xs-4">
+						<form>
+							<input type="text"/>
+						</form>
+					</div>
+				</div>
+			</div>
+		);
+	}
+});

文件差異過大導致無法顯示
+ 4 - 0
ui/bootstrap-theme.min.css


文件差異過大導致無法顯示
+ 4 - 0
ui/bootstrap.min.css


+ 22 - 0
ui/index.html

@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+	<title>ZeroTier One</title>
+	<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
+	<meta name="viewport" content="width=device-width, initial-scale=1">
+	<link rel="stylesheet" href="bootstrap.min.css">
+	<link rel="stylesheet" href="bootstrap-theme.min.css">
+	<link rel="stylesheet" href="zerotier.css">
+
+	<script src="simpleajax.min.js"></script>
+	<script src="react.min.js"></script>
+	<script src="JSXTransformer.js"></script>
+
+	<script type="text/jsx" src="ZeroTierNode.jsx"></script>
+
+	<script type="text/jsx" src="main.jsx"></script>
+</head>
+<body>
+<div style="width: 100%; height: 100%;" id="main"></div>
+</body>
+</html>

+ 4 - 0
ui/main.jsx

@@ -0,0 +1,4 @@
+React.render(
+	<ZeroTierNode authToken={'5d6181b71fae2684f9cc64ed'} />,
+	document.getElementById('main')
+);

文件差異過大導致無法顯示
+ 11 - 0
ui/react.min.js


文件差異過大導致無法顯示
+ 1 - 0
ui/simpleajax.min.js


+ 39 - 0
ui/zerotier.css

@@ -0,0 +1,39 @@
+/* Dark blue-grey: #234447
+ * Light blue-grey: #91a2a3
+ * Light yellow: #ffffcc
+ * Orange: #ffb354 */
+
+/*
+@font-face {
+	font-family: 'Clear Sans Thin';
+	src: url('fonts/clearsans_thin/ClearSans-Thin-webfont.eot');
+	src: url('fonts/clearsans_thin/ClearSans-Thin-webfont.eot?#iefix') format('embedded-opentype'), url('fonts/clearsans_thin/ClearSans-Thin-webfont.woff') format('woff'), url('fonts/clearsans_thin/ClearSans-Thin-webfont.ttf') format('truetype'), url('fonts/clearsans_thin/ClearSans-Thin-webfont.svg#clear_sans_thinregular') format('svg');
+	font-weight: normal;
+	font-style: normal;
+}
+*/
+/*
+@font-face {
+	font-family: 'Clear Sans Regular';
+	src: url('ClearSans-Regular-webfont.eot');
+	src: url('ClearSans-Regular-webfont.eot?#iefix') format('embedded-opentype'), url('ClearSans-Regular-webfont.woff') format('woff'), url('ClearSans-Regular-webfont.ttf') format('truetype'), url('ClearSans-Regular-webfont.svg#clear_sansregular') format('svg');
+	font-weight: normal;
+	font-style: normal;
+}
+*/
+
+html,body {
+	font-family: "Helvetica Neue","Lucida Sans Unicode",sans-serif;
+	font-size: 12pt;
+	margin: 0;
+	padding: 0;
+	width: 100%;
+}
+
+.zerotier-address {
+	font-family: monospace;
+}
+.zerotier-node-statusline {
+	font-size: smaller;
+	padding: 0 0.75rem 0 0.75rem;
+}

部分文件因文件數量過多而無法顯示