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