Application Server: Difference between revisions
Contributor1 (talk | contribs) (→Notes) |
|||
| (69 intermediate revisions by 3 users not shown) | |||
| Line 3: | Line 3: | ||
= Microservice Architecture = |
= Microservice Architecture = |
||
The FullJS applications server follows a microservice-based server architecture with deep systemd integration. The |
The FullJS applications server follows a microservice-based server architecture with deep systemd integration. The application server is represented as a Systemd target ([application].target), with each individual microservice corresponding to a systemd unit instance ([application]@[microservice]). |
||
== Adding and Removing Microservices from the Application Server == |
== Adding and Removing Microservices from the Application Server == |
||
To add a microservice (a.k.a systemd unit instance) to the application server (a.k.a systemd target): |
|||
< |
<code> |
||
sudo systemctl enable [application]@[microservice] |
sudo systemctl enable [application]@[microservice] |
||
</ |
</code> |
||
To remove a microservice from the application server group: |
|||
< |
<code> |
||
sudo systemctl disable [application]@[microservice] |
sudo systemctl disable [application]@[microservice] |
||
</ |
</code> |
||
''Note:'' Enabling a microservice only registers it with the application server for automatic start at boot. It does **not** start the microservice immediately in the current session. |
|||
== Starting and Stopping a Microservice == |
|||
Each microservice runs as a separate systemd unit instance ([application]@[microservice]). |
|||
To start a microservice: |
|||
<code> |
|||
sudo systemctl start [application]@[microservice] |
|||
</code> |
|||
To stop a microservice: |
|||
<code> |
|||
sudo systemctl stop [application]@[microservice] |
|||
</code> |
|||
''Note:'' starting a microservice only affects the current session and does not configure it to start automatically at boot. |
|||
== Checking the Status of a Microservice == |
|||
Each microservice runs as a separate systemd unit instance (<code>[application]@[microservice]</code>). |
|||
* To check the current status of a microservice instance: |
|||
<code> |
|||
sudo systemctl status [application]@[microservice] |
|||
</code> |
|||
This command shows whether the service is running, stopped, or failed, along with recent log entries. |
|||
''Note:'' Enabling a microservice instance only registers it with the application target for automatic start at boot. It does **not** start the service immediately in the current session. |
|||
== Starting and Stopping |
== Starting and Stopping the application server == |
||
To start the application server use systemd’s start command. |
To start the application server use systemd’s start command. |
||
<code> |
<code> |
||
| Line 36: | Line 63: | ||
Keep in mind that starting an application server only affects the current session and does not configure it to start automatically at boot. |
Keep in mind that starting an application server only affects the current session and does not configure it to start automatically at boot. |
||
== Enabling and Disabling |
== Enabling and Disabling the application server == |
||
To start the application server automatically at boot, you must enable it. |
|||
The above commands are useful for starting or stopping an application server during the current session. |
|||
To tell systemd to start the application server automatically at boot, you must enable it. |
|||
To start the application server at boot: |
|||
<code>sudo systemctl enable [application].target</code> |
<code>sudo systemctl enable [application].target</code> |
||
To disable the application service from starting automatically: |
|||
<code>sudo systemctl disable [application].target</code> |
<code>sudo systemctl disable [application].target</code> |
||
Keep in mind that enabling an application |
Keep in mind that enabling an application server does not start it in the current session. |
||
= Filesystem Layout = |
|||
== Starting and Stopping a Microservice == |
|||
The application conforms to the Linux Filesystem Hierarchy Standard (FHS). Executables, data, runtime state, and configuration are separated cleanly: |
|||
Each microservice runs as a separate systemd unit instance ([application]@[microservice]). |
|||
To start a microservice: |
|||
* <code>/srv/[application]/[microservice]/</code> – contains the deployed application |
|||
<code> |
|||
* <code>/run/[application]/[microservice]/**</code> – runtime files (e.g., sockets, PID files) |
|||
* <code>/var/lib/[application]/[microservice]/**</code> – persistent state data (e.g., storage files) |
|||
</code> |
|||
* <code>/etc/[application]/[microservice]/**</code> – configuration files (if any) |
|||
= Plugin-based architecture = |
|||
To stop a microservice: |
|||
Note: This documentation is not final and may be updated as the architecture evolves. |
|||
<code> |
|||
sudo systemctl stop [application]@[microservice] |
|||
</code> |
|||
The plugin-based architecture allows the application server and other modules to be extended and customized by dynamically enabling plugins. Each plugin is a self-contained module and encapsulates a specific functionality, such as HTTP handling, authentication, permission management, systemd integration. This design decouples core module logic from optional features, making it easier to maintain, test, and evolve the system. |
|||
As with the application server, starting a microservice instance only affects the current session and does not configure it to start automatically at boot. |
|||
'''How it works:''' |
|||
# '''Core Server Class''': The main ''Server'' class provides the infrastructure for running the application and managing plugins. |
|||
== Filesystem Layout == |
|||
# '''Enabling a Plugin''': Plugins are enabled using the <code>@enable</code> decorator, which associates a plugin class with optional configuration options. Plugin options can configure ports, endpoints, or internal slots for routing. The <code>@enable</code> decorator can be applied multiple times to register different plugins. |
|||
The application conforms to the Linux Filesystem Hierarchy Standard (FHS). Executables, data, runtime state, and configuration are separated cleanly: |
|||
# '''Plugin Enabling Order''': When enabling plugins with the <code>@enable</code> decorator, the order in which they are declared is important. |
|||
# '''Class-Based Plugin Implementation''': In practice, plugins extend your new server class from bottom to top. The last declared plugin will be the closest descendant of your class, while the first declared plugin will be the farthest down in the chain. This also means that if you define a method in your own class with the same name as one provided by a plugin, your method will override the plugin’s version. (you probably will call <code>super</code> to extend the functionality) |
|||
# '''Static Initialization''': The static initialization ensures the server is instantiated immediately upon class definition. |
|||
* <code>/srv/fulljs/[instance]/</code> – contains the deployed application |
|||
* <code>/run/fulljs/[instance]/**</code> – runtime files (e.g., sockets, PID files) |
|||
* <code>/var/lib/fulljs/[instance]/**</code> – persistent state data (e.g., storage files) |
|||
* <code>/etc/fulljs/**</code> – configuration files (if any) |
|||
<syntaxhighlight lang="JavaScript"> |
|||
= Managing the FullJS Application Server = |
|||
/* @babel ES2025 */ |
|||
import { Server, enable } from "appserver"; |
|||
== Starting and Stopping == |
|||
import { ApplicationServer } from "appserver"; |
|||
To start the FullJS server use systemd’s start command. If you are running as a non-root user, you will have to use sudo since this will affect the state of the operating system: |
|||
import { HttpServer, http } from "appserver"; |
|||
import { AuthenticationService, auth } from "appserver"; |
|||
import { PermissionManager, perm } from "appserver"; |
|||
import { SystemdIntegration } from "appserver"; |
|||
import { HAProxyIntegration } from "appserver"; |
|||
import { ApplicationConsole } from "appserver"; |
|||
<syntaxhighlight lang="bash"> |
|||
sudo systemctl start fulljs@[instance] |
|||
</syntaxhighlight> |
|||
@enable( ApplicationConsole ) |
|||
To stop a running FullJS server, you can use: |
|||
@enable( HAProxyIntegration, {port: 8080, endpoint: /^(?!.*\.jss$).*/, slot: "http_slot00"} ) |
|||
@enable( SystemdIntegration ) |
|||
@enable( HttpServer, {port: 8080} ) |
|||
@enable( ApplicationServer ) |
|||
@enable( AuthenticationService ) |
|||
@enable( PermissionManager ) |
|||
class MyServer extends Server { |
|||
static { |
|||
new this(); |
|||
} |
|||
constructor(...args) { |
|||
<syntaxhighlight lang="bash"> |
|||
super(...args); |
|||
sudo systemctl stop fulljs@[instance] |
|||
} |
|||
</syntaxhighlight> |
|||
} |
|||
== Enabling and Disabling Services == |
|||
The above commands are useful for starting or stopping FullJS during the current session. |
|||
To tell systemd to start FullJS server automatically at boot, you must enable it. |
|||
* To start the server at boot: |
|||
<syntaxhighlight lang="bash"> |
|||
sudo systemctl enable fulljs@[instance] |
|||
</syntaxhighlight> |
|||
* To disable the service from starting automatically: |
|||
<syntaxhighlight lang="bash"> |
|||
sudo systemctl disable fulljs@[instance] |
|||
</syntaxhighlight> |
</syntaxhighlight> |
||
= HttpPlugin = |
|||
Keep in mind that enabling a service does not start it in the current session. |
|||
If you wish to start the service and enable it at boot, you will have to issue both the start and enable commands. |
|||
The '''HttpPlugin''' extends the core '''Server''' class with HTTP capabilities. |
|||
== Checking Status == |
|||
When this plugin is enabled, the server can listen on a given port and expose REST-like endpoints. |
|||
This makes it possible to build fully functional web APIs directly on top of the server, using standard '''Request''' and '''Response''' handling from the Web API specification. |
|||
<syntaxhighlight lang="bash"> |
|||
systemctl status fulljs@[instance] |
|||
</syntaxhighlight> |
|||
== Decorator-Based Routing == |
|||
This will provide you with the server state. |
|||
Once the '''HttpPlugin''' is enabled, endpoints can be defined directly in the server class using decorators. |
|||
Each decorator corresponds to a specific HTTP method. |
|||
=== Supported Decorators === |
|||
= Customizing and Re-branding = |
|||
* '''@http.get(path)''' – Declares a GET endpoint for the given path. Used for fetching resources or data. |
|||
* '''@http.post(path)''' – Declares a POST endpoint for the given path. Used for creating new resources or submitting data. |
|||
* '''@http.put(path)''' – Declares a PUT endpoint for the given path. Used for updating or replacing an existing resource. |
|||
* '''@http.patch(path)''' – Declares a PATCH endpoint for the given path. Used for partially updating an existing resource. |
|||
* '''@http.delete(path)''' – Declares a DELETE endpoint for the given path. Used for removing resources. |
|||
* '''@http.head(path)''' – Declares a HEAD endpoint for the given path. Similar to GET but returns only headers without the body. |
|||
* '''@http.options(path)''' – Declares an OPTIONS endpoint for the given path. Used for describing communication options, often in CORS scenarios. |
|||
The path may be a string for an exact match or a RegExp for flexible matching. |
|||
You can rebrand FullJS to fit your project name. For example, if your app is called <code>myapp</code>, you can define services like: |
|||
== Request and Response Handling == |
|||
<syntaxhighlight lang="bash"> |
|||
The plugin uses standard Web API classes: |
|||
systemctl start myapp@microservice1 |
|||
systemctl start myapp@microservice2 |
|||
</syntaxhighlight> |
|||
* '''Request''' – represents the incoming HTTP request (method, URL, headers, body). |
|||
== Example Unit File == |
|||
* '''Response''' – represents the outgoing HTTP response (status, headers, body). |
|||
<syntaxhighlight lang="ini"> |
|||
[Unit] |
|||
Description=myapp service |
|||
After=network.target auditd.service haproxy.service |
|||
Once a request is routed, the method must return a new <code>Response</code> object so that the server can properly send the HTTP response back to the client. |
|||
[Service] |
|||
This ensures full compatibility with modern JavaScript patterns for handling HTTP. |
|||
Type=notify |
|||
User=myapp |
|||
Group=myapp |
|||
RuntimeDirectory=%p/%i |
|||
StateDirectory=%p/%i |
|||
ExecStart=/usr/bin/fulljs /srv/%p/%i.jss |
|||
ExecReload=/bin/kill -HUP $MAINPID |
|||
KillMode=process |
|||
Restart=always |
|||
WatchdogSec=30 |
|||
Environment="DBUS_NAME=com.mydomain.%p.%i" |
|||
<syntaxhighlight lang="javascript"> |
|||
[Install] |
|||
/* @babel ES2025 */ |
|||
WantedBy=multi-user.target |
|||
</syntaxhighlight> |
|||
import { Server, enable } from "appserver"; |
|||
This way, your services become first-class, brand-aligned components of your Linux environment, while keeping all the benefits of FullJS’s microservice infrastructure. |
|||
import { ApplicationServer } from "appserver"; |
|||
import { HttpServer, http } from "appserver"; |
|||
import { AuthenticationService, auth } from "appserver"; |
|||
import { PermissionManager, perm } from "appserver"; |
|||
import { SystemdIntegration } from "appserver"; |
|||
import { HAProxyIntegration } from "appserver"; |
|||
import { ApplicationConsole } from "appserver"; |
|||
@enable( ApplicationConsole ) |
|||
@enable( HAProxyIntegration, {port: 8080, endpoint: /^(?!.*\.jss$).*/, slot: "http_slot00"} ) |
|||
@enable( SystemdIntegration ) |
|||
@enable( HttpServer, {port: 8080} ) |
|||
@enable( ApplicationServer ) |
|||
@enable( AuthenticationService ) |
|||
@enable( PermissionManager ) |
|||
class MyServer extends Server { |
|||
static { |
|||
new this(); |
|||
} |
|||
constructor(...args) { |
|||
super(...args); |
|||
} |
|||
@http.get("/myendpoint") |
|||
async handleRequest(request) { |
|||
return new Response("Hello world", { |
|||
status: 200, |
|||
headers: { |
|||
"Content-Type": "text/plain" |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
Latest revision as of 10:27, 22 September 2025
The FullJS application server provides a robust, modular, and secure foundation for running server-side services. It combines modern architectural principles with deep Linux integration to deliver enterprise-grade capabilitie
Microservice Architecture
The FullJS applications server follows a microservice-based server architecture with deep systemd integration. The application server is represented as a Systemd target ([application].target), with each individual microservice corresponding to a systemd unit instance ([application]@[microservice]).
Adding and Removing Microservices from the Application Server
To add a microservice (a.k.a systemd unit instance) to the application server (a.k.a systemd target):
sudo systemctl enable [application]@[microservice]
To remove a microservice from the application server group:
sudo systemctl disable [application]@[microservice]
Note: Enabling a microservice only registers it with the application server for automatic start at boot. It does **not** start the microservice immediately in the current session.
Starting and Stopping a Microservice
Each microservice runs as a separate systemd unit instance ([application]@[microservice]). To start a microservice:
sudo systemctl start [application]@[microservice]
To stop a microservice:
sudo systemctl stop [application]@[microservice]
Note: starting a microservice only affects the current session and does not configure it to start automatically at boot.
Checking the Status of a Microservice
Each microservice runs as a separate systemd unit instance ([application]@[microservice]).
- To check the current status of a microservice instance:
sudo systemctl status [application]@[microservice]
This command shows whether the service is running, stopped, or failed, along with recent log entries.
Starting and Stopping the application server
To start the application server use systemd’s start command.
sudo systemctl start [application].target
To stop a running application server, you can use:
sudo systemctl stop [application].target
Keep in mind that starting an application server only affects the current session and does not configure it to start automatically at boot.
Enabling and Disabling the application server
To start the application server automatically at boot, you must enable it.
To start the application server at boot:
sudo systemctl enable [application].target
To disable the application service from starting automatically:
sudo systemctl disable [application].target
Keep in mind that enabling an application server does not start it in the current session.
Filesystem Layout
The application conforms to the Linux Filesystem Hierarchy Standard (FHS). Executables, data, runtime state, and configuration are separated cleanly:
/srv/[application]/[microservice]/– contains the deployed application/run/[application]/[microservice]/**– runtime files (e.g., sockets, PID files)/var/lib/[application]/[microservice]/**– persistent state data (e.g., storage files)/etc/[application]/[microservice]/**– configuration files (if any)
Plugin-based architecture
Note: This documentation is not final and may be updated as the architecture evolves.
The plugin-based architecture allows the application server and other modules to be extended and customized by dynamically enabling plugins. Each plugin is a self-contained module and encapsulates a specific functionality, such as HTTP handling, authentication, permission management, systemd integration. This design decouples core module logic from optional features, making it easier to maintain, test, and evolve the system.
How it works:
- Core Server Class: The main Server class provides the infrastructure for running the application and managing plugins.
- Enabling a Plugin: Plugins are enabled using the
@enabledecorator, which associates a plugin class with optional configuration options. Plugin options can configure ports, endpoints, or internal slots for routing. The@enabledecorator can be applied multiple times to register different plugins. - Plugin Enabling Order: When enabling plugins with the
@enabledecorator, the order in which they are declared is important. - Class-Based Plugin Implementation: In practice, plugins extend your new server class from bottom to top. The last declared plugin will be the closest descendant of your class, while the first declared plugin will be the farthest down in the chain. This also means that if you define a method in your own class with the same name as one provided by a plugin, your method will override the plugin’s version. (you probably will call
superto extend the functionality) - Static Initialization: The static initialization ensures the server is instantiated immediately upon class definition.
/* @babel ES2025 */
import { Server, enable } from "appserver";
import { ApplicationServer } from "appserver";
import { HttpServer, http } from "appserver";
import { AuthenticationService, auth } from "appserver";
import { PermissionManager, perm } from "appserver";
import { SystemdIntegration } from "appserver";
import { HAProxyIntegration } from "appserver";
import { ApplicationConsole } from "appserver";
@enable( ApplicationConsole )
@enable( HAProxyIntegration, {port: 8080, endpoint: /^(?!.*\.jss$).*/, slot: "http_slot00"} )
@enable( SystemdIntegration )
@enable( HttpServer, {port: 8080} )
@enable( ApplicationServer )
@enable( AuthenticationService )
@enable( PermissionManager )
class MyServer extends Server {
static {
new this();
}
constructor(...args) {
super(...args);
}
}
HttpPlugin
The HttpPlugin extends the core Server class with HTTP capabilities. When this plugin is enabled, the server can listen on a given port and expose REST-like endpoints.
This makes it possible to build fully functional web APIs directly on top of the server, using standard Request and Response handling from the Web API specification.
Decorator-Based Routing
Once the HttpPlugin is enabled, endpoints can be defined directly in the server class using decorators. Each decorator corresponds to a specific HTTP method.
Supported Decorators
- @http.get(path) – Declares a GET endpoint for the given path. Used for fetching resources or data.
- @http.post(path) – Declares a POST endpoint for the given path. Used for creating new resources or submitting data.
- @http.put(path) – Declares a PUT endpoint for the given path. Used for updating or replacing an existing resource.
- @http.patch(path) – Declares a PATCH endpoint for the given path. Used for partially updating an existing resource.
- @http.delete(path) – Declares a DELETE endpoint for the given path. Used for removing resources.
- @http.head(path) – Declares a HEAD endpoint for the given path. Similar to GET but returns only headers without the body.
- @http.options(path) – Declares an OPTIONS endpoint for the given path. Used for describing communication options, often in CORS scenarios.
The path may be a string for an exact match or a RegExp for flexible matching.
Request and Response Handling
The plugin uses standard Web API classes:
- Request – represents the incoming HTTP request (method, URL, headers, body).
- Response – represents the outgoing HTTP response (status, headers, body).
Once a request is routed, the method must return a new Response object so that the server can properly send the HTTP response back to the client.
This ensures full compatibility with modern JavaScript patterns for handling HTTP.
/* @babel ES2025 */
import { Server, enable } from "appserver";
import { ApplicationServer } from "appserver";
import { HttpServer, http } from "appserver";
import { AuthenticationService, auth } from "appserver";
import { PermissionManager, perm } from "appserver";
import { SystemdIntegration } from "appserver";
import { HAProxyIntegration } from "appserver";
import { ApplicationConsole } from "appserver";
@enable( ApplicationConsole )
@enable( HAProxyIntegration, {port: 8080, endpoint: /^(?!.*\.jss$).*/, slot: "http_slot00"} )
@enable( SystemdIntegration )
@enable( HttpServer, {port: 8080} )
@enable( ApplicationServer )
@enable( AuthenticationService )
@enable( PermissionManager )
class MyServer extends Server {
static {
new this();
}
constructor(...args) {
super(...args);
}
@http.get("/myendpoint")
async handleRequest(request) {
return new Response("Hello world", {
status: 200,
headers: {
"Content-Type": "text/plain"
}
});
}
}