1+ package config
2+
3+ import (
4+ "context"
5+ "encoding/json"
6+ "fmt"
7+ "os"
8+ "path/filepath"
9+
10+ "github.com/0xJacky/Nginx-UI/internal/config"
11+ "github.com/0xJacky/Nginx-UI/internal/helper"
12+ "github.com/0xJacky/Nginx-UI/internal/nginx"
13+ "github.com/mark3labs/mcp-go/mcp"
14+ )
15+
16+ const nginxConfigEnableToolName = "nginx_config_enable"
17+
18+ var nginxConfigEnableTool = mcp .NewTool (
19+ nginxConfigEnableToolName ,
20+ mcp .WithDescription ("Enable a previously created Nginx configuration (creates symlink in sites-enabled)" ),
21+ mcp .WithString ("name" , mcp .Description ("The name of the configuration file to enable" )),
22+ mcp .WithString ("base_dir" , mcp .Description ("The source directory (default: sites-available)" )),
23+ mcp .WithBoolean ("overwrite" , mcp .Description ("Whether to overwrite an existing enabled configuration" )),
24+ )
25+
26+ func handleNginxConfigEnable (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
27+ args := request .GetArguments ()
28+ name := args ["name" ].(string )
29+ baseDir := args ["base_dir" ].(string )
30+ overwrite := args ["overwrite" ].(bool )
31+
32+ if name == "" {
33+ return nil , fmt .Errorf ("argument 'name' is required" )
34+ }
35+
36+ // Default to sites-available if base_dir is not provided
37+ if baseDir == "" {
38+ baseDir = "sites-available"
39+ }
40+
41+ // Resolve Source Path (e.g., /etc/nginx/sites-available/my-site)
42+ // This is the file that must already exist.
43+ srcDir := nginx .GetConfPath (baseDir )
44+ srcPath := filepath .Join (srcDir , name )
45+
46+ // Ensure the resolved source path is actually inside the intended directory
47+ if ! helper .IsUnderDirectory (srcPath , srcDir ) {
48+ return nil , config .ErrPathIsNotUnderTheNginxConfDir
49+ }
50+
51+ // Validate Source Exists
52+ if _ , err := os .Stat (srcPath ); err != nil {
53+ return nil , fmt .Errorf ("source configuration file not found at %s: %w" , srcPath , err )
54+ }
55+
56+ sitesEnabledDir := nginx .GetConfPath ("sites-enabled" )
57+ dstPath := nginx .GetConfSymlinkPath (filepath .Join (sitesEnabledDir , name ))
58+
59+ // Ensure the link we are about to create doesn't point outside sites-enabled
60+ if ! helper .IsUnderDirectory (dstPath , sitesEnabledDir ) {
61+ return nil , config .ErrPathIsNotUnderTheNginxConfDir
62+ }
63+
64+ // Ensure destination directory exists
65+ if ! helper .FileExists (sitesEnabledDir ) {
66+ if err := os .MkdirAll (sitesEnabledDir , 0755 ); err != nil {
67+ return nil , fmt .Errorf ("failed to create sites-enabled directory: %w" , err )
68+ }
69+ }
70+
71+ // Check if Destination Already Exists
72+ if helper .FileExists (dstPath ) {
73+ if ! overwrite {
74+ return nil , fmt .Errorf ("configuration is already enabled (symlink exists at %s)" , dstPath )
75+ }
76+ // Remove existing symlink/file if overwrite is true
77+ if err := os .Remove (dstPath ); err != nil {
78+ return nil , fmt .Errorf ("failed to remove existing configuration at %s: %w" , dstPath , err )
79+ }
80+ }
81+
82+ // Create Symlink
83+ // We link srcPath -> dstPath
84+ if err := os .Symlink (srcPath , dstPath ); err != nil {
85+ return nil , fmt .Errorf ("failed to create symlink: %w" , err )
86+ }
87+
88+ // Test Nginx Configuration
89+ // As per internal/site/enable.go, we must verify config before reloading
90+ res := nginx .Control (nginx .TestConfig )
91+ if res .IsError () {
92+ // Revert change (remove symlink) if test fails to prevent breaking Nginx
93+ os .Remove (dstPath )
94+ return nil , fmt .Errorf ("nginx config test failed: %v" , res .GetError ())
95+ }
96+
97+ // Reload Nginx
98+ res = nginx .Control (nginx .Reload )
99+ if res .IsError () {
100+ return nil , fmt .Errorf ("nginx reload failed: %v" , res .GetError ())
101+ }
102+
103+ // Construct Success Response
104+ result := map [string ]string {
105+ "status" : "success" ,
106+ "message" : "Site enabled and Nginx reloaded successfully" ,
107+ "source" : srcPath ,
108+ "destination" : dstPath ,
109+ }
110+ jsonResult , _ := json .Marshal (result )
111+
112+ return mcp .NewToolResultText (string (jsonResult )), nil
113+
114+ }
0 commit comments