添加BARK支持

This commit is contained in:
xpnas 2021-04-03 23:26:09 +08:00
parent d4b1f9faf2
commit 939f64549c
24 changed files with 535 additions and 309 deletions

View File

@ -24,6 +24,7 @@
"normalize.css": "7.0.0", "normalize.css": "7.0.0",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"path-to-regexp": "2.4.0", "path-to-regexp": "2.4.0",
"qrcodejs": "^1.0.0",
"vue": "2.6.10", "vue": "2.6.10",
"vue-router": "3.0.6", "vue-router": "3.0.6",
"vuex": "3.1.0" "vuex": "3.1.0"

View File

@ -12,7 +12,7 @@
<el-input v-model="selectTemplate.name"></el-input> <el-input v-model="selectTemplate.name"></el-input>
</el-form-item> </el-form-item>
<el-form-item v-for="(item) in selectTemplate.values" :label="item.description" :key="item.name" required> <el-form-item v-for="(item) in selectTemplate.values" :label="item.description" :key="item.name" required>
<el-input v-model="item.value" :placeholder="item.default"></el-input> <el-input v-model="item.value" :placeholder="item.default" :readonly="item.readonly"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -71,6 +71,7 @@ import {
getSendTemplates, getSendTemplates,
addAuthInfo, addAuthInfo,
deepClone, deepClone,
getSendKey
} from '@/api/setting' } from '@/api/setting'
@ -102,7 +103,7 @@ export default {
isModify: false, isModify: false,
authform: {}, authform: {},
title: "设置", title: "设置",
sendKey: ""
} }
}, },
created() { created() {
@ -117,14 +118,23 @@ export default {
}) })
getSendTemplates().then((response) => { getSendTemplates().then((response) => {
if (response.code == 200) { if (response.code == 200) {
this.sendTemplates = response.data; this.sendTemplates = response.data;
} }
}) })
getSendKey().then(response => {
if (response.code == 200) {
this.sendKey = response.data;
}
})
}, },
selectTemplateChange(selectTemplate) { selectTemplateChange(selectTemplate) {
this.selectTemplate = deepClone(selectTemplate)
this.selectTemplate = deepClone(selectTemplate); if (this.selectTemplate.warning) {
this.$message({
message: this.selectTemplate.warning,
type: 'warning'
})
}
}, },
submitForm(formName) { submitForm(formName) {
this.$refs[formName].validate((valid) => { this.$refs[formName].validate((valid) => {
@ -181,8 +191,23 @@ export default {
this.title = '修改设置' this.title = '修改设置'
this.isModify = true; this.isModify = true;
this.selectTemplate = deepClone(row); this.selectTemplate = deepClone(row);
this.dialogVisible = true; if (this.selectTemplate.type == "Bark") {
let wPath = window.document.location.href;
let pathName = this.$route.path;
let pos = wPath.indexOf(pathName);
let localhostPath = wPath.substring(0, pos);
localhostPath = localhostPath.replace("#", "");
var devieItem = this.selectTemplate.values.find(item => {
return item.name == "DeviceKey"
})
var sendUrlItem = this.selectTemplate.values.find(item => {
return item.name == "SendUrl"
});
sendUrlItem.value = localhostPath + "?act=" + this.sendKey + "/" + devieItem.value + "/{title}/{data}"
}
this.dialogVisible = true;
}, },
deleteAuth(index, row) { deleteAuth(index, row) {
deleteAuthInfo(row.sendAuthId).then((response) => { deleteAuthInfo(row.sendAuthId).then((response) => {
@ -213,24 +238,25 @@ export default {
} }
} }
</script> </script>
<style lang="scss">
<style lang="scss">
@media screen and (min-width:800px) { @media screen and (min-width:800px) {
.el-dialog__wrapper .el-dialog { .el-dialog__wrapper .el-dialog {
width: 800px !important; width: 800px !important;
} }
.el-dialog__wrapper .el-dialog .el-dialog__body { .el-dialog__wrapper .el-dialog .el-dialog__body {
overflow: auto overflow: auto
} }
} }
@media screen and (max-width: 800px) { @media screen and (max-width: 800px) {
.el-dialog__wrapper .el-dialog { .el-dialog__wrapper .el-dialog {
width: 99% !important; width: 99% !important;
} }
.el-dialog__wrapper .el-dialog .el-dialog__body { .el-dialog__wrapper .el-dialog .el-dialog__body {
overflow: auto overflow: auto
} }
} }
</style> </style>

View File

@ -26,13 +26,23 @@
<el-form-item label="快捷地址(标题)"> <el-form-item label="快捷地址(标题)">
<el-input type="textarea" v-model="keyForm.sendUrlTitle" :readonly="true"></el-input> <el-input type="textarea" v-model="keyForm.sendUrlTitle" :readonly="true"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="快捷地址(完整)"> <el-form-item label="快捷地址(完整)">
<el-input type="textarea" v-model="keyForm.sendUrl" :readonly="true"></el-input> <el-input type="textarea" v-model="keyForm.sendUrl" :readonly="true"></el-input>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="onReSendKey('resetform')">重置SendKey</el-button> <el-button type="primary" @click="onReSendKey('resetform')">重置SendKey</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-divider content-position="left">BARK授权</el-divider>
<el-form ref="resetform" :model="keyForm" label-width="20%">
<el-form-item label="绑定地址">
<el-input v-model="keyForm.barkUrl" :readonly="true"></el-input>
</el-form-item>
<el-form-item>
<div id="qrcode"> </div>
</el-form-item>
</el-form>
</el-card> </el-card>
</div> </div>
</template> </template>
@ -95,10 +105,15 @@ export default {
let pos = wPath.indexOf(pathName); let pos = wPath.indexOf(pathName);
let localhostPath = wPath.substring(0, pos); let localhostPath = wPath.substring(0, pos);
localhostPath = localhostPath.replace("#", ""); localhostPath = localhostPath.replace("#", "");
this.keyForm.sendUrlTitle = localhostPath + 'api/'+ this.keyForm.sendKey+'.send' + "/{title}" this.keyForm.sendUrlTitle = localhostPath + this.keyForm.sendKey + '.send' + "/{title}"
this.keyForm.sendUrl = localhostPath + 'api/'+ this.keyForm.sendKey+'.send' + "/{title}/{data}" this.keyForm.sendUrl = localhostPath + this.keyForm.sendKey + '.send' + "/{title}/{data}"
// this.keyForm.sendUrl = localhostPath + 'api/send?token=' + this.keyForm.sendKey + "&title={title}&data={data}" this.keyForm.barkUrl = wPath.substring(0, pos - 2) + '?act=' + this.keyForm.sendKey
this.listLoading = false; this.listLoading = false;
new QRCode(document.getElementById("qrcode"), {
text: this.keyForm.barkUrl,
width: 150,
height: 150,
});
}); });
}, },
onMessage(fromname) { onMessage(fromname) {

View File

@ -36,6 +36,17 @@
<el-switch active-color='#13ce66' v-model='settingForm.githubEnable' inactive-color='#ff4949'></el-switch> <el-switch active-color='#13ce66' v-model='settingForm.githubEnable' inactive-color='#ff4949'></el-switch>
</template> </template>
</el-form-item> </el-form-item>
<el-divider content-position="left">BARK授权</el-divider>
<el-form-item label="KeyID">
<el-input v-model="settingForm.barkKeyId" placeholder="KeyID">></el-input>
</el-form-item>
<el-form-item label="TempID">
<el-input v-model="settingForm.barkTeamId" placeholder="TempID"></el-input>
</el-form-item>
<el-form-item label="P8Key">
<el-input type="textarea" v-model="settingForm.barkPrivateKey" placeholder="P8Key"></el-input>
</el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="submitForm('settingForm')">确认修改</el-button> <el-button type="primary" @click="submitForm('settingForm')">确认修改</el-button>
</el-form-item> </el-form-item>

View File

@ -7,7 +7,7 @@ function resolve(dir) {
} }
const name = defaultSettings.title || 'Inotify' const name = defaultSettings.title || 'Inotify'
const port = process.env.port || process.env.npm_config_port || 9528 const port = process.env.port || process.env.npm_config_port || 9000
const axiosV = require('axios/package.json').version const axiosV = require('axios/package.json').version
const echartsV = require('echarts/package.json').version const echartsV = require('echarts/package.json').version
@ -43,7 +43,8 @@ const cdn = {
`https://cdn.bootcdn.net/ajax/libs/vue-router/${routerV}/vue-router.min.js`, `https://cdn.bootcdn.net/ajax/libs/vue-router/${routerV}/vue-router.min.js`,
`https://cdn.bootcdn.net/ajax/libs/element-ui/${elementV}/locale/zh-CN.js`, `https://cdn.bootcdn.net/ajax/libs/element-ui/${elementV}/locale/zh-CN.js`,
`https://cdn.bootcdn.net/ajax/libs/js-cookie/${cookieV}/js.cookie.min.js`, `https://cdn.bootcdn.net/ajax/libs/js-cookie/${cookieV}/js.cookie.min.js`,
`https://cdn.bootcdn.net/ajax/libs/nprogress/${nprogressV}/nprogress.min.js` `https://cdn.bootcdn.net/ajax/libs/nprogress/${nprogressV}/nprogress.min.js`,
`https://cdn.bootcdn.net/ajax/libs/qrcodejs/1.0.0/qrcode.js`
] ]
} }

View File

@ -17,3 +17,9 @@ dotnet_diagnostic.CS8602.severity = none
# CS8600: 将 null 文本或可能的 null 值转换为不可为 null 类型。 # CS8600: 将 null 文本或可能的 null 值转换为不可为 null 类型。
dotnet_diagnostic.CS8600.severity = none dotnet_diagnostic.CS8600.severity = none
# IDE0037: 使用推断的成员名称
dotnet_diagnostic.IDE0037.severity = none
# IDE0008: 使用显式类型
dotnet_diagnostic.IDE0008.severity = none

View File

@ -9,6 +9,8 @@ namespace Inotify.Common
{ {
public static class Extensions public static class Extensions
{ {
static int rep = 0;
/// <summary> /// <summary>
/// MD5加密字符串32位大写 /// MD5加密字符串32位大写
/// </summary> /// </summary>
@ -30,6 +32,7 @@ namespace Inotify.Common
public static string Base64Encode(this string source) public static string Base64Encode(this string source)
{ {
if (string.IsNullOrEmpty(source)) return "";
byte[] bytes = (Encoding.UTF8.GetBytes(source)); byte[] bytes = (Encoding.UTF8.GetBytes(source));
return Convert.ToBase64String(bytes); return Convert.ToBase64String(bytes);
@ -37,8 +40,32 @@ namespace Inotify.Common
public static string Base64Decode(this string source) public static string Base64Decode(this string source)
{ {
if (string.IsNullOrEmpty(source)) return "";
var bytes = Convert.FromBase64String(source); var bytes = Convert.FromBase64String(source);
return System.Text.Encoding.Default.GetString(bytes); return System.Text.Encoding.Default.GetString(bytes);
} }
public static string GenerateCheckCode(this int codeCount)
{
string str = string.Empty;
long num2 = DateTime.Now.Ticks + rep;
rep++;
Random random = new Random(((int)(((ulong)num2) & 0xffffffffL)) | ((int)(num2 >> rep)));
for (int i = 0; i < codeCount; i++)
{
char ch;
int num = random.Next();
if ((num % 2) == 0)
{
ch = (char)(0x30 + ((ushort)(num % 10)));
}
else
{
ch = (char)(0x41 + ((ushort)(num % 0x1a)));
}
str += ch.ToString();
}
return str;
}
} }
} }

View File

@ -1,4 +1,5 @@
using Inotify.Data; using Inotify.Common;
using Inotify.Data;
using Inotify.Data.Models; using Inotify.Data.Models;
using Inotify.Sends; using Inotify.Sends;
using Inotify.Sends.Products; using Inotify.Sends.Products;
@ -13,106 +14,115 @@ using System.Threading.Tasks;
namespace Inotify.Controllers namespace Inotify.Controllers
{ {
public class DeviceInfo
{
public string? Token { get; set; }
public string? Key { get; set; }
public string? DeviceToken { get; set; }
public string? Device_key { get; set; }
public string? Device_token { get; set; }
}
[ApiController] [ApiController]
[Route("/")] [Route("/")]
public class BarkControlor : BaseControlor public class BarkControlor : BaseControlor
{ {
[HttpGet, Route("Ping")] [HttpGet, Route("Ping")]
public JsonResult Ping(string? token) public JsonResult Ping()
{ {
return OK(); return Me("pong");
} }
[HttpGet, Route("Info")] [HttpGet, Route("Info")]
public JsonResult Info(string? token) public JsonResult Info()
{ {
var dateTime = System.IO.File.GetLastWriteTime(this.GetType().Assembly.Location);
var devices = DBManager.Instance.DBase.Query<SendAuthInfo>().Count();
return Json(new return Json(new
{ {
version = "v2", version = "v2.0.1",
build = "2021.03.29", build = dateTime.ToString("yyyy-MM-dd HH:mm:ss"),
arch = RuntimeInformation.OSDescription, arch = RuntimeInformation.OSDescription,
commit = "inotfy", commit = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(),
devices = RuntimeInformation.OSDescription devices
}); });
} }
[HttpGet, Route("Healthz")] [HttpGet, Route("Healthz")]
public string Healthz(string? token) public string Healthz()
{ {
return "ok"; return "ok";
} }
[HttpGet, Route("Register")]
public JsonResult Register(string? act, string? key, string? devicetoken, string? device_key) => !string.IsNullOrEmpty(device_key) ?
Register(device_key) : Register(act, key, devicetoken);
[HttpPost, Route("Register")] [HttpPost, Route("Register")]
public JsonResult Register(DeviceInfo deviceInfo) public JsonResult Register(string? act, string? device_key, string? device_token)
{ {
if (!string.IsNullOrEmpty(deviceInfo.Key)) if (string.IsNullOrEmpty(act))
deviceInfo.Device_key = deviceInfo.Key; return Fail(400, "request bind failed : act is empty");
if (!string.IsNullOrEmpty(deviceInfo.DeviceToken)) if (string.IsNullOrEmpty(device_token))
deviceInfo.Device_token = deviceInfo.DeviceToken; return Fail(400, "request bind failed : device_token is empty");
if (string.IsNullOrEmpty(deviceInfo.Device_key)) var userInfo = DBManager.Instance.DBase.Query<SendUserInfo>().FirstOrDefault(e => e.Token == act);
return Fail(400, "request bind failed : device_key is empty");
if (string.IsNullOrEmpty(deviceInfo.Device_token))
return Fail(400, "request bind failed : device_token not empty");
var userInfo = DBManager.Instance.DBase.Query<SendUserInfo>().FirstOrDefault(e => e.Token == deviceInfo.Token);
if (userInfo == null) if (userInfo == null)
{ {
return Fail(400, "request bind failed : device not registered"); return Fail(400, "request bind failed : act is not registered");
} }
else else
{ {
BarkAuth barkAuth = null;
var barkAuth = new BarkAuth() { DeviceToken = deviceInfo.Device_token }; SendAuthInfo barkSendAuthInfo = null;
var barkTemplateAttribute = typeof(BarkSendTemplate).GetCustomAttributes(typeof(SendMethodKeyAttribute), false).OfType<SendMethodKeyAttribute>().First(); var barkTemplateAttribute = typeof(BarkSendTemplate).GetCustomAttributes(typeof(SendMethodKeyAttribute), false).OfType<SendMethodKeyAttribute>().First();
var barkSendAuthInfo = DBManager.Instance.DBase.Query<SendAuthInfo>().FirstOrDefault(e => e.UserId == userInfo.Id && e.SendMethodTemplate == barkTemplateAttribute.Key);
if (!string.IsNullOrEmpty(device_key))
{
barkSendAuthInfo = DBManager.Instance.DBase.Query<SendAuthInfo>().FirstOrDefault(e => e.Key == device_key);
if (barkSendAuthInfo != null)
{
barkAuth = JsonConvert.DeserializeObject<BarkAuth>(barkSendAuthInfo.AuthData);
barkAuth.DeviceToken = device_token;
barkSendAuthInfo.AuthData = JsonConvert.SerializeObject(barkAuth);
barkSendAuthInfo.ModifyTime = DateTime.Now;
DBManager.Instance.DBase.Update(barkSendAuthInfo);
}
}
if (barkSendAuthInfo == null) if (barkSendAuthInfo == null)
{ {
device_key = 16.GenerateCheckCode();
barkAuth = new BarkAuth() { DeviceKey = device_key, DeviceToken = device_token, IsArchive = "1", AutoMaticallyCopy = "1", Sound = "1107" };
barkSendAuthInfo = new SendAuthInfo() barkSendAuthInfo = new SendAuthInfo()
{ {
Name = barkTemplateAttribute.Name, Name = barkTemplateAttribute.Name,
SendMethodTemplate = barkTemplateAttribute.Key, SendMethodTemplate = barkTemplateAttribute.Key,
Key = device_key,
AuthData = JsonConvert.SerializeObject(barkAuth), AuthData = JsonConvert.SerializeObject(barkAuth),
UserId = userInfo.Id, UserId = userInfo.Id,
CreateTime = DateTime.Now, CreateTime = DateTime.Now,
ModifyTime = DateTime.Now ModifyTime = DateTime.Now,
Active = true,
}; };
DBManager.Instance.DBase.Insert(barkSendAuthInfo);
var sendAuthId = Convert.ToInt32(DBManager.Instance.DBase.Insert<SendAuthInfo>(barkSendAuthInfo));
userInfo.SendAuthId = sendAuthId;
DBManager.Instance.DBase.Update(userInfo, e => e.SendAuthId);
}
else
{
barkSendAuthInfo.AuthData = JsonConvert.SerializeObject(barkAuth);
barkSendAuthInfo.ModifyTime = DateTime.Now;
DBManager.Instance.DBase.Update(barkSendAuthInfo);
} }
return Json(new return Json(new
{ {
key = deviceInfo.Device_key, key = device_key,
device_key = deviceInfo.Device_key, device_key = device_key,
device_token = deviceInfo.Device_token device_token = device_token
}); });
} }
} }
[HttpGet, Route("RegisterCheck")]
public JsonResult Register(string device_key)
{
if (string.IsNullOrEmpty(device_key))
{
return Fail(400, "device key is empty");
}
if (!DBManager.Instance.DBase.Query<SendAuthInfo>().Any(e => e.Key == device_key))
{
return Fail(400, "device not registered");
}
return OK();
}
} }
} }

View File

@ -70,7 +70,7 @@ namespace Inotify.Controllers
{ {
code = 200, code = 200,
message = "sucess", message = "sucess",
timestamp = (DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds timestamp = (int)(DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds
}); });
} }
@ -81,7 +81,17 @@ namespace Inotify.Controllers
code = 200, code = 200,
message = "sucess", message = "sucess",
data = obj ?? "", data = obj ?? "",
timestamp = (DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds timestamp = (int)(DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds
});
}
protected JsonResult Me(string message)
{
return Json(new
{
code = 200,
message = message,
timestamp = (int)(DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds
}); });
} }
@ -91,7 +101,7 @@ namespace Inotify.Controllers
{ {
code = 404, code = 404,
message = "failed", message = "failed",
timestamp = (DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds timestamp = (int)(DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds
}); });
} }
@ -99,9 +109,9 @@ namespace Inotify.Controllers
{ {
return Json(new return Json(new
{ {
code, code= code,
message = "failed", message = "failed",
timestamp = (DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds timestamp = (int)(DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds
}); });
} }
@ -109,9 +119,9 @@ namespace Inotify.Controllers
{ {
return new JsonResult(new return new JsonResult(new
{ {
code, code=code,
message, message=message,
timestamp = (DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds timestamp = (int)(DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds
}); });
} }

View File

@ -12,20 +12,39 @@ namespace Inotify.Controllers
public class SendController : BaseControlor public class SendController : BaseControlor
{ {
[HttpGet, Route("send")] [HttpGet, Route("send")]
public JsonResult Send(string token, string title, string? data) public JsonResult Send(string token, string title, string? data, string? key)
{ {
if (DBManager.Instance.IsSendKey(token)) if (DBManager.Instance.IsToken(token, out bool hasActive))
{ {
if (!hasActive) return Fail(400, "you have no tunnel is acitve");
if (!string.IsNullOrEmpty(key))
{
if (DBManager.Instance.IsSendKey(key, out bool isActive))
{
if (!isActive)
{
return Fail(400, $"device:{key} tunnel is not acitve");
}
}
else
{
return Fail(400, $"device:{key} is not registered");
}
}
var message = new SendMessage() var message = new SendMessage()
{ {
Token = token, Token = token,
Title = title, Title = title,
Data = data, Data = data,
Key = key,
}; };
if (SendTaskManager.Instance.SendMessage(message))
return OK(); if (SendTaskManager.Instance.SendMessage(message)) return OK();
} }
return Fail();
return Fail(400, $"token:{token} is not registered");
} }
} }
} }

View File

@ -39,6 +39,7 @@ namespace Inotify.Controllers
var userInfo = DBManager.Instance.GetUser(UserName); var userInfo = DBManager.Instance.GetUser(UserName);
if (userInfo != null) if (userInfo != null)
{ {
var barkTemplateAttribute = typeof(BarkSendTemplate).GetCustomAttributes(typeof(SendMethodKeyAttribute), false).OfType<SendMethodKeyAttribute>().First();
var sendAuthInfos = DBManager.Instance.DBase.Query<SendAuthInfo>().Where(e => e.UserId == userInfo.Id).ToArray(); var sendAuthInfos = DBManager.Instance.DBase.Query<SendAuthInfo>().Where(e => e.UserId == userInfo.Id).ToArray();
var userSendTemplates = new List<InputTemeplate>(); var userSendTemplates = new List<InputTemeplate>();
foreach (var sendAuthInfo in sendAuthInfos) foreach (var sendAuthInfo in sendAuthInfos)
@ -53,6 +54,10 @@ namespace Inotify.Controllers
sendTemplate.AuthToTemplate(sendAuthInfo.AuthData); sendTemplate.AuthToTemplate(sendAuthInfo.AuthData);
userSendTemplates.Add(sendTemplate); userSendTemplates.Add(sendTemplate);
} }
if (barkTemplateAttribute.Key == sendTemplate.Key)
{
sendTemplate.Values.FirstOrDefault(e => e.Name == nameof(BarkSendTemplate.Auth.SendUrl)).Value = "";
}
} }
return OK(userSendTemplates); return OK(userSendTemplates);
@ -100,10 +105,9 @@ namespace Inotify.Controllers
if (userInfo != null && inputTemeplate.Key != null && inputTemeplate.Name != null) if (userInfo != null && inputTemeplate.Key != null && inputTemeplate.Name != null)
{ {
var barkKey = typeof(BarkSendTemplate).GetCustomAttributes(typeof(SendMethodKeyAttribute), false).OfType<SendMethodKeyAttribute>().First().Key; var barkKey = typeof(BarkSendTemplate).GetCustomAttributes(typeof(SendMethodKeyAttribute), false).OfType<SendMethodKeyAttribute>().First().Key;
if (barkKey == inputTemeplate.Key if (barkKey == inputTemeplate.Key)
&& DBManager.Instance.DBase.Query<SendAuthInfo>().FirstOrDefault(e => e.UserId == userInfo.Id && e.SendMethodTemplate == barkKey) != null)
{ {
return Fail(406, "您只能添加一个BARK通道"); return Fail(406, "BARK通道勿手动添加请使用APP添加BARK地址绑定");
} }
else else
{ {
@ -131,6 +135,7 @@ namespace Inotify.Controllers
var userInfo = DBManager.Instance.GetUser(UserName); var userInfo = DBManager.Instance.GetUser(UserName);
if (userInfo != null) if (userInfo != null)
{ {
var barkTemplateAttribute = typeof(BarkSendTemplate).GetCustomAttributes(typeof(SendMethodKeyAttribute), false).OfType<SendMethodKeyAttribute>().First();
var oldSendInfo = DBManager.Instance.DBase.Query<SendAuthInfo>().FirstOrDefault(e => e.Id == inputTemeplate.SendAuthId); var oldSendInfo = DBManager.Instance.DBase.Query<SendAuthInfo>().FirstOrDefault(e => e.Id == inputTemeplate.SendAuthId);
if (oldSendInfo != null && inputTemeplate.Name != null) if (oldSendInfo != null && inputTemeplate.Name != null)
{ {

View File

@ -30,6 +30,9 @@ namespace Inotify.Controllers
githubClientID = SendCacheStore.GetSystemValue("githubClientID"), githubClientID = SendCacheStore.GetSystemValue("githubClientID"),
githubClientSecret = SendCacheStore.GetSystemValue("githubClientSecret"), githubClientSecret = SendCacheStore.GetSystemValue("githubClientSecret"),
githubEnable = githubEnable != "" && bool.Parse(githubEnable), githubEnable = githubEnable != "" && bool.Parse(githubEnable),
barkKeyId= SendCacheStore.GetSystemValue("barkKeyId"),
barkTeamId = SendCacheStore.GetSystemValue("barkTeamId"),
barkPrivateKey = SendCacheStore.GetSystemValue("barkPrivateKey"),
}); });
} }
@ -41,7 +44,10 @@ namespace Inotify.Controllers
string? proxyenable, string? proxyenable,
string? githubClientID, string? githubClientID,
string? githubClientSecret, string? githubClientSecret,
string? githubEnable) string? githubEnable,
string? barkKeyId,
string? barkTeamId,
string? barkPrivateKey)
{ {
SendCacheStore.SetSystemValue("sendthread", sendthread); SendCacheStore.SetSystemValue("sendthread", sendthread);
SendCacheStore.SetSystemValue("administrators", administrators); SendCacheStore.SetSystemValue("administrators", administrators);
@ -50,6 +56,9 @@ namespace Inotify.Controllers
SendCacheStore.SetSystemValue("githubClientID", githubClientID); SendCacheStore.SetSystemValue("githubClientID", githubClientID);
SendCacheStore.SetSystemValue("githubClientSecret", githubClientSecret); SendCacheStore.SetSystemValue("githubClientSecret", githubClientSecret);
SendCacheStore.SetSystemValue("githubEnable", githubEnable); SendCacheStore.SetSystemValue("githubEnable", githubEnable);
SendCacheStore.SetSystemValue("barkKeyId", barkKeyId);
SendCacheStore.SetSystemValue("barkTeamId", barkTeamId);
SendCacheStore.SetSystemValue("barkPrivateKey", barkPrivateKey);
SendTaskManager.Instance.Stop(); SendTaskManager.Instance.Stop();
SendTaskManager.Instance.Run(); SendTaskManager.Instance.Run();

View File

@ -105,16 +105,38 @@ namespace Inotify.Data
m_dbConnection = new SqliteConnection(string.Format("Data Source={0}", m_dbPath)); m_dbConnection = new SqliteConnection(string.Format("Data Source={0}", m_dbPath));
if (m_dbConnection.State == ConnectionState.Closed) if (m_dbConnection.State == ConnectionState.Closed)
m_dbConnection.Open(); m_dbConnection.Open();
DBase = new NPoco.Database(m_dbConnection, DatabaseType.SQLite); DBase = new Database(m_dbConnection, DatabaseType.SQLite);
DBase.KeepConnectionAlive = true;
m_migrator = new Migrator(DBase); m_migrator = new Migrator(DBase);
} }
public bool IsSendKey(string token) public bool IsToken(string token,out bool hasActive)
{ {
return DBase.Query<SendUserInfo>().Any(e => e.Token == token); hasActive = false;
var userInfo= DBase.Query<SendUserInfo>().FirstOrDefault(e => e.Token == token);
if (userInfo != null)
{
hasActive= DBase.Query<SendAuthInfo>().Any(e => e.UserId== userInfo.Id && e.Active);
return true;
}
return false;
}
public bool IsSendKey(string key, out bool isActive)
{
isActive = false;
var sendAuthInfo = DBase.Query<SendAuthInfo>().FirstOrDefault(e => e.Key == key);
if (sendAuthInfo != null)
{
isActive = sendAuthInfo.Active;
return true;
}
return false;
} }
public bool IsUser(string userName) public bool IsUser(string userName)
@ -124,10 +146,10 @@ namespace Inotify.Data
public SendUserInfo GetUser(string userName) public SendUserInfo GetUser(string userName)
{ {
return DBase.Query<SendUserInfo>().First(e => e.UserName == userName); return DBase.Query<SendUserInfo>().FirstOrDefault(e => e.UserName == userName);
} }
public string GetAuth(string token, out string guid) public string GetSendAuthInfo(string token, out string guid)
{ {
guid = string.Empty; guid = string.Empty;
var upToekn = token.ToUpper(); var upToekn = token.ToUpper();
@ -142,18 +164,25 @@ namespace Inotify.Data
return authInfo.AuthData; return authInfo.AuthData;
} }
public void GetAuth(string token, out SendAuthInfo[] sendAuthInfos) public void GetSendAuthInfos(string token, string key, out SendAuthInfo[] sendAuthInfos)
{ {
sendAuthInfos = null; sendAuthInfos = null;
var upToekn = token.ToUpper(); var upToekn = token.ToUpper();
var userInfo = DBManager.Instance.DBase.Query<SendUserInfo>().FirstOrDefault(e => e.Token == upToekn && e.Active); var userInfo = DBManager.Instance.DBase.Query<SendUserInfo>().FirstOrDefault(e => e.Token == upToekn && e.Active);
if (userInfo != null) if (userInfo != null)
{
sendAuthInfos = DBManager.Instance.DBase.Query<SendAuthInfo>().Where(e => e.UserId == userInfo.Id && e.Active).ToArray(); if (string.IsNullOrEmpty(key))
{
sendAuthInfos = DBManager.Instance.DBase.Query<SendAuthInfo>().Where(e => e.UserId == userInfo.Id && e.Active).ToArray();
}
else
{
sendAuthInfos = DBManager.Instance.DBase.Query<SendAuthInfo>().Where(e => e.UserId == userInfo.Id && e.Active &&e.Key==key).ToArray();
}
}
} }
public void Run() public void Run()
{ {
@ -175,10 +204,9 @@ namespace Inotify.Data
var builder = new MigrationBuilder(MigrationName, DBase); var builder = new MigrationBuilder(MigrationName, DBase);
builder.Append(new Version(2, 0, 0, 0), new V2UpdateMigration()); builder.Append(new Version(2, 0, 0, 0), new V2UpdateMigration());
builder.Append(new Version(2, 0, 0, 1), new V2001UpdateMigration());
builder.Execute(); builder.Execute();
} }
} }
} }
} }

View File

@ -59,8 +59,7 @@ namespace Inotify.Data
{ {
protected override void execute() protected override void execute()
{ {
//V2版本允许多通道,激活标记放入SendAuthInfo表中增加Active列 //V2版本允许多通道,激活标记放入SendAuthInfo表中增加Active列同时更新原有用户的激活通道
//更新原有用户的激活通道
Migrator.AlterTable<SendAuthInfo>().AddColumn(e => e.Active).Execute(); Migrator.AlterTable<SendAuthInfo>().AddColumn(e => e.Active).Execute();
Migrator.Database.UpdateMany<SendAuthInfo>().OnlyFields(e => e.Active).Execute(new SendAuthInfo() { Active = false }); Migrator.Database.UpdateMany<SendAuthInfo>().OnlyFields(e => e.Active).Execute(new SendAuthInfo() { Active = false });
var activeUsers = Migrator.Database.Query<SendUserInfo>().ToList(); var activeUsers = Migrator.Database.Query<SendUserInfo>().ToList();
@ -75,4 +74,38 @@ namespace Inotify.Data
}); });
} }
} }
public class V2001UpdateMigration : Migration, IMigration
{
protected override void execute()
{
//V2001版本增加SendInfo的key字段
Migrator.AlterTable<SendAuthInfo>().AddColumn(e => e.Key).Execute();
//对AuthInfo的AuthDate字段进行加密
var sendAuthInfos = Migrator.Database.Query<SendAuthInfo>().ToList();
sendAuthInfos.ForEach(sendAuthInfo =>
{
sendAuthInfo.AuthData = sendAuthInfo.AuthDataSave;
Migrator.Database.Update(sendAuthInfo);
});
//添加bark密钥相关内容
Migrator.Database.Insert(new SystemInfo()
{
key = "barkKeyId",
Value = "TEg0VDlWNVU0Ug==".Base64Decode(),
});
Migrator.Database.Insert(new SystemInfo()
{
key = "barkTeamId",
Value = "NVU4TEJSWEczQQ==".Base64Decode(),
});
Migrator.Database.Insert(new SystemInfo()
{
key = "barkPrivateKey",
Value = "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR1RBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJIa3dkd0lCQVFRZzR2dEMzZzVMNUhnS0dKMitUMWVBMHRPaXZSRXZFQVkyZytqdVJYSmtZTDJnQ2dZSUtvWkl6ajBEQVFlaFJBTkNBQVNtT3MzSmtTeW9HRVdac1VHeEZzLzRwdzFySWxTVjJJQzE5TTh1M0c1a3EzNnVwT3d5RldqOUdpM0VqYzlkM3NDNytTSFJxWHJFQUpvdzgvN3RScFYrCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=".Base64Decode()
});
}
}
} }

View File

@ -1,5 +1,5 @@
using System; using System;
using Inotify.Common;
namespace Inotify.Data.Models namespace Inotify.Data.Models
{ {
[NPoco.TableName("sendAuthInfo")] [NPoco.TableName("sendAuthInfo")]
@ -19,7 +19,20 @@ namespace Inotify.Data.Models
public string SendMethodTemplate { get; set; } public string SendMethodTemplate { get; set; }
[NPoco.Column("authData")] [NPoco.Column("authData")]
public string AuthData { get; set; } public string AuthDataSave { get; set; }
[NPoco.Ignore]
public string AuthData
{
get
{
return AuthDataSave.Base64Decode();
}
set
{
AuthDataSave = value.Base64Encode();
}
}
[NPoco.Column("modifyTime")] [NPoco.Column("modifyTime")]
public DateTime ModifyTime { get; set; } public DateTime ModifyTime { get; set; }
@ -29,5 +42,9 @@ namespace Inotify.Data.Models
[NPoco.Column("active")] [NPoco.Column("active")]
public bool Active { get; set; } public bool Active { get; set; }
[NPoco.Column("key")]
public string Key { get; set; }
} }
} }

View File

@ -6,8 +6,8 @@
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<StartupObject>Inotify.Program</StartupObject> <StartupObject>Inotify.Program</StartupObject>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AssemblyVersion>2.0.0.0</AssemblyVersion> <AssemblyVersion>2.0.0.1</AssemblyVersion>
<FileVersion>2.0.0.0</FileVersion> <FileVersion>2.0.0.1</FileVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@ -31,7 +31,7 @@
<ItemGroup> <ItemGroup>
<None Update="inotify_data\AuthKey_LH4T9V5U4R_5U8LBRXG3A.p8"> <None Update="inotify_data\AuthKey_LH4T9V5U4R_5U8LBRXG3A.p8">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>

View File

@ -1,9 +1,11 @@
using Inotify.Common; using Inotify.Common;
using Inotify.Data; using Inotify.Data;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
@ -16,166 +18,78 @@ using System.Threading.Tasks;
namespace Inotify.Sends.Products namespace Inotify.Sends.Products
{ {
public class BarkMessage
{
public BarkMessage(string body) : this(string.Empty, body) { }
public BarkMessage(string title, string body)
{
this.Title = title;
this.Body = body;
}
#region
/// <summary>
/// 标题,加粗
/// </summary>
public string Title { get; set; } = string.Empty;
/// <summary>
/// 正文
/// </summary>
public string Body { get; set; } = string.Empty;
/// <summary>
/// 自动保存
/// </summary>
public string IsArchive { get; set; } = "1";
/// <summary>
/// 链接
/// </summary>
public string Url { get; set; } = string.Empty;
/// <summary>
/// 自动复制
/// </summary>
public string AutoMaticallyCopy { get; set; } = "0";
/// <summary>
/// 复制文本
/// </summary>
public string Copy { get; set; } = string.Empty;
#endregion
#region
/// <summary>
/// 设置链接
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public BarkMessage SetUrl(string url)
{
this.Url = url;
return this;
}
/// <summary>
/// 设置保存,默认保存
/// </summary>
/// <returns></returns>
public BarkMessage SetArchive()
{
IsArchive = "1";
return this;
}
/// <summary>
/// 设置不保存,默认保存
/// </summary>
/// <returns></returns>
public BarkMessage SetNotArchive()
{
IsArchive = "0";
return this;
}
/// <summary>
/// 设置自动复制,默认不自动复制
/// </summary>
/// <returns></returns>
public BarkMessage SetAutoCopy()
{
this.AutoMaticallyCopy = "1";
return this;
}
/// <summary>
/// 设置不自动复制,默认不自动复制
/// </summary>
/// <returns></returns>
public BarkMessage SetNotAutoCopy()
{
this.AutoMaticallyCopy = "1";
return this;
}
/// <summary>
/// 设置自动拷贝的文本,默认拷贝全文
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public BarkMessage SetCopyText(string text)
{
Copy = text;
return this;
}
#endregion
}
public class BarkAuth public class BarkAuth
{ {
[InputTypeAttribte(1, "Sound", "声音", "1107")]
public string Sound { get; set; }
[InputTypeAttribte(0, "IsArchive", "自动保存", "1")] [InputTypeAttribte(2, "IsArchive", "自动保存", "1或0")]
public string IsArchive { get; set; } public string IsArchive { get; set; }
[InputTypeAttribte(0, "AutoMaticallyCopy", "自动复制", "0")] [InputTypeAttribte(3, "AutoMaticallyCopy", "自动复制", "1或0")]
public string AutoMaticallyCopy { get; set; } public string AutoMaticallyCopy { get; set; }
[InputTypeAttribte(0, "DeviceToken", "DeviceToken", "DeviceToken",false)] [InputTypeAttribte(4, "DeviceKey", "DeviceKey", "DeviceKey", true, true)]
public string DeviceKey { get; set; }
[InputTypeAttribte(5, "DeviceToken", "DeviceToken", "DeviceToken", true, true)]
public string DeviceToken { get; set; } public string DeviceToken { get; set; }
[InputTypeAttribte(6, "SendUrl", "SendUrl", "SendUrl", true, true)]
public string SendUrl { get; set; }
} }
[SendMethodKey("3B6DE04D-A9EF-4C91-A151-60B7425C5AB2", "Bark(未完成)", Order = -1)] [SendMethodKey("3B6DE04D-A9EF-4C91-A151-60B7425C5AB2", "Bark", Order = 6, Waring = "BARK通道勿手动添加请使用APP添加BARK地址绑定")]
public class BarkSendTemplate : SendTemplate<BarkAuth> public class BarkSendTemplate : SendTemplate<BarkAuth>
{ {
private static readonly string Topic = "me.fin.bark"; private static string KeyID;
private static readonly string KeyID = "LH4T9V5U4R";
private static readonly string TeamID = "5U8LBRXG3A"; private static string TeamID;
private static CngKey SecretKey; private static CngKey SecretKey;
public override BarkAuth Auth { get; set; } public override BarkAuth Auth { get; set; }
public override bool SendMessage(SendMessage message) public override bool SendMessage(SendMessage message)
{
var barkMessage = new BarkMessage(message.Title, message.Data)
{
IsArchive = Auth.IsArchive,
AutoMaticallyCopy = Auth.AutoMaticallyCopy
};
SendMesssage(barkMessage, Auth.DeviceToken);
return false;
}
private bool SendMesssage(BarkMessage barkMessage, string device_Tokne)
{ {
if (SecretKey == null) if (SecretKey == null)
{ {
var authPath = Path.Combine(DBManager.Instance.Inotify_Data, "AuthKey_LH4T9V5U4R_5U8LBRXG3A.p8"); KeyID = SendCacheStore.GetSystemValue("barkKeyId");
var privateKeyContent = File.ReadAllText(authPath).Split('\n')[1]; TeamID = SendCacheStore.GetSystemValue("barkTeamId");
SecretKey = CngKey.Import(Convert.FromBase64String(privateKeyContent), CngKeyBlobFormat.Pkcs8PrivateBlob); var privateKey = SendCacheStore.GetSystemValue("barkPrivateKey");
var privateKeyContent = privateKey.Split('\n')[1];
var decodeKey = Convert.FromBase64String(privateKeyContent);
SecretKey = CngKey.Import(decodeKey, CngKeyBlobFormat.Pkcs8PrivateBlob);
} }
if (barkMessage == null) if (Auth.DeviceToken == null)
return false; return false;
if (device_Tokne == null) var payload = CreatePayload(message);
return false; var accessToken = CreateAccessToken(payload);
var result = CreatePush(payload, accessToken, Auth.DeviceToken);
return result;
}
private string CreatePayload(SendMessage message)
{
var expiration = DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); var expiration = DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var expirationSeconds = (long)expiration.TotalSeconds; var expirationSeconds = (long)expiration.TotalSeconds;
var alert = new Dictionary<string, object>(); var alert = new Dictionary<string, object>();
if (!string.IsNullOrEmpty(barkMessage.Body)) if (!string.IsNullOrEmpty(message.Data))
alert.Add("body", barkMessage.Body); alert.Add("body", message.Data);
if (!string.IsNullOrEmpty(barkMessage.Title)) if (!string.IsNullOrEmpty(message.Title))
alert.Add("title", barkMessage.Title); alert.Add("title", message.Title);
var aps = new Dictionary<string, object> var aps = new Dictionary<string, object>
{ {
{ "category", "Bark" }, { "category", "Bark" },
{ "sound", "1107" }, { "sound", Auth.Sound },
{ "badge", "0" }, { "badge", "0" },
{ "mutable-content", "1" }, { "mutable-content", "1" },
{ "alert", alert } { "alert", alert }
@ -184,65 +98,88 @@ namespace Inotify.Sends.Products
var payload = new Dictionary<string, object> var payload = new Dictionary<string, object>
{ {
{ "aps", aps }, { "aps", aps },
{ "isarchive", barkMessage.IsArchive }, { "isarchive", Auth.IsArchive },
{ "automaticallycopy", barkMessage.AutoMaticallyCopy }, { "automaticallycopy", Auth.AutoMaticallyCopy },
{ "iss", TeamID}, { "iss", TeamID},
{ "iat", expirationSeconds} { "iat", expirationSeconds}
}; };
if (!string.IsNullOrEmpty(barkMessage.Url)) if (!string.IsNullOrEmpty(message.Title))
payload.Add("url", barkMessage.Url); payload.Add("copy", message.Title);
if (!string.IsNullOrEmpty(barkMessage.Copy))
payload.Add("copy", barkMessage.Copy);
var headers = new
{
alg = "ES256",
kid = KeyID
};
var hearderString = JObject.FromObject(headers).ToString();
var payloadString = JObject.FromObject(payload).ToString(); var payloadString = JObject.FromObject(payload).ToString();
var accessToken = SignES256(SecretKey, hearderString, payloadString);
var data = Encoding.UTF8.GetBytes(payloadString);
var httpClient = new HttpClient(); return payloadString;
var requestMessage = new HttpRequestMessage
{
RequestUri = new Uri(string.Format("https://{0}:{1}/3/device/{2}", "api.development.push.apple.com", 443, device_Tokne))
};
requestMessage.Headers.Add("authorization", string.Format("bearer {0}", accessToken));
requestMessage.Headers.Add("apns-id", Guid.NewGuid().ToString());
requestMessage.Headers.Add("apns-expiration", "0");
requestMessage.Headers.Add("apns-priority", "10");
requestMessage.Headers.Add("apns-topic", Topic);
requestMessage.Method = HttpMethod.Post;
requestMessage.Content = new ByteArrayContent(data);
var task = httpClient.SendAsync(requestMessage);
task.Wait();
var responseMessage = task.Result;
if (responseMessage.StatusCode == System.Net.HttpStatusCode.OK)
{
return true;
}
return true;
} }
private string CreateAccessToken(string payload)
private string SignES256(CngKey secretKey, string header, string payload)
{ {
using ECDsaCng dsa = new ECDsaCng(secretKey) using ECDsaCng dsa = new ECDsaCng(SecretKey)
{ {
HashAlgorithm = CngAlgorithm.Sha256 HashAlgorithm = CngAlgorithm.Sha256
}; };
var unsignedJwtData = Convert.ToBase64String(Encoding.UTF8.GetBytes(header)) + "." + Convert.ToBase64String(Encoding.UTF8.GetBytes(payload)); var headers = JObject.FromObject(new
{
alg = "ES256",
kid = KeyID
}).ToString();
var unsignedJwtData = Convert.ToBase64String(Encoding.UTF8.GetBytes(headers)) + "." + Convert.ToBase64String(Encoding.UTF8.GetBytes(payload));
var signature = dsa.SignData(Encoding.UTF8.GetBytes(unsignedJwtData)); var signature = dsa.SignData(Encoding.UTF8.GetBytes(unsignedJwtData));
return unsignedJwtData + "." + Convert.ToBase64String(signature); return unsignedJwtData + "." + Convert.ToBase64String(signature);
} }
private bool CreatePush(string payload, string accessToken, string device_Tokne)
{
try
{
var data = Encoding.UTF8.GetBytes(payload);
var request = new HttpRequestMessage
{
Version = new Version(2, 0),
RequestUri = new Uri($"https://api.development.push.apple.com:443/3/device/{device_Tokne}")
};
request.Headers.Add("authorization", string.Format("bearer {0}", accessToken));
request.Headers.Add("apns-id", Guid.NewGuid().ToString());
request.Headers.Add("apns-expiration", "0");
request.Headers.Add("apns-priority", "10");
request.Headers.Add("apns-topic", "me.fin.bark");
request.Method = HttpMethod.Post;
request.Content = new ByteArrayContent(data);
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Connection.Add("Keep-Alive");
var task = httpClient.SendAsync(request);
task.Wait();
var responseMessage = task.Result;
if (responseMessage.StatusCode == System.Net.HttpStatusCode.OK)
{
var _response_uuid = "";
if (responseMessage.Headers.TryGetValues("apns-id", out IEnumerable<string> values))
{
_response_uuid = values.First();
Console.WriteLine($"success: '{_response_uuid}'");
return true;
}
else
return false;
}
else
{
var respoinseBody = responseMessage.Content.ReadAsStringAsync().Result;
var responseJson = JObject.Parse(respoinseBody);
var reason = responseJson.Value<string>("reason");
Console.WriteLine($"failure: '{reason}'");
}
}
catch (Exception ex)
{
Console.WriteLine($"exception: '{ex.Message}'");
}
return false;
}
} }
} }

View File

@ -9,5 +9,6 @@ namespace Inotify.Sends
public string Token; public string Token;
public string Title; public string Title;
public string? Data; public string? Data;
public string? Key;
} }
} }

View File

@ -37,7 +37,6 @@ namespace Inotify.Sends
private readonly Dictionary<string, Type> m_sendMethodTemplateTypes; private readonly Dictionary<string, Type> m_sendMethodTemplateTypes;
private SendTaskManager() private SendTaskManager()
{ {
m_sendMessages = new BlockingCollection<SendMessage>(); m_sendMessages = new BlockingCollection<SendMessage>();
@ -70,7 +69,6 @@ namespace Inotify.Sends
m_analyseThread.Start(); m_analyseThread.Start();
} }
public EventHandler<SendMessage> OnMessageAdd; public EventHandler<SendMessage> OnMessageAdd;
public EventHandler<SendMessage> OnSendSucessed; public EventHandler<SendMessage> OnSendSucessed;
@ -152,7 +150,7 @@ namespace Inotify.Sends
try try
{ {
var message = m_sendMessages.Take(); var message = m_sendMessages.Take();
DBManager.Instance.GetAuth(message.Token, out SendAuthInfo[] sendAuthInfos); DBManager.Instance.GetSendAuthInfos(message.Token, message.Key, out SendAuthInfo[] sendAuthInfos);
foreach (var authInfo in sendAuthInfos) foreach (var authInfo in sendAuthInfos)
{ {
var authData = authInfo.AuthData; var authData = authInfo.AuthData;
@ -212,7 +210,7 @@ namespace Inotify.Sends
{ {
var message = m_analyseMessages.Take(); var message = m_analyseMessages.Take();
var date = DateTime.Now.ToString("yyyyMMdd"); var date = DateTime.Now.ToString("yyyyMMdd");
var authData = DBManager.Instance.GetAuth(message.Token, out string temeplateId); var authData = DBManager.Instance.GetSendAuthInfo(message.Token, out string temeplateId);
if (temeplateId != null) if (temeplateId != null)
{ {

View File

@ -31,16 +31,25 @@ namespace Inotify.Sends
public int Order; public int Order;
public SendMethodKeyAttribute(string key, string name, bool open = true) public string Waring;
public SendMethodKeyAttribute(string key, string name, bool open = true, string waring = "")
{ {
Key = key; Key = key;
Name = name; Name = name;
Open = open; Open = open;
Waring = waring;
} }
} }
public class InputTypeValue public class InputTypeValue
{ {
public InputTypeValue()
{
Show = true;
Readonly = false;
}
public string? Name { get; set; } public string? Name { get; set; }
public string? Description { get; set; } public string? Description { get; set; }
public string? Default { get; set; } public string? Default { get; set; }
@ -48,7 +57,11 @@ namespace Inotify.Sends
public int? Order { get; set; } public int? Order { get; set; }
public string? Value { get; set; } public string? Value { get; set; }
public bool Visuable { get; set; } public string? Warning { get; set; }
public bool Show { get; set; }
public bool Readonly { get; set; }
} }
public class InputTemeplate public class InputTemeplate
@ -57,6 +70,7 @@ namespace Inotify.Sends
public string? Type { get; set; } public string? Type { get; set; }
public string? Name { get; set; } public string? Name { get; set; }
public bool IsActive { get; set; } public bool IsActive { get; set; }
public string Warning { get; set; }
public string? AuthData { get; set; } public string? AuthData { get; set; }
public int? SendAuthId { get; set; } public int? SendAuthId { get; set; }
public List<InputTypeValue>? Values { get; set; } public List<InputTypeValue>? Values { get; set; }
@ -112,7 +126,7 @@ namespace Inotify.Sends
public InputTypeValue InputTypeData { get; set; } public InputTypeValue InputTypeData { get; set; }
private InputTypeAttribte(int order, string name, string description, string defaultValue, InputType type,bool visuable=true) private InputTypeAttribte(int order, string name, string description, string defaultValue, InputType type,bool show=true,bool readOnly=false)
{ {
InputTypeData = new InputTypeValue InputTypeData = new InputTypeValue
{ {
@ -121,18 +135,19 @@ namespace Inotify.Sends
Default = defaultValue, Default = defaultValue,
Order = order, Order = order,
Type = type, Type = type,
Visuable=visuable Show = show,
Readonly = readOnly
}; };
} }
public InputTypeAttribte(int order, string name, string description, string defaultValue, bool visuable = true) public InputTypeAttribte(int order, string name, string description, string defaultValue, bool show = true, bool readOnly = false)
: this(order, name, description, defaultValue, InputType.TEXT, visuable) : this(order, name, description, defaultValue, InputType.TEXT, show, readOnly)
{ {
} }
public InputTypeAttribte(int order, string name, string description, bool defaultValue, bool visuable = true) public InputTypeAttribte(int order, string name, string description, bool defaultValue, bool show = true, bool readOnly = false)
: this(order, name, description, "", InputType.CHECK, visuable) : this(order, name, description, "", InputType.CHECK, show, readOnly)
{ {
InputTypeData.Default = defaultValue ? "是" : "否"; InputTypeData.Default = defaultValue ? "是" : "否";
} }
@ -156,7 +171,6 @@ namespace Inotify.Sends
.SelectMany(e => e.GetCustomAttributes(typeof(InputTypeAttribte), false)) .SelectMany(e => e.GetCustomAttributes(typeof(InputTypeAttribte), false))
.Cast<InputTypeAttribte>() .Cast<InputTypeAttribte>()
.Select(e => e.InputTypeData) .Select(e => e.InputTypeData)
.Where(e=>e.Visuable)
.ToList(); .ToList();
var sendMethodKeyAttribute = this.GetType().GetCustomAttribute<SendMethodKeyAttribute>(); var sendMethodKeyAttribute = this.GetType().GetCustomAttribute<SendMethodKeyAttribute>();
@ -168,6 +182,7 @@ namespace Inotify.Sends
Name = sendMethodKeyAttribute.Name, Name = sendMethodKeyAttribute.Name,
Type = sendMethodKeyAttribute.Name, Type = sendMethodKeyAttribute.Name,
Key = sendMethodKeyAttribute.Key, Key = sendMethodKeyAttribute.Key,
Warning = sendMethodKeyAttribute.Waring,
Values = values Values = values
}; };
} }

View File

@ -20,6 +20,7 @@ using System.Net.Http;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.RegularExpressions;
using System.Text.Unicode; using System.Text.Unicode;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -96,26 +97,63 @@ namespace Inotify
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{ {
app.UseStaticFiles();
app.UseFileServer();
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
} }
var options = new RewriteOptions(); var options = new RewriteOptions();
options.AddRewrite(@"api/(.*).send/(.*)/(.*)", "api/send?token=$1&title=$2&data=$3", true); options.Add(rewriteContext =>
options.AddRewrite(@"api/(.*).send/(.*)", "api/send?token=$1&title=$2", true); {
options.AddRewrite(@"\?act=(.*)/{.*}/(.*)/(.*)", "api/send?token=$1&title=$3&data=$4", true); Match match;
options.AddRewrite(@"\?act=(.*)/{.*}/(.*)", "api/send?token=$1&title=$3", true); if (rewriteContext.HttpContext.Request.Path == "/")
{
var queryValue = rewriteContext.HttpContext.Request.QueryString.Value;
match = Regex.Match(queryValue, @"^\?act=(.*)/(.*)/(.*)/(.*)$");
var groups = match.Groups;
if (match.Success)
{
rewriteContext.HttpContext.Request.Path = @"/api/send";
rewriteContext.HttpContext.Request.QueryString = new QueryString($"?token={groups[1]}&key={groups[2]}&title={groups[3]}&date={groups[4]}");
}
else
{
match = Regex.Match(queryValue, @"^\?act=(.*)/(.*)/(.*)$");
if (match.Success)
{
rewriteContext.HttpContext.Request.Path = @"/api/send";
rewriteContext.HttpContext.Request.QueryString = new QueryString($"?token={groups[1]}&key={groups[2]}&title={groups[3]}");
}
else
{
match = Regex.Match(queryValue, @"^\?act=(.*)/(.*)$");
if (match.Success)
{
rewriteContext.HttpContext.Request.Path = @"/api/send";
rewriteContext.HttpContext.Request.QueryString = new QueryString($"?token={groups[1]}&key={groups[2]}");
}
else if(rewriteContext.HttpContext.Request.QueryString.Value.StartsWith("?"))
{
rewriteContext.HttpContext.Request.Path = @"/info";
rewriteContext.HttpContext.Request.QueryString = new QueryString();
}
}
}
}
rewriteContext.Result = RuleResult.ContinueRules;
});
//https://im.xpnas.com/?act=123456/ZtCLMPWQWtjJQpKmQS6hoV/ options.AddRewrite(@"^(.*).send/(.*)/(.*)", "api/send?token=$1&title=$2&data=$3", true);
options.AddRewrite(@"^(.*).send/(.*)", "api/send?token=$1&title=$2", true);
options.AddRewrite(@"^api/(.*).send/(.*)/(.*)", "api/send?token=$1&title=$2&data=$3", true);
options.AddRewrite(@"^api/(.*).send/(.*)", "api/send?token=$1&title=$2", true);
app.UseRewriter(options); app.UseRewriter(options);
app.UseRouting(); app.UseRouting();
app.UseStaticFiles();
app.UseFileServer();
app.UseAuthentication(); app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>

View File

@ -26,7 +26,7 @@ namespace Inotify.ThridOauth.Service
private readonly string _authorizeUrl; private readonly string _authorizeUrl;
public GitHubLogin(IHttpContextAccessor contextAccessor, IOptions<GitHubCredential> options) : base( public GitHubLogin(IHttpContextAccessor contextAccessor) : base(
contextAccessor) contextAccessor)
{ {
Credential = new CredentialSetting() Credential = new CredentialSetting()

View File

@ -22,30 +22,49 @@
- [x] 企业微信应用消息 - [x] 企业微信应用消息
- [x] 电报机器人消息 - [x] 电报机器人消息
- [x] SMTP邮箱消息 - [x] SMTP邮箱消息
- [x] BARK
- [ ] 钉钉群机器人 - [ ] 钉钉群机器人
- [ ] 飞书群机器人 - [ ] 飞书群机器人
- [x] 自定义 - [x] 自定义
## 使用方法 ## 更新日志
1. Docker安装
```
docker run --name=inotify -d -p 8000:80 -v inotify_data:/inotify_data --restart=always xpnas/inotify:latest
```
* V1.0
* 支持企业微信应用、电报、SMTP消息
* V2.0.0.1
* 支持自定义Get、POST
* V2.0.0.2
* 支持BARK
## 使用方法
1. Docker安装
* 稳定版V1.0
```
docker run --name=inotify -d -p 8000:80 -v inotify_data:/inotify_data --restart=always xpnas/inotify:latest
```
* 开发版V2.0.0.2
```
docker run --name=inotify -d -p 8000:80 -v inotify_data:/inotify_data --restart=always xpnas/inotify:master
```
2. 配置Nginx代理 2. 配置Nginx代理
``` ```
server server
{ {
location / { proxy_pass http://127.0.0.1:8000; } location / { proxy_pass http://127.0.0.1:8000; }
} }
``` ```
3. 进入`Github/Settings/Developer settings/OAuth Apps`创建应用 3. 进入`Github/Settings/Developer settings/OAuth Apps`创建应用
* 记录`Client ID`,创建`Client secrets` * 记录`Client ID`,创建`Client secrets`
* `Authorization callback URL`回调地址填写https://{您的域名}/api/oauth/githubLogin * `Authorization callback URL`回调地址填写https://{您的域名}/api/oauth/githubLogin
4. 使用`默认用户名admin密码123456`登陆后台/全局参数,修改Github登陆的`应用ID`、`应用密钥`并启动登陆 4. 使用`默认用户名admin密码123456`登陆后台/全局参数,修改Github登陆的`应用ID`、`应用密钥`并启动登陆
5. 建议将`管理权限`的用户名设置成自己的github用户名再使用Github登陆后在用户管理页面`删除默认账号admin` 5. 建议将`管理权限`的用户名设置成自己的github用户名再使用Github登陆后在用户管理页面`删除默认账号admin`
## BARK设置
1. 本项目依据Bark-Server接口规范实现了内置BARK服务端
2. 复制或扫码`消息验证\BARK授权`中的地址填入BARK应用的服务器地址中,如`https://inotify.cf?act=6D474C0DB1474F19BD8F7342D570C0FC`
3. BARK的APP会自动在本系统注册数据记录将直接出现在`消息通道`
## 系统截图 ## 系统截图
![](../master/public/A.png) ![](../master/public/A.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 104 KiB