initial commit
This commit is contained in:
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.pdb
|
||||||
|
bin
|
||||||
|
obj
|
||||||
|
desktop.ini
|
||||||
|
.vs
|
||||||
|
packages
|
||||||
|
*.zip
|
||||||
12
App.config
Normal file
12
App.config
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<configSections>
|
||||||
|
</configSections>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
||||||
|
</startup>
|
||||||
|
<connectionStrings>
|
||||||
|
<add name="VNCServerList.Properties.Settings.VNCServerDB" connectionString="Data Source=K4FASQL.kr.ds.amkor.com,50150;Initial Catalog=EE;User ID=eeadm;Password=uJnU8a8q&DJ+ug-D"
|
||||||
|
providerName="System.Data.SqlClient" />
|
||||||
|
</connectionStrings>
|
||||||
|
</configuration>
|
||||||
13
CreateTable.sql
Normal file
13
CreateTable.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
CREATE TABLE [dbo].[VNC_ServerList](
|
||||||
|
[User] [varchar](50) NOT NULL,
|
||||||
|
[IP] [varchar](20) NOT NULL,
|
||||||
|
[Category] [varchar](20) NULL,
|
||||||
|
[Description] [varchar](100) NULL,
|
||||||
|
[Password] [varchar](20) NULL,
|
||||||
|
[Argument] [varchar](50) NULL,
|
||||||
|
CONSTRAINT [PK_VNC_ServerList] PRIMARY KEY CLUSTERED
|
||||||
|
(
|
||||||
|
[User] ASC,
|
||||||
|
[IP] ASC
|
||||||
|
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
|
||||||
|
) ON [PRIMARY]
|
||||||
47
Form1.Designer.cs
generated
Normal file
47
Form1.Designer.cs
generated
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
namespace VNCServerList
|
||||||
|
{
|
||||||
|
partial class Form1
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 필수 디자이너 변수입니다.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 사용 중인 모든 리소스를 정리합니다.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form 디자이너에서 생성한 코드
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 디자이너 지원에 필요한 메서드입니다.
|
||||||
|
/// 이 메서드의 내용을 코드 편집기로 수정하지 마세요.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// Form1
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(463, 310);
|
||||||
|
this.Name = "Form1";
|
||||||
|
this.Text = "Form1";
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
59
Form1.cs
Normal file
59
Form1.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using Microsoft.Web.WebView2.WinForms;
|
||||||
|
using Microsoft.Owin.Hosting;
|
||||||
|
using VNCServerList.Web;
|
||||||
|
|
||||||
|
namespace VNCServerList
|
||||||
|
{
|
||||||
|
public partial class Form1 : Form
|
||||||
|
{
|
||||||
|
private WebView2 webView;
|
||||||
|
private IDisposable webApp;
|
||||||
|
|
||||||
|
public Form1()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
StartWebServer();
|
||||||
|
InitializeWebView();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void InitializeWebView()
|
||||||
|
{
|
||||||
|
webView = new WebView2();
|
||||||
|
webView.Dock = DockStyle.Fill;
|
||||||
|
this.Controls.Add(webView);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await webView.EnsureCoreWebView2Async(null);
|
||||||
|
webView.CoreWebView2.Navigate("http://localhost:8080");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"WebView2 초기화 중 오류가 발생했습니다: {ex.Message}", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartWebServer()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var options = new StartOptions("http://localhost:8080");
|
||||||
|
|
||||||
|
webApp = WebApp.Start<Startup>(options);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"웹 서버 시작 중 오류가 발생했습니다: {ex.Message}", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFormClosing(FormClosingEventArgs e)
|
||||||
|
{
|
||||||
|
webApp?.Dispose();
|
||||||
|
base.OnFormClosing(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
120
Form1.resx
Normal file
120
Form1.resx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<?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>
|
||||||
|
</root>
|
||||||
14
Models/VNCServer.cs
Normal file
14
Models/VNCServer.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VNCServerList.Models
|
||||||
|
{
|
||||||
|
public class VNCServer
|
||||||
|
{
|
||||||
|
public string User { get; set; }
|
||||||
|
public string IP { get; set; }
|
||||||
|
public string Category { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
public string Argument { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Program.cs
Normal file
22
Program.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace VNCServerList
|
||||||
|
{
|
||||||
|
static class Program
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 해당 응용 프로그램의 주 진입점입니다.
|
||||||
|
/// </summary>
|
||||||
|
[STAThread]
|
||||||
|
static void Main()
|
||||||
|
{
|
||||||
|
Application.EnableVisualStyles();
|
||||||
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
Application.Run(new Form1());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
Properties/AssemblyInfo.cs
Normal file
36
Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
|
||||||
|
// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
|
||||||
|
// 이러한 특성 값을 변경하세요.
|
||||||
|
[assembly: AssemblyTitle("VNCServerList")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("VNCServerList")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2025")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에
|
||||||
|
// 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면
|
||||||
|
// 해당 형식에 대해 ComVisible 특성을 true로 설정하세요.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다.
|
||||||
|
[assembly: Guid("0403ac4c-8858-4ace-8d66-9eb307503b04")]
|
||||||
|
|
||||||
|
// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다.
|
||||||
|
//
|
||||||
|
// 주 버전
|
||||||
|
// 부 버전
|
||||||
|
// 빌드 번호
|
||||||
|
// 수정 버전
|
||||||
|
//
|
||||||
|
// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호가 자동으로
|
||||||
|
// 지정되도록 할 수 있습니다.
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||||
71
Properties/Resources.Designer.cs
generated
Normal file
71
Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// 이 코드는 도구를 사용하여 생성되었습니다.
|
||||||
|
// 런타임 버전:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
|
||||||
|
// 이러한 변경 내용이 손실됩니다.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace VNCServerList.Properties
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 지역화된 문자열 등을 찾기 위한 강력한 형식의 리소스 클래스입니다.
|
||||||
|
/// </summary>
|
||||||
|
// 이 클래스는 ResGen 또는 Visual Studio와 같은 도구를 통해 StronglyTypedResourceBuilder
|
||||||
|
// 클래스에서 자동으로 생성되었습니다.
|
||||||
|
// 멤버를 추가하거나 제거하려면 .ResX 파일을 편집한 다음 /str 옵션을 사용하여
|
||||||
|
// ResGen을 다시 실행하거나 VS 프로젝트를 다시 빌드하십시오.
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class Resources
|
||||||
|
{
|
||||||
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal Resources()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 이 클래스에서 사용하는 캐시된 ResourceManager 인스턴스를 반환합니다.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Resources.ResourceManager ResourceManager
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if ((resourceMan == null))
|
||||||
|
{
|
||||||
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("VNCServerList.Properties.Resources", typeof(Resources).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 이 강력한 형식의 리소스 클래스를 사용하여 모든 리소스 조회에 대해 현재 스레드의 CurrentUICulture 속성을
|
||||||
|
/// 재정의합니다.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Globalization.CultureInfo Culture
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
117
Properties/Resources.resx
Normal file
117
Properties/Resources.resx
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<?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.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: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" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
</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" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
</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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
||||||
37
Properties/Settings.Designer.cs
generated
Normal file
37
Properties/Settings.Designer.cs
generated
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// 이 코드는 도구를 사용하여 생성되었습니다.
|
||||||
|
// 런타임 버전:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
|
||||||
|
// 이러한 변경 내용이 손실됩니다.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace VNCServerList.Properties {
|
||||||
|
|
||||||
|
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")]
|
||||||
|
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||||
|
|
||||||
|
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||||
|
|
||||||
|
public static Settings Default {
|
||||||
|
get {
|
||||||
|
return defaultInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.ConnectionString)]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("Data Source=K4FASQL.kr.ds.amkor.com,50150;Initial Catalog=EE;User ID=eeadm;Passwo" +
|
||||||
|
"rd=uJnU8a8q&DJ+ug-D")]
|
||||||
|
public string VNCServerDB {
|
||||||
|
get {
|
||||||
|
return ((string)(this["VNCServerDB"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
Properties/Settings.settings
Normal file
14
Properties/Settings.settings
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="VNCServerList.Properties" GeneratedClassName="Settings">
|
||||||
|
<Profiles />
|
||||||
|
<Settings>
|
||||||
|
<Setting Name="VNCServerDB" Type="(Connection string)" Scope="Application">
|
||||||
|
<DesignTimeValue Profile="(Default)"><?xml version="1.0" encoding="utf-16"?>
|
||||||
|
<SerializableConnectionString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<ConnectionString>Data Source=K4FASQL.kr.ds.amkor.com,50150;Initial Catalog=EE;User ID=eeadm;Password=uJnU8a8q&amp;DJ+ug-D</ConnectionString>
|
||||||
|
<ProviderName>System.Data.SqlClient</ProviderName>
|
||||||
|
</SerializableConnectionString></DesignTimeValue>
|
||||||
|
<Value Profile="(Default)">Data Source=K4FASQL.kr.ds.amkor.com,50150;Initial Catalog=EE;User ID=eeadm;Password=uJnU8a8q&DJ+ug-D</Value>
|
||||||
|
</Setting>
|
||||||
|
</Settings>
|
||||||
|
</SettingsFile>
|
||||||
16
ReadMe.MD
Normal file
16
ReadMe.MD
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
## vnc runtime : C:\Program Files\TightVNC\tvnviewer.exe
|
||||||
|
## 프로그램 설명
|
||||||
|
VNC 서버목록을 관리하고 목록을 더블클릭해서 VNC Viewer 를 표시하는 기능을 제공
|
||||||
|
목록의 경우 MSSQL 서버에 있음 서버정보는 우선
|
||||||
|
Net fx 4.8 winform 버젼으로 개발
|
||||||
|
화면구성은 winform 에 Webview2 를 활용하여 구성
|
||||||
|
tailwindcss 사용하고 자바스크립트는 필요하면 바닐라
|
||||||
|
호스트통신은 owin self host 방식으로 처리한다.
|
||||||
|
웹페이지는 static file 이용해서 처리되며,, 서버통신은 ajax 기술로 진행하기
|
||||||
|
|
||||||
|
## MSSQL 서버 정보
|
||||||
|
IP : 127.0.0.1
|
||||||
|
포트 : 1433
|
||||||
|
ID : id
|
||||||
|
PW : pw
|
||||||
|
|
||||||
177
Services/DatabaseService.cs
Normal file
177
Services/DatabaseService.cs
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.SqlClient;
|
||||||
|
using System.Configuration;
|
||||||
|
using VNCServerList.Models;
|
||||||
|
|
||||||
|
namespace VNCServerList.Services
|
||||||
|
{
|
||||||
|
public class DatabaseService
|
||||||
|
{
|
||||||
|
private readonly string _connectionString;
|
||||||
|
|
||||||
|
public DatabaseService()
|
||||||
|
{
|
||||||
|
_connectionString = Properties.Settings.Default.VNCServerDB;
|
||||||
|
InitializeDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeDatabase()
|
||||||
|
{
|
||||||
|
using (var connection = new SqlConnection(_connectionString))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
|
||||||
|
// VNC_ServerList 테이블이 존재하는지 확인
|
||||||
|
string checkTableSql = @"
|
||||||
|
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='VNC_ServerList' AND xtype='U')
|
||||||
|
CREATE TABLE [dbo].[VNC_ServerList](
|
||||||
|
[User] [varchar](50) NOT NULL,
|
||||||
|
[IP] [varchar](20) NOT NULL,
|
||||||
|
[Category] [varchar](20) NULL,
|
||||||
|
[Description] [varchar](100) NULL,
|
||||||
|
[Password] [varchar](20) NULL,
|
||||||
|
[Argument] [varchar](50) NULL,
|
||||||
|
CONSTRAINT [PK_VNC_ServerList] PRIMARY KEY CLUSTERED
|
||||||
|
(
|
||||||
|
[User] ASC,
|
||||||
|
[IP] ASC
|
||||||
|
)
|
||||||
|
)";
|
||||||
|
|
||||||
|
using (var command = new SqlCommand(checkTableSql, connection))
|
||||||
|
{
|
||||||
|
command.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<VNCServer> GetAllServers()
|
||||||
|
{
|
||||||
|
var servers = new List<VNCServer>();
|
||||||
|
|
||||||
|
using (var connection = new SqlConnection(_connectionString))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
string sql = "SELECT * FROM VNC_ServerList ORDER BY [User]";
|
||||||
|
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
using (var reader = command.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
servers.Add(new VNCServer
|
||||||
|
{
|
||||||
|
User = reader["User"].ToString(),
|
||||||
|
IP = reader["IP"].ToString(),
|
||||||
|
Category = reader["Category"] == DBNull.Value ? null : reader["Category"].ToString(),
|
||||||
|
Description = reader["Description"] == DBNull.Value ? null : reader["Description"].ToString(),
|
||||||
|
Password = reader["Password"] == DBNull.Value ? null : reader["Password"].ToString(),
|
||||||
|
Argument = reader["Argument"] == DBNull.Value ? null : reader["Argument"].ToString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return servers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VNCServer GetServerByUserAndIP(string user, string ip)
|
||||||
|
{
|
||||||
|
using (var connection = new SqlConnection(_connectionString))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
string sql = "SELECT * FROM VNC_ServerList WHERE [User] = @User AND [IP] = @IP";
|
||||||
|
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
command.Parameters.AddWithValue("@User", user);
|
||||||
|
command.Parameters.AddWithValue("@IP", ip);
|
||||||
|
|
||||||
|
using (var reader = command.ExecuteReader())
|
||||||
|
{
|
||||||
|
if (reader.Read())
|
||||||
|
{
|
||||||
|
return new VNCServer
|
||||||
|
{
|
||||||
|
User = reader["User"].ToString(),
|
||||||
|
IP = reader["IP"].ToString(),
|
||||||
|
Category = reader["Category"] == DBNull.Value ? null : reader["Category"].ToString(),
|
||||||
|
Description = reader["Description"] == DBNull.Value ? null : reader["Description"].ToString(),
|
||||||
|
Password = reader["Password"] == DBNull.Value ? null : reader["Password"].ToString(),
|
||||||
|
Argument = reader["Argument"] == DBNull.Value ? null : reader["Argument"].ToString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AddServer(VNCServer server)
|
||||||
|
{
|
||||||
|
using (var connection = new SqlConnection(_connectionString))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
string sql = @"
|
||||||
|
INSERT INTO VNC_ServerList ([User], [IP], [Category], [Description], [Password], [Argument])
|
||||||
|
VALUES (@User, @IP, @Category, @Description, @Password, @Argument)";
|
||||||
|
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
command.Parameters.AddWithValue("@User", server.User);
|
||||||
|
command.Parameters.AddWithValue("@IP", server.IP);
|
||||||
|
command.Parameters.AddWithValue("@Category", (object)server.Category ?? DBNull.Value);
|
||||||
|
command.Parameters.AddWithValue("@Description", (object)server.Description ?? DBNull.Value);
|
||||||
|
command.Parameters.AddWithValue("@Password", (object)server.Password ?? DBNull.Value);
|
||||||
|
command.Parameters.AddWithValue("@Argument", (object)server.Argument ?? DBNull.Value);
|
||||||
|
|
||||||
|
return command.ExecuteNonQuery() > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UpdateServer(VNCServer server)
|
||||||
|
{
|
||||||
|
using (var connection = new SqlConnection(_connectionString))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
string sql = @"
|
||||||
|
UPDATE VNC_ServerList
|
||||||
|
SET [Category] = @Category, [Description] = @Description,
|
||||||
|
[Password] = @Password, [Argument] = @Argument
|
||||||
|
WHERE [User] = @User AND [IP] = @IP";
|
||||||
|
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
command.Parameters.AddWithValue("@User", server.User);
|
||||||
|
command.Parameters.AddWithValue("@IP", server.IP);
|
||||||
|
command.Parameters.AddWithValue("@Category", (object)server.Category ?? DBNull.Value);
|
||||||
|
command.Parameters.AddWithValue("@Description", (object)server.Description ?? DBNull.Value);
|
||||||
|
command.Parameters.AddWithValue("@Password", (object)server.Password ?? DBNull.Value);
|
||||||
|
command.Parameters.AddWithValue("@Argument", (object)server.Argument ?? DBNull.Value);
|
||||||
|
|
||||||
|
return command.ExecuteNonQuery() > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DeleteServer(string user, string ip)
|
||||||
|
{
|
||||||
|
using (var connection = new SqlConnection(_connectionString))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
string sql = "DELETE FROM VNC_ServerList WHERE [User] = @User AND [IP] = @IP";
|
||||||
|
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
command.Parameters.AddWithValue("@User", user);
|
||||||
|
command.Parameters.AddWithValue("@IP", ip);
|
||||||
|
return command.ExecuteNonQuery() > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
Services/VNCService.cs
Normal file
69
Services/VNCService.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using VNCServerList.Models;
|
||||||
|
|
||||||
|
namespace VNCServerList.Services
|
||||||
|
{
|
||||||
|
public class VNCService
|
||||||
|
{
|
||||||
|
private readonly string _vncViewerPath;
|
||||||
|
private readonly DatabaseService _databaseService;
|
||||||
|
|
||||||
|
public VNCService(DatabaseService databaseService)
|
||||||
|
{
|
||||||
|
_vncViewerPath = @"C:\Program Files\TightVNC\tvnviewer.exe";
|
||||||
|
_databaseService = databaseService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ConnectToServer(VNCServer server)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!File.Exists(_vncViewerPath))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException($"VNC Viewer를 찾을 수 없습니다: {_vncViewerPath}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// VNC Viewer 실행
|
||||||
|
var startInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = _vncViewerPath,
|
||||||
|
Arguments = $"-host={server.IP} {server.Argument}",
|
||||||
|
UseShellExecute = true
|
||||||
|
};
|
||||||
|
|
||||||
|
Process.Start(startInfo);
|
||||||
|
|
||||||
|
// 연결 성공 (마지막 연결 시간 업데이트는 현재 테이블 구조에 없으므로 제거)
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"VNC 연결 중 오류가 발생했습니다: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ConnectToServer(string user, string ip)
|
||||||
|
{
|
||||||
|
var server = _databaseService.GetServerByUserAndIP(user, ip);
|
||||||
|
if (server == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"서버 {user}@{ip}를 찾을 수 없습니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConnectToServer(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsVNCViewerInstalled()
|
||||||
|
{
|
||||||
|
return File.Exists(_vncViewerPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetVNCViewerPath()
|
||||||
|
{
|
||||||
|
return _vncViewerPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
105
VNCServerList.csproj
Normal file
105
VNCServerList.csproj
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{0403AC4C-8858-4ACE-8D66-9EB307503B04}</ProjectGuid>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<RootNamespace>VNCServerList</RootNamespace>
|
||||||
|
<AssemblyName>VNCServerList</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Deployment" />
|
||||||
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Windows.Forms" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="System.Web" />
|
||||||
|
<Reference Include="System.Configuration" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2151.40" />
|
||||||
|
<PackageReference Include="Microsoft.Owin.Hosting" Version="4.2.2" />
|
||||||
|
<PackageReference Include="Microsoft.Owin.Host.HttpListener" Version="4.2.2" />
|
||||||
|
<PackageReference Include="Microsoft.Owin.StaticFiles" Version="4.2.2" />
|
||||||
|
<PackageReference Include="Microsoft.AspNet.WebApi.Core" Version="5.2.9" />
|
||||||
|
<PackageReference Include="Microsoft.AspNet.WebApi.Owin" Version="5.2.9" />
|
||||||
|
<PackageReference Include="System.Data.SqlClient" Version="4.8.5" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Form1.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Form1.Designer.cs">
|
||||||
|
<DependentUpon>Form1.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Models\VNCServer.cs" />
|
||||||
|
<Compile Include="Services\DatabaseService.cs" />
|
||||||
|
<Compile Include="Services\VNCService.cs" />
|
||||||
|
<Compile Include="Web\Startup.cs" />
|
||||||
|
<Compile Include="Web\Controllers\VNCServerController.cs" />
|
||||||
|
<EmbeddedResource Include="Form1.resx">
|
||||||
|
<DependentUpon>Form1.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<None Include="Properties\Settings.settings">
|
||||||
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
|
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||||
|
</None>
|
||||||
|
<Compile Include="Properties\Settings.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Settings.settings</DependentUpon>
|
||||||
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Web\wwwroot\**\*">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="App.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
||||||
25
VNCServerList.sln
Normal file
25
VNCServerList.sln
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Express 15 for Windows Desktop
|
||||||
|
VisualStudioVersion = 15.0.36123.18
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VNCServerList", "VNCServerList.csproj", "{0403AC4C-8858-4ACE-8D66-9EB307503B04}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{0403AC4C-8858-4ACE-8D66-9EB307503B04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0403AC4C-8858-4ACE-8D66-9EB307503B04}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0403AC4C-8858-4ACE-8D66-9EB307503B04}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0403AC4C-8858-4ACE-8D66-9EB307503B04}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {4720988D-5D40-4AFC-8D65-C7AFD269D71B}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
173
Web/Controllers/VNCServerController.cs
Normal file
173
Web/Controllers/VNCServerController.cs
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Web.Http;
|
||||||
|
using VNCServerList.Models;
|
||||||
|
using VNCServerList.Services;
|
||||||
|
|
||||||
|
namespace VNCServerList.Web.Controllers
|
||||||
|
{
|
||||||
|
[RoutePrefix("api/vncserver")]
|
||||||
|
public class VNCServerController : ApiController
|
||||||
|
{
|
||||||
|
private readonly DatabaseService _databaseService;
|
||||||
|
private readonly VNCService _vncService;
|
||||||
|
|
||||||
|
public VNCServerController()
|
||||||
|
{
|
||||||
|
_databaseService = new DatabaseService();
|
||||||
|
_vncService = new VNCService(_databaseService);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("list")]
|
||||||
|
public IHttpActionResult GetServerList()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var servers = _databaseService.GetAllServers();
|
||||||
|
return Ok(servers);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return InternalServerError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("get/{user}/{ip}")]
|
||||||
|
public IHttpActionResult GetServer(string user, string ip)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var server = _databaseService.GetServerByUserAndIP(user, ip);
|
||||||
|
if (server == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
return Ok(server);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return InternalServerError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Route("add")]
|
||||||
|
public IHttpActionResult AddServer([FromBody] VNCServer server)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (server == null)
|
||||||
|
{
|
||||||
|
return BadRequest("서버 정보가 없습니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = _databaseService.AddServer(server);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
return Ok(new { Message = "서버가 성공적으로 추가되었습니다." });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return BadRequest("서버 추가에 실패했습니다.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return InternalServerError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut]
|
||||||
|
[Route("update")]
|
||||||
|
public IHttpActionResult UpdateServer([FromBody] VNCServer server)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (server == null)
|
||||||
|
{
|
||||||
|
return BadRequest("서버 정보가 없습니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = _databaseService.UpdateServer(server);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
return Ok(new { Message = "서버가 성공적으로 업데이트되었습니다." });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return InternalServerError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete]
|
||||||
|
[Route("delete/{user}/{ip}")]
|
||||||
|
public IHttpActionResult DeleteServer(string user, string ip)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool success = _databaseService.DeleteServer(user, ip);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
return Ok(new { Message = "서버가 성공적으로 삭제되었습니다." });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return InternalServerError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Route("connect/{user}/{ip}")]
|
||||||
|
public IHttpActionResult ConnectToServer(string user, string ip)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool success = _vncService.ConnectToServer(user, ip);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
return Ok(new { Message = "VNC 연결이 시작되었습니다." });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return BadRequest("VNC 연결에 실패했습니다.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return InternalServerError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("vnc-status")]
|
||||||
|
public IHttpActionResult GetVNCStatus()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool isInstalled = _vncService.IsVNCViewerInstalled();
|
||||||
|
string path = _vncService.GetVNCViewerPath();
|
||||||
|
|
||||||
|
return Ok(new {
|
||||||
|
IsInstalled = isInstalled,
|
||||||
|
Path = path
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return InternalServerError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
Web/Startup.cs
Normal file
41
Web/Startup.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using Microsoft.Owin;
|
||||||
|
using Microsoft.Owin.StaticFiles;
|
||||||
|
using Owin;
|
||||||
|
using System.Web.Http;
|
||||||
|
using VNCServerList.Web.Controllers;
|
||||||
|
|
||||||
|
namespace VNCServerList.Web
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public void Configuration(IAppBuilder app)
|
||||||
|
{
|
||||||
|
// Web API 설정
|
||||||
|
var config = new HttpConfiguration();
|
||||||
|
|
||||||
|
// 라우팅 설정
|
||||||
|
config.MapHttpAttributeRoutes();
|
||||||
|
|
||||||
|
config.Routes.MapHttpRoute(
|
||||||
|
name: "DefaultApi",
|
||||||
|
routeTemplate: "api/{controller}/{action}/{id}",
|
||||||
|
defaults: new { id = RouteParameter.Optional }
|
||||||
|
);
|
||||||
|
|
||||||
|
// JSON 포맷터 설정
|
||||||
|
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
|
||||||
|
|
||||||
|
app.UseWebApi(config);
|
||||||
|
|
||||||
|
// 정적 파일 서빙 설정
|
||||||
|
var options = new FileServerOptions
|
||||||
|
{
|
||||||
|
EnableDefaultFiles = true,
|
||||||
|
DefaultFilesOptions = { DefaultFileNames = { "index.html" } },
|
||||||
|
FileSystem = new Microsoft.Owin.FileSystems.PhysicalFileSystem("Web/wwwroot")
|
||||||
|
};
|
||||||
|
|
||||||
|
app.UseFileServer(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
117
Web/wwwroot/index.html
Normal file
117
Web/wwwroot/index.html
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ko">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>VNC 서버 목록 관리</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script>
|
||||||
|
tailwind.config = {
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
primary: '#3B82F6',
|
||||||
|
secondary: '#6B7280',
|
||||||
|
success: '#10B981',
|
||||||
|
danger: '#EF4444',
|
||||||
|
warning: '#F59E0B'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-50 min-h-screen">
|
||||||
|
<div class="container mx-auto px-4 py-8">
|
||||||
|
<!-- 헤더 -->
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6 mb-6">
|
||||||
|
<h1 class="text-3xl font-bold text-gray-800 mb-2">VNC 서버 목록 관리</h1>
|
||||||
|
<p class="text-gray-600">VNC 서버를 관리하고 연결할 수 있습니다.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 상태 표시 -->
|
||||||
|
<div id="status" class="mb-6"></div>
|
||||||
|
|
||||||
|
<!-- 서버 추가 버튼 -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<button id="addServerBtn" class="bg-primary hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-lg transition duration-200">
|
||||||
|
<svg class="w-5 h-5 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
||||||
|
</svg>
|
||||||
|
새 서버 추가
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 서버 목록 -->
|
||||||
|
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
||||||
|
<div class="px-6 py-4 border-b border-gray-200">
|
||||||
|
<h2 class="text-xl font-semibold text-gray-800">서버 목록</h2>
|
||||||
|
</div>
|
||||||
|
<div id="serverList" class="divide-y divide-gray-200">
|
||||||
|
<!-- 서버 목록이 여기에 동적으로 로드됩니다 -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 서버 추가/편집 모달 -->
|
||||||
|
<div id="serverModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden z-50">
|
||||||
|
<div class="flex items-center justify-center min-h-screen p-4">
|
||||||
|
<div class="bg-white rounded-lg shadow-xl max-w-md w-full">
|
||||||
|
<div class="px-6 py-4 border-b border-gray-200">
|
||||||
|
<h3 id="modalTitle" class="text-lg font-semibold text-gray-800">서버 추가</h3>
|
||||||
|
</div>
|
||||||
|
<form id="serverForm" class="p-6">
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="serverUser" class="block text-sm font-medium text-gray-700 mb-2">사용자</label>
|
||||||
|
<input type="text" id="serverUser" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="serverIp" class="block text-sm font-medium text-gray-700 mb-2">IP 주소</label>
|
||||||
|
<input type="text" id="serverIp" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="serverCategory" class="block text-sm font-medium text-gray-700 mb-2">카테고리</label>
|
||||||
|
<input type="text" id="serverCategory" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="serverDescription" class="block text-sm font-medium text-gray-700 mb-2">설명</label>
|
||||||
|
<textarea id="serverDescription" rows="2" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="serverPassword" class="block text-sm font-medium text-gray-700 mb-2">비밀번호</label>
|
||||||
|
<input type="password" id="serverPassword" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
|
||||||
|
</div>
|
||||||
|
<div class="mb-6">
|
||||||
|
<label for="serverArgument" class="block text-sm font-medium text-gray-700 mb-2">VNC 인수</label>
|
||||||
|
<input type="text" id="serverArgument" placeholder="-port=5900" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end space-x-3">
|
||||||
|
<button type="button" id="cancelBtn" class="px-4 py-2 text-gray-700 bg-gray-200 rounded-md hover:bg-gray-300 transition duration-200">취소</button>
|
||||||
|
<button type="submit" class="px-4 py-2 bg-primary text-white rounded-md hover:bg-blue-700 transition duration-200">저장</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 확인 모달 -->
|
||||||
|
<div id="confirmModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden z-50">
|
||||||
|
<div class="flex items-center justify-center min-h-screen p-4">
|
||||||
|
<div class="bg-white rounded-lg shadow-xl max-w-md w-full">
|
||||||
|
<div class="px-6 py-4 border-b border-gray-200">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-800">확인</h3>
|
||||||
|
</div>
|
||||||
|
<div class="p-6">
|
||||||
|
<p id="confirmMessage" class="text-gray-700 mb-6"></p>
|
||||||
|
<div class="flex justify-end space-x-3">
|
||||||
|
<button id="confirmCancel" class="px-4 py-2 text-gray-700 bg-gray-200 rounded-md hover:bg-gray-300 transition duration-200">취소</button>
|
||||||
|
<button id="confirmOk" class="px-4 py-2 bg-danger text-white rounded-md hover:bg-red-700 transition duration-200">확인</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="js/app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
305
Web/wwwroot/js/app.js
Normal file
305
Web/wwwroot/js/app.js
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
class VNCServerApp {
|
||||||
|
constructor() {
|
||||||
|
this.apiBase = '/api/vncserver';
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.bindEvents();
|
||||||
|
this.loadServerList();
|
||||||
|
this.checkVNCStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
bindEvents() {
|
||||||
|
// 서버 추가 버튼
|
||||||
|
document.getElementById('addServerBtn').addEventListener('click', () => {
|
||||||
|
this.showServerModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 모달 취소 버튼
|
||||||
|
document.getElementById('cancelBtn').addEventListener('click', () => {
|
||||||
|
this.hideServerModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 서버 폼 제출
|
||||||
|
document.getElementById('serverForm').addEventListener('submit', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.saveServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 확인 모달 이벤트
|
||||||
|
document.getElementById('confirmCancel').addEventListener('click', () => {
|
||||||
|
this.hideConfirmModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('confirmOk').addEventListener('click', () => {
|
||||||
|
this.executeConfirmAction();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadServerList() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${this.apiBase}/list`);
|
||||||
|
if (!response.ok) throw new Error('서버 목록을 불러올 수 없습니다.');
|
||||||
|
|
||||||
|
const servers = await response.json();
|
||||||
|
this.renderServerList(servers);
|
||||||
|
} catch (error) {
|
||||||
|
this.showError('서버 목록을 불러오는 중 오류가 발생했습니다: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderServerList(servers) {
|
||||||
|
const serverList = document.getElementById('serverList');
|
||||||
|
|
||||||
|
if (servers.length === 0) {
|
||||||
|
serverList.innerHTML = `
|
||||||
|
<div class="px-6 py-8 text-center text-gray-500">
|
||||||
|
<svg class="w-12 h-12 mx-auto mb-4 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
||||||
|
</svg>
|
||||||
|
<p>등록된 서버가 없습니다.</p>
|
||||||
|
<p class="text-sm">새 서버를 추가해보세요.</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
serverList.innerHTML = servers.map(server => `
|
||||||
|
<div class="px-6 py-4 hover:bg-gray-50 transition duration-200">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<div class="w-3 h-3 bg-green-500 rounded-full"></div>
|
||||||
|
<h3 class="text-lg font-medium text-gray-900">${this.escapeHtml(server.user)}@${this.escapeHtml(server.ip)}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 text-sm text-gray-600">
|
||||||
|
<p><span class="font-medium">IP:</span> ${this.escapeHtml(server.ip)}</p>
|
||||||
|
${server.category ? `<p class="mt-1"><span class="font-medium">카테고리:</span> ${this.escapeHtml(server.category)}</p>` : ''}
|
||||||
|
${server.description ? `<p class="mt-1"><span class="font-medium">설명:</span> ${this.escapeHtml(server.description)}</p>` : ''}
|
||||||
|
${server.argument ? `<p class="mt-1"><span class="font-medium">인수:</span> ${this.escapeHtml(server.argument)}</p>` : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex space-x-2">
|
||||||
|
<button onclick="app.connectToServer('${this.escapeHtml(server.user)}', '${this.escapeHtml(server.ip)}')" class="bg-primary hover:bg-blue-700 text-white px-3 py-1 rounded text-sm transition duration-200">
|
||||||
|
연결
|
||||||
|
</button>
|
||||||
|
<button onclick="app.editServer('${this.escapeHtml(server.user)}', '${this.escapeHtml(server.ip)}')" class="bg-secondary hover:bg-gray-600 text-white px-3 py-1 rounded text-sm transition duration-200">
|
||||||
|
편집
|
||||||
|
</button>
|
||||||
|
<button onclick="app.deleteServer('${this.escapeHtml(server.user)}', '${this.escapeHtml(server.ip)}', '${this.escapeHtml(server.user)}@${this.escapeHtml(server.ip)}')" class="bg-danger hover:bg-red-700 text-white px-3 py-1 rounded text-sm transition duration-200">
|
||||||
|
삭제
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkVNCStatus() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${this.apiBase}/vnc-status`);
|
||||||
|
if (!response.ok) throw new Error('VNC 상태를 확인할 수 없습니다.');
|
||||||
|
|
||||||
|
const status = await response.json();
|
||||||
|
this.showVNCStatus(status);
|
||||||
|
} catch (error) {
|
||||||
|
this.showError('VNC 상태 확인 중 오류가 발생했습니다: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showVNCStatus(status) {
|
||||||
|
const statusDiv = document.getElementById('status');
|
||||||
|
if (status.isInstalled) {
|
||||||
|
statusDiv.innerHTML = `
|
||||||
|
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
VNC Viewer가 설치되어 있습니다.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
statusDiv.innerHTML = `
|
||||||
|
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
VNC Viewer가 설치되어 있지 않습니다. 경로: ${status.path}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showServerModal(server = null) {
|
||||||
|
const modal = document.getElementById('serverModal');
|
||||||
|
const title = document.getElementById('modalTitle');
|
||||||
|
const form = document.getElementById('serverForm');
|
||||||
|
|
||||||
|
if (server) {
|
||||||
|
title.textContent = '서버 편집';
|
||||||
|
document.getElementById('serverUser').value = server.user;
|
||||||
|
document.getElementById('serverIp').value = server.ip;
|
||||||
|
document.getElementById('serverCategory').value = server.category || '';
|
||||||
|
document.getElementById('serverDescription').value = server.description || '';
|
||||||
|
document.getElementById('serverPassword').value = server.password || '';
|
||||||
|
document.getElementById('serverArgument').value = server.argument || '';
|
||||||
|
} else {
|
||||||
|
title.textContent = '서버 추가';
|
||||||
|
form.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
modal.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
hideServerModal() {
|
||||||
|
document.getElementById('serverModal').classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveServer() {
|
||||||
|
const serverUser = document.getElementById('serverUser').value;
|
||||||
|
const serverIp = document.getElementById('serverIp').value;
|
||||||
|
const serverData = {
|
||||||
|
user: serverUser,
|
||||||
|
ip: serverIp,
|
||||||
|
category: document.getElementById('serverCategory').value,
|
||||||
|
description: document.getElementById('serverDescription').value,
|
||||||
|
password: document.getElementById('serverPassword').value,
|
||||||
|
argument: document.getElementById('serverArgument').value
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = serverUser && serverIp ? `${this.apiBase}/update` : `${this.apiBase}/add`;
|
||||||
|
const method = serverUser && serverIp ? 'PUT' : 'POST';
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: method,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(serverData)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error('서버 저장에 실패했습니다.');
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
this.showSuccess(result.message);
|
||||||
|
this.hideServerModal();
|
||||||
|
this.loadServerList();
|
||||||
|
} catch (error) {
|
||||||
|
this.showError('서버 저장 중 오류가 발생했습니다: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async editServer(user, ip) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${this.apiBase}/get/${encodeURIComponent(user)}/${encodeURIComponent(ip)}`);
|
||||||
|
if (!response.ok) throw new Error('서버 정보를 불러올 수 없습니다.');
|
||||||
|
|
||||||
|
const server = await response.json();
|
||||||
|
this.showServerModal(server);
|
||||||
|
} catch (error) {
|
||||||
|
this.showError('서버 정보를 불러오는 중 오류가 발생했습니다: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteServer(user, ip, name) {
|
||||||
|
this.showConfirmModal(
|
||||||
|
`"${name}" 서버를 삭제하시겠습니까?`,
|
||||||
|
() => this.executeDeleteServer(user, ip)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeDeleteServer(user, ip) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${this.apiBase}/delete/${encodeURIComponent(user)}/${encodeURIComponent(ip)}`, {
|
||||||
|
method: 'DELETE'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error('서버 삭제에 실패했습니다.');
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
this.showSuccess(result.message);
|
||||||
|
this.loadServerList();
|
||||||
|
} catch (error) {
|
||||||
|
this.showError('서버 삭제 중 오류가 발생했습니다: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async connectToServer(user, ip) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${this.apiBase}/connect/${encodeURIComponent(user)}/${encodeURIComponent(ip)}`, {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error('VNC 연결에 실패했습니다.');
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
this.showSuccess(result.message);
|
||||||
|
} catch (error) {
|
||||||
|
this.showError('VNC 연결 중 오류가 발생했습니다: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showConfirmModal(message, onConfirm) {
|
||||||
|
document.getElementById('confirmMessage').textContent = message;
|
||||||
|
document.getElementById('confirmModal').classList.remove('hidden');
|
||||||
|
this.confirmAction = onConfirm;
|
||||||
|
}
|
||||||
|
|
||||||
|
hideConfirmModal() {
|
||||||
|
document.getElementById('confirmModal').classList.add('hidden');
|
||||||
|
this.confirmAction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
executeConfirmAction() {
|
||||||
|
if (this.confirmAction) {
|
||||||
|
this.confirmAction();
|
||||||
|
}
|
||||||
|
this.hideConfirmModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
showSuccess(message) {
|
||||||
|
this.showNotification(message, 'success');
|
||||||
|
}
|
||||||
|
|
||||||
|
showError(message) {
|
||||||
|
this.showNotification(message, 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
showNotification(message, type) {
|
||||||
|
const notification = document.createElement('div');
|
||||||
|
notification.className = `fixed top-4 right-4 px-6 py-3 rounded-lg text-white z-50 transition-all duration-300 transform translate-x-full ${
|
||||||
|
type === 'success' ? 'bg-green-500' : 'bg-red-500'
|
||||||
|
}`;
|
||||||
|
notification.textContent = message;
|
||||||
|
|
||||||
|
document.body.appendChild(notification);
|
||||||
|
|
||||||
|
// 애니메이션
|
||||||
|
setTimeout(() => {
|
||||||
|
notification.classList.remove('translate-x-full');
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
// 자동 제거
|
||||||
|
setTimeout(() => {
|
||||||
|
notification.classList.add('translate-x-full');
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(notification);
|
||||||
|
}, 300);
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
escapeHtml(text) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.textContent = text;
|
||||||
|
return div.innerHTML;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 앱 초기화
|
||||||
|
const app = new VNCServerApp();
|
||||||
Reference in New Issue
Block a user