diff --git a/.github/workflows/edge.yml b/.github/workflows/edge.yml index 1c6e486f..4c2538f4 100644 --- a/.github/workflows/edge.yml +++ b/.github/workflows/edge.yml @@ -25,30 +25,7 @@ jobs: npm run build npm run build:plug-compile - name: Build Go binaries for multiple platforms - run: | - # Linux ARM64 - GOOS=linux GOARCH=arm64 go build -o silverbullet . - zip silverbullet-server-linux-aarch64.zip silverbullet - - # Linux x86_64 - GOOS=linux GOARCH=amd64 go build -o silverbullet . - zip silverbullet-server-linux-x86_64.zip silverbullet - - # macOS ARM64 - GOOS=darwin GOARCH=arm64 go build -o silverbullet . - zip silverbullet-server-darwin-aarch64.zip silverbullet - - # macOS x86_64 - GOOS=darwin GOARCH=amd64 go build -o silverbullet . - zip silverbullet-server-darwin-x86_64.zip silverbullet - - # Windows x86_64 - GOOS=windows GOARCH=amd64 go build -o silverbullet.exe . - zip silverbullet-server-windows-x86_64.zip silverbullet.exe - - # FreeBSD x86_64 - GOOS=freebsd GOARCH=amd64 go build -o silverbullet . - zip silverbullet-server-freebsd-x86_64.zip silverbullet + run: make build-server-releases - name: Update edge release uses: softprops/action-gh-release@v1 diff --git a/.github/workflows/server.yml b/.github/workflows/server.yml index b5e6e98b..03a0befa 100644 --- a/.github/workflows/server.yml +++ b/.github/workflows/server.yml @@ -25,30 +25,7 @@ jobs: npm run build npm run build:plug-compile - name: Build Go binaries for multiple platforms - run: | - # Linux ARM64 - GOOS=linux GOARCH=arm64 go build -o silverbullet . - zip silverbullet-server-linux-aarch64.zip silverbullet - - # Linux x86_64 - GOOS=linux GOARCH=amd64 go build -o silverbullet . - zip silverbullet-server-linux-x86_64.zip silverbullet - - # macOS ARM64 - GOOS=darwin GOARCH=arm64 go build -o silverbullet . - zip silverbullet-server-darwin-aarch64.zip silverbullet - - # macOS x86_64 - GOOS=darwin GOARCH=amd64 go build -o silverbullet . - zip silverbullet-server-darwin-x86_64.zip silverbullet - - # Windows x86_64 - GOOS=windows GOARCH=amd64 go build -o silverbullet.exe . - zip silverbullet-server-windows-x86_64.zip silverbullet.exe - - # FreeBSD x86_64 - GOOS=freebsd GOARCH=amd64 go build -o silverbullet . - zip silverbullet-server-freebsd-x86_64.zip silverbullet + run: make build-server-releases - name: Release uses: softprops/action-gh-release@v1 env: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 52ab065b..6a34f597 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ So you're interested in helping out? That's great! -First of all, please have a look at our [LLM use policy](https://silverbullet.md/LLM%20Use) (TL;DR: no). All good? Let's proceed. +First of all, please have a look at our [LLM use policy](https://silverbullet.md/LLM%20Use). All good? Let's proceed. ## Issuing PRs Before issuing a PR, please run a few commands: diff --git a/Makefile b/Makefile index a6d1290a..926e24cc 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,31 @@ +LDFLAGS = -X main.buildTime=$$(date -u +%Y-%m-%dT%H:%M:%SZ) + build: # Build client npm run build # Build plug-compile npm run build:plug-compile # Build server - go build + go build -ldflags "$(LDFLAGS)" build-for-docker: build - GOOS=linux GOARCH=arm64 go build -o silverbullet-arm64 . - GOOS=linux GOARCH=amd64 go build -o silverbullet-amd64 . + GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o silverbullet-arm64 . + GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o silverbullet-amd64 . docker: build-for-docker docker buildx build --platform linux/arm64,linux/amd64 --push . +build-server-releases: + GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o silverbullet . && zip silverbullet-server-linux-aarch64.zip silverbullet + GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o silverbullet . && zip silverbullet-server-linux-x86_64.zip silverbullet + GOOS=darwin GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o silverbullet . && zip silverbullet-server-darwin-aarch64.zip silverbullet + GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o silverbullet . && zip silverbullet-server-darwin-x86_64.zip silverbullet + GOOS=windows GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o silverbullet.exe . && zip silverbullet-server-windows-x86_64.zip silverbullet.exe + GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o silverbullet . && zip silverbullet-server-freebsd-x86_64.zip silverbullet + clean: rm -rf client_bundle/{base_fs,client} dist public_version.ts - rm -f silverbullet silverbullet-arm64 silverbullet-amd64 + rm -f silverbullet silverbullet-arm64 silverbullet-amd64 silverbullet.exe silverbullet-server-*.zip check: # Frontend type check diff --git a/client/space_lua.ts b/client/space_lua.ts index 3bf831b2..86747dd5 100644 --- a/client/space_lua.ts +++ b/client/space_lua.ts @@ -34,6 +34,9 @@ export class SpaceLuaEnvironment { expr: parseExpressionString("script.priority or 0"), desc: true, nulls: "first", + }, { + expr: parseExpressionString("script.ref"), + desc: false, }], } as LuaCollectionQuery, ); diff --git a/libraries/Library/Std/Pages/Space Overview.md b/libraries/Library/Std/Pages/Space Overview.md index bf8eadcb..70f45928 100644 --- a/libraries/Library/Std/Pages/Space Overview.md +++ b/libraries/Library/Std/Pages/Space Overview.md @@ -16,8 +16,8 @@ ${query[[ # Active Space Lua These are all Space Lua scripts and the order in which they are loaded: ${template.each(query[[ - from index.tag "space-lua" - order by _.priority desc + from script = index.tag "space-lua" + order by (script.priority or 0) desc, script.ref ]], template.new[==[ * [[${ref}]] (priority: ${priority or "default"}) ]==])} diff --git a/plugs/editor/help.ts b/plugs/editor/help.ts index 80c4deaa..aec99011 100644 --- a/plugs/editor/help.ts +++ b/plugs/editor/help.ts @@ -1,8 +1,7 @@ -import { editor } from "@silverbulletmd/silverbullet/syscalls"; -import { publicVersion } from "../../public_version.ts"; +import { editor, system } from "@silverbulletmd/silverbullet/syscalls"; export async function versionCommand() { await editor.flashNotification( - `You are currently running SilverBullet ${publicVersion}`, + `You are currently running SilverBullet ${await system.getVersion()}`, ); } diff --git a/server/cmd/server.go b/server/cmd/server.go index 6ec76e28..f665688b 100644 --- a/server/cmd/server.go +++ b/server/cmd/server.go @@ -17,7 +17,7 @@ import ( "github.com/spf13/cobra" ) -func buildConfig(bundledFiles fs.FS, args []string) *server.ServerConfig { +func buildConfig(bundledFiles fs.FS, args []string, buildTime string) *server.ServerConfig { serverConfig := &server.ServerConfig{ BindHost: "127.0.0.1", Port: 3000, @@ -168,13 +168,17 @@ func buildConfig(bundledFiles fs.FS, args []string) *server.ServerConfig { } } - // Extract the last modified time from the main binary, best effort - bundlePathDate := time.Now() - if executablePath, err := os.Executable(); err == nil { - if stat, err := os.Stat(executablePath); err == nil { - bundlePathDate = stat.ModTime() + // Determine bundle timestamp from build-time injected value, fall back to current time + var bundlePathDate time.Time + if buildTime != "" { + if t, err := time.Parse(time.RFC3339, buildTime); err == nil { + bundlePathDate = t } } + if bundlePathDate.IsZero() { + log.Println("Couldn't determine build time of binary, falling back to current time stamp") + bundlePathDate = time.Now() + } serverConfig.ClientBundle = server.NewReadOnlyFallthroughSpacePrimitives(bundledFiles, "client", bundlePathDate, nil) rootSpaceConfig.SpacePrimitives = server.NewReadOnlyFallthroughSpacePrimitives(bundledFiles, "base_fs", bundlePathDate, spacePrimitives) @@ -231,7 +235,7 @@ func ensureIndexAndConfig(rootSpaceConfig *server.SpaceConfig) { } } -func ServerCommand(bundledFiles fs.FS) *cobra.Command { +func ServerCommand(bundledFiles fs.FS, versionFileText string, buildTime string) *cobra.Command { var hostname string var port int var c = &cobra.Command{ @@ -239,7 +243,10 @@ func ServerCommand(bundledFiles fs.FS) *cobra.Command { Short: "Run the Silverbullet server", Args: cobra.MinimumNArgs(0), Run: func(cmd *cobra.Command, args []string) { - serverConfig := buildConfig(bundledFiles, args) + if version, err := server.ParseVersionFromTypeScript(versionFileText); err == nil { + log.Printf("Starting SilverBullet version %s", version) + } + serverConfig := buildConfig(bundledFiles, args, buildTime) if port != 3000 { serverConfig.Port = port } diff --git a/silverbullet.go b/silverbullet.go index 1ba641fb..c128c55b 100644 --- a/silverbullet.go +++ b/silverbullet.go @@ -10,8 +10,11 @@ import ( //go:embed public_version.ts var VersionFileText string +// Set via LDFLAGS +var buildTime string + func main() { - c := cmd.ServerCommand(client_bundle.BundledFiles) + c := cmd.ServerCommand(client_bundle.BundledFiles, VersionFileText, buildTime) c.AddCommand(cmd.VersionCommand(VersionFileText), cmd.UpgradeCommand(), cmd.UpgradeEdgeCommand()) c.Execute() } diff --git a/version.ts b/version.ts index 84bf009b..d2f8a9cd 100644 --- a/version.ts +++ b/version.ts @@ -1 +1 @@ -export const version = "2.4.1"; +export const version = "2.5.1"; diff --git a/website/CHANGELOG.md b/website/CHANGELOG.md index 85484e7d..e1047f31 100644 --- a/website/CHANGELOG.md +++ b/website/CHANGELOG.md @@ -3,33 +3,9 @@ An attempt at documenting the changes/new features introduced in each release. ## Edge Whenever a commit is pushed to the `main` branch, within ~5 minutes, it will be released as a docker image with the `:v2` tag, and a binary in the [edge release](https://github.com/silverbulletmd/silverbullet/releases/tag/edge). If you want to live on the bleeding edge of SilverBullet goodness (or regression) this is where to do it. -* New _experimental_ API: [[API/tag#tag.define(spec)]], see linked page for docs and example uses. Brings back ability to define 📅 deadlines for tasks (see example). Another part of this is [[Schema]] support for [[Tag|tags]]. When a schema is defined for a tag, you get: - * [[Frontmatter]] **attribute completion and linting** (in-editor error indicators) for attributes defined as part of the tag’s schema. - * [[Space Lua/Lua Integrated Query]] **attribute code completion** _if_ you use the `from v = index.tag(“bla”)` style syntax (so explicitly bind your iterator variable). - * Item-level linting (highlights the object in-line in case of validation errors). -* Tag schema updates: - * `pos` (present in link, item and some other tags) is now _deprecated_, use `range` instead - * `range` is a tuple of two numbers: _from_ and _to_ (e.g. `{0, 10}`) identify where the object appears in the page -* [[Space Lua/Lua Integrated Query]] improvements (courtesy of [Matouš Jan Fialka](https://github.com/mjf)): - * [[Space Lua/Lua Integrated Query/Grouping|group by]] and `having` clauses with [[Space Lua/Lua Integrated Query/Aggregating|aggregator]] support - * `filter(where )` clause for per-row aggregate filtering - * `nulls first`/`nulls last` in `order by` - * Null/missing query cells now render as empty -* [[Space Lua]] engine general improvements (also courtesy of [Matouš Jan Fialka](https://github.com/mjf)): - * [Native Lua pattern matching engine](https://github.com/silverbulletmd/silverbullet/pull/1838) (replacing previous implementation) - * [Support for `` attribute and __close metamethod](https://github.com/silverbulletmd/silverbullet/commit/9419cdcd9be61908330e1dce68a9156dbb911d23) - * [Better arithmetic error messages](https://github.com/silverbulletmd/silverbullet/commit/5a20a5f8f476a98172609e80c799cd1d83765585) - * [Refactor of control flow (performance)](https://github.com/silverbulletmd/silverbullet/commit/e5b4c8feb22a44cb4b22b3a77f9f2ed21dd09297) - * [Improved numeric type semantics](https://github.com/silverbulletmd/silverbullet/pull/1803) - * Implement `string.pack`, `string.unpack` and `string.packsize` - * Implement `math.random`, `math.randomseed`, `math.tointeger`, `math.frexp` and `math.ldexp` - * Implement `table.move`; align `table.pack` and `table.unpack` with Lua semantics - * [Extend `os` module](https://github.com/silverbulletmd/silverbullet/pull/1836) - * Add `_VERSION` environment variable - * `tostring()` now respects `__tostring` metamethod; `#` operator now respects `__len` metamethod - * Fix: `table.sort` comparator, `string.gsub` table replacement, `math.modf` return types, number formatting in `..` and `table.concat` -* Lua APIs: - * [[API/table#table.select(table, keys...)]] (non-standard in Lua) API, convenient to use in [[Space Lua/Lua Integrated Query]] `select` clauses, see example in docs. +* Nothing new yet + +## 2.5.0 * Changed keyboard bindings (sorry!). CodeMirror no longer directly allows `Alt-` and `Alt-` [[Keyboard Shortcuts]], meaning I had to **remap a few key bindings**. It’s basically a mission impossible to pick great ones, but here are the new defaults: * `Quick note` is now bound to both `Ctrl-q q` (type `Ctrl-q` first, then hit `q` again) and `Ctrl-q Ctrl-q` (hit `Ctrl-q` twice) * `Navigate: Home` is now bound to `Ctrl-g h` @@ -51,6 +27,33 @@ Whenever a commit is pushed to the `main` branch, within ~5 minutes, it will be * Sync snapshots are now persisted after every file sync, reducing (and hopefully eliminating) edge cases where the sync engine is killed mid-sync (for whatever reason) and the snapshot becomes of sync with “reality”. * The index status progress indicator (blue circle) should now be more reliably reflect the actual indexing status. * HTTP status codes >= 500 are now treated as offline (better offline detection). +* [[Space Lua/Lua Integrated Query]] improvements (courtesy of [Matouš Jan Fialka](https://github.com/mjf)): + * [[Space Lua/Lua Integrated Query/Grouping|group by]] and `having` clauses with [[Space Lua/Lua Integrated Query/Aggregating|aggregator]] support + * `filter(where )` clause for per-row aggregate filtering + * `nulls first`/`nulls last` in `order by` + * Null/missing query cells now render as empty +* [[Space Lua]] engine general improvements (most courtesey of [Matouš Jan Fialka](https://github.com/mjf)): + * [Native Lua pattern matching engine](https://github.com/silverbulletmd/silverbullet/pull/1838) (replacing previous implementation) + * [Support for `` attribute and __close metamethod](https://github.com/silverbulletmd/silverbullet/commit/9419cdcd9be61908330e1dce68a9156dbb911d23) + * [Better arithmetic error messages](https://github.com/silverbulletmd/silverbullet/commit/5a20a5f8f476a98172609e80c799cd1d83765585) + * [Refactor of control flow (performance)](https://github.com/silverbulletmd/silverbullet/commit/e5b4c8feb22a44cb4b22b3a77f9f2ed21dd09297) + * [Improved numeric type semantics](https://github.com/silverbulletmd/silverbullet/pull/1803) + * Implement `string.pack`, `string.unpack` and `string.packsize` + * Implement `math.random`, `math.randomseed`, `math.tointeger`, `math.frexp` and `math.ldexp` + * Implement `table.move`; align `table.pack` and `table.unpack` with Lua semantics + * [[API/table#table.select(table, keys...)]] (non-standard in Lua) API, convenient to use in [[Space Lua/Lua Integrated Query]] `select` clauses, see example in docs. + * [Extend `os` module](https://github.com/silverbulletmd/silverbullet/pull/1836) + * Add `_VERSION` environment variable + * `tostring()` now respects `__tostring` metamethod; `#` operator now respects `__len` metamethod + * Fix: `table.sort` comparator, `string.gsub` table replacement, `math.modf` return types, number formatting in `..` and `table.concat` + * **Load order** of scripts is now well defined: `order by (script.priority or 0) desc, script.ref` +* New _experimental_ API: [[API/tag#tag.define(spec)]], see linked page for docs and example uses. Brings back ability to define 📅 deadlines for tasks (see example). Another part of this is [[Schema]] support for [[Tag|tags]]. When a schema is defined for a tag, you get: + * [[Frontmatter]] **attribute completion and linting** (in-editor error indicators) for attributes defined as part of the tag’s schema. + * [[Space Lua/Lua Integrated Query]] **attribute code completion** _if_ you use the `from v = index.tag(“bla”)` style syntax (so explicitly bind your iterator variable). + * Item-level linting (highlights the object in-line in case of validation errors). +* Tag schema updates: + * `pos` (present in link, item and some other tags) is now _deprecated_, use `range` instead + * `range` is a tuple of two numbers: _from_ and _to_ (e.g. `{0, 10}`) identify where the object appears in the page * Editor improvements: * New `Page: Create Under Cursor` command, useful to pre-create an aspiring page link. Put your cursor in a wiki link to a non-existing page, and hit `Cmd-Shift-Enter` (`Ctrl-Shift-Enter`) to create it (empty) without navigating there. * [[Linked Mention|Linked Mentions]] now list full page path rather than abbreviated version. diff --git a/website/HTTP API.md b/website/HTTP API.md index d18a43aa..3d6de696 100644 --- a/website/HTTP API.md +++ b/website/HTTP API.md @@ -2,11 +2,19 @@ The server API is relatively small. The client primarily communicates with the s All API requests from the client will always set the `X-Sync-Mode` request header set to `true`. The server _will_ use this fact to distinguish between requests coming from the client and regular e.g. `GET` requests from the browser (through navigation) and redirect appropriately (for instance to the UI URL associated with a specific `.md` file). +# Authentication +When authentication is enabled, most endpoints require a valid session cookie (JWT) or a Bearer token. The following paths are excluded from auth: `/service_worker.js`, `/.client/*`, `/.auth`, `/.ping`. + +* `GET /.auth`: Serves the login page. Returns 403 if authentication is not enabled. +* `POST /.auth`: Authenticates a user. Expects form fields: `username`, `password`, `rememberMe` (optional), `from` (optional redirect path). Returns JSON `{"status":"ok","redirect":"..."}` on success or `{"status":"error","error":"..."}` on failure. Implements lockout after repeated failed attempts. +* `GET /.logout`: Clears authentication cookies and redirects to `/.auth`. +* **Bearer token**: When [[Install/Configuration#Authentication|SB_AUTH_TOKEN]] is set, requests can authenticate via the `Authorization: Bearer ` header instead of cookies. + # File system The space file system is exposed under the `/.fs` prefix: * `GET /.fs` will return a full listing of all files in your space including metadata like when the file was last modified, as well as permissions in JSON format. This is primarily used for sync purposes with the client. -* `GET /.fs/*`: _Reads_ and returns the content of the file at the given path. This means that if you `GET /.fs/index.md` you will receive the content of your `index` page. If the optional `X-Get-Meta` _request header_ is set, the server does not _need to_ return the body of the file (but it can). The `GET` _response_ will have a few additional SB-specific headers: +* `GET /.fs/*`: _Reads_ and returns the content of the file at the given path. This means that if you `GET /.fs/index.md` you will receive the content of your `index` page. If the optional `X-Get-Meta` _request header_ is set, the server does not _need to_ return the body of the file (but it can). The `GET` _response_ will have a few additional SB-specific headers: * (optional) `X-Last-Modified` the last modified time of the file as a UNIX timestamp in ms since the epoch (as coming from `Data.now()`). This timestamp _has_ to match the `lastModified` listed for this file in `/.fs` otherwise syncing issues may occur. When this header is missing, frequent polling-based sync will be disabled for this file. * (optional) `X-Created` the created time of the file as a UNIX timestamp in ms since the epoch (as coming from `Data.now()`). * (optional) `X-Permission`: either `rw` or `ro` which will change whether the editor opens in read-only or edit mode. When missing, `ro` is assumed. @@ -15,16 +23,25 @@ The space file system is exposed under the `/.fs` prefix: * `DELETE /.fs/*`: Again the same, except this will _delete_ the given file. # RPC -Some functionality is exposed as RPC calls +Some functionality is exposed as RPC-style calls -* `GET /.ping`: Returns 200 if the server is available -* `POST /.shell`: Run a shell command on the server side and return the result -* `* /.proxy/`: Proxy a HTTP request (to avoid CORS issues) +* `GET /.ping`: Returns 200 with body `OK` if the server is available. +* `POST /.shell`: Run a shell command on the server side and return the result. +* `POST /.logs`: Accepts a JSON array of log entry objects with fields `source`, `level`, `message`, and `timestamp`. The server logs these with the client's IP. Returns `OK`. +* `* /.proxy//path`: Proxy an HTTP request to avoid CORS issues. + * Blocked in read-only mode (returns 405). + * The URI does not include a scheme — it is auto-detected: `http://` is used for `localhost`, `127.0.0.1`, bare IP addresses, and `host.docker.internal`; `https://` is used for everything else. + * Request headers prefixed with `X-Proxy-Header-` have the prefix stripped and are forwarded to the upstream server. + * Upstream response headers are returned with an `x-proxy-header-` prefix. + * The `x-proxy-status-code` header contains the actual upstream HTTP status code; the proxy response itself always returns 200 to avoid interference. # Client * `GET /.config`: Retrieve client configuration, JSON with the following keys: * `readOnly`: Run the client in read-only mode * `spaceFolderPath`: Path of the space (used to prefix client database names to support switching space folders) * `indexPage`: name of the index page + * `logPush`: whether the client should push logs to the server via `/.logs` + * `enableClientEncryption`: true when authentication is enabled, offering client-side encryption as an option * `GET /.client/*`: Retrieve files implementing the client +* `GET /.client/manifest.json`: Dynamically generated PWA manifest containing the space name and description (not served from the static bundle). * `GET /*` (every other path) will serve the HTML of the SilverBullet UI