mirror of
https://github.com/2dust/v2rayN.git
synced 2025-08-23 19:36:55 +00:00
add github remote storage feature
This commit is contained in:
parent
54a053b9d7
commit
0d02b4c345
18 changed files with 3134 additions and 755 deletions
|
@ -7,6 +7,7 @@ namespace v2rayN.Forms
|
|||
public partial class BaseForm : Form
|
||||
{
|
||||
protected static Config config;
|
||||
protected static GithubRemoteStorageConfig githubRemoteStorageConfig;
|
||||
protected static System.Drawing.Icon icon;
|
||||
|
||||
public BaseForm()
|
||||
|
|
158
v2rayN/v2rayN/Forms/GithubSettingsForm.Designer.cs
generated
Normal file
158
v2rayN/v2rayN/Forms/GithubSettingsForm.Designer.cs
generated
Normal file
|
@ -0,0 +1,158 @@
|
|||
namespace v2rayN.Forms {
|
||||
partial class GithubSettingsForm {
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(GithubSettingsForm));
|
||||
this.btnClose = new System.Windows.Forms.Button();
|
||||
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
||||
this.txtRemotePath = new System.Windows.Forms.TextBox();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.txtRepoName = new System.Windows.Forms.TextBox();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.txtToken = new System.Windows.Forms.TextBox();
|
||||
this.txtUsername = new System.Windows.Forms.TextBox();
|
||||
this.label6 = new System.Windows.Forms.Label();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.panel2 = new System.Windows.Forms.Panel();
|
||||
this.btnOK = new System.Windows.Forms.Button();
|
||||
this.panel1 = new System.Windows.Forms.Panel();
|
||||
this.groupBox1.SuspendLayout();
|
||||
this.panel2.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// btnClose
|
||||
//
|
||||
this.btnClose.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
resources.ApplyResources(this.btnClose, "btnClose");
|
||||
this.btnClose.Name = "btnClose";
|
||||
this.btnClose.UseVisualStyleBackColor = true;
|
||||
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
|
||||
//
|
||||
// groupBox1
|
||||
//
|
||||
this.groupBox1.Controls.Add(this.txtRemotePath);
|
||||
this.groupBox1.Controls.Add(this.label3);
|
||||
this.groupBox1.Controls.Add(this.txtRepoName);
|
||||
this.groupBox1.Controls.Add(this.label2);
|
||||
this.groupBox1.Controls.Add(this.txtToken);
|
||||
this.groupBox1.Controls.Add(this.txtUsername);
|
||||
this.groupBox1.Controls.Add(this.label6);
|
||||
this.groupBox1.Controls.Add(this.label1);
|
||||
resources.ApplyResources(this.groupBox1, "groupBox1");
|
||||
this.groupBox1.Name = "groupBox1";
|
||||
this.groupBox1.TabStop = false;
|
||||
//
|
||||
// txtRemotePath
|
||||
//
|
||||
resources.ApplyResources(this.txtRemotePath, "txtRemotePath");
|
||||
this.txtRemotePath.Name = "txtRemotePath";
|
||||
//
|
||||
// label3
|
||||
//
|
||||
resources.ApplyResources(this.label3, "label3");
|
||||
this.label3.Name = "label3";
|
||||
//
|
||||
// txtRepoName
|
||||
//
|
||||
resources.ApplyResources(this.txtRepoName, "txtRepoName");
|
||||
this.txtRepoName.Name = "txtRepoName";
|
||||
//
|
||||
// label2
|
||||
//
|
||||
resources.ApplyResources(this.label2, "label2");
|
||||
this.label2.Name = "label2";
|
||||
//
|
||||
// txtToken
|
||||
//
|
||||
resources.ApplyResources(this.txtToken, "txtToken");
|
||||
this.txtToken.Name = "txtToken";
|
||||
//
|
||||
// txtUsername
|
||||
//
|
||||
resources.ApplyResources(this.txtUsername, "txtUsername");
|
||||
this.txtUsername.Name = "txtUsername";
|
||||
//
|
||||
// label6
|
||||
//
|
||||
resources.ApplyResources(this.label6, "label6");
|
||||
this.label6.Name = "label6";
|
||||
//
|
||||
// label1
|
||||
//
|
||||
resources.ApplyResources(this.label1, "label1");
|
||||
this.label1.Name = "label1";
|
||||
//
|
||||
// panel2
|
||||
//
|
||||
this.panel2.Controls.Add(this.btnClose);
|
||||
this.panel2.Controls.Add(this.btnOK);
|
||||
resources.ApplyResources(this.panel2, "panel2");
|
||||
this.panel2.Name = "panel2";
|
||||
//
|
||||
// btnOK
|
||||
//
|
||||
resources.ApplyResources(this.btnOK, "btnOK");
|
||||
this.btnOK.Name = "btnOK";
|
||||
this.btnOK.UseVisualStyleBackColor = true;
|
||||
this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
|
||||
//
|
||||
// panel1
|
||||
//
|
||||
resources.ApplyResources(this.panel1, "panel1");
|
||||
this.panel1.Name = "panel1";
|
||||
//
|
||||
// GithubSettingsForm
|
||||
//
|
||||
resources.ApplyResources(this, "$this");
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.CancelButton = this.btnClose;
|
||||
this.Controls.Add(this.groupBox1);
|
||||
this.Controls.Add(this.panel2);
|
||||
this.Controls.Add(this.panel1);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Name = "GithubSettingsForm";
|
||||
this.groupBox1.ResumeLayout(false);
|
||||
this.groupBox1.PerformLayout();
|
||||
this.panel2.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.GroupBox groupBox1;
|
||||
private System.Windows.Forms.Button btnClose;
|
||||
private System.Windows.Forms.Button btnOK;
|
||||
private System.Windows.Forms.TextBox txtUsername;
|
||||
private System.Windows.Forms.Label label6;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Panel panel1;
|
||||
private System.Windows.Forms.Panel panel2;
|
||||
private System.Windows.Forms.TextBox txtToken;
|
||||
private System.Windows.Forms.TextBox txtRepoName;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.TextBox txtRemotePath;
|
||||
private System.Windows.Forms.Label label3;
|
||||
}
|
||||
}
|
49
v2rayN/v2rayN/Forms/GithubSettingsForm.cs
Normal file
49
v2rayN/v2rayN/Forms/GithubSettingsForm.cs
Normal file
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using System.Windows.Forms;
|
||||
using v2rayN.Handler;
|
||||
using v2rayN.Mode;
|
||||
|
||||
namespace v2rayN.Forms {
|
||||
public partial class GithubSettingsForm : BaseForm {
|
||||
public GithubSettingsForm() {
|
||||
InitializeComponent();
|
||||
BindingConfig();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绑定数据
|
||||
/// </summary>
|
||||
private void BindingConfig() {
|
||||
var config = githubRemoteStorageConfig;
|
||||
txtUsername.Text = config?.userName ?? "";
|
||||
txtRepoName.Text = config?.repoName ?? "";
|
||||
txtRemotePath.Text = config?.path ?? "";
|
||||
txtToken.Text = config?.token ?? "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除设置
|
||||
/// </summary>
|
||||
private void ClearServer() {
|
||||
txtUsername.Text = "";
|
||||
}
|
||||
|
||||
private void btnOK_Click(object sender, EventArgs e) {
|
||||
//vmessItem.remarks = remarks;
|
||||
githubRemoteStorageConfig.userName = txtUsername.Text.Trim();
|
||||
githubRemoteStorageConfig.repoName = txtRepoName.Text.Trim();
|
||||
githubRemoteStorageConfig.path = txtRemotePath.Text.Trim();
|
||||
githubRemoteStorageConfig.token = txtToken.Text.Trim();
|
||||
if (ConfigHandler.SaveGithubRemoteStorageConfig(githubRemoteStorageConfig) == 0) {
|
||||
this.DialogResult = DialogResult.OK;
|
||||
}
|
||||
else {
|
||||
UI.ShowWarning(UIRes.I18N("OperationFailed"));
|
||||
}
|
||||
}
|
||||
|
||||
private void btnClose_Click(object sender, EventArgs e) {
|
||||
this.DialogResult = DialogResult.Cancel;
|
||||
}
|
||||
}
|
||||
}
|
1606
v2rayN/v2rayN/Forms/GithubSettingsForm.resx
Normal file
1606
v2rayN/v2rayN/Forms/GithubSettingsForm.resx
Normal file
File diff suppressed because it is too large
Load diff
144
v2rayN/v2rayN/Forms/GithubSettingsForm.zh-Hans.resx
Normal file
144
v2rayN/v2rayN/Forms/GithubSettingsForm.zh-Hans.resx
Normal file
|
@ -0,0 +1,144 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="$this.Text" xml:space="preserve">
|
||||
<value>Github配置</value>
|
||||
</data>
|
||||
<data name="btnClose.Text" xml:space="preserve">
|
||||
<value>取消(&C)</value>
|
||||
</data>
|
||||
<data name="btnOK.Text" xml:space="preserve">
|
||||
<value>确定(&O)</value>
|
||||
</data>
|
||||
<data name="groupBox1.Text" xml:space="preserve">
|
||||
<value>配置</value>
|
||||
</data>
|
||||
<data name="label1.Text" xml:space="preserve">
|
||||
<value>令牌</value>
|
||||
</data>
|
||||
<data name="label2.Text" xml:space="preserve">
|
||||
<value>仓库名</value>
|
||||
</data>
|
||||
<data name="label3.Text" xml:space="preserve">
|
||||
<value>远端配置文件路径</value>
|
||||
</data>
|
||||
<data name="label6.Text" xml:space="preserve">
|
||||
<value>用户名</value>
|
||||
</data>
|
||||
</root>
|
1513
v2rayN/v2rayN/Forms/MainForm.Designer.cs
generated
1513
v2rayN/v2rayN/Forms/MainForm.Designer.cs
generated
File diff suppressed because it is too large
Load diff
|
@ -1629,8 +1629,83 @@ namespace v2rayN.Forms
|
|||
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
#region Github Remote Storage
|
||||
private void LoadGithubRemoteStorageConfig() {
|
||||
if (!File.Exists(Global.GithubRemoteStorageConfigFileName)) {
|
||||
File.WriteAllText(Global.GithubRemoteStorageConfigFileName, "{}");
|
||||
}
|
||||
var githubRemoteStorageConfigText = File.ReadAllText(Global.GithubRemoteStorageConfigFileName, Encoding.UTF8);
|
||||
try {
|
||||
githubRemoteStorageConfig = Newtonsoft.Json.JsonConvert.DeserializeObject<GithubRemoteStorageConfig>(githubRemoteStorageConfigText);
|
||||
}
|
||||
catch {
|
||||
githubRemoteStorageConfig = null;
|
||||
}
|
||||
}
|
||||
private bool CheckGithubSettings() {
|
||||
LoadGithubRemoteStorageConfig();
|
||||
if (githubRemoteStorageConfig == null) {
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return githubRemoteStorageConfig.IsConfigVerificationPassed;
|
||||
}
|
||||
|
||||
private async void menuGithubUpload_Click(object sender, EventArgs e) {
|
||||
if (!this.CheckGithubSettings()) {
|
||||
GithubSettingsForm fm = new GithubSettingsForm();
|
||||
if (fm.ShowDialog() != DialogResult.OK) {
|
||||
//得到Config
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
await GithubRemoteStorageHelper.Upload(config.vmess, githubRemoteStorageConfig);
|
||||
UI.Show(UIRes.I18N("GithubRemoteStoreUploadSucceed"));
|
||||
}
|
||||
catch (Octokit.AuthorizationException githubAuthException) {
|
||||
AppendText(githubAuthException.Message);
|
||||
AppendText(githubAuthException.StackTrace);
|
||||
UI.Show(UIRes.I18N("GithubRemoteStoreAuthorizationFailed"));
|
||||
}
|
||||
catch (Exception exception) {
|
||||
AppendText(exception.Message);
|
||||
AppendText(exception.StackTrace);
|
||||
UI.Show(UIRes.I18N("GithubRemoteStoreUploadFailed"));
|
||||
}
|
||||
}
|
||||
|
||||
private async void menuGithubFetch_Click(object sender, EventArgs e) {
|
||||
if (!this.CheckGithubSettings()) {
|
||||
GithubSettingsForm fm = new GithubSettingsForm();
|
||||
if (fm.ShowDialog() != DialogResult.OK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
await GithubRemoteStorageHelper.Fetch(config.vmess, githubRemoteStorageConfig);
|
||||
RefreshServers();
|
||||
UI.Show(UIRes.I18N("GithubRemoteStoreFetchSucceed"));
|
||||
}
|
||||
catch (Octokit.AuthorizationException githubAuthException) {
|
||||
AppendText(githubAuthException.Message);
|
||||
AppendText(githubAuthException.StackTrace);
|
||||
UI.Show(UIRes.I18N("GithubRemoteStoreAuthorizationFailed"));
|
||||
}
|
||||
catch (Exception exception) {
|
||||
AppendText(exception.Message);
|
||||
AppendText(exception.StackTrace);
|
||||
UI.Show(UIRes.I18N("GithubRemoteStoreFetchFailed"));
|
||||
}
|
||||
}
|
||||
private void menuGithubSettings_Click(object sender, EventArgs e) {
|
||||
LoadGithubRemoteStorageConfig();
|
||||
GithubSettingsForm fm = new GithubSettingsForm();
|
||||
fm.ShowDialog();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -290,6 +290,9 @@
|
|||
<data name="menuExport2SubContent.Text" xml:space="preserve">
|
||||
<value>Export subscription (base64) share to clipboard</value>
|
||||
</data>
|
||||
<data name="toolStripSeparator14.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>352, 6</value>
|
||||
</data>
|
||||
<data name="menuGithubUpload.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>355, 22</value>
|
||||
</data>
|
||||
|
@ -302,8 +305,14 @@
|
|||
<data name="menuGithubFetch.Text" xml:space="preserve">
|
||||
<value>Fetch vmesses from github.</value>
|
||||
</data>
|
||||
<data name="menuGithubSettings.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>355, 22</value>
|
||||
</data>
|
||||
<data name="menuGithubSettings.Text" xml:space="preserve">
|
||||
<value>Github Remote Settings</value>
|
||||
</data>
|
||||
<data name="cmsLv.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>356, 644</value>
|
||||
<value>356, 650</value>
|
||||
</data>
|
||||
<data name=">>cmsLv.Name" xml:space="preserve">
|
||||
<value>cmsLv</value>
|
||||
|
@ -996,7 +1005,7 @@
|
|||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>108</value>
|
||||
<value>78</value>
|
||||
</metadata>
|
||||
<data name="$this.AutoScaleDimensions" type="System.Drawing.SizeF, System.Drawing">
|
||||
<value>6, 12</value>
|
||||
|
@ -1190,6 +1199,18 @@
|
|||
<data name=">>menuGithubUpload.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>menuGithubFetch.Name" xml:space="preserve">
|
||||
<value>menuGithubFetch</value>
|
||||
</data>
|
||||
<data name=">>menuGithubFetch.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>menuGithubSettings.Name" xml:space="preserve">
|
||||
<value>menuGithubSettings</value>
|
||||
</data>
|
||||
<data name=">>menuGithubSettings.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>tsbServer.Name" xml:space="preserve">
|
||||
<value>tsbServer</value>
|
||||
</data>
|
||||
|
@ -1520,11 +1541,11 @@
|
|||
<data name=">>tsbClose.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.ToolStripButton, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>menuGithubFetch.Name" xml:space="preserve">
|
||||
<value>menuGithubFetch</value>
|
||||
<data name=">>toolStripSeparator14.Name" xml:space="preserve">
|
||||
<value>toolStripSeparator14</value>
|
||||
</data>
|
||||
<data name=">>menuGithubFetch.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
<data name=">>toolStripSeparator14.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>$this.Name" xml:space="preserve">
|
||||
<value>MainForm</value>
|
||||
|
|
|
@ -522,4 +522,7 @@
|
|||
<data name="menuGithubUpload.Text" xml:space="preserve">
|
||||
<value>上传到Github</value>
|
||||
</data>
|
||||
<data name="menuGithubSettings.Text" xml:space="preserve">
|
||||
<value>Github远端存储设置</value>
|
||||
</data>
|
||||
</root>
|
|
@ -30,6 +30,11 @@ namespace v2rayN
|
|||
/// </summary>
|
||||
public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=";
|
||||
|
||||
/// <summary>
|
||||
/// Github远端配置文件名
|
||||
/// </summary>
|
||||
public const string GithubRemoteStorageConfigFileName = "githubRemoteStorageConfig.json";
|
||||
|
||||
/// <summary>
|
||||
/// 本软件配置文件名
|
||||
/// </summary>
|
||||
|
|
|
@ -348,6 +348,15 @@ namespace v2rayN.Handler
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 存储文件
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
private static void ToJsonFile(object config,string path)
|
||||
{
|
||||
Utils.ToJsonFile(config, Utils.GetPath(path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 存储文件
|
||||
|
@ -1011,5 +1020,14 @@ namespace v2rayN.Handler
|
|||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存Github存储配置
|
||||
/// </summary>
|
||||
/// <param name="githubRemoteStorageConfig"></param>
|
||||
/// <returns></returns>
|
||||
public static int SaveGithubRemoteStorageConfig(GithubRemoteStorageConfig githubRemoteStorageConfig) {
|
||||
ToJsonFile(githubRemoteStorageConfig,Global.GithubRemoteStorageConfigFileName);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
38
v2rayN/v2rayN/Mode/GithubRemoteStorageConfig.cs
Normal file
38
v2rayN/v2rayN/Mode/GithubRemoteStorageConfig.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace v2rayN.Mode {
|
||||
public class GithubRemoteStorageConfig {
|
||||
/// <summary>
|
||||
/// 用户名
|
||||
/// </summary>
|
||||
public string userName { get; set; }
|
||||
/// <summary>
|
||||
/// 仓库名称
|
||||
/// </summary>
|
||||
public string repoName { get; set; }
|
||||
/// <summary>
|
||||
/// 远端配置文件路径
|
||||
/// </summary>
|
||||
public string path { get; set; }
|
||||
/// <summary>
|
||||
/// 令牌
|
||||
/// </summary>
|
||||
public string token { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 配置验证通过
|
||||
/// </summary>
|
||||
public bool IsConfigVerificationPassed {
|
||||
get {
|
||||
if (string.IsNullOrWhiteSpace(userName) || string.IsNullOrWhiteSpace(repoName) || string.IsNullOrWhiteSpace(path) || string.IsNullOrWhiteSpace(token)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using v2rayN.Forms;
|
||||
|
@ -19,6 +20,9 @@ namespace v2rayN
|
|||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
//Octokit
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
|
||||
|
||||
if (Environment.OSVersion.Version.Major >= 6)
|
||||
{
|
||||
SetProcessDPIAware();
|
||||
|
|
63
v2rayN/v2rayN/Resx/ResUI.Designer.cs
generated
63
v2rayN/v2rayN/Resx/ResUI.Designer.cs
generated
|
@ -240,6 +240,69 @@ namespace v2rayN.Resx {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Failed to read the configuration file from Github, please check the settings and the configuration file on Github 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string GithubRemoteStorageConfigDamaged {
|
||||
get {
|
||||
return ResourceManager.GetString("GithubRemoteStorageConfigDamaged", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Failed to download the configuration file from Github, the configuration file was not found on Github, please check the settings.If it is the first time, you can upload to Github first. 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string GithubRemoteStorageConfigNotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("GithubRemoteStorageConfigNotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Github authentication failed, please check the settings 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string GithubRemoteStoreAuthorizationFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("GithubRemoteStoreAuthorizationFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Failed to download configuration file from Github 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string GithubRemoteStoreFetchFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("GithubRemoteStoreFetchFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Download the configuration file from Github successfully 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string GithubRemoteStoreFetchSucceed {
|
||||
get {
|
||||
return ResourceManager.GetString("GithubRemoteStoreFetchSucceed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Failed to upload configuration file to Github 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string GithubRemoteStoreUploadFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("GithubRemoteStoreUploadFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Upload configuration file to Github successfully 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string GithubRemoteStoreUploadSucceed {
|
||||
get {
|
||||
return ResourceManager.GetString("GithubRemoteStoreUploadSucceed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 is not the correct client configuration file, please check 的本地化字符串。
|
||||
/// </summary>
|
||||
|
|
|
@ -177,6 +177,27 @@
|
|||
<data name="FillUUID" xml:space="preserve">
|
||||
<value>Please fill in the user ID</value>
|
||||
</data>
|
||||
<data name="GithubRemoteStorageConfigDamaged" xml:space="preserve">
|
||||
<value>Failed to read the configuration file from Github, please check the settings and the configuration file on Github</value>
|
||||
</data>
|
||||
<data name="GithubRemoteStorageConfigNotFound" xml:space="preserve">
|
||||
<value>Failed to download the configuration file from Github, the configuration file was not found on Github, please check the settings.If it is the first time, you can upload to Github first.</value>
|
||||
</data>
|
||||
<data name="GithubRemoteStoreAuthorizationFailed" xml:space="preserve">
|
||||
<value>Github authentication failed, please check the settings</value>
|
||||
</data>
|
||||
<data name="GithubRemoteStoreFetchFailed" xml:space="preserve">
|
||||
<value>Failed to download configuration file from Github</value>
|
||||
</data>
|
||||
<data name="GithubRemoteStoreFetchSucceed" xml:space="preserve">
|
||||
<value>Download the configuration file from Github successfully</value>
|
||||
</data>
|
||||
<data name="GithubRemoteStoreUploadFailed" xml:space="preserve">
|
||||
<value>Failed to upload configuration file to Github</value>
|
||||
</data>
|
||||
<data name="GithubRemoteStoreUploadSucceed" xml:space="preserve">
|
||||
<value>Upload configuration file to Github successfully</value>
|
||||
</data>
|
||||
<data name="IncorrectClientConfiguration" xml:space="preserve">
|
||||
<value> is not the correct client configuration file, please check</value>
|
||||
</data>
|
||||
|
|
|
@ -177,6 +177,27 @@
|
|||
<data name="FillUUID" xml:space="preserve">
|
||||
<value>请填写用户ID</value>
|
||||
</data>
|
||||
<data name="GithubRemoteStorageConfigDamaged" xml:space="preserve">
|
||||
<value>从Github读取远端配置文件失败,请检查设置或Github上的配置文件</value>
|
||||
</data>
|
||||
<data name="GithubRemoteStorageConfigNotFound" xml:space="preserve">
|
||||
<value>从Github下载配置文件失败,没有在Github找到配置文件,请检查设置。如果是第一次可以先上传到Github</value>
|
||||
</data>
|
||||
<data name="GithubRemoteStoreAuthorizationFailed" xml:space="preserve">
|
||||
<value>Github认证失败,请检查设置</value>
|
||||
</data>
|
||||
<data name="GithubRemoteStoreFetchFailed" xml:space="preserve">
|
||||
<value>从Github下载配置文件失败</value>
|
||||
</data>
|
||||
<data name="GithubRemoteStoreFetchSucceed" xml:space="preserve">
|
||||
<value>从Github下载配置文件成功</value>
|
||||
</data>
|
||||
<data name="GithubRemoteStoreUploadFailed" xml:space="preserve">
|
||||
<value>上传配置文件到Github失败</value>
|
||||
</data>
|
||||
<data name="GithubRemoteStoreUploadSucceed" xml:space="preserve">
|
||||
<value>上传配置文件到Github成功</value>
|
||||
</data>
|
||||
<data name="IncorrectClientConfiguration" xml:space="preserve">
|
||||
<value>不是正确的客户端配置文件,请检查</value>
|
||||
</data>
|
||||
|
|
114
v2rayN/v2rayN/Tool/GithubRemoteStorageHelper.cs
Normal file
114
v2rayN/v2rayN/Tool/GithubRemoteStorageHelper.cs
Normal file
|
@ -0,0 +1,114 @@
|
|||
using Octokit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using v2rayN.Mode;
|
||||
using v2rayN.Protos.Statistics;
|
||||
|
||||
namespace v2rayN.Tool {
|
||||
public static class GithubRemoteStorageHelper {
|
||||
private static GitHubClient GetClient(GithubRemoteStorageConfig config) {
|
||||
var client = new GitHubClient(new ProductHeaderValue(config.userName));
|
||||
var auth = new Credentials(config.token);
|
||||
client.Credentials = auth;
|
||||
return client;
|
||||
}
|
||||
private static async Task<RepositoryContent> GetFile(this GitHubClient client, Repository repo, string path) {
|
||||
string errorMessage = "";
|
||||
IReadOnlyList<RepositoryContent> files = null;
|
||||
try {
|
||||
files = await client.Repository.Content.GetAllContentsByRef(repo.Id, path, repo.DefaultBranch);
|
||||
}
|
||||
catch (Exception e) {
|
||||
errorMessage = e.Message;
|
||||
files = null;
|
||||
}
|
||||
return files?.FirstOrDefault();
|
||||
}
|
||||
private static async Task CreateFile(this GitHubClient client, Repository repo, string text, string path) {
|
||||
var createFileReq = new CreateFileRequest("V2Ray config changed(Initial).", text, repo.DefaultBranch, true);
|
||||
await client.Repository.Content.CreateFile(repo.Id, path, createFileReq);
|
||||
}
|
||||
private static async Task UpdateFile(this GitHubClient client, Repository repo, string text, string path, string sha) {
|
||||
var updateFileReq = new UpdateFileRequest("V2Ray config changed.", text, sha, true);
|
||||
await client.Repository.Content.UpdateFile(repo.Id, path, updateFileReq);
|
||||
}
|
||||
/// <summary>
|
||||
/// upload
|
||||
/// </summary>
|
||||
/// <param name="localVmessItems"></param>
|
||||
/// <param name="config"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task Upload(IList<VmessItem> localVmessItems, GithubRemoteStorageConfig config) {
|
||||
if (localVmessItems?.Any() != true) {
|
||||
return;
|
||||
}
|
||||
var vmessesJson = Newtonsoft.Json.JsonConvert.SerializeObject(localVmessItems);
|
||||
var client = GetClient(config);
|
||||
var repo = await client.Repository.Get(config.userName, config.repoName);
|
||||
var remoteFile = await client.GetFile(repo, config.path);
|
||||
if (remoteFile == null) {
|
||||
await client.CreateFile(repo, vmessesJson, config.path);
|
||||
}
|
||||
else {
|
||||
var md5 = MD5.Create();
|
||||
if (remoteFile.Content.Length != vmessesJson.Length || md5.ComputeHash(Encoding.UTF8.GetBytes(remoteFile.Content)) != md5.ComputeHash(Encoding.UTF8.GetBytes(vmessesJson))) {
|
||||
await client.UpdateFile(repo, vmessesJson, config.path, remoteFile.Sha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// fetch
|
||||
/// </summary>
|
||||
/// <param name="localVmessItems"></param>
|
||||
/// <param name="config"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task Fetch(IList<VmessItem> localVmessItems, GithubRemoteStorageConfig config) {
|
||||
var client = GetClient(config);
|
||||
var repo = await client.Repository.Get(config.userName, config.repoName);
|
||||
var file = await client.GetFile(repo, config.path);
|
||||
if (file != null) {
|
||||
MD5 md5 = MD5.Create();
|
||||
|
||||
try {
|
||||
|
||||
var remoteVmessItems = Newtonsoft.Json.JsonConvert.DeserializeObject<IList<VmessItem>>(file.Content);
|
||||
|
||||
var remoteVmessItemMD5Pairs = remoteVmessItems.Select(vmessItem => {
|
||||
var rawText = Newtonsoft.Json.JsonConvert.SerializeObject(vmessItem);
|
||||
var textBytes = Encoding.UTF8.GetBytes(rawText);
|
||||
return new KeyValuePair<string, VmessItem>(Encoding.UTF8.GetString(md5.ComputeHash(textBytes)), vmessItem);
|
||||
});
|
||||
|
||||
var localVmessItemMD5s = localVmessItems.Select(vmessItem => {
|
||||
var rawText = Newtonsoft.Json.JsonConvert.SerializeObject(vmessItem);
|
||||
var textBytes = Encoding.UTF8.GetBytes(rawText);
|
||||
return Encoding.UTF8.GetString(md5.ComputeHash(textBytes));
|
||||
}).Distinct().ToList();
|
||||
|
||||
foreach (var remoteVmessItem in remoteVmessItemMD5Pairs) {
|
||||
if (!localVmessItemMD5s.Contains(remoteVmessItem.Key)) {
|
||||
localVmessItems.Add(remoteVmessItem.Value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch {
|
||||
UI.ShowWarning(UIRes.I18N("GithubRemoteStorageConfigDamaged"));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//OperationFailed
|
||||
UI.ShowWarning(UIRes.I18N("GithubRemoteStorageConfigNotFound"));
|
||||
throw new Exception(UIRes.I18N("GithubRemoteStorageConfigNotFound"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -98,6 +98,12 @@
|
|||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Forms\GithubSettingsForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Forms\GithubSettingsForm.Designer.cs">
|
||||
<DependentUpon>GithubSettingsForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Forms\AddServer4Form.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
|
@ -165,6 +171,7 @@
|
|||
<Compile Include="HttpProxyHandler\SysProxyHandle.cs" />
|
||||
<Compile Include="Mode\EMove.cs" />
|
||||
<Compile Include="Mode\EServerColName.cs" />
|
||||
<Compile Include="Mode\GithubRemoteStorageConfig.cs" />
|
||||
<Compile Include="Mode\ServerStatistics.cs" />
|
||||
<Compile Include="Mode\SysproxyConfig.cs" />
|
||||
<Compile Include="Mode\EConfigType.cs" />
|
||||
|
@ -215,12 +222,21 @@
|
|||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Tool\FileManager.cs" />
|
||||
<Compile Include="Tool\GithubRemoteStorageHelper.cs" />
|
||||
<Compile Include="Tool\Job.cs" />
|
||||
<Compile Include="Tool\QueryableExtension.cs" />
|
||||
<Compile Include="Tool\UIRes.cs" />
|
||||
<Compile Include="Tool\UI.cs" />
|
||||
<Compile Include="Tool\Utils.cs" />
|
||||
<Compile Include="Handler\V2rayConfigHandler.cs" />
|
||||
<EmbeddedResource Include="Forms\GithubSettingsForm.resx">
|
||||
<DependentUpon>GithubSettingsForm.cs</DependentUpon>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Forms\GithubSettingsForm.zh-Hans.resx">
|
||||
<DependentUpon>GithubSettingsForm.cs</DependentUpon>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Forms\AddServer2Form.zh-Hans.resx">
|
||||
<DependentUpon>AddServer2Form.cs</DependentUpon>
|
||||
<SubType>Designer</SubType>
|
||||
|
@ -411,6 +427,9 @@
|
|||
<PackageReference Include="Newtonsoft.Json">
|
||||
<Version>12.0.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Octokit">
|
||||
<Version>0.48.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="ZXing.Net">
|
||||
<Version>0.16.5</Version>
|
||||
</PackageReference>
|
||||
|
|
Loading…
Reference in a new issue