Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ jobs:
- uses: actions/checkout@v6
- run: npm ci
- run: npm test
- run: scripts/generate_configuration_reference.py --check
- run: scripts/check_configuration_examples.py
- name: Set up cargo cache
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4
env:
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG.md

## Unreleased

- SQLPage configuration now has a checked-in JSON Schema that powers the runtime configuration structure and the generated official-site reference. Example JSON configurations link to the live schema for editor validation and completion.

## v0.44.1

An AI-assisted security audit found three vulnerabilities: one authentication bypass that is high severity for affected OIDC deployments, and two lower-severity issues. It also led to three hardening changes. Upgrade now if you use custom OIDC protected paths.
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ actix-http = "3"
tokio = { version = "1", features = ["rt", "time", "test-util"] }

[build-dependencies]
serde_json = "1"
awc = { version = "3", features = ["rustls-0_23-webpki-roots"] }
rustls = "0.23"
actix-rt = "2.8"
Expand Down
48 changes: 48 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ async fn main() {
.unwrap();

println!("cargo:rerun-if-changed=build.rs");
generate_app_config();
let c = Rc::new(make_client());

for h in [
Expand All @@ -35,6 +36,53 @@ async fn main() {
set_odbc_rpath();
}

fn generate_app_config() {
const SCHEMA_PATH: &str = "sqlpage/sqlpage.schema.json";
println!("cargo:rerun-if-changed={SCHEMA_PATH}");
let schema: serde_json::Value = serde_json::from_reader(
File::open(SCHEMA_PATH).expect("Unable to open the SQLPage configuration JSON Schema"),
)
.expect("Invalid SQLPage configuration JSON Schema");
let properties = schema["properties"]
.as_object()
.expect("Configuration schema must have an object of properties");

let mut generated = String::from(
"// Generated from sqlpage/sqlpage.schema.json by build.rs. Do not edit.\n\
#[derive(Debug, Deserialize, PartialEq, Clone)]\n\
#[allow(clippy::doc_markdown, clippy::struct_excessive_bools)]\n\
pub struct AppConfig {\n",
);
for (schema_name, property) in properties {
if property["x-rust-exclude"].as_bool() == Some(true) {
continue;
}
let description = property["description"]
.as_str()
.unwrap_or_else(|| panic!("Configuration property {schema_name} needs a description"));
for line in description.lines() {
generated.push_str(" /// ");
generated.push_str(line);
generated.push('\n');
}
if let Some(serde) = property["x-rust-serde"].as_str() {
generated.push_str(" #[serde(");
generated.push_str(serde);
generated.push_str(")]\n");
}
let field_name = property["x-rust-field-name"]
.as_str()
.unwrap_or(schema_name);
let rust_type = property["x-rust-type"]
.as_str()
.unwrap_or_else(|| panic!("Configuration property {schema_name} needs x-rust-type"));
generated.push_str(&format!(" pub {field_name}: {rust_type},\n"));
}
generated.push_str("}\n");
let output = PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("app_config.rs");
std::fs::write(output, generated).expect("Unable to write generated AppConfig");
}

fn make_client() -> awc::Client {
awc::ClientBuilder::new()
.timeout(Duration::from_secs(10))
Expand Down
314 changes: 20 additions & 294 deletions configuration.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions examples/CRUD - Authentication/sqlpage/sqlpage.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"$schema": "https://github.com/sqlpage/SQLPage/raw/refs/heads/main/sqlpage/sqlpage.schema.json",
"listen_on": "0.0.0.0:8080",
"database_url": "sqlite://./db/Components.sqlite?mode=rwc",
"allow_exec": false,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"$schema": "https://github.com/sqlpage/SQLPage/raw/refs/heads/main/sqlpage/sqlpage.schema.json",
"database_url": "postgres://my_username:my_password@localhost:5432/my_geographic_database"
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"$schema": "https://github.com/sqlpage/SQLPage/raw/refs/heads/main/sqlpage/sqlpage.schema.json",
"content_security_policy": "script-src blob: 'self' https://cdn.jsdelivr.net;"
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"$schema": "https://github.com/sqlpage/SQLPage/raw/refs/heads/main/sqlpage/sqlpage.schema.json",
"max_uploaded_file_size": 5000000
}
5 changes: 5 additions & 0 deletions examples/light-dark-toggle/sqlpage/sqlpage.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"$schema": "https://github.com/sqlpage/SQLPage/raw/refs/heads/main/sqlpage/sqlpage.schema.json",
"database_url": "sqlite://:memory:",
"port": 5005
}
2 changes: 0 additions & 2 deletions examples/light-dark-toggle/sqlpage/sqlpage.yaml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{
"sqlite_extensions": ["mod_spatialite"]
"$schema": "https://github.com/sqlpage/SQLPage/raw/refs/heads/main/sqlpage/sqlpage.schema.json",
"sqlite_extensions": [
"mod_spatialite"
]
}
1 change: 1 addition & 0 deletions examples/nginx/sqlpage_config/sqlpage.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"$schema": "https://github.com/sqlpage/SQLPage/raw/refs/heads/main/sqlpage/sqlpage.schema.json",
"max_database_pool_connections": 10,
"database_connection_idle_timeout_seconds": 1800,
"max_uploaded_file_size": 10485760,
Expand Down
51 changes: 51 additions & 0 deletions examples/official-site/configuration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
-- Generated by scripts/generate_configuration_reference.py from sqlpage/sqlpage.schema.json.
-- Do not edit this file directly.
select 'dynamic' as component, json_patch(json_extract(properties, '$[0]'), json_object(
'title', 'SQLPage configuration reference'
)) as properties from example where component = 'shell' limit 1;

select 'text' as component;
select '# SQLPage configuration reference

SQLPage reads `sqlpage/sqlpage.json`. Add the [`$schema`](https://json-schema.org/understanding-json-schema/reference/schema) property to get validation and editor completion. Every option can also be supplied as an uppercase environment variable; environment variables override file values.

The [JSON Schema](https://github.com/sqlpage/SQLPage/raw/refs/heads/main/sqlpage/sqlpage.schema.json) is the source of truth for this reference and SQLPage''s in-memory configuration structure.' as contents_md;

select 'table' as component, true as sort, true as search;
select 'database_url' as option, 'string' as type, '—' as default_value, 'Database connection URL or ODBC connection string.' as description union all
select 'database_password' as option, 'string' as type, '—' as default_value, 'Database password. When set, overrides a password embedded in database_url.' as description union all
select 'max_database_pool_connections' as option, 'integer' as type, '—' as default_value, 'Maximum number of simultaneous database connections.' as description union all
select 'database_connection_idle_timeout_seconds' as option, 'number' as type, '—' as default_value, 'Close idle database connections after this many seconds. Use 0 to disable.' as description union all
select 'database_connection_max_lifetime_seconds' as option, 'number' as type, '—' as default_value, 'Close database connections after this many seconds regardless of activity. Use 0 to disable.' as description union all
select 'sqlite_extensions' as option, 'array' as type, '[]' as default_value, 'SQLite extensions to load when connecting.' as description union all
select 'listen_on' as option, 'string' as type, '—' as default_value, 'Socket address on which the HTTP server listens. Defaults to 0.0.0.0:8080, or port 443 with https_domain.' as description union all
select 'port' as option, 'integer or string' as type, '—' as default_value, 'TCP port on which to listen. A Kubernetes service URI is ignored for compatibility.' as description union all
select 'unix_socket' as option, 'string' as type, '—' as default_value, 'UNIX socket path to listen on instead of TCP.' as description union all
select 'database_connection_retries' as option, 'integer' as type, '6' as default_value, 'Number of database connection retries at startup, five seconds apart.' as description union all
select 'database_connection_acquire_timeout_seconds' as option, 'number' as type, '10' as default_value, 'Maximum seconds to wait for a connection from the database pool.' as description union all
select 'web_root' as option, 'string' as type, '"."' as default_value, 'Directory containing the SQL files served by SQLPage.' as description union all
select 'configuration_directory' as option, 'string' as type, '"./sqlpage"' as default_value, 'Directory containing SQLPage configuration, templates, and migrations.' as description union all
select 'allow_exec' as option, 'boolean' as type, 'false' as default_value, 'Allow SQL queries to call sqlpage.exec. Enable only when all SQL authors are trusted.' as description union all
select 'max_uploaded_file_size' as option, 'integer' as type, '5242880' as default_value, 'Maximum uploaded file size in bytes.' as description union all
select 'oidc_issuer_url' as option, 'string' as type, '—' as default_value, 'Base URL of the OpenID Connect provider.' as description union all
select 'oidc_client_id' as option, 'string' as type, '"sqlpage"' as default_value, 'OIDC client ID assigned to SQLPage.' as description union all
select 'oidc_client_secret' as option, 'string' as type, '—' as default_value, 'OIDC client secret assigned to SQLPage.' as description union all
select 'oidc_scopes' as option, 'string' as type, '"openid email profile"' as default_value, 'Space-separated OIDC scopes requested during authentication.' as description union all
select 'oidc_protected_paths' as option, 'array' as type, '["/"]' as default_value, 'Path prefixes that require OIDC authentication. Public paths take precedence.' as description union all
select 'oidc_public_paths' as option, 'array' as type, '[]' as default_value, 'Path prefixes excluded from OIDC authentication.' as description union all
select 'oidc_additional_trusted_audiences' as option, 'array' as type, '—' as default_value, 'Additional trusted OIDC JWT audiences. Null trusts all additional audiences; an empty array trusts only the client ID.' as description union all
select 'https_domain' as option, 'string' as type, '—' as default_value, 'Domain name for automatic HTTPS with a Let’s Encrypt certificate.' as description union all
select 'host' as option, 'string' as type, '—' as default_value, 'Public hostname used for OIDC redirect URLs. Defaults to https_domain.' as description union all
select 'https_certificate_email' as option, 'string' as type, '—' as default_value, 'Email address used when requesting the automatic HTTPS certificate.' as description union all
select 'https_certificate_cache_dir' as option, 'string' as type, '"./sqlpage/https"' as default_value, 'Directory used to store automatic HTTPS certificates.' as description union all
select 'https_acme_directory_url' as option, 'string' as type, '"https://acme-v02.api.letsencrypt.org/directory"' as default_value, 'ACME directory URL used for automatic HTTPS certificates.' as description union all
select 'environment' as option, 'string' as type, '"development"' as default_value, 'Runtime environment, controlling whether detailed errors are shown.' as description union all
select 'site_prefix' as option, 'string' as type, '"/"' as default_value, 'URL path prefix from which the site is served. SQLPage normalizes leading and trailing slashes.' as description union all
select 'max_pending_rows' as option, 'integer' as type, '256' as default_value, 'Maximum number of result rows buffered before sending them to the client.' as description union all
select 'compress_responses' as option, 'boolean' as type, 'false' as default_value, 'Compress HTTP response bodies when supported by the client.' as description union all
select 'content_security_policy' as option, 'string' as type, '—' as default_value, 'Content-Security-Policy header template. Use {{@csp_nonce}} for SQLPage’s generated nonce.' as description union all
select 'system_root_ca_certificates' as option, 'boolean' as type, 'false' as default_value, 'Load trusted certificates from the operating system certificate store for sqlpage.fetch.' as description union all
select 'max_recursion_depth' as option, 'integer' as type, '10' as default_value, 'Maximum recursion depth for run_sql.' as description union all
select 'markdown_allow_dangerous_html' as option, 'boolean' as type, 'false' as default_value, 'Allow raw HTML in Markdown.' as description union all
select 'markdown_allow_dangerous_protocol' as option, 'boolean' as type, 'false' as default_value, 'Allow dangerous URL protocols in Markdown links.' as description union all
select 'cache_stale_duration_ms' as option, 'integer' as type, '—' as default_value, 'Milliseconds for which cached SQL files remain fresh. Defaults to 0 in development and 1000 in production.' as description;
2 changes: 1 addition & 1 deletion examples/official-site/llms.txt.sql
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Key features:
component
) || ' built-in UI components with parameters and examples
- [Functions reference](/functions.sql): SQLPage built-in functions for handling requests, encoding data, and more
- [Configuration guide](https://github.com/sqlpage/SQLPage/blob/main/configuration.md): Complete list of configuration options in sqlpage.json
- [Configuration guide](/configuration.sql): Complete list of configuration options in sqlpage.json

## Components

Expand Down
2 changes: 1 addition & 1 deletion examples/official-site/safety.sql
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ For more information, see the [this discussion](https://github.com/sqlpage/SQLPa
## Database connections

SQLPage uses a fixed pool of database connections, and will never open more connections than the ones you
[configured](https://github.com/sqlpage/SQLPage/blob/main/configuration.md). So even under heavy load, your database
[configured](/configuration.sql). So even under heavy load, your database
connection limit will never be saturated by SQLPage.

And SQLPage will accept any restriction you put on the database user you use to connect to your database, so you can
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1317,7 +1317,7 @@ and to include custom visual styles (with CSS) or interactive behavior (with Jav
');

INSERT INTO parameter(component, name, description_md, type, top_level, optional) SELECT 'shell', * FROM (VALUES
('favicon', 'The URL of the icon the web browser should display in bookmarks and tabs. This property is particularly useful if multiple sites are hosted on the same domain with different [``site_prefix``](https://github.com/sqlpage/SQLPage/blob/main/configuration.md#configuring-sqlpage).', 'URL', TRUE, TRUE),
('favicon', 'The URL of the icon the web browser should display in bookmarks and tabs. This property is particularly useful if multiple sites are hosted on the same domain with different [``site_prefix``](/configuration.sql).', 'URL', TRUE, TRUE),
('manifest', 'The location of the [manifest.json](https://developer.mozilla.org/en-US/docs/Web/Manifest) if the site is a [PWA](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps). Among other features, serving a manifest enables your site to be "installed" as an app on most mobile devices.', 'URL', TRUE, TRUE)
) x;
INSERT INTO parameter(component, name, description, type, top_level, optional) SELECT 'shell', * FROM (VALUES
Expand Down Expand Up @@ -1399,7 +1399,7 @@ You see the [page layouts demo](./examples/layouts.sql) for a live example of th
{"link": "/functions.sql", "title": "SQLPage Functions", "icon": "math-function"},
{"link": "/extensions-to-sql", "title": "Extensions to SQL", "icon": "cube-plus"},
{"link": "/custom_components.sql", "title": "Custom Components", "icon": "puzzle"},
{"link": "//github.com/sqlpage/SQLPage/blob/main/configuration.md#configuring-sqlpage", "title": "Configuration", "icon": "settings"}
{"link": "/configuration.sql", "title": "Configuration", "icon": "settings"}
]},
{"title": "Search", "link": "/search"}
],
Expand Down
4 changes: 2 additions & 2 deletions examples/official-site/sqlpage/migrations/08_functions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ Currently running from `/home/user/my_sqlpage_website`

The current working directory is the directory from which the SQLPage server process was started.
By default, this is also the directory from which `.sql` files are loaded and served.
However, this can be changed by setting the `web_root` [configuration option](https://github.com/sqlpage/SQLPage/blob/main/configuration.md).
However, this can be changed by setting the `web_root` [configuration option](/configuration.sql).
'
);
INSERT INTO sqlpage_functions (
Expand Down Expand Up @@ -291,7 +291,7 @@ SQL files are served from `/home/user/my_sqlpage_website`
The web root is the directory from which `.sql` files are loaded and served.
By default, it is the current working directory, but it can be changed using:
- the `--web-root` command line argument
- the `web_root` [configuration option](https://github.com/sqlpage/SQLPage/blob/main/configuration.md) in `sqlpage.json`
- the `web_root` [configuration option](/configuration.sql) in `sqlpage.json`
- the `WEB_ROOT` environment variable

This is more reliable than `sqlpage.current_working_directory()` when you need to reference the location of your SQL files.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ containing the contents of the given file.

The file path is relative to the `web root` directory, which is the directory from which your website is served.
By default, this is the directory SQLPage is launched from, but you can change it
with the `web_root` [configuration option](https://github.com/sqlpage/SQLPage/blob/main/configuration.md).
with the `web_root` [configuration option](/configuration.sql).

If the given argument is null, the function will return null.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ insert into files (url) values (sqlpage.read_file_as_data_url(sqlpage.uploaded_f
returning ''text'' as component, ''Uploaded new file with id: '' || id as contents;
```

The maximum size of uploaded files is configurable with the [`max_uploaded_file_size`](https://github.com/sqlpage/SQLPage/blob/main/configuration.md)
The maximum size of uploaded files is configurable with the [`max_uploaded_file_size`](/configuration.sql)
configuration parameter. By default, it is set to 5 MiB.

### Parsing CSV files
Expand Down Expand Up @@ -120,7 +120,7 @@ And while we''re at it, SQLPage also supports HTTP/2, for even faster page loads

To enable HTTPS, you need to buy a [domain name](https://en.wikipedia.org/wiki/Domain_name)
and make it point to the server where SQLPage is running.
Then set the `https_domain` configuration parameter to `yourdomain.com` in your [`sqlpage.json` configuration file](./configuration.md).
Then set the `https_domain` configuration parameter to `yourdomain.com` in your [`sqlpage.json` configuration file](/configuration.sql).

```json
{
Expand Down Expand Up @@ -163,7 +163,7 @@ and to create JSON APIs.
- The `cookie` component now supports setting an explicit expiration date for cookies.
- The `cookie` component now supports setting the `SameSite` attribute of cookies, and defaults to `SameSite=Strict` for all cookies. What this means in practice is that cookies set by SQLPage will not be sent to your website if the user is coming from another website. This prevents someone from tricking your users into executing SQLPage queries on your website by sending them a malicious link.
- Bugfix: setting `min` or `max` to `0` in a number field in the `form` component now works as expected.
- Added support for `.env` files to set SQLPage''s [environment variables](./configuration.md#environment-variables).
- Added support for `.env` files to set SQLPage''s [environment variables](/configuration.sql).
- Better responsive design in the card component. Up to 5 cards per line on large screens. The number of cards per line is still customizable with the `columns` attribute.
- [New icons](https://tabler-icons.io/changelog):
- ![new icons in tabler 42](https://github.com/tabler/tabler-icons/assets/1282324/00856af9-841d-4aa9-995d-121c7ddcc005)
Expand Down
Loading