github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fs/rc/webgui/rc.go (about) 1 package webgui 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "path/filepath" 8 9 "github.com/rclone/rclone/fs" 10 "github.com/rclone/rclone/fs/rc" 11 ) 12 13 func init() { 14 rc.Add(rc.Call{ 15 Path: "pluginsctl/listTestPlugins", 16 AuthRequired: true, 17 Fn: rcListTestPlugins, 18 Title: "Show currently loaded test plugins", 19 Help: `Allows listing of test plugins with the rclone.test set to true in package.json of the plugin. 20 21 This takes no parameters and returns: 22 23 - loadedTestPlugins - list of currently available test plugins. 24 25 E.g. 26 27 rclone rc pluginsctl/listTestPlugins 28 `, 29 }) 30 } 31 32 func rcListTestPlugins(_ context.Context, _ rc.Params) (out rc.Params, err error) { 33 err = initPluginsOrError() 34 if err != nil { 35 return nil, err 36 } 37 return rc.Params{ 38 "loadedTestPlugins": filterPlugins(loadedPlugins, func(json *PackageJSON) bool { return json.isTesting() }), 39 }, nil 40 } 41 42 func init() { 43 rc.Add(rc.Call{ 44 Path: "pluginsctl/removeTestPlugin", 45 AuthRequired: true, 46 Fn: rcRemoveTestPlugin, 47 Title: "Remove a test plugin", 48 Help: `This allows you to remove a plugin using it's name. 49 50 This takes the following parameters: 51 52 - name - name of the plugin in the format ` + "`author`/`plugin_name`" + `. 53 54 Example: 55 56 rclone rc pluginsctl/removeTestPlugin name=rclone/rclone-webui-react 57 `, 58 }) 59 } 60 func rcRemoveTestPlugin(_ context.Context, in rc.Params) (out rc.Params, err error) { 61 err = initPluginsOrError() 62 if err != nil { 63 return nil, err 64 } 65 name, err := in.GetString("name") 66 if err != nil { 67 return nil, err 68 } 69 err = loadedPlugins.removePlugin(name) 70 if err != nil { 71 return nil, err 72 } 73 return nil, nil 74 } 75 76 func init() { 77 rc.Add(rc.Call{ 78 Path: "pluginsctl/addPlugin", 79 AuthRequired: true, 80 Fn: rcAddPlugin, 81 Title: "Add a plugin using url", 82 Help: `Used for adding a plugin to the webgui. 83 84 This takes the following parameters: 85 86 - url - http url of the github repo where the plugin is hosted (http://github.com/rclone/rclone-webui-react). 87 88 Example: 89 90 rclone rc pluginsctl/addPlugin 91 `, 92 }) 93 } 94 95 func rcAddPlugin(_ context.Context, in rc.Params) (out rc.Params, err error) { 96 err = initPluginsOrError() 97 if err != nil { 98 return nil, err 99 } 100 pluginURL, err := in.GetString("url") 101 if err != nil { 102 return nil, err 103 } 104 105 author, repoName, repoBranch, err := getAuthorRepoBranchGitHub(pluginURL) 106 if err != nil { 107 return nil, err 108 } 109 110 branch, err := in.GetString("branch") 111 if err != nil || branch == "" { 112 branch = repoBranch 113 } 114 115 version, err := in.GetString("version") 116 if err != nil || version == "" { 117 version = "latest" 118 } 119 120 err = CreatePathIfNotExist(PluginsPath) 121 if err != nil { 122 return nil, err 123 } 124 125 // fetch and package.json 126 // https://raw.githubusercontent.com/rclone/rclone-webui-react/master/package.json 127 128 pluginID := fmt.Sprintf("%s/%s", author, repoName) 129 130 currentPluginPath := filepath.Join(PluginsPath, pluginID) 131 132 err = CreatePathIfNotExist(currentPluginPath) 133 if err != nil { 134 return nil, err 135 } 136 137 packageJSONUrl := fmt.Sprintf("https://raw.githubusercontent.com/%s/%s/%s/package.json", author, repoName, branch) 138 packageJSONFilePath := filepath.Join(currentPluginPath, "package.json") 139 err = DownloadFile(packageJSONFilePath, packageJSONUrl) 140 if err != nil { 141 return nil, err 142 } 143 // register in plugins 144 145 // download release and save in plugins/<author>/repo-name/app 146 // https://api.github.com/repos/rclone/rclone-webui-react/releases/latest 147 releaseURL, tag, _, err := GetLatestReleaseURL(fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/%s", author, repoName, version)) 148 if err != nil { 149 return nil, err 150 } 151 zipName := tag + ".zip" 152 zipPath := filepath.Join(currentPluginPath, zipName) 153 154 err = DownloadFile(zipPath, releaseURL) 155 if err != nil { 156 return nil, err 157 } 158 159 extractPath := filepath.Join(currentPluginPath, "app") 160 161 err = CreatePathIfNotExist(extractPath) 162 if err != nil { 163 return nil, err 164 } 165 err = os.RemoveAll(extractPath) 166 if err != nil { 167 fs.Logf(nil, "No previous downloads to remove") 168 } 169 170 fs.Logf(nil, "Unzipping plugin binary") 171 172 err = Unzip(zipPath, extractPath) 173 if err != nil { 174 return nil, err 175 } 176 177 err = loadedPlugins.addPlugin(pluginID, packageJSONFilePath) 178 if err != nil { 179 return nil, err 180 } 181 182 return nil, nil 183 184 } 185 186 func init() { 187 rc.Add(rc.Call{ 188 Path: "pluginsctl/listPlugins", 189 AuthRequired: true, 190 Fn: rcGetPlugins, 191 Title: "Get the list of currently loaded plugins", 192 Help: `This allows you to get the currently enabled plugins and their details. 193 194 This takes no parameters and returns: 195 196 - loadedPlugins - list of current production plugins. 197 - testPlugins - list of temporarily loaded development plugins, usually running on a different server. 198 199 E.g. 200 201 rclone rc pluginsctl/listPlugins 202 `, 203 }) 204 } 205 206 func rcGetPlugins(_ context.Context, _ rc.Params) (out rc.Params, err error) { 207 err = initPluginsOrError() 208 if err != nil { 209 return nil, err 210 } 211 err = loadedPlugins.readFromFile() 212 if err != nil { 213 return nil, err 214 } 215 return rc.Params{ 216 "loadedPlugins": filterPlugins(loadedPlugins, func(packageJSON *PackageJSON) bool { return !packageJSON.isTesting() }), 217 "loadedTestPlugins": filterPlugins(loadedPlugins, func(packageJSON *PackageJSON) bool { return packageJSON.isTesting() }), 218 }, nil 219 } 220 221 func init() { 222 rc.Add(rc.Call{ 223 Path: "pluginsctl/removePlugin", 224 AuthRequired: true, 225 Fn: rcRemovePlugin, 226 Title: "Remove a loaded plugin", 227 Help: `This allows you to remove a plugin using it's name. 228 229 This takes parameters: 230 231 - name - name of the plugin in the format ` + "`author`/`plugin_name`" + `. 232 233 E.g. 234 235 rclone rc pluginsctl/removePlugin name=rclone/video-plugin 236 `, 237 }) 238 } 239 240 func rcRemovePlugin(_ context.Context, in rc.Params) (out rc.Params, err error) { 241 err = initPluginsOrError() 242 if err != nil { 243 return nil, err 244 } 245 name, err := in.GetString("name") 246 if err != nil { 247 return nil, err 248 } 249 250 err = loadedPlugins.removePlugin(name) 251 if err != nil { 252 return nil, err 253 } 254 return nil, nil 255 } 256 257 func init() { 258 rc.Add(rc.Call{ 259 Path: "pluginsctl/getPluginsForType", 260 AuthRequired: true, 261 Fn: rcGetPluginsForType, 262 Title: "Get plugins with type criteria", 263 Help: `This shows all possible plugins by a mime type. 264 265 This takes the following parameters: 266 267 - type - supported mime type by a loaded plugin e.g. (video/mp4, audio/mp3). 268 - pluginType - filter plugins based on their type e.g. (DASHBOARD, FILE_HANDLER, TERMINAL). 269 270 Returns: 271 272 - loadedPlugins - list of current production plugins. 273 - testPlugins - list of temporarily loaded development plugins, usually running on a different server. 274 275 Example: 276 277 rclone rc pluginsctl/getPluginsForType type=video/mp4 278 `, 279 }) 280 } 281 282 func rcGetPluginsForType(_ context.Context, in rc.Params) (out rc.Params, err error) { 283 err = initPluginsOrError() 284 if err != nil { 285 return nil, err 286 } 287 handlesType, err := in.GetString("type") 288 if err != nil { 289 handlesType = "" 290 } 291 292 pluginType, err := in.GetString("pluginType") 293 if err != nil { 294 pluginType = "" 295 } 296 var loadedPluginsResult map[string]PackageJSON 297 298 var loadedTestPluginsResult map[string]PackageJSON 299 300 if pluginType == "" || pluginType == "FileHandler" { 301 302 loadedPluginsResult = filterPlugins(loadedPlugins, func(packageJSON *PackageJSON) bool { 303 for i := range packageJSON.Rclone.HandlesType { 304 if packageJSON.Rclone.HandlesType[i] == handlesType && !packageJSON.Rclone.Test { 305 return true 306 } 307 } 308 return false 309 }) 310 311 loadedTestPluginsResult = filterPlugins(loadedPlugins, func(packageJSON *PackageJSON) bool { 312 for i := range packageJSON.Rclone.HandlesType { 313 if packageJSON.Rclone.HandlesType[i] == handlesType && packageJSON.Rclone.Test { 314 return true 315 } 316 } 317 return false 318 }) 319 } else { 320 loadedPluginsResult = filterPlugins(loadedPlugins, func(packageJSON *PackageJSON) bool { 321 return packageJSON.Rclone.PluginType == pluginType && !packageJSON.isTesting() 322 }) 323 324 loadedTestPluginsResult = filterPlugins(loadedPlugins, func(packageJSON *PackageJSON) bool { 325 return packageJSON.Rclone.PluginType == pluginType && packageJSON.isTesting() 326 }) 327 } 328 329 return rc.Params{ 330 "loadedPlugins": loadedPluginsResult, 331 "loadedTestPlugins": loadedTestPluginsResult, 332 }, nil 333 334 }