Closed terreng closed 1 year ago
That sounds like a really good idea! I think the snippet of json you have is a really good idea on how to store the data I have some questions about the feature
I think this sounds like a really good idea!
Great questions! And I'm glad you like the idea.
- Would there be a place in the app you can browse and download these plugins or would you need to do so manually?
I'm not sure about adding a "plugin store" type of thing into the app, because I'm not sure the best way to handle plugins that other people make, and I think we might run into issues with app store approval if we do that. I think it would be easiest to have them downloadable on the website, and easy to drag and drop into the program. But thanks for the idea, I'll keep thinking about it.
- Security. We cant just validate a key if we are transferring files over the internet. Something like how npm does it we need to find a way to validate the security of the javascript file before running it. This would be difficult for me. Do you know how I/we could do this?
Do you just mean making sure that the file wasn't modified during download?
- Unhandled requests. How would we respond unhandled requests. They will be caught and logged (I added a listener for whenever anything throws an error so we at least have it)
What do you mean? Do you mean requests that a plugin just doesn't "return" for? Seeing as the program would be executing javascript in the plugin files, there's only so much you can do to keep it on the rails. A broken plugin could make a server fail to start, hang, or maybe even crash the program. I don't see this as an issue though, because people should only be using plugins that work and aren't malicious.
- Ability to disable built-in options would be needed. Could we do this?
Do you mean forcing an option to have a specific value, and preventing it from being changed in the UI? If so, what's an example use case? I was thinking a plugin could just ignore an option if it wants to.
- With the proxy feature at least that would completely handle the request itself. I think we need a boolean value in the json that will tell the server whether or not to handle the request afterwards.
- The proxy plugin at least needs to set up a websocket listener when the server object is initialized. I think there should be an option for a function to run on server startup that provides the server object.
The config file could specify multiple "hooks", like before_start
, before_request
, before_response
, etc.
I'm not sure about adding a "plugin store" type of thing into the app, because I'm not sure the best way to handle plugins that other people make, and I think we might run into issues with app store approval if we do that. I think it would be easiest to have them downloadable on the website, and easy to drag and drop into the program. But thanks for the idea, I'll keep thinking about it.
Makes sense.
Do you just mean making sure that the file wasn't modified during download?
Yes, to make sure the request wasnt intercepted. This is more for if we decide to do the plugin store thing.
What do you mean? Do you mean requests that a plugin just doesn't "return" for? Seeing as the program would be executing javascript in the plugin files, there's only so much you can do to keep it on the rails. A broken plugin could make a server fail to start, hang, or maybe even crash the program. I don't see this as an issue though, because people should only be using plugins that work and aren't malicious.
Im saying that what if an error is thrown processing the request. Would we be able to respond to it?
Do you mean forcing an option to have a specific value, and preventing it from being changed in the UI? If so, what's an example use case? I was thinking a plugin could just ignore an option if it wants to.
The proxy option wouldnt need options like htaccess or choose path or any of the upload/deleting options and it seems really bulky to leave un-needed options in the UI.
The config file could specify multiple "hooks", like before_start, before_request, before_response, etc.
Sounds good.
Im saying that what if an error is thrown processing the request. Would we be able to respond to it?
I guess this depends on how exactly we implement it. I believe server side javascript is currently implemented using eval, right? Is it possible to execute a function from a file, and give it access to context like res, req, and opts?
It would be great if you could look in to how plugins would be implemented, and what hooks it makes sense to have.
The proxy option wouldnt need options like htaccess or choose path or any of the upload/deleting options and it seems really bulky to leave un-needed options in the UI.
I see. Let me think about this one.
I believe server side javascript is currently implemented using eval, right?
Correct
Is it possible to execute a function from a file, and give it access to context like res, req, and opts?
I think the only thing we could really do is what I already have, to surround it with a try/catch block, although it wouldnt be caught if it went into a callback function
It would be great if you could look in to how plugins would be implemented, and what hooks it makes sense to have.
Will do. We would probably need at least the following. Let me know if I am missing any
Functions:
onStart
: will be called when the server is created. Will include the server object
onRequest
: will be called when an incoming request is received. The arguments will be the req
and res
objects along with a preventDefault
function (for when the plugin is made to handle the entire request, will cancel the default processing of reading and sending the file.)
JSON args: id, options, version, main (as you showed in the screenshot). and probably (If you agree) a json array of the options to hide.
I think this is all. Let me know if there are any things I am missing.
I see. Let me think about this one.
Sounds good
Good idea with using a preventDefault. Using a try/catch sounds like the best approach.
As for disabling options, I'm thinking about how users would active plugins on servers. I was thinking the plugin's options would show up on every server, but maybe instead the user would specifically enable the plugin on each server. If we did it this way, then it would make more sense for certain options to disappear when you do that. I'll keep thinking about how best to implement it.
Sounds good!
Another good idea is maybe to also have an option in the json file that tells the app that no other plugins can be enabled while this plugin is in use, because with complicated plugins (like the proxy plugin) additional plugins may break the plugin
@terreng I have pushed the beta plugin code to the plugin-work
branch. Let me know what you think.
Here is a simple plugin that all it does is either override the response and sends something itself or it just adds a test header. You can change which in the main.js
file
Looks good! Thanks for the example plugin. Could you make a new draft pr from that branch that closes this issue?
Thanks for working to make the code more modular by moving bookmarks stuff to its own file! However, the change actually breaks the code because mas_bookmarks
and addToSecurityScopedBookmarks
are used in index.js
. Do you think that you could fix that?
When it comes to options, I was thinking that they could be stored as [pluginid].[optionname]
in the config file. (For example, for the proxy plugin manifest in my screenshot above, the enabled option would be stored under proxy.enabled
) So plugin options are stored together with built in options, but prefixed with the plugin's id. The plugin config file should have an id
field, and that should be used to differentiate plugins internally. If you install a new plugin with the same id as an existing plugin, it replaces the existing one (like for updating plugins). So the id field should be unique per plugin, but the display name can vary.
When it comes to options, I was thinking that they could be stored as [pluginid].[optionname] in the config file
I actually have a better idea. If the plugin value was an array this array could be filled with the plugin info along with the config of the plugin. I went ahead and did this because its much cleaner, let me know if you want to change it.
the change actually breaks the code because mas_bookmarks and addToSecurityScopedBookmarks are used in index.js. Do you think that you could fix that?
Fixed.
I also changed the thing to use require (do we need to call a bookmark thing for require if its outside the app?)
Let me know how it looks so far.
Also, I updated the example plugin to go along with me using require to execute the main file. Heres the new example
@terreng if I’m correct I finished all the backend stuff. Let me know if there’s anything more I need to do
Could you now turn the proxy feature into a plugin? This can be a separate repo that you own.
Could you now turn the proxy feature into a plugin?
Will do, one question first.
What should the file that stores the information be called? I set it to manifest.json
for testing but this can interfere if the plugin uses nodejs (like the proxy plugin). What should the json info file be named?
I also think we should add the ability to import zip files (so the user could download the zip and just import without having to unpack). Maybe JSZip to unpack this to the plugins folder?
Also, thought I'd explain what all the plugin functions do.
registerPlugins(data)
: for when the server is started, attaches any plugins to the server.
data
: the this_server
object
Returns the needed callbacks along with other data required to operate the function (stored in this_server.plugin
)
importPlugin(path, config)
: to import a plugin to the server
path
: The path to the plugin folder.
config
: The serverconfig.plugin
value.
Returns the new value to be set to serverconfig.plugin
and saved.
removePlugin(id, serverConfig, config)
: removes the plugin.
id
: Id of plugin to remove
serverConfig
: The array of all the servers (used to check if the server needs to be deleted from the plugins folder).
config
: The serverconfig.plugin
object of the server we are removing the plugin from.
Returns the new serverconfig.plugin
object to be set and saved.
How about plugin.json
?
Yeah, if the user adds a zip file, we can unzip it and add it to the plugins directory.
Here's what I'm thinking for the UI: A place to view the list of installed plugins, add new ones, and remove plugins. On each server, an option to enable/disable each plugin on that server. I'm thinking that each plugin has an options section on every server, with a switch on it to enable/disable the plugin for that server.
I'm a little unclear on how it is implemented now. Are plugins added per server, or to all the servers?
Plugins should be installed/uninstalled at the app level.
Once a plugin is installed, it should be enabled/disabled per server.
How about
plugin.json
?
Got it.
I'm a little unclear on how it is implemented now. Are plugins added per server, or to all the servers?
per server
I'm a little unclear on how it is implemented now. Are plugins added per server, or to all the servers?
I currently have it per server (kind of) I should probably do what you suggested. I have more details in the reply below.
Once a plugin is installed, it should be enabled/disabled per server.
So you're saying to have a separate way to delete the plugin entirely? This wouldn't be too hard. I would just have the remove plugin function instead delete it from the plugin array and create a new function to remove it from the app data folder (to delete it entirely). Also I would need, in the import function, to replace the path with the id of the plugin to register, and have a separate function to store the plugin in the app data folder. This wouldn't be too hard.
Here's what I'm thinking. Let me know if you can do this or if you think we should do it differently:
Installing plugins
Add a folder to the /plugins
directory of the app data folder. The plugin folder name doesn't matter. Inside it must have a plugin.json file to be recognized.
Example:
- plugins
- proxy
- plugin.json
- script.js
In the app, installing a plugin through the UI adds a folder to /plugins
. Uninstalling a plugin deletes the corresponding folder. The app determines the list of available plugins by scanning the plugins directory.
Enabling plugins per server
Let's have a new server option called plugins
. It's an object, and by default it's empty {}
.
A plugin is enabled on a server if its id is a key of the object. For example, this value of the plugins
option would indicate that the proxy
plugin is enabled on the server:
{
"proxy": {}
}
Inside the inner object is where the proxy plugin stores the values for its options.
As you can see, installing and uninstalling plugins does not change config.json in this model. If a plugin is uninstalled, its options stay within the servers, but they are ignored because no plugin exists with that id.
So basically all the import function will do is copy the folder? No set array of installed plugins?
So basically will there be an ID for each server and in the config of the plugin will that server ID be added and the value of that server thingy the values of the additional UI options?
If a plugin is uninstalled, its options stay within the servers, but they are ignored because no plugin exists with that id.
I’m a little confused by this. What options stay within the servers if we are not editing the config.json?
Also, are we going to support the ability to hide unnecessary options from the UI?
So basically all the import function will do is copy the folder? No set array of installed plugins?
Yes. Installing a plugin just means adding a folder to /plugins
. The list of installed plugins isn't stored in any config file.
So basically will there be an ID for each server and in the config of the plugin will that server ID be added and the value of that server thingy the values of the additional UI options?
If a plugin is uninstalled, its options stay within the servers, but they are ignored because no plugin exists with that id.
I’m a little confused by this. What options stay within the servers if we are not editing the config.json?
I'm not sure what you meant by each server having an id. Each plugin has an id, which is set in plugin.json
. Plugin options are stored per-server. So a plugin might be configured differently on every server, or only be enabled on certain servers.
In my previous comment I said: "A plugin is enabled on a server if its id is a key of the object." I changed my mind about this. A plugin should be enabled on a server if its enabled option is set to true on that server. Let's give every plugin an enabled
option, which is required and reserved. The plugin code only runs if the enabled
option is set to true.
Here is an example of what config.json
might look like with one server:
{
"servers": [
{
"enabled": true,
"path": "/Users/username/Documents/GitHub/website",
"localnetwork": false,
"index": true,
"port": 8080,
"cors": false,
"showIndex": true,
"spa": false,
"rewriteTo": "/index.html",
"directoryListing": true,
"excludeDotHtml": false,
"ipv6": false,
"cacheControl": "",
"hiddenDotFiles": false,
"upload": false,
"replace": false,
"delete": false,
"staticDirectoryListing": false,
"hiddenDotFilesDirectoryListing": true,
"htaccess": false,
"custom404": "",
"custom403": "",
"custom401": "",
"customErrorReplaceString": "",
"https": false,
"httpsCert": "",
"httpsKey": "",
"httpAuth": false,
"httpAuthUsername": "",
"httpAuthPassword": "",
"ipThrottling": 10,
"plugins": {
"proxy": {
"enabled": false,
"site2Proxy": "https://simplewebserver.org"
},
"test": {
"enabled": true
}
}
}
],
"background": true,
"updates": true
}
In this example, the one server has only the test
plugin enabled. Notice that the proxy
plugin has additional configuration options, which are still stored even though it is not enabled on this server. That way you can toggle a plugin without losing your options.
Let's say you uninstalled the test
plugin. This just means removing its folder from /plugins
. This would not change config.json
. It would still have the test plugin in the config for this server, and its enabled option would still be true. However, because the plugin is not installed, this will be ignored. That way, if the user installs the plugin again, they won't lose their previously chosen options.
Please let me know if this makes sense, or if you have any thoughts about it.
Also, are we going to support the ability to hide unnecessary options from the UI?
Would most plugins need this option? I pictured most plugins being things that don't completely change the server. For example, would it be possible to implement htaccess files using only a plugin?
I see, so it’s very similar to what I currently have. I’ll get that done
Would most plugins need this option?
Any plugins that completely change the way the server behaves. Most, I’m not sure.
I pictured most plugins being things that don't completely change the server.
I think they could be both ways, in my view most of the things that don’t completely change the server can be done with htaccess so wouldn’t the more complicated things that change the way the server behaves be used here?
For example, would it be possible to implement htaccess files using only a plugin?
Yes, this would be completely possible. You can do anything with the plugins (like re-write the entire handler (that’s similar to what web server chrome was for if you look at the readme for the project.)) what do you think?
what do you think?
Do you want to make the htaccess feature a plugin?
Also, is this guide for custom scripts, or something else? https://simplewebserver.org/docs/custom%20request%20handler.html#step-1-create-the-request
I'll add the UI for installing and uninstalling plugins, and changing their options on servers. Could you add the code that scans the /plugins
directory and populates a global plugins
variable with an array of their configs. Do you think we should make it so that the name of the plugin directory must match the plugin's id? This could be done at the time of install.
Do you want to make the htaccess feature a plugin?
I’d really rather not, the htaccess feature is incredibly embedded and would be a pain to remove.
Also, is this guide for custom scripts, or something else?
Yes, that’s is the custom scripts guide.
Could you add the code that scans the /plugins directory and populates a global plugins variable with an array of their configs.
Will do
Do you think we should make it so that the name of the plugin directory must match the plugin's id?
it doesn’t really matter, if you would like
it doesn’t really matter, if you would like
If we don't, then the global array of plugin configs needs to add a directory name attribute to each one.
Ok, yeah I can make it the ID.
Ok, here are the new functions
registerPlugins(data)
: attaches plugins to the server on server start
data
: the this_server
object
returns the necessary listeners to call the plugin
importPlugin(path)
: imports a plugin. Will not update the global.plugin
object.
path
: the path of the plugin to import
no return value
removePlugin(id)
: deletes the plugin. Will not update the global.plugin
object.
id
: id of plugin to remove
no return value
getInstalledPlugins()
: returns the manifest of each plugin, stored in json. used to populate the global.plugin
object on load.
returns a json object containing the installed plugins
activate(id, config)
: will activate the plugin for a specific server.
id
: the id of the plugin to activate
config
: the current serverconfig.plugin
value
returns the new value for serverconfig.plugin
deActivate(id, config)
: will deactivate the plugin for a specific server.
id
: the id of the plugin to deactivate
config
: the current serverconfig.plugin
value
returns the new value for serverconfig.plugin
let me know if you have any questions or if you find any bugs
Thank you!
@terreng The proxy plugin is ready! You can view it here - https://github.com/ethanaobrien/web-proxy (note, the web proxy index.js
file is a standalone version of the web proxy).
Let me know what you think!
I'll add plugins to the UI next week probably.
Also, let's add a few more optional arguments for plugin.json:
It's probably going to be another month before I work on this. Just wanted to give you an update.
All good! (I still haven’t found time to implement importing zip files either)
I've had a great experience using the program over the last few months. I'm really happy with what we've built together. 183 people have downloaded it from the Microsoft Store, 90 from the Mac App Store, and approximately 20-40 downloads from GitHub (I don't remember how many of them were me). We have a few 5 star ratings on the Microsoft Store, and one review that says "Great! Works well. Easy to use."
Awesome! It is really cool with what we built.
I was thinking, on some computers something like this is a little, heavy (on resources), what do you think about, instead of completely translating the whole app, having like a “mini version” with only the basics written in another language. What do you think about this. I think C# would be a choice we have, but I haven’t ever actually tried it on macOS and Linux and I don’t know if it requires downloading dot net or anything.
I have a simple C# server you can take a look at (it’s pretty close to being done, there are a few final bugs), and if you have any interest in doing it, if you go back in the commit history it was originally written in plain C. I don’t recommend using plain C though, it took me a single day to rewrite most of the features into C# that took me months in regular C (and it’s not easily cross platform). Here’s the project: https://github.com/ethanaobrien/C-server
I agree that the app isn't particularly resource efficient. I think the most notable part of this is the large file size. I like the idea of rewriting the web server code in something closer to the metal.
I've been keeping an eye on a cool new technology called Tauri, which has officially launched version 1.0. Tuari works kinda like Electron, but instead of bundling Chrome and Node, it uses the system webview (WebKit/Safari on macOS, Chromium on Windows 11) and Rust. Rust is nearly as fast as C, and has a really good ecosystem of packages (called crates).
What's great about switching to Tauri is that (pretty much) none of the UI would need to change! With Tauri, the app would be super memory efficient because only the Rust code would be running when the program isn't visible, and the package size would be WAY smaller because it wouldn't need to bundle Chromium.
I know I've mentioned Rust before. I think Rust would be a better choice than C/C# for this project because Tauri is built around it. I haven't done much in Rust, but I don't imagine it's any harder than C#.
What do you think about making a new branch and building a basic web server using Rust? It doesn't have to support all the features of the app, just more as a proof of concept / mini version. If that works out, then maybe we rewrite the rest of the web server code to use Rust? What do you think?
I think thats a good idea. Would you mind sharing with me a simple demo project and get me the link to the compiler and the docs (if any).
I dont know about re-writing the htaccess and plugins and server side functions. I think we should have a mini mode (like the one I described before) that on first launch it asks if you want to use the mini mode or the electron version and you can switch between the two (would require restart which is pretty obvious) but I think this is the best approach. What do you think?
Thanks!
You're right that the server side javascript stuff and plugins wouldn't work unless we found a way to execute javascript. The Tauri version of the app would have to give up those features, but I suppose people could still download the Electron version if they want those features.
I haven't messed around much with Tauri yet. Maybe follow the demo app tutorial to get a basic project, and see if you can figure out how to get a basic web server up and running. I can try getting a basic Tauri project started too, in a little while.
Hello, I finally got a chance to sit down and look into building a simple rust server today and it has proven to be very difficult. When compiling I kept getting an openssl utils compile error and everything I found online was all for Linux systems. Because of this I'm not sure how easy it would be to make it cross platform. What do you think @terreng ?
I also think if rust is too big of a pain that C# using dotnet could be another option, it would easily be cross platform (between windows, Linux, and macOS) and the only downside was that the user would need to install dotnet on their machine. C# is very light weight much like many other base languages and really isn’t hard. I actually have a pretty simple C# server here - https://github.com/ethanaobrien/C-server
For android support we could write an android specific version using android studio, this would enable maximum compatibility. I think if we do this we could also take in mind if we should do this for windows/macOS/Linux too, as in platform specific versions. If we’re going for efficiency between resources that would be the best option, since every language is going to have its catches. Let me know what you think
Do you want to share your code and the error with me? I have a feeling that this won't be too difficult to sort out. If you want, I can have a go at building a basic Rust prototype.
Very cool that you've written a web server in C#! However, the reason we should use Rust is because it's what Tauri primarily supports, and we need Tauri in order to copy over the existing user interface.
I don't think we need to build native versions for each platform. Using Rust and Tauri will already be significantly more efficient than Nodejs and Electron. Plus, we can write once and support Windows, macOS, and Linux. It looks like Tauri has mobile support coming soon too!
I really do think that Rust and Tauri is the way forward.
Here is what I have so far: rust server.zip
When I run cargo build
, the error I get is failed to run custom build command for 'openssl-sys-extras v0.7.14'
when compiling on windows
We might want to keep other languages as a possibility just in case something doesnt work, but hopefully Tauri works for what we need it to
@ethanaobrien I'm planning to work on building the UI for plugins in December - January. I'm also planning to add a dark mode UI #14, the ability to reorder servers #62, and help icons / documentation #66. This will also close this issue as well as #9. It'll be version 1.2.
Just to confirm, you've tested the proxy plugin and everything works, right? Is everything in that repo part of the plugin?
Would be great if we could also get fixes for #101 and #107 into version 1.2.
Just to confirm, you've tested the proxy plugin and everything works, right? Is everything in that repo part of the plugin?
The only thing I have left to do is the ability to import zips
Would be great if we could also get fixes for https://github.com/terreng/simple-web-server/issues/101 and https://github.com/terreng/simple-web-server/issues/107 into version 1.2.
I should be able to look at these this week. Sorry for the delay
The only thing I have left to do is the ability to import zips
You mean for the program? Or for the plugin?
To import plugins from a zip file. Sorry for the misunderstanding
This would just be unzipping the file and copying its contents to the plugins directory, right? I'm assuming we'll trigger that if you pick a zip file when adding a plugin in the UI.
Correct. On both statements
Okay sounds good. Could you make a PR that adds a function which takes in the path to a zip file and takes care of adding it?
Also, could you add a function that scans the plugin directory and returns an array of all the plugin configs?
As for the proxy plugin, is everything in the repo just for the plugin? It looked like a lot of files, so I wanted to check. If not, could you make a new repo just for the plugin? And could you add a little bit of documentation to the README explaining that it's a plugin for Simple Web Server and explaining what the arguments do?
Could you make a PR that adds a function which takes in the path to a zip file and takes care of adding it?
I can do that
Also, could you add a function that scans the plugin directory and returns an array of all the plugin configs?
I already have getInstalledPlugins
which returns an array of the installed plugins and then getPluginInfo
which will get you the manifest. Do you want me to combine these?
As for the proxy plugin, is everything in the repo just for the plugin?
Its actually a standalone app, with plugin compatibility. Yes everything in there is just for the proxy server. If you'd like me to make another repo, I could do so, but that would basically be the same thing in a different place.
And could you add a little bit of documentation to the README explaining that it's a plugin for Simple Web Server and explaining what the arguments do?
Can do
I already have
getInstalledPlugins
which returns an array of the installed plugins and thengetPluginInfo
which will get you the manifest. Do you want me to combine these?
Oh perfect! No need to combine, that's already great.
I think it would be better if you made it a separate repo with only the plugin, and included documentation.
@ethanaobrien Could you make the function that adds plugins from a ZIP work with directories too? I'm making it so that you can pick either a ZIP file or a directory from the UI. It would be great if, given the path, the function could check if it's a zip or not and do the right thing.
Also, I noticed that getInstalledPlugins
is called once when the program starts up. Could you make the function that adds a new plugin also refresh the list of plugins so that you don't have to restart the program to use them?
Finally, could you add a function that removes a plugin given its id? And have that refresh the list of plugins too so that it gets disabled immediately.
So far on #114 I've added dark mode, the ability to reorder servers, included documentation, and made it sync config changes in real time (so the UI updates when you edit config.json while the program is open).
I think can get this all done and the new version released by the end of the month, if you can finish up your part by then.
Could you make the function that adds plugins from a ZIP work with directories too?
In theory, the current input function should be able to handle both. Does it not work?
Also, I noticed that getInstalledPlugins is called once when the program starts up. Could you make the function that adds a new plugin also refresh the list of plugins so that you don't have to restart the program to use them?
Yeah, I can do that if you'd like. The getInstalledPlugins is actually called in the index.js file, not the plugin function itself and it just returns the set plugins, it itself doesn't set the plugins.
Finally, could you add a function that removes a plugin given its id? And have that refresh the list of plugins too so that it gets disabled immediately.
The removePlugin
function will remove a plug-in based on its id, it should at least. I can also add on that it will update the global plugins object if that's what you'd like
Oh, I missed that you already had written importPlugin
and removePlugin
. My bad.
It would be great if you could update them however needed so that adding and removing plugins takes effect immediately.
Also, could you use fs.watch
to watch for changes to the plugin directory, and reload the plugins when anything changes? This would enable someone to edit the plugin's javascript and have the changes take effect in real time, which would be cool.
Plugins would allow anyone to create additional features or customize the behavior of the app. We could have a list of additional plugins on the website. I think a plugins system would strike a nice balance between keeping the app simple, but also letting people take it further if they want additional functionality.
Plugins would consist of a config file and some javascript. The config file would have details like the plugin name and version, and an array of additional options (that could be checkboxes, textboxes, or number inputs). In the UI, it would add an extra options section for each plugin with these options. Then in the javascript, you could write a custom script that can apply custom logic to requests, like adding headers or turning it into a proxy. This would work like the custom scripts/custom request handler features you've already made - it would have access to
req
andres
, as well as the values of all the server options.Users could download plugins - possibly as a zip of multiple files or perhaps as one combined file with a custom file extension - and then add them into the app from the UI or by placing them in a directory within the app's data folder like
/plugins
.We'd want to make sure that it's easy to write basic plugins for things like adding additional headers. Ideally, this should be possible with only a few lines of code.
Alongside the introduction of plugins, I'd like you to create the first official plugin which adds the proxy feature.
Let me know what you think!
Here are some prototype/example screenshots of what it might look like: