guowengui пре 8 година
родитељ
комит
3977d4bb00

+ 17 - 0
README.md

@@ -0,0 +1,17 @@
+# naiveboom
+
+让Naive的事情不再发生
+
+这是一个简单的项目,可以一定程度保证你的安全。
+
+举例:你有个链接或者一段文字需要发送给某人,但是是透过QQ、WeChat这类含赵量极高的服务去发送,这样会非常不安全,甚至会被查水表。
+
+现在可以透过这样的一个方案得以解决
+
+1. 你将私密的链接或者文字粘贴到我们项目
+2. 产生一个一次性链接
+3. 阅后即焚
+
+嗯,好像就是阅后即焚来的(是的
+
+这样你就不会被查水表了!

+ 46 - 0
app.js

@@ -0,0 +1,46 @@
+var express = require('express');
+var path = require('path');
+var favicon = require('serve-favicon');
+var logger = require('morgan');
+var cookieParser = require('cookie-parser');
+var bodyParser = require('body-parser');
+
+var index = require('./routes/index');
+var apis = require('./routes/apis');
+
+var app = express();
+
+// view engine setup
+app.set('views', path.join(__dirname, 'views'));
+app.set('view engine', 'jade');
+
+// uncomment after placing your favicon in /public
+//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
+app.use(logger('dev'));
+app.use(bodyParser.json());
+app.use(bodyParser.urlencoded({ extended: false }));
+app.use(cookieParser());
+app.use(express.static(path.join(__dirname, 'public')));
+
+app.use('/', index);
+app.use('/api', apis); // 加在app.use部分
+
+// catch 404 and forward to error handler
+app.use(function(req, res, next) {
+  var err = new Error('Not Found');
+  err.status = 404;
+  next(err);
+});
+
+// error handler
+app.use(function(err, req, res, next) {
+  // set locals, only providing error in development
+  res.locals.message = err.message;
+  res.locals.error = req.app.get('env') === 'development' ? err : {};
+
+  // render the error page
+  res.status(err.status || 500);
+  res.render('error');
+});
+
+module.exports = app;

+ 90 - 0
bin/www

@@ -0,0 +1,90 @@
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var app = require('../app');
+var debug = require('debug')('node-api:server');
+var http = require('http');
+
+/**
+ * Get port from environment and store in Express.
+ */
+
+var port = normalizePort(process.env.PORT || '3000');
+app.set('port', port);
+
+/**
+ * Create HTTP server.
+ */
+
+var server = http.createServer(app);
+
+/**
+ * Listen on provided port, on all network interfaces.
+ */
+
+server.listen(port);
+server.on('error', onError);
+server.on('listening', onListening);
+
+/**
+ * Normalize a port into a number, string, or false.
+ */
+
+function normalizePort(val) {
+  var port = parseInt(val, 10);
+
+  if (isNaN(port)) {
+    // named pipe
+    return val;
+  }
+
+  if (port >= 0) {
+    // port number
+    return port;
+  }
+
+  return false;
+}
+
+/**
+ * Event listener for HTTP server "error" event.
+ */
+
+function onError(error) {
+  if (error.syscall !== 'listen') {
+    throw error;
+  }
+
+  var bind = typeof port === 'string'
+    ? 'Pipe ' + port
+    : 'Port ' + port;
+
+  // handle specific listen errors with friendly messages
+  switch (error.code) {
+    case 'EACCES':
+      console.error(bind + ' requires elevated privileges');
+      process.exit(1);
+      break;
+    case 'EADDRINUSE':
+      console.error(bind + ' is already in use');
+      process.exit(1);
+      break;
+    default:
+      throw error;
+  }
+}
+
+/**
+ * Event listener for HTTP server "listening" event.
+ */
+
+function onListening() {
+  var addr = server.address();
+  var bind = typeof addr === 'string'
+    ? 'pipe ' + addr
+    : 'port ' + addr.port;
+  debug('Listening on ' + bind);
+}

+ 14 - 0
config.js

@@ -0,0 +1,14 @@
+/**
+ * naiveboom配置文件
+ * @type {Object}
+ */
+module.exports = {
+	// redis服务器配置
+	redis_host: '127.0.0.1',
+	redis_port: '6379',
+	redis_pwd: null, // 没有redis密码则写 null
+	// 列表Hash表名
+	field_lists: 'naives',
+	// 匹配GUID的正则表达式
+	regex_guid: /[?a-zA-Z0-9]{8}-[?a-zA-Z0-9]{4}-[?a-zA-Z0-9]{4}-[?a-zA-Z0-9]{4}-[?a-zA-Z0-9]{12}$/,
+}

+ 99 - 0
lib/tools.js

@@ -0,0 +1,99 @@
+var redis = require("redis");
+var guidtool = require('guid');
+
+/**
+ * Redis存档的Hash消息管理工具
+ * @param  {Object} config  配置文件
+ * @return {[type]}        [description]
+ */
+module.exports = function(conf)
+{
+
+	/**
+	 * 初始化Redis连接
+	 * @return {[type]} [description]
+	 */
+	var init = () => {
+		this.conf = conf;
+		// noderedis密码等选项
+		var options = conf.redis_pwd ? { password: conf.redis_pwd } : null;
+		// redis client instance
+		this.client = redis.createClient(conf.redis_port, conf.redis_host, options);
+
+		this.client.on("error", (err) => {
+		    console.log("redis Error " + err);
+		});
+	}
+
+	/**
+	 * 删除Redis中的某条消息
+	 * @param  {string}   guid     消息ID
+	 * @param  {function} callback 回调函数 (status: 成功1,失败0)
+	 * @return {[type]}            [description]
+	 */
+	this.delete = (guid, callback) => {
+		this.client.hdel(this.conf.field_lists, guid,(err, status) => {
+			if (callback) {
+				callback(status);
+			}
+		});
+	}
+
+	/**
+	 * 获取一个不会重复的唯一ID
+	 * @param  {Function} callback 回调函数,({ guid })
+	 * @return {[type]}            [description]
+	 */
+	this.getUniID = (callback) => {
+		var guid = guidtool.raw();
+		this.exists(guid, (exists) => {
+			// 如果存在则递归重新获取,直到获取到不重复的GUID并返回
+			exists ? this.getUniID(callback) : callback(guid);
+		});
+	}
+
+	/**
+	 * 插入一条消息
+	 * @param  {string}    text       欲被插入的内容
+	 * @param  {Function}  callback   回调函数,( status:1|0, guid:string )
+	 * @example 回调函数 (status, guid) => { ... }
+	 * @return {[type]}            [description]
+	 */
+	this.insert = (text, callback) => {
+		this.getUniID((guid) => {
+			// 存入消息
+			this.client.hset(conf.field_lists, guid, text, (err, status) => {
+				var _status, _guid = false;
+				if (status) { _status = true; _guid = guid }
+				callback(_status, _guid);
+			});
+		})
+	}
+
+	/**
+	 * 检查是否存在某条消息
+	 * @param  {string}   guid     消息ID
+	 * @param  {Function} callback 回调函数 (status: 存在1,不存在0)
+	 * @return {[type]}            [description]
+	 */
+	this.exists = (guid, callback) => {
+		this.client.hexists(this.conf.field_lists, guid, (err, exists) => {
+			callback(exists);
+		});
+	}
+
+	/**
+	 * 获取某条消息
+	 * @param  {string}   guid     消息ID
+	 * @param  {Function} callback 回调函数 (msg 消息内容)
+	 * @return {[type]}            [description]
+	 */
+	this.get = (guid, callback) => {
+		this.client.hget(this.conf.field_lists, guid, (err, msg) => {
+			callback(msg);
+		});
+	}
+
+	// 初始化
+	init();
+}

+ 19 - 0
package.json

@@ -0,0 +1,19 @@
+{
+  "name": "node-api",
+  "version": "0.0.0",
+  "private": true,
+  "scripts": {
+    "start": "node ./bin/www"
+  },
+  "dependencies": {
+    "body-parser": "~1.18.2",
+    "cookie-parser": "~1.4.3",
+    "debug": "~2.6.9",
+    "express": "~4.15.5",
+    "guid": "^0.0.12",
+    "jade": "~1.11.0",
+    "morgan": "~1.9.0",
+    "redis": "^2.8.0",
+    "serve-favicon": "~2.4.5"
+  }
+}

BIN
public/favicon.ico


BIN
public/images/zhao.png


+ 37 - 0
public/javascripts/a.js

@@ -0,0 +1,37 @@
+app = new Vue({
+	el: '#app',
+	data: {
+		text: null,
+		enurl: null,
+		showtg: false,// is show the tugong ICON
+		tgstyle: [],
+	},
+	watch: {
+		showtg: function(status) {
+			status ? this.tgstyle = ['show'] : this.tgstyle = [];
+			if (status) {
+				setTimeout(function() {
+					app.showtg = false;// 显示2秒自动关闭
+				}, 2700);
+			}
+		}
+	},
+	methods: {
+		getTempURL: function() {
+			axios.post('/api/get-temp', {
+				text: app.text
+			}).then(function(response) {
+				var data = response.data;
+				if (data.status == 1) {
+					var newURL = window.location.protocol + "//" + window.location.host + window.location.pathname;
+					app.enurl = newURL + data.guid;
+				} else {
+					throw 'failure to get the tmp url!';
+				}
+				console.log(response);
+			}).catch(function(error) {
+				console.warn(error);
+			})
+		}
+	}
+})

Разлика између датотеке није приказан због своје велике величине
+ 1 - 0
public/javascripts/axios.min.js


+ 30 - 0
public/javascripts/view.js

@@ -0,0 +1,30 @@
+// 查看消息需要用到的JS
+app = new Vue({
+	el: '#app',
+	data: {
+		// 解析GUID需要的正则
+		guidregEx: /[?a-zA-Z0-9]{8}-[?a-zA-Z0-9]{4}-[?a-zA-Z0-9]{4}-[?a-zA-Z0-9]{4}-[?a-zA-Z0-9]{12}/,
+		status: -1,
+		text: null,
+	},
+	methods: {
+		getMsg: function() {
+			// 从URL获取GUID,然后透过POST获取消息
+			var guid = this.guidregEx.exec(window.location.href)[0];
+			axios.post('/api/get-msg', {
+				guid: guid
+			}).then(function (response) {
+				var data = response.data;
+				app.status = data.status;
+				app.text = data.text;
+				console.log(data);
+			}).catch(function (error) {
+				throw error;
+				alert('发生错误!')
+			});
+		}
+	},
+	mounted: function() {
+		this.getMsg();// 开始获取消息
+	}
+});

Разлика између датотеке није приказан због своје велике величине
+ 5 - 0
public/javascripts/vue.min.js


+ 70 - 0
public/stylesheets/style.css

@@ -0,0 +1,70 @@
+body {
+  padding: 50px;
+  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
+}
+
+a {
+  color: #00B7FF;
+}
+
+#warning{
+	color: red;
+}
+
+textarea { border: 2px dotted black;  }
+
+button#dobtn { margin-top: 10px; padding: 5px 30px; cursor: pointer; border: 1px solid black;  transition: all .3s ease-in-out}
+button#dobtn:hover {background-color: black; color: white; transform: scale(.96); }
+button#dobtn:active { transform: scale(1.03); }
+#text_edit
+{
+	width: 70vw;
+}
+
+pre {
+white-space: pre-wrap;
+word-wrap: break-word;
+}
+
+hr {
+	border:0; height:1px; background-color:#d4d4d4;
+	color:#d4d4d4	/* IE6 */
+}
+
+#zhao {
+	color: red;
+}
+
+#enurl:hover { transform: scale(1.01); }
+
+#tg.show
+{
+	background: red;
+	width: 100vh;
+	opacity: 1;
+	color: yellow;
+	font-size: 10em;
+}
+
+#tg
+{
+	top: 1vw;
+	left: 1vw;
+	width: 1px;
+	display: block;
+	position: absolute;
+	background: gray;
+	transition: all 2s ease-in;
+	opacity: 0;
+}
+
+#enurl
+{
+	transition: all .3s ease-in-out;
+	margin: 3px 0;
+	display: inline-block;
+	color: white;
+	background-color: #66CCCC;
+	padding: 6px 12px;
+	border-radius: .5;
+}

+ 40 - 0
routes/apis.js

@@ -0,0 +1,40 @@
+var express = require('express');
+var router = express.Router();
+var conf = require('../config.js');
+var mtool = new (require('../lib/tools.js'))(conf);// 消息操作工具
+
+/* GET home page. */
+router.post('/get-temp', function(req, res, next) {
+	// 用户输出的内容
+	var text = req.body.text;
+
+	mtool.insert(text, (status, guid) => {
+		rep = { status: status, guid: guid };
+		res.send(rep);
+	});
+});
+
+/**
+ * 提取信息
+ * @param  {[type]} req  [description]
+ * @param  {[type]} res) {	var        guid [description]
+ * @return {[type]}      [description]
+ */
+router.post('/get-msg', function(req, res) {
+	var guid = req.body.guid;
+	console.log("GUID", guid);
+	mtool.exists(guid, (exists) => {
+		var rep = {status: 0, text: null};
+		if (exists) {
+			mtool.get(guid, (text) => {
+				rep.status = 1; rep.text = text;
+				mtool.delete(guid);// 删除消息
+				res.send(rep);
+			});
+		} else {
+			res.send(rep);
+		}
+	});
+})
+
+module.exports = router;

+ 21 - 0
routes/index.js

@@ -0,0 +1,21 @@
+var express = require('express');
+var router = express.Router();
+var conf = require('../config.js');
+var mtool = new (require('../lib/tools.js'))(conf);// 消息操作工具
+
+/* GET home page. */
+router.get('/', function(req, res, next) {
+  res.render('index', { title: 'Naiveboom - 比较安全' });
+});
+
+/**
+ * 提取信息
+ * @param  {[type]} req  [description]
+ * @param  {[type]} res) {	var        guid [description]
+ * @return {[type]}      [description]
+ */
+router.get(conf.regex_guid, function(req, res) {
+	res.render('look', {title: '查看内容 - naiveBoom!'});
+})
+
+module.exports = router;

+ 14 - 0
run.js

@@ -0,0 +1,14 @@
+var spawn = require('child_process').spawn;
+free = spawn('npm', ['start']);
+
+free.stdout.on('data', function (data) {
+console.log('standard output:\n' + data);
+});
+
+free.stderr.on('data', function (data) {
+console.log('standard error output:\n' + data);
+});
+
+free.on('exit', function (code, signal) {
+console.log('child process eixt ,exit:' + code);
+});

+ 7 - 0
views/error.jade

@@ -0,0 +1,7 @@
+extends layout
+
+block content
+  h1= message
+  h2= error.status
+  h3 出现了一些情况,页面发生错误了。
+  pre #{error.stack}

+ 32 - 0
views/index.jade

@@ -0,0 +1,32 @@
+extends layout
+block content
+
+    p 在下面的框框里输入一些内容,然后点一下「产生」按钮
+      | 你就会得到一个长的网址,将这个网址发给你的朋友,他只能看一次,内容就会 <b>永远消失</b>!
+
+      div#app
+        textarea(v-model='text' rows='10' id='text_edit')
+        br
+        button(v-on:click='getTempURL' id='dobtn') 去产生
+
+        br
+        span(v-show='enurl') 可以把这个网址分享给你的朋友: 
+        br
+        span(v-show='enurl' id='enurl') {{ enurl }}
+
+        hr
+        br
+
+        p Naiveboom - 让Naive的事情不再发生
+        | 这是一个简单的项目,可以一定程度保证你的安全。 <br>
+        | 举例:你有个链接或者一段文字需要发送给某人,但是是透过QQ、WeChat这类含
+        <span id='zhao' v-on:mouseenter='showtg=true'>赵☭</span>
+        | 量极高的服务去发送,这样会非常不安全,甚至会被查水表。<br>
+        | 现在可以透过这样的一个方案得以解决<br>
+        | 你将私密的链接或者文字粘贴到我们这个页面<br>
+        | 产生一个一次性链接<br>
+        | 阅后即焚<br>
+        | 嗯,这回你不会被查水表了(确信<br>
+
+        img(src='/images/zhao.png' id='tg', 'v-bind:class'='tgstyle')
+        script(src='/javascripts/a.js')

+ 18 - 0
views/layout.jade

@@ -0,0 +1,18 @@
+doctype html
+html
+  head
+    title= title
+    link(rel='stylesheet', href='/stylesheets/style.css')
+    meta(content='width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0' name='viewport')
+  body
+    h5= title
+    noscript
+      h3#warning !!!警告: 您禁用了JavaScript,请开启它。<br>如果您禁止使用JavaScript,将无法使用我们的服务!
+    script(src='/javascripts/vue.min.js')
+    script(src='/javascripts/axios.min.js')
+    block content
+    hr
+    footer
+         a(href='/') 首页
+         span &nbsp;&nbsp;
+         a(href='https://github.com/kchown/naiveboom' target='_blank') GitHub

+ 18 - 0
views/look.jade

@@ -0,0 +1,18 @@
+extends layout
+block content
+	hr
+	#app
+		<div v-if='status==1'>
+		pre {{ text }}
+		</div>
+		<div v-else-if='status==0'>
+		h3 啊喔😯,你查看的内容已经不见了...
+		p 这是因为这个网址已经被看过一次了
+		| 被看过一次的内容就会自动消失
+		| 对,这就是人们常说的 <b>阅后即焚!</b>
+		a(href="/") 我也要生成一个!
+		</div>
+		<div v-else>
+		p#warning 正在获取...
+		</div>
+	script(src='/javascripts/view.js')

Неке датотеке нису приказане због велике количине промена