Compare commits
2 commits
f4a37d6851
...
7f055c98be
Author | SHA1 | Date | |
---|---|---|---|
7f055c98be | |||
70e8586f8c |
58 changed files with 3910 additions and 910 deletions
39
Cargo.lock
generated
39
Cargo.lock
generated
|
@ -1776,6 +1776,44 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "6.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b68543d5527e158213414a92832d2aab11a84d2571a5eb021ebe22c43aab066"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"mime_guess",
|
||||
"poem",
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
"tokio",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "6.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d4e0f0ced47ded9a68374ac145edd65a6c1fa13a96447b873660b2a568a0fd7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"syn",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "7.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512b0ab6853f7e14e3c8754acb43d6f748bb9ced66aa5915a6553ac8213f7731"
|
||||
dependencies = [
|
||||
"sha2",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
|
@ -2051,6 +2089,7 @@ dependencies = [
|
|||
"regex",
|
||||
"rmp-serde",
|
||||
"rstest",
|
||||
"rust-embed",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
|
|
|
@ -7,6 +7,7 @@ license = "MIT"
|
|||
description = "Static site management system"
|
||||
|
||||
build = "build.rs"
|
||||
default-run = "talon"
|
||||
|
||||
[dependencies]
|
||||
poem = "1.3.55"
|
||||
|
@ -51,6 +52,7 @@ async-compression = { version = "0.3.15", features = [
|
|||
clap = { version = "4.1.8", features = ["derive"] }
|
||||
shadow-rs = "0.21.0"
|
||||
walkdir = "2.3.2"
|
||||
rust-embed = { version = "6.6.1", features = ["poem-ex"] }
|
||||
|
||||
[dev-dependencies]
|
||||
rstest = "0.16.0"
|
||||
|
|
28
Justfile
Normal file
28
Justfile
Normal file
|
@ -0,0 +1,28 @@
|
|||
ok: lint test
|
||||
|
||||
lint:
|
||||
cargo fmt --all
|
||||
cargo clippy --all --all-features -- -D warnings
|
||||
|
||||
test:
|
||||
cargo test --workspace
|
||||
|
||||
# Run the Talon server
|
||||
run:
|
||||
cargo run -- run -d tmp
|
||||
|
||||
# Generate the openapi.json documentation
|
||||
oai-doc:
|
||||
cargo run --bin openapi
|
||||
|
||||
# Generate the JS API client
|
||||
oai-client:
|
||||
openapi-generator-cli generate -i openapi.json -g typescript-fetch -o ui/talon-client -p "npmName=talon-client"
|
||||
|
||||
# Start the dev server for the sidebar menu
|
||||
menu-dev:
|
||||
cd ui/menu && npm run dev
|
||||
|
||||
# Build the sidebar menu -> ui/menu/dist/talon.js
|
||||
menu-build:
|
||||
cd ui/menu && npm run build
|
836
openapi.json
Normal file
836
openapi.json
Normal file
|
@ -0,0 +1,836 @@
|
|||
{
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"title": "Talon",
|
||||
"description": "API for the Talon static site management system",
|
||||
"version": "0.1.0",
|
||||
"license": {
|
||||
"name": "MIT License"
|
||||
}
|
||||
},
|
||||
"servers": [],
|
||||
"tags": [],
|
||||
"paths": {
|
||||
"/website/{subdomain}": {
|
||||
"get": {
|
||||
"summary": "Get a website",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "subdomain",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json; charset=utf-8": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Website"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"summary": "Create a new website",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "subdomain",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json; charset=utf-8": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/WebsiteNew"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuthorization": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"patch": {
|
||||
"summary": "Update website",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "subdomain",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json; charset=utf-8": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/WebsiteUpdate"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuthorization": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"summary": "Delete website",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "subdomain",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuthorization": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/websites": {
|
||||
"get": {
|
||||
"summary": "Get all publicly listed websites",
|
||||
"description": "Returns all publicly listed websites (visibility != `hidden`)",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "visibility",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Visibility"
|
||||
},
|
||||
{
|
||||
"default": "hidden"
|
||||
}
|
||||
]
|
||||
},
|
||||
"in": "query",
|
||||
"description": "Mimimum visibility of the websites",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json; charset=utf-8": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Website"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websitesAll": {
|
||||
"get": {
|
||||
"summary": "Get all websites",
|
||||
"description": "Returns all websites from Talon's database (including hidden ones, if the current user\nhas access to them). This endpoint requires authentication (use the `/websites` endpoint\nfor unauthenticated users).",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "visibility",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Visibility"
|
||||
},
|
||||
{
|
||||
"default": "hidden"
|
||||
}
|
||||
]
|
||||
},
|
||||
"in": "query",
|
||||
"description": "Mimimum visibility of the websites",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json; charset=utf-8": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Website"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuthorization": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/website/{subdomain}/versions": {
|
||||
"get": {
|
||||
"summary": "Get website versions",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "subdomain",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json; charset=utf-8": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Version"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/website/{subdomain}/version/{version}": {
|
||||
"get": {
|
||||
"summary": "Get version",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "subdomain",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "uint32"
|
||||
},
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json; charset=utf-8": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Version"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"summary": "Delete version",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "subdomain",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "uint32"
|
||||
},
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuthorization": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/website/{subdomain}/version/{version}/files": {
|
||||
"get": {
|
||||
"summary": "Get version files",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "subdomain",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "uint32"
|
||||
},
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json; charset=utf-8": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/VersionFile"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/website/{subdomain}/upload": {
|
||||
"post": {
|
||||
"summary": "Upload a new version",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "subdomain",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
},
|
||||
{
|
||||
"name": "fallback",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"in": "query",
|
||||
"description": "Fallback page\n\nThe fallback page gets returned when the requested page does not exist",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
},
|
||||
{
|
||||
"name": "spa",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"in": "query",
|
||||
"description": "SPA mode (return fallback page with OK status)",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
},
|
||||
{
|
||||
"name": "version_data",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"in": "query",
|
||||
"description": "Associated version data\n\nThis is an arbitrary string map that can hold build information and other stuff\nand will be displayed in the site info dialog.",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/octet-stream": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "binary"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuthorization": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/file/{hash}": {
|
||||
"get": {
|
||||
"summary": "Retrieve a file",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "hash",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "File content",
|
||||
"content": {
|
||||
"application/octet-stream": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "binary"
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"etag": {
|
||||
"description": "File hash",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"last-modified": {
|
||||
"description": "Date when the file was last modified",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/info": {
|
||||
"get": {
|
||||
"summary": "Get information about your server",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json; charset=utf-8": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Info"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"Info": {
|
||||
"type": "object",
|
||||
"description": "Server information",
|
||||
"required": [
|
||||
"stats",
|
||||
"version",
|
||||
"uptime"
|
||||
],
|
||||
"properties": {
|
||||
"stats": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Stats"
|
||||
},
|
||||
{
|
||||
"description": "Stats about your instance"
|
||||
}
|
||||
]
|
||||
},
|
||||
"version": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/VersionInfo"
|
||||
},
|
||||
{
|
||||
"description": "Version information"
|
||||
}
|
||||
]
|
||||
},
|
||||
"uptime": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"description": "Current uptime of the server in seconds"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SourceIcon": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"link",
|
||||
"git",
|
||||
"github",
|
||||
"gitlab",
|
||||
"gitea",
|
||||
"bitbucket"
|
||||
]
|
||||
},
|
||||
"Stats": {
|
||||
"type": "object",
|
||||
"description": "Stats about your Talon instance",
|
||||
"required": [
|
||||
"n_websites",
|
||||
"n_files",
|
||||
"storage_used"
|
||||
],
|
||||
"properties": {
|
||||
"n_websites": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"description": "Number of websites"
|
||||
},
|
||||
"n_files": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"description": "Number of unique files"
|
||||
},
|
||||
"storage_used": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"description": "Amount of used storage space (in bytes)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Version": {
|
||||
"type": "object",
|
||||
"description": "Website version",
|
||||
"required": [
|
||||
"id",
|
||||
"created_at",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"description": "Version ID"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Version creation date"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"description": "Associated version data\n\nThis is an arbitrary string map that can hold build information and other stuff\nand will be displayed in the site info dialog.",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"VersionFile": {
|
||||
"type": "object",
|
||||
"description": "Website file",
|
||||
"required": [
|
||||
"path",
|
||||
"hash"
|
||||
],
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "File path"
|
||||
},
|
||||
"hash": {
|
||||
"type": "string",
|
||||
"description": "File hash"
|
||||
},
|
||||
"mime": {
|
||||
"type": "string",
|
||||
"description": "MIME file type"
|
||||
}
|
||||
}
|
||||
},
|
||||
"VersionInfo": {
|
||||
"type": "object",
|
||||
"description": "Information about a Talon version",
|
||||
"required": [
|
||||
"version",
|
||||
"commit",
|
||||
"commit_date",
|
||||
"rust_version",
|
||||
"build_target",
|
||||
"build_mode"
|
||||
],
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Talon version"
|
||||
},
|
||||
"commit": {
|
||||
"type": "string",
|
||||
"description": "Commit hash"
|
||||
},
|
||||
"commit_date": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Commit date"
|
||||
},
|
||||
"rust_version": {
|
||||
"type": "string",
|
||||
"description": "Rust version"
|
||||
},
|
||||
"build_target": {
|
||||
"type": "string",
|
||||
"description": "Build target (OS and architecture)"
|
||||
},
|
||||
"build_mode": {
|
||||
"type": "string",
|
||||
"description": "Rust build mode (`debug` / `release`)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Visibility": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"featured",
|
||||
"searchable",
|
||||
"hidden"
|
||||
]
|
||||
},
|
||||
"Website": {
|
||||
"type": "object",
|
||||
"description": "Website",
|
||||
"required": [
|
||||
"subdomain",
|
||||
"name",
|
||||
"created_at",
|
||||
"visibility"
|
||||
],
|
||||
"properties": {
|
||||
"subdomain": {
|
||||
"type": "string",
|
||||
"description": "Website subdomain"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Website name"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Website creation date"
|
||||
},
|
||||
"latest_version": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"description": "Latest version ID"
|
||||
},
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "Color of the page icon\n\nFormat: `#7935df`"
|
||||
},
|
||||
"visibility": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Visibility"
|
||||
},
|
||||
{
|
||||
"description": "Visibility of the page in the sidebar menu"
|
||||
}
|
||||
]
|
||||
},
|
||||
"source_url": {
|
||||
"type": "string",
|
||||
"description": "Link to the source of the page"
|
||||
},
|
||||
"source_icon": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/SourceIcon"
|
||||
},
|
||||
{
|
||||
"description": "Icon for the source link"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"WebsiteNew": {
|
||||
"type": "object",
|
||||
"description": "Create a new website",
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Website name"
|
||||
},
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "Color of the page icon"
|
||||
},
|
||||
"visibility": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Visibility"
|
||||
},
|
||||
{
|
||||
"description": "Visibility of the page in the sidebar menu",
|
||||
"default": "hidden"
|
||||
}
|
||||
]
|
||||
},
|
||||
"source_url": {
|
||||
"type": "string",
|
||||
"description": "Link to the source of the page"
|
||||
},
|
||||
"source_icon": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/SourceIcon"
|
||||
},
|
||||
{
|
||||
"description": "Icon for the source link"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"WebsiteUpdate": {
|
||||
"type": "object",
|
||||
"description": "Update a website with the contained values\n\nValues set to `None` remain unchanged.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Website name"
|
||||
},
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "Color of the page icon"
|
||||
},
|
||||
"visibility": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Visibility"
|
||||
},
|
||||
{
|
||||
"description": "Visibility of the page in the sidebar menu"
|
||||
}
|
||||
]
|
||||
},
|
||||
"source_url": {
|
||||
"type": "string",
|
||||
"description": "Link to the source of the page"
|
||||
},
|
||||
"source_icon": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/SourceIcon"
|
||||
},
|
||||
{
|
||||
"description": "Icon for the source link"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes": {
|
||||
"ApiKeyAuthorization": {
|
||||
"type": "apiKey",
|
||||
"name": "X-API-Key",
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
openapitools.json
Normal file
7
openapitools.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
|
||||
"spaces": 2,
|
||||
"generator-cli": {
|
||||
"version": "6.4.0"
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ use poem_openapi::{
|
|||
auth::ApiKey,
|
||||
param::{Path, Query},
|
||||
payload::{Binary, Html, Json, Response},
|
||||
OpenApi, SecurityScheme,
|
||||
LicenseObject, OpenApi, OpenApiService, SecurityScheme,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -388,3 +388,9 @@ impl TalonApi {
|
|||
Ok(Json(talon.info()?))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn api_service() -> OpenApiService<TalonApi, ()> {
|
||||
OpenApiService::new(TalonApi, "Talon", crate::API_VERSION)
|
||||
.description("API for the Talon static site management system")
|
||||
.license(LicenseObject::new("MIT License"))
|
||||
}
|
||||
|
|
69
src/assets.rs
Normal file
69
src/assets.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use crate::{storage::CompressionAlg, util};
|
||||
use poem::{
|
||||
handler,
|
||||
http::{header, Method, StatusCode},
|
||||
web::Path,
|
||||
Request, Response, Result,
|
||||
};
|
||||
|
||||
#[derive(rust_embed::RustEmbed)]
|
||||
#[folder = "ui/menu/dist/"]
|
||||
struct MenuAsset;
|
||||
|
||||
#[handler]
|
||||
pub fn menu_assets(request: &Request, path: Path<String>) -> Result<Response> {
|
||||
assets(request, path, PhantomData::<MenuAsset>)
|
||||
}
|
||||
|
||||
fn assets<A: rust_embed::RustEmbed + Sync + Send>(
|
||||
request: &Request,
|
||||
path: Path<String>,
|
||||
_: PhantomData<A>,
|
||||
) -> Result<Response> {
|
||||
if request.method() != Method::GET {
|
||||
return Ok(StatusCode::METHOD_NOT_ALLOWED.into());
|
||||
}
|
||||
let path = path.0;
|
||||
|
||||
match A::get(&path) {
|
||||
Some(content) => {
|
||||
let hash = hex::encode(content.metadata.sha256_hash());
|
||||
|
||||
// if etag is matched, return 304
|
||||
if request
|
||||
.headers()
|
||||
.get(header::IF_NONE_MATCH)
|
||||
.map(|etag| etag.to_str().unwrap_or("000000").eq(&hash))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return Ok(StatusCode::NOT_MODIFIED.into());
|
||||
}
|
||||
|
||||
let alg = util::parse_accept_encoding(request.headers(), &[CompressionAlg::Brotli]);
|
||||
let (body, body_alg) = if let Some(CompressionAlg::Brotli) = alg {
|
||||
let path_compressed = format!("{path}.br");
|
||||
if let Some(compressed_content) = MenuAsset::get(&path_compressed) {
|
||||
(compressed_content.data, CompressionAlg::Brotli)
|
||||
} else {
|
||||
(content.data, CompressionAlg::None)
|
||||
}
|
||||
} else {
|
||||
(content.data, CompressionAlg::None)
|
||||
};
|
||||
|
||||
// otherwise, return 200 with etag hash
|
||||
let mime = mime_guess::from_path(path).first_or_octet_stream();
|
||||
let mut builder = Response::builder()
|
||||
.header(header::CONTENT_TYPE, mime.as_ref())
|
||||
.header(header::ETAG, hash);
|
||||
if let Some(encoding) = body_alg.encoding() {
|
||||
builder = builder.header(header::CONTENT_ENCODING, encoding);
|
||||
}
|
||||
|
||||
Ok(builder.body(body.to_vec()))
|
||||
}
|
||||
None => Ok(Response::builder().status(StatusCode::NOT_FOUND).finish()),
|
||||
}
|
||||
}
|
8
src/bin/openapi.rs
Normal file
8
src/bin/openapi.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use std::{fs::File, io::Error, io::Write};
|
||||
|
||||
/// Generate Talon's OpenAPI documentation
|
||||
fn main() -> Result<(), Error> {
|
||||
let api = talon::api::api_service();
|
||||
let mut file = File::create("openapi.json")?;
|
||||
file.write_all(api.spec().as_bytes())
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
#![warn(clippy::todo, clippy::dbg_macro)]
|
||||
|
||||
pub mod api;
|
||||
pub mod assets;
|
||||
pub mod config;
|
||||
pub mod db;
|
||||
pub mod model;
|
||||
|
@ -21,6 +22,8 @@ use time::OffsetDateTime;
|
|||
|
||||
shadow_rs::shadow!(build);
|
||||
|
||||
pub const API_VERSION: &str = "0.1.0";
|
||||
|
||||
pub static LAST_COMMIT_DATE: Lazy<SystemTime> = Lazy::new(|| {
|
||||
OffsetDateTime::parse(
|
||||
build::COMMIT_DATE_3339,
|
||||
|
|
|
@ -131,6 +131,14 @@ pub struct Stats {
|
|||
pub storage_used: u64,
|
||||
}
|
||||
|
||||
/// Embedded site config
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TalonConfig<'a> {
|
||||
pub api: &'a str,
|
||||
pub version: &'a str,
|
||||
pub root_domain: &'a str,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Enum, Serialize, Deserialize,
|
||||
)]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::{ops::Deref, path::Path, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
api::TalonApi,
|
||||
assets,
|
||||
config::Config,
|
||||
db::Db,
|
||||
model::{Info, VersionInfo},
|
||||
|
@ -14,7 +14,6 @@ use poem::{
|
|||
http::header, listener::TcpListener, middleware, Endpoint, EndpointExt, Route, RouteDomain,
|
||||
Server,
|
||||
};
|
||||
use poem_openapi::OpenApiService;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -74,8 +73,8 @@ impl Talon {
|
|||
}
|
||||
|
||||
pub fn endpoint(&self) -> impl Endpoint {
|
||||
let api_service = OpenApiService::new(TalonApi, "Talon", "0.1.0")
|
||||
.server(format!("{}/api", self.i.cfg.server.internal_url));
|
||||
let api_service =
|
||||
crate::api::api_service().server(format!("{}/api", self.i.cfg.server.internal_url));
|
||||
let swagger_ui = api_service.swagger_ui();
|
||||
let spec = api_service.spec();
|
||||
|
||||
|
@ -84,14 +83,18 @@ impl Talon {
|
|||
"/",
|
||||
poem::endpoint::make_sync(|_| "Hello World, I am Talon"),
|
||||
)
|
||||
.nest("/api", api_service)
|
||||
.nest(
|
||||
"/api",
|
||||
api_service
|
||||
.with(middleware::Cors::new())
|
||||
.with(crate::middleware::LastModified),
|
||||
)
|
||||
.nest("/api/swagger", swagger_ui)
|
||||
.at(
|
||||
"/api/spec",
|
||||
poem::endpoint::make_sync(move |_| spec.clone()),
|
||||
)
|
||||
.with(middleware::Cors::new())
|
||||
.with(crate::middleware::LastModified);
|
||||
.at("/assets/menu/*path", assets::menu_assets);
|
||||
|
||||
let internal_domain = format!(
|
||||
"{}.{}",
|
||||
|
|
|
@ -24,7 +24,7 @@ use zip::ZipArchive;
|
|||
use crate::{
|
||||
config::Config,
|
||||
db::{Db, DbError},
|
||||
model::Stats,
|
||||
model::{Stats, TalonConfig},
|
||||
util,
|
||||
};
|
||||
|
||||
|
@ -32,6 +32,7 @@ pub struct Storage {
|
|||
path: PathBuf,
|
||||
db: Db,
|
||||
cfg: Config,
|
||||
to_inject: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
|
@ -44,7 +45,7 @@ pub enum CompressionAlg {
|
|||
|
||||
impl CompressionAlg {
|
||||
/// Get value of the http encoding header
|
||||
fn encoding(&self) -> Option<&'static str> {
|
||||
pub fn encoding(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
CompressionAlg::None => None,
|
||||
CompressionAlg::Gzip => Some("gzip"),
|
||||
|
@ -117,10 +118,28 @@ const TMPDIR_PREFIX: &str = "talon";
|
|||
impl Storage {
|
||||
/// Create a new file storage using the root folder and the database
|
||||
pub fn new<P: Into<PathBuf>>(path: P, db: Db, cfg: Config) -> Self {
|
||||
// Build the string to inject into html pages
|
||||
let talon_cfg = TalonConfig {
|
||||
api: &format!("{}/api", cfg.server.internal_url),
|
||||
version: crate::build::PKG_VERSION,
|
||||
root_domain: &cfg.server.root_domain,
|
||||
};
|
||||
|
||||
let to_inject = format!(
|
||||
r#"<!-- INJECTED BY TALON -->
|
||||
<script id="talon-config" type="application/json">{}</script>
|
||||
<script src="{}/assets/menu/talon.js"></script>
|
||||
<!-- INJECTED BY TALON -->
|
||||
"#,
|
||||
serde_json::to_string(&talon_cfg).unwrap_or_default(),
|
||||
cfg.server.internal_url
|
||||
);
|
||||
|
||||
Self {
|
||||
path: path.into(),
|
||||
db,
|
||||
cfg,
|
||||
to_inject,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -472,15 +491,13 @@ impl Storage {
|
|||
// HTML files are not precompressed and need to have UI code injected
|
||||
if is_html {
|
||||
// Inject UI code into HTML
|
||||
let to_inject = "<!-- Hello World -->\n";
|
||||
|
||||
let mut html = String::with_capacity(metadata.len() as usize);
|
||||
tokio::fs::File::from_std(file)
|
||||
.read_to_string(&mut html)
|
||||
.await?;
|
||||
|
||||
if let Some(ctag_pos) = html.rfind("</html>") {
|
||||
html.insert_str(ctag_pos, to_inject);
|
||||
html.insert_str(ctag_pos, &self.to_inject);
|
||||
}
|
||||
|
||||
// Compress response if possible
|
||||
|
|
|
@ -426,7 +426,7 @@ mod storage {
|
|||
let resp = tokio_test::block_on(tln.storage.file_to_response(gf, &HeaderMap::new(), true))
|
||||
.unwrap();
|
||||
let body = tokio_test::block_on(resp.into_body().into_string()).unwrap();
|
||||
assert!(body.contains("<!-- Hello World -->\n"));
|
||||
assert!(body.contains("<!-- INJECTED BY TALON -->\n"));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
|
|
|
@ -32,5 +32,10 @@
|
|||
* Use globals.d.ts instead of compilerOptions.types
|
||||
* to avoid limiting type declarations.
|
||||
*/
|
||||
"include": ["globals.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
|
||||
"include": [
|
||||
"ui/*/globals.d.ts",
|
||||
"ui/*/src/**/*.ts",
|
||||
"ui/*/src/**/*.js",
|
||||
"ui/*/src/**/*.svelte"
|
||||
]
|
||||
}
|
|
@ -37,4 +37,4 @@ module.exports = {
|
|||
],
|
||||
rules: {},
|
||||
ignorePatterns: [".rollup/**", "public/**", "dist/**"],
|
||||
}
|
||||
};
|
||||
|
|
52
ui/menu/package-lock.json
generated
52
ui/menu/package-lock.json
generated
|
@ -9,7 +9,8 @@
|
|||
"dependencies": {
|
||||
"@fortawesome/free-brands-svg-icons": "^5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|
||||
"minify-html-stream": "^0.3.1"
|
||||
"minify-html-stream": "^0.3.1",
|
||||
"talon-client": "file:../talon-client"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.5",
|
||||
|
@ -17,7 +18,7 @@
|
|||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-commonjs": "^20.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.0.5",
|
||||
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||
"@rollup/plugin-replace": "^3.0.0",
|
||||
"@rollup/plugin-typescript": "^8.2.5",
|
||||
"@rollup/pluginutils": "^4.1.1",
|
||||
|
@ -29,6 +30,7 @@
|
|||
"prettier": "^2.2.1",
|
||||
"prettier-plugin-svelte": "^1.2.0",
|
||||
"rollup": "^2.57.0",
|
||||
"rollup-plugin-brotli": "^3.1.0",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"rollup-plugin-html-minifier": "^2.0.0",
|
||||
"rollup-plugin-livereload": "^2.0.5",
|
||||
|
@ -41,7 +43,8 @@
|
|||
"svelte-keydown": "^0.3.1",
|
||||
"svelte-modals": "^1.0.4",
|
||||
"svelte-preprocess": "^4.9.5",
|
||||
"typescript": "^4.4.3"
|
||||
"tslib": "^2.5.0",
|
||||
"typescript": "^4.9.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
|
@ -2758,9 +2761,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001460",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001460.tgz",
|
||||
"integrity": "sha512-Bud7abqjvEjipUkpLs4D7gR0l8hBYBHoa+tGtKJHvT2AYzLp1z7EmVkUT4ERpVUfca8S2HGIVs883D8pUH1ZzQ==",
|
||||
"version": "1.0.30001462",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001462.tgz",
|
||||
"integrity": "sha512-PDd20WuOBPiasZ7KbFnmQRyuLE7cFXW2PVd7dmALzbkUXEP46upAuCDm9eY9vho8fgNMGmbAX92QBZHzcnWIqw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
@ -2973,9 +2976,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.320",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.320.tgz",
|
||||
"integrity": "sha512-h70iRscrNluMZPVICXYl5SSB+rBKo22XfuIS1ER0OQxQZpKTnFpuS6coj7wY9M/3trv7OR88rRMOlKmRvDty7Q==",
|
||||
"version": "1.4.325",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.325.tgz",
|
||||
"integrity": "sha512-K1C03NT4I7BuzsRdCU5RWkgZxtswnKDYM6/eMhkEXqKu4e5T+ck610x3FPzu1y7HVFSiQKZqP16gnJzPpji1TQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
|
@ -4489,9 +4492,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/resolve.exports": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.0.tgz",
|
||||
"integrity": "sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.1.tgz",
|
||||
"integrity": "sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
@ -4537,6 +4540,15 @@
|
|||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup-plugin-brotli": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup-plugin-brotli/-/rollup-plugin-brotli-3.1.0.tgz",
|
||||
"integrity": "sha512-vXRPVd9B1x+aaXeBdmLKNNsai9AH3o0Qikf4u0m1icKqgi3qVA4UhOfwGaPYoAHML1GLMUnR//PDhiMHXN/M6g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=11.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup-plugin-copy": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.4.0.tgz",
|
||||
|
@ -5150,6 +5162,10 @@
|
|||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/talon-client": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "file:../talon-client"
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.16.5",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.16.5.tgz",
|
||||
|
@ -5226,9 +5242,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
|
||||
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/tsutils": {
|
||||
|
@ -5246,6 +5262,12 @@
|
|||
"typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
|
||||
}
|
||||
},
|
||||
"node_modules/tsutils/node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"start": "sirv public --single",
|
||||
"lint": "eslint .",
|
||||
"fix": "eslint . --fix",
|
||||
"check": "svelte-check --tsconfig tsconfig.json",
|
||||
"check": "svelte-check --tsconfig ../../tsconfig.json",
|
||||
"format": "prettier --plugin=./node_modules/prettier-plugin-svelte --write .",
|
||||
"pc": "npm run fix & npm run check & npm run format",
|
||||
"ci": "npm run lint & npm run check & prettier --plugin=./node_modules/prettier-plugin-svelte --check ."
|
||||
|
@ -20,7 +20,7 @@
|
|||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-commonjs": "^20.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.0.5",
|
||||
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||
"@rollup/plugin-replace": "^3.0.0",
|
||||
"@rollup/plugin-typescript": "^8.2.5",
|
||||
"@rollup/pluginutils": "^4.1.1",
|
||||
|
@ -32,6 +32,7 @@
|
|||
"prettier": "^2.2.1",
|
||||
"prettier-plugin-svelte": "^1.2.0",
|
||||
"rollup": "^2.57.0",
|
||||
"rollup-plugin-brotli": "^3.1.0",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"rollup-plugin-html-minifier": "^2.0.0",
|
||||
"rollup-plugin-livereload": "^2.0.5",
|
||||
|
@ -44,11 +45,13 @@
|
|||
"svelte-keydown": "^0.3.1",
|
||||
"svelte-modals": "^1.0.4",
|
||||
"svelte-preprocess": "^4.9.5",
|
||||
"typescript": "^4.4.3"
|
||||
"tslib": "^2.5.0",
|
||||
"typescript": "^4.9.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/free-brands-svg-icons": "^5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|
||||
"minify-html-stream": "^0.3.1"
|
||||
"minify-html-stream": "^0.3.1",
|
||||
"talon-client": "file:../talon-client"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,11 +72,7 @@
|
|||
fill="#000000"
|
||||
font-family="sans-serif"
|
||||
font-size="40px"
|
||||
style="
|
||||
line-height: 1.25;
|
||||
shape-inside: url(#rect3620);
|
||||
white-space: pre;
|
||||
"
|
||||
style="line-height: 1.25; shape-inside: url(#rect3620); white-space: pre"
|
||||
xml:space="preserve"
|
||||
/>
|
||||
<g
|
||||
|
@ -106,191 +102,98 @@
|
|||
</div>
|
||||
<h1>development</h1>
|
||||
<p>
|
||||
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar
|
||||
plum pudding halvah chocolate. Cotton candy tart cake bonbon tart.
|
||||
Shortbread jelly fruitcake icing pastry. Dragée dessert cupcake cake
|
||||
sesame snaps toffee pie. Sweet roll sweet roll chupa chups jelly-o
|
||||
gummies tootsie roll sweet halvah oat cake. Carrot cake carrot cake
|
||||
muffin bonbon sesame snaps brownie. Bonbon candy macaroon fruitcake
|
||||
candy canes. Cake pudding danish liquorice cupcake jelly-o ice cream.
|
||||
Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
|
||||
candy canes donut lemon drops apple pie danish bear claw. Caramels cake
|
||||
jelly jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop
|
||||
pudding marshmallow candy canes tootsie roll danish.
|
||||
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar plum
|
||||
pudding halvah chocolate. Cotton candy tart cake bonbon tart. Shortbread jelly
|
||||
fruitcake icing pastry. Dragée dessert cupcake cake sesame snaps toffee pie.
|
||||
Sweet roll sweet roll chupa chups jelly-o gummies tootsie roll sweet halvah oat
|
||||
cake. Carrot cake carrot cake muffin bonbon sesame snaps brownie. Bonbon candy
|
||||
macaroon fruitcake candy canes. Cake pudding danish liquorice cupcake jelly-o
|
||||
ice cream. Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
|
||||
candy canes donut lemon drops apple pie danish bear claw. Caramels cake jelly
|
||||
jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop pudding
|
||||
marshmallow candy canes tootsie roll danish.
|
||||
</p>
|
||||
<p>
|
||||
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar
|
||||
plum pudding halvah chocolate. Cotton candy tart cake bonbon tart.
|
||||
Shortbread jelly fruitcake icing pastry. Dragée dessert cupcake cake
|
||||
sesame snaps toffee pie. Sweet roll sweet roll chupa chups jelly-o
|
||||
gummies tootsie roll sweet halvah oat cake. Carrot cake carrot cake
|
||||
muffin bonbon sesame snaps brownie. Bonbon candy macaroon fruitcake
|
||||
candy canes. Cake pudding danish liquorice cupcake jelly-o ice cream.
|
||||
Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
|
||||
candy canes donut lemon drops apple pie danish bear claw. Caramels cake
|
||||
jelly jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop
|
||||
pudding marshmallow candy canes tootsie roll danish.
|
||||
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar plum
|
||||
pudding halvah chocolate. Cotton candy tart cake bonbon tart. Shortbread jelly
|
||||
fruitcake icing pastry. Dragée dessert cupcake cake sesame snaps toffee pie.
|
||||
Sweet roll sweet roll chupa chups jelly-o gummies tootsie roll sweet halvah oat
|
||||
cake. Carrot cake carrot cake muffin bonbon sesame snaps brownie. Bonbon candy
|
||||
macaroon fruitcake candy canes. Cake pudding danish liquorice cupcake jelly-o
|
||||
ice cream. Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
|
||||
candy canes donut lemon drops apple pie danish bear claw. Caramels cake jelly
|
||||
jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop pudding
|
||||
marshmallow candy canes tootsie roll danish.
|
||||
</p>
|
||||
<p>
|
||||
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar
|
||||
plum pudding halvah chocolate. Cotton candy tart cake bonbon tart.
|
||||
Shortbread jelly fruitcake icing pastry. Dragée dessert cupcake cake
|
||||
sesame snaps toffee pie. Sweet roll sweet roll chupa chups jelly-o
|
||||
gummies tootsie roll sweet halvah oat cake. Carrot cake carrot cake
|
||||
muffin bonbon sesame snaps brownie. Bonbon candy macaroon fruitcake
|
||||
candy canes. Cake pudding danish liquorice cupcake jelly-o ice cream.
|
||||
Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
|
||||
candy canes donut lemon drops apple pie danish bear claw. Caramels cake
|
||||
jelly jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop
|
||||
pudding marshmallow candy canes tootsie roll danish.
|
||||
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar plum
|
||||
pudding halvah chocolate. Cotton candy tart cake bonbon tart. Shortbread jelly
|
||||
fruitcake icing pastry. Dragée dessert cupcake cake sesame snaps toffee pie.
|
||||
Sweet roll sweet roll chupa chups jelly-o gummies tootsie roll sweet halvah oat
|
||||
cake. Carrot cake carrot cake muffin bonbon sesame snaps brownie. Bonbon candy
|
||||
macaroon fruitcake candy canes. Cake pudding danish liquorice cupcake jelly-o
|
||||
ice cream. Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
|
||||
candy canes donut lemon drops apple pie danish bear claw. Caramels cake jelly
|
||||
jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop pudding
|
||||
marshmallow candy canes tootsie roll danish.
|
||||
</p>
|
||||
<p>
|
||||
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar
|
||||
plum pudding halvah chocolate. Cotton candy tart cake bonbon tart.
|
||||
Shortbread jelly fruitcake icing pastry. Dragée dessert cupcake cake
|
||||
sesame snaps toffee pie. Sweet roll sweet roll chupa chups jelly-o
|
||||
gummies tootsie roll sweet halvah oat cake. Carrot cake carrot cake
|
||||
muffin bonbon sesame snaps brownie. Bonbon candy macaroon fruitcake
|
||||
candy canes. Cake pudding danish liquorice cupcake jelly-o ice cream.
|
||||
Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
|
||||
candy canes donut lemon drops apple pie danish bear claw. Caramels cake
|
||||
jelly jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop
|
||||
pudding marshmallow candy canes tootsie roll danish.
|
||||
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar plum
|
||||
pudding halvah chocolate. Cotton candy tart cake bonbon tart. Shortbread jelly
|
||||
fruitcake icing pastry. Dragée dessert cupcake cake sesame snaps toffee pie.
|
||||
Sweet roll sweet roll chupa chups jelly-o gummies tootsie roll sweet halvah oat
|
||||
cake. Carrot cake carrot cake muffin bonbon sesame snaps brownie. Bonbon candy
|
||||
macaroon fruitcake candy canes. Cake pudding danish liquorice cupcake jelly-o
|
||||
ice cream. Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
|
||||
candy canes donut lemon drops apple pie danish bear claw. Caramels cake jelly
|
||||
jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop pudding
|
||||
marshmallow candy canes tootsie roll danish.
|
||||
</p>
|
||||
<p>
|
||||
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar
|
||||
plum pudding halvah chocolate. Cotton candy tart cake bonbon tart.
|
||||
Shortbread jelly fruitcake icing pastry. Dragée dessert cupcake cake
|
||||
sesame snaps toffee pie. Sweet roll sweet roll chupa chups jelly-o
|
||||
gummies tootsie roll sweet halvah oat cake. Carrot cake carrot cake
|
||||
muffin bonbon sesame snaps brownie. Bonbon candy macaroon fruitcake
|
||||
candy canes. Cake pudding danish liquorice cupcake jelly-o ice cream.
|
||||
Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
|
||||
candy canes donut lemon drops apple pie danish bear claw. Caramels cake
|
||||
jelly jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop
|
||||
pudding marshmallow candy canes tootsie roll danish.
|
||||
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar plum
|
||||
pudding halvah chocolate. Cotton candy tart cake bonbon tart. Shortbread jelly
|
||||
fruitcake icing pastry. Dragée dessert cupcake cake sesame snaps toffee pie.
|
||||
Sweet roll sweet roll chupa chups jelly-o gummies tootsie roll sweet halvah oat
|
||||
cake. Carrot cake carrot cake muffin bonbon sesame snaps brownie. Bonbon candy
|
||||
macaroon fruitcake candy canes. Cake pudding danish liquorice cupcake jelly-o
|
||||
ice cream. Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
|
||||
candy canes donut lemon drops apple pie danish bear claw. Caramels cake jelly
|
||||
jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop pudding
|
||||
marshmallow candy canes tootsie roll danish.
|
||||
</p>
|
||||
<p>
|
||||
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar
|
||||
plum pudding halvah chocolate. Cotton candy tart cake bonbon tart.
|
||||
Shortbread jelly fruitcake icing pastry. Dragée dessert cupcake cake
|
||||
sesame snaps toffee pie. Sweet roll sweet roll chupa chups jelly-o
|
||||
gummies tootsie roll sweet halvah oat cake. Carrot cake carrot cake
|
||||
muffin bonbon sesame snaps brownie. Bonbon candy macaroon fruitcake
|
||||
candy canes. Cake pudding danish liquorice cupcake jelly-o ice cream.
|
||||
Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
|
||||
candy canes donut lemon drops apple pie danish bear claw. Caramels cake
|
||||
jelly jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop
|
||||
pudding marshmallow candy canes tootsie roll danish.
|
||||
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar plum
|
||||
pudding halvah chocolate. Cotton candy tart cake bonbon tart. Shortbread jelly
|
||||
fruitcake icing pastry. Dragée dessert cupcake cake sesame snaps toffee pie.
|
||||
Sweet roll sweet roll chupa chups jelly-o gummies tootsie roll sweet halvah oat
|
||||
cake. Carrot cake carrot cake muffin bonbon sesame snaps brownie. Bonbon candy
|
||||
macaroon fruitcake candy canes. Cake pudding danish liquorice cupcake jelly-o
|
||||
ice cream. Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
|
||||
candy canes donut lemon drops apple pie danish bear claw. Caramels cake jelly
|
||||
jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop pudding
|
||||
marshmallow candy canes tootsie roll danish.
|
||||
</p>
|
||||
<p>
|
||||
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar
|
||||
plum pudding halvah chocolate. Cotton candy tart cake bonbon tart.
|
||||
Shortbread jelly fruitcake icing pastry. Dragée dessert cupcake cake
|
||||
sesame snaps toffee pie. Sweet roll sweet roll chupa chups jelly-o
|
||||
gummies tootsie roll sweet halvah oat cake. Carrot cake carrot cake
|
||||
muffin bonbon sesame snaps brownie. Bonbon candy macaroon fruitcake
|
||||
candy canes. Cake pudding danish liquorice cupcake jelly-o ice cream.
|
||||
Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
|
||||
candy canes donut lemon drops apple pie danish bear claw. Caramels cake
|
||||
jelly jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop
|
||||
pudding marshmallow candy canes tootsie roll danish.
|
||||
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar plum
|
||||
pudding halvah chocolate. Cotton candy tart cake bonbon tart. Shortbread jelly
|
||||
fruitcake icing pastry. Dragée dessert cupcake cake sesame snaps toffee pie.
|
||||
Sweet roll sweet roll chupa chups jelly-o gummies tootsie roll sweet halvah oat
|
||||
cake. Carrot cake carrot cake muffin bonbon sesame snaps brownie. Bonbon candy
|
||||
macaroon fruitcake candy canes. Cake pudding danish liquorice cupcake jelly-o
|
||||
ice cream. Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
|
||||
candy canes donut lemon drops apple pie danish bear claw. Caramels cake jelly
|
||||
jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop pudding
|
||||
marshmallow candy canes tootsie roll danish.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script id="talon-data" type="application/json">
|
||||
<script id="talon-config" type="application/json">
|
||||
{
|
||||
"root_path": "/",
|
||||
"current_page": "2",
|
||||
"current_version": "6",
|
||||
"versions": {
|
||||
"3": {
|
||||
"date": "2021-06-15T11:12:21+00:00",
|
||||
"name": "0.1.0",
|
||||
"user": "ThetaDev",
|
||||
"tags": {
|
||||
"commit": "ec55eba5ae45640c3d225c2471920fd3c9a36489"
|
||||
}
|
||||
},
|
||||
"4": {
|
||||
"date": "2021-06-20T16:48:21+00:00",
|
||||
"name": "0.1.1",
|
||||
"user": "ThetaDev",
|
||||
"tags": {
|
||||
"commit": "ec55eba5ae45640c3d225c2471920fd3c9a36489"
|
||||
}
|
||||
},
|
||||
"5": {
|
||||
"date": "2021-06-22T12:08:21+00:00",
|
||||
"name": "0.1.2",
|
||||
"user": "ThetaDev",
|
||||
"tags": {
|
||||
"commit": "ec55eba5ae45640c3d225c2471920fd3c9a36489"
|
||||
}
|
||||
},
|
||||
"6": {
|
||||
"date": "2021-07-02T16:12:40+00:00",
|
||||
"name": "0.1.3",
|
||||
"user": "ThetaDev",
|
||||
"tags": {
|
||||
"commit": "bbc7342580b48433481857bfe95e58784a508275"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pages": {
|
||||
"1": {
|
||||
"name": "ThetaDev",
|
||||
"path": "",
|
||||
"color": "#1f91ee",
|
||||
"visibility": "featured",
|
||||
"source": {
|
||||
"url": "https://github.com/Theta-Dev",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"name": "Talon",
|
||||
"path": "Talon",
|
||||
"color": "#4b228a",
|
||||
"visibility": "featured",
|
||||
"source": {
|
||||
"url": "https://github.com/Theta-Dev/Talon",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"name": "Spotify-Gender-Ex",
|
||||
"path": "Spotify-Gender-Ex",
|
||||
"color": "#1DB954",
|
||||
"image": "https://raw.githubusercontent.com/Theta-Dev/Spotify-Gender-Ex/master/assets/logo_square.svg",
|
||||
"visibility": "featured",
|
||||
"source": {
|
||||
"url": "https://github.com/Theta-Dev/Spotify-Gender-Ex",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"4": {
|
||||
"name": "A1",
|
||||
"path": "tests/a1",
|
||||
"color": "#ff0000",
|
||||
"visibility": "searchable"
|
||||
},
|
||||
"5": {
|
||||
"name": "B1",
|
||||
"path": "tests/b1",
|
||||
"color": "#00ff00",
|
||||
"visibility": "searchable"
|
||||
},
|
||||
"6": {
|
||||
"name": "C1",
|
||||
"path": "tests/c1",
|
||||
"color": "#0000ff",
|
||||
"visibility": "searchable"
|
||||
}
|
||||
}
|
||||
"api": "http://talon.localhost:3000/api",
|
||||
"version": "0.1.0",
|
||||
"root_domain": "localhost:3000"
|
||||
}
|
||||
</script>
|
||||
|
||||
<script src="./talon.js"></script>
|
||||
</html>
|
||||
|
|
|
@ -13,6 +13,7 @@ import sveltePreprocess from "svelte-preprocess"
|
|||
import typescript from "@rollup/plugin-typescript"
|
||||
import replace from "@rollup/plugin-replace"
|
||||
import babel from "@rollup/plugin-babel"
|
||||
import brotli from "rollup-plugin-brotli"
|
||||
|
||||
import css from ".rollup/css-only"
|
||||
import {serve} from ".rollup/serve"
|
||||
|
@ -148,6 +149,8 @@ export default {
|
|||
],
|
||||
babelHelpers: "bundled",
|
||||
}),
|
||||
|
||||
brotli(),
|
||||
],
|
||||
watch: {
|
||||
chokidar: true,
|
||||
|
|
|
@ -1,16 +1,32 @@
|
|||
<script lang="ts">
|
||||
import {closeModal, Modals} from "svelte-modals"
|
||||
import { onMount } from "svelte";
|
||||
import { closeModal, Modals } from "svelte-modals";
|
||||
import type { Website } from "ui/talon-client/src";
|
||||
|
||||
import Menu from "./components/Menu.svelte"
|
||||
import {currentPage, isPresent} from "./util/talonData"
|
||||
import Menu from "./components/Menu.svelte";
|
||||
import { currentWebsiteStore, fetchWebsites } from "./util/api";
|
||||
|
||||
let currentWebsite: Website;
|
||||
|
||||
currentWebsiteStore.subscribe((ws) => {
|
||||
console.log("current ws changed", ws);
|
||||
currentWebsite = ws;
|
||||
});
|
||||
|
||||
onMount(fetchWebsites);
|
||||
|
||||
let color: String;
|
||||
$: {
|
||||
if (currentWebsite && currentWebsite.color) {
|
||||
color = currentWebsite.color;
|
||||
} else {
|
||||
color = "#7935df";
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
// Default theme
|
||||
.wrapper
|
||||
--talon-color: #7935df
|
||||
|
||||
.backdrop
|
||||
position: fixed
|
||||
top: 0
|
||||
|
@ -20,12 +36,13 @@
|
|||
background: rgba(0, 0, 0, 0.6)
|
||||
</style>
|
||||
|
||||
<div class="wrapper" style={`--talon-color: ${currentPage.color}`}>
|
||||
{#if isPresent}
|
||||
<div class="wrapper" style={`--talon-color: ${color}`}>
|
||||
{#if currentWebsite}
|
||||
<Menu />
|
||||
{/if}
|
||||
|
||||
<Modals>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="backdrop" slot="backdrop" on:click={closeModal} />
|
||||
</Modals>
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<script lang="ts">
|
||||
export let imageSrc: string
|
||||
export let size = 32
|
||||
export let scale = 1
|
||||
export let alt = "??"
|
||||
export let color = "#4b228a"
|
||||
export let imageSrc: string;
|
||||
export let size = 32;
|
||||
export let scale = 1;
|
||||
export let alt = "??";
|
||||
export let color = "#4b228a";
|
||||
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,63 +1,38 @@
|
|||
<script lang="ts">
|
||||
import {fly} from "svelte/transition"
|
||||
import {closeModal} from "svelte-modals"
|
||||
import Keydown from "svelte-keydown"
|
||||
import { fly } from "svelte/transition";
|
||||
import { closeModal } from "svelte-modals";
|
||||
import Keydown from "svelte-keydown";
|
||||
|
||||
import type {TalonVersion} from "../util/types"
|
||||
import PageIcon from "./PageIcon.svelte"
|
||||
import Icon from "./Icon.svelte"
|
||||
import {formatDate} from "../util/functions"
|
||||
import InlineIcon from "./InlineIcon.svelte"
|
||||
import Tag from "./Tag.svelte"
|
||||
import {
|
||||
currentPage,
|
||||
currentVersion,
|
||||
currentVersionId,
|
||||
rootPath,
|
||||
versions,
|
||||
} from "../util/talonData"
|
||||
import PageIcon from "./PageIcon.svelte";
|
||||
import Icon from "./Icon.svelte";
|
||||
import { formatDate, getWebsiteVersionUrl } from "../util/functions";
|
||||
import InlineIcon from "./InlineIcon.svelte";
|
||||
import Tag from "./Tag.svelte";
|
||||
import type { Version, Website } from "talon-client";
|
||||
import { client, currentWebsiteStore } from "../util/api";
|
||||
import { talonConfig } from "../util/talonData";
|
||||
|
||||
export let isOpen: boolean
|
||||
let currentWebsite: Website;
|
||||
currentWebsiteStore.subscribe((ws) => {
|
||||
currentWebsite = ws;
|
||||
});
|
||||
|
||||
function getVersionName(versionId: string, version: TalonVersion): string {
|
||||
return version.name ? version.name : "#" + versionId
|
||||
export let isOpen: boolean;
|
||||
$: {
|
||||
if (isOpen && currentWebsite) {
|
||||
client
|
||||
.websiteSubdomainVersionsGet({ subdomain: currentWebsite.subdomain })
|
||||
.then((v) => {
|
||||
versions = v;
|
||||
if (v && v.length > 0) {
|
||||
currentVersion = v[v.length - 1];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getVersionUrl(versionId: string, version: TalonVersion): string {
|
||||
return (
|
||||
rootPath +
|
||||
(currentPage && version.name
|
||||
? currentPage.path + "@" + version.name
|
||||
: "&v/" + versionId)
|
||||
)
|
||||
}
|
||||
|
||||
let versionName: string
|
||||
$: versionName = getVersionName(currentVersionId, currentVersion)
|
||||
|
||||
let versionUrl: string
|
||||
$: versionUrl = getVersionUrl(currentVersionId, currentVersion)
|
||||
|
||||
let uploadDate: string
|
||||
$: uploadDate = formatDate(currentVersion.date)
|
||||
|
||||
let pageTags: [string, string][]
|
||||
$: pageTags = currentVersion.tags
|
||||
? Object.entries(currentVersion.tags).map(([key, val]) => [
|
||||
key.replace(/^\w/, (c) => c.toUpperCase()),
|
||||
val,
|
||||
])
|
||||
: []
|
||||
|
||||
let history: [string, string, string][]
|
||||
$: history = Object.entries(versions)
|
||||
.filter((e) => e[0] !== currentVersionId)
|
||||
.map(([key, version]) => [
|
||||
formatDate(version.date),
|
||||
getVersionName(key, version),
|
||||
getVersionUrl(key, version),
|
||||
])
|
||||
|
||||
let versions: Version[] = [];
|
||||
let currentVersion: Version = null;
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
|
@ -143,62 +118,55 @@
|
|||
|
||||
<Keydown paused={!isOpen} on:Escape={closeModal} />
|
||||
|
||||
{#if isOpen}
|
||||
{#if isOpen && currentWebsite}
|
||||
<div class="modal" role="dialog" transition:fly={{ y: 50 }} on:introstart on:outroend>
|
||||
<div>
|
||||
<div class="tag">
|
||||
<PageIcon page={currentPage} size={60} scale={0.8} />
|
||||
<span>{currentPage ? currentPage.name : 'v' + currentVersionId}</span>
|
||||
<PageIcon website={currentWebsite} size={60} scale={0.8} />
|
||||
<span>{currentWebsite.name}</span>
|
||||
</div>
|
||||
|
||||
{#if !currentPage}
|
||||
<p>
|
||||
This is a dangling version, i.e. it does not belong to a page.
|
||||
Assign it to a page or it will be purged within 24 hours.
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
{#if currentVersion}
|
||||
<p class="dhead">
|
||||
<InlineIcon iconName="question" />
|
||||
Current version
|
||||
Current version #{currentVersion.id}
|
||||
</p>
|
||||
<Tag key="Upload date" value={formatDate(currentVersion.createdAt)} />
|
||||
<!--<Tag key="Uploaded by" value={currentVersion.user} />-->
|
||||
|
||||
<Tag key="Version" value={versionName} href={versionUrl} />
|
||||
<Tag key="Upload date" value={uploadDate} />
|
||||
<Tag key="Uploaded by" value={currentVersion.user} />
|
||||
|
||||
{#each pageTags as [key, value]}
|
||||
{#each Object.entries(currentVersion.data) as [key, value]}
|
||||
<Tag {key} {value} />
|
||||
{/each}
|
||||
|
||||
{#if history.length}
|
||||
{#if versions && versions.length}
|
||||
<p class="dhead">
|
||||
<InlineIcon iconName="history" />
|
||||
History
|
||||
</p>
|
||||
|
||||
{#each history as [date, name, url]}
|
||||
{#each versions as version}
|
||||
<p class="smalltag">
|
||||
<a href={url}> <span>{name}</span> <span>{date}</span> </a>
|
||||
<a href={getWebsiteVersionUrl(currentWebsite.subdomain, version.id)}>
|
||||
<span>#{version.id}</span>
|
||||
<span>{formatDate(version.createdAt)}</span>
|
||||
</a>
|
||||
</p>
|
||||
{/each}
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<p class="dhead" />
|
||||
|
||||
<div>
|
||||
This site is powered by
|
||||
Powered by
|
||||
<a
|
||||
href="https://github.com/Theta-Dev/Talon/tree/__VERSION__"
|
||||
href="https://code.thetadev.de/ThetaDev/Talon/src/tag/{talonConfig.version}"
|
||||
target="_blank"
|
||||
referrerpolicy="no-referrer">Talon __VERSION__</a>, a static site
|
||||
management system created by
|
||||
<a
|
||||
href="https://thetadev.de"
|
||||
target="_blank"
|
||||
referrerpolicy="no-referrer">ThetaDev</a>
|
||||
rel="noreferrer"
|
||||
referrerpolicy="no-referrer">Talon
|
||||
{talonConfig.version}</a>
|
||||
</div>
|
||||
<p><a href={rootPath + '&credits'} target="_blank">View licenses</a></p>
|
||||
<!--<p><a href="" target="_blank">View licenses</a></p>-->
|
||||
<button on:click={closeModal}>
|
||||
<Icon iconName="close" size={40} scale={0.6} transparent={true} />
|
||||
</button>
|
||||
|
|
|
@ -1,87 +1,97 @@
|
|||
<script lang="ts">
|
||||
import {openModal} from "svelte-modals"
|
||||
import { openModal } from "svelte-modals";
|
||||
|
||||
import Icon from "./Icon.svelte"
|
||||
import MenuItem from "./MenuItem.svelte"
|
||||
import MenuItemPage from "./MenuItemPage.svelte"
|
||||
import InfoModal from "./InfoModal.svelte"
|
||||
import FloatingButton from "./FloatingButton.svelte"
|
||||
import Icon from "./Icon.svelte";
|
||||
import MenuItem from "./MenuItem.svelte";
|
||||
import MenuItemPage from "./MenuItemPage.svelte";
|
||||
import InfoModal from "./InfoModal.svelte";
|
||||
import FloatingButton from "./FloatingButton.svelte";
|
||||
|
||||
import type {Focusable, TalonPage} from "../util/types"
|
||||
import {TalonVisibility} from "../util/types"
|
||||
import PageIcon from "./PageIcon.svelte"
|
||||
import MenuItemInput from "./MenuItemInput.svelte"
|
||||
import {currentPage, currentPageId, pages, rootPath} from "../util/talonData"
|
||||
import type { Focusable } from "../util/types";
|
||||
import PageIcon from "./PageIcon.svelte";
|
||||
import MenuItemInput from "./MenuItemInput.svelte";
|
||||
import { currentWebsiteStore, websitesStore } from "../util/api";
|
||||
import { Visibility, Website } from "talon-client";
|
||||
import { getWebsiteUrl } from "../util/functions";
|
||||
|
||||
let currentWebsite: Website;
|
||||
let websites: Website[];
|
||||
|
||||
currentWebsiteStore.subscribe((ws) => {
|
||||
currentWebsite = ws;
|
||||
});
|
||||
websitesStore.subscribe((ws) => {
|
||||
websites = ws;
|
||||
});
|
||||
|
||||
function showSidebar(): void {
|
||||
sidebarShown = true
|
||||
sidebarShown = true;
|
||||
}
|
||||
|
||||
function hideSidebar(): void {
|
||||
sidebarShown = false
|
||||
sidebarShown = false;
|
||||
}
|
||||
|
||||
function isMobile(): boolean {
|
||||
return window.innerWidth < 768
|
||||
return window.innerWidth < 768;
|
||||
}
|
||||
|
||||
function openSearch(): void {
|
||||
searchOpen = true
|
||||
searchInput.focus()
|
||||
searchOpen = true;
|
||||
searchInput.focus();
|
||||
}
|
||||
|
||||
function closeSearch() {
|
||||
searchOpen = false
|
||||
searchInput.blur()
|
||||
searchOpen = false;
|
||||
searchInput.blur();
|
||||
|
||||
if (displayedPages.length === 0) searchText = ""
|
||||
if (displayedWebsites.length === 0) searchText = "";
|
||||
}
|
||||
|
||||
function clearSearch() {
|
||||
searchText = ""
|
||||
closeSearch()
|
||||
searchText = "";
|
||||
closeSearch();
|
||||
}
|
||||
|
||||
function searchKeypress(e: KeyboardEvent) {
|
||||
switch (e.key) {
|
||||
case "Enter":
|
||||
if (!searchText) {
|
||||
closeSearch()
|
||||
} else if (displayedPages.length) {
|
||||
window.location.href = rootPath + displayedPages[0].path
|
||||
closeSearch();
|
||||
} else if (displayedWebsites.length) {
|
||||
window.location.href = getWebsiteUrl(displayedWebsites[0].subdomain);
|
||||
} else {
|
||||
closeSearch()
|
||||
closeSearch();
|
||||
}
|
||||
break
|
||||
break;
|
||||
case "Escape":
|
||||
clearSearch()
|
||||
break
|
||||
clearSearch();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function openInfo() {
|
||||
openModal(InfoModal)
|
||||
openModal(InfoModal);
|
||||
}
|
||||
|
||||
let sidebarShown = !isMobile()
|
||||
let searchInput: Focusable
|
||||
let searchOpen = false
|
||||
let searchText = ""
|
||||
let sidebarShown = !isMobile();
|
||||
let searchInput: Focusable;
|
||||
let searchOpen = false;
|
||||
let searchText = "";
|
||||
|
||||
let displayedPages: TalonPage[]
|
||||
$: displayedPages = Object.entries(pages)
|
||||
.filter(([id, page]) => {
|
||||
if (id === currentPageId) return false
|
||||
let displayedWebsites: Website[];
|
||||
|
||||
$: displayedWebsites = websites.filter((ws) => {
|
||||
if (ws.subdomain == currentWebsite.subdomain) return false;
|
||||
|
||||
if (searchText) {
|
||||
return (
|
||||
page.visibility !== TalonVisibility.HIDDEN &&
|
||||
page.name.toLowerCase().includes(searchText.toLowerCase())
|
||||
)
|
||||
ws.visibility !== Visibility.Hidden &&
|
||||
ws.name.toLowerCase().includes(searchText.toLowerCase())
|
||||
);
|
||||
}
|
||||
return page.visibility === TalonVisibility.FEATURED
|
||||
})
|
||||
.map(([, page]) => page)
|
||||
return ws.visibility === Visibility.Featured;
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -128,25 +138,22 @@
|
|||
bind:text={searchText} />
|
||||
</div>
|
||||
<div>
|
||||
{#each displayedPages as page, i}
|
||||
<MenuItemPage
|
||||
{page}
|
||||
{rootPath}
|
||||
active={searchOpen && searchText && i === 0} />
|
||||
{#each displayedWebsites as website, i}
|
||||
<MenuItemPage {website} active={searchOpen && searchText && i === 0} />
|
||||
{/each}
|
||||
</div>
|
||||
<div>
|
||||
{#if currentPage && currentPage.source}
|
||||
{#if currentWebsite && currentWebsite.sourceUrl}
|
||||
<MenuItem
|
||||
text="View source"
|
||||
link={currentPage.source.url}
|
||||
link={currentWebsite.sourceUrl}
|
||||
newTab={true}
|
||||
privacy={true}>
|
||||
<Icon iconName={currentPage.source.type} size={40} scale={0.6} />
|
||||
<Icon iconName={currentWebsite.sourceIcon} size={40} scale={0.6} />
|
||||
</MenuItem>
|
||||
{/if}
|
||||
<MenuItem text="Info" on:click={openInfo}>
|
||||
<PageIcon page={currentPage} />
|
||||
<PageIcon website={currentWebsite} />
|
||||
</MenuItem>
|
||||
<MenuItem text="Hide sidebar" on:click={hideSidebar}>
|
||||
<Icon iconName="arrowRight" size={40} scale={0.6} />
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
<script lang="ts">
|
||||
import type {TalonPage} from "../util/types"
|
||||
import PageIcon from "./PageIcon.svelte"
|
||||
import MenuItem from "./MenuItem.svelte"
|
||||
import PageIcon from "./PageIcon.svelte";
|
||||
import MenuItem from "./MenuItem.svelte";
|
||||
import type { Website } from "talon-client";
|
||||
import { getWebsiteUrl } from "../util/functions";
|
||||
|
||||
export let page: TalonPage
|
||||
export let rootPath = "/"
|
||||
export let active = false
|
||||
export let website: Website;
|
||||
export let active = false;
|
||||
|
||||
const MAX_TEXT_LEN = 20
|
||||
const MAX_TEXT_LEN = 20;
|
||||
|
||||
let text: string
|
||||
let text: string;
|
||||
$: text =
|
||||
page.name.length > MAX_TEXT_LEN
|
||||
? page.name.substr(0, 20).trim() + "..."
|
||||
: page.name
|
||||
website.name.length > MAX_TEXT_LEN
|
||||
? website.name.substring(0, 20).trim() + "..."
|
||||
: website.name;
|
||||
|
||||
</script>
|
||||
|
||||
<MenuItem {active} {text} link={rootPath + page.path}>
|
||||
<PageIcon {page} />
|
||||
<MenuItem {active} {text} link={getWebsiteUrl(website.subdomain)}>
|
||||
<PageIcon {website} />
|
||||
</MenuItem>
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
<script lang="ts">
|
||||
import ImageIcon from "./ImageIcon.svelte"
|
||||
import Icon from "./Icon.svelte"
|
||||
import type {TalonPage} from "../util/types"
|
||||
import type { Website } from "ui/apiclient";
|
||||
|
||||
export let page: TalonPage
|
||||
export let website: Website
|
||||
export let size = 40
|
||||
export let scale = 0.8
|
||||
|
||||
</script>
|
||||
|
||||
{#if page}
|
||||
{#if website}
|
||||
<ImageIcon
|
||||
imageSrc={page.image}
|
||||
color={page.color}
|
||||
alt={page.name.substr(0, 2)}
|
||||
imageSrc=""
|
||||
color={website.color}
|
||||
alt={website.name.substring(0, 2)}
|
||||
{size}
|
||||
{scale} />
|
||||
{:else}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const sidebar = document.createElement("talon-sidebar")
|
||||
document.body.append(sidebar)
|
||||
const sidebar = document.createElement("talon-sidebar");
|
||||
document.body.append(sidebar);
|
||||
|
||||
export {default as default} from "./App.svelte"
|
||||
export { default as default } from "./App.svelte";
|
||||
|
|
18
ui/menu/src/util/api.ts
Normal file
18
ui/menu/src/util/api.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import {Writable, writable} from "svelte/store"
|
||||
|
||||
import {Configuration, DefaultApi, Website} from "talon-client"
|
||||
import {getSubdomain} from "./functions"
|
||||
import { talonConfig } from "./talonData"
|
||||
|
||||
export const websitesStore: Writable<Website[]> = writable([])
|
||||
export const currentWebsiteStore: Writable<Website> = writable(null)
|
||||
|
||||
export const client = new DefaultApi(new Configuration({basePath: talonConfig.api}));
|
||||
|
||||
export function fetchWebsites(): void {
|
||||
client.websitesGet().then((ws) => {
|
||||
websitesStore.set(ws)
|
||||
const subdomain = getSubdomain()
|
||||
currentWebsiteStore.set(ws.find((w) => w.subdomain == subdomain))
|
||||
})
|
||||
}
|
|
@ -1,5 +1,41 @@
|
|||
function formatDate(dateString: string): string {
|
||||
return new Date(dateString).toLocaleString(navigator.language)
|
||||
import {talonConfig} from "./talonData"
|
||||
|
||||
export function formatDate(date: Date): string {
|
||||
return date.toLocaleString(navigator.language)
|
||||
}
|
||||
|
||||
export {formatDate}
|
||||
export function getSubdomain(): string {
|
||||
const hn = window.location.hostname
|
||||
const rd_noport = talonConfig.root_domain.split(":", 1)[0]
|
||||
|
||||
if (hn.endsWith("." + rd_noport)) {
|
||||
const subdomain = hn.substring(0, hn.length - rd_noport.length - 1).split("--", 1)[0]
|
||||
if (subdomain === "x") {
|
||||
return "-"
|
||||
} else {
|
||||
return subdomain
|
||||
}
|
||||
}
|
||||
return "-"
|
||||
}
|
||||
|
||||
export function getWebsiteUrl(subdomain: string): string {
|
||||
const proto = window.location.protocol
|
||||
|
||||
if (subdomain === "-") {
|
||||
return `${proto}//${talonConfig.root_domain}`
|
||||
} else {
|
||||
return `${proto}//${subdomain}.${talonConfig.root_domain}`
|
||||
}
|
||||
}
|
||||
|
||||
export function getWebsiteVersionUrl(subdomain: string, version: number): string {
|
||||
const proto = window.location.protocol
|
||||
const v = `--v${version}`
|
||||
|
||||
if (subdomain === "-") {
|
||||
return `${proto}//x${v}.${talonConfig.root_domain}`
|
||||
} else {
|
||||
return `${proto}//${subdomain}${v}.${talonConfig.root_domain}`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,54 +1,33 @@
|
|||
import type {TalonData, TalonPage, TalonVersion} from "./types"
|
||||
import {TalonVisibility} from "./types"
|
||||
import type {TalonConfig} from "./types"
|
||||
|
||||
const talonData: TalonData = JSON.parse(
|
||||
document.getElementById("talon-data").textContent
|
||||
) as TalonData
|
||||
export const talonConfig: TalonConfig = JSON.parse(
|
||||
document.getElementById("talon-config").textContent
|
||||
) as TalonConfig
|
||||
|
||||
const isTalonData = (obj: TalonData) =>
|
||||
"root_path" in obj &&
|
||||
"current_page" in obj &&
|
||||
"current_version" in obj &&
|
||||
"versions" in obj &&
|
||||
"pages" in obj
|
||||
// const rootPath = talonData.root_path
|
||||
|
||||
const isTalonVersion = (obj: TalonVersion) => "date" in obj && "user" in obj
|
||||
// const currentVersion = talonData.versions[talonData.current_version]
|
||||
// const currentVersionId = talonData.current_version
|
||||
|
||||
const rootPath = talonData.root_path
|
||||
// const getCurrentPage = () => {
|
||||
// if (talonData.current_page) {
|
||||
// return talonData.pages[talonData.current_page]
|
||||
// } else {
|
||||
// return {
|
||||
// name: "#" + talonData.current_version,
|
||||
// path: "&v/" + talonData.current_version,
|
||||
// color: "#7935df",
|
||||
// visibility: TalonVisibility.HIDDEN,
|
||||
// image: undefined,
|
||||
// source: undefined,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
const currentVersion = talonData.versions[talonData.current_version]
|
||||
const currentVersionId = talonData.current_version
|
||||
// const currentPage: TalonPage = getCurrentPage()
|
||||
// const currentPageId = talonData.current_page
|
||||
|
||||
const getCurrentPage = () => {
|
||||
if (talonData.current_page) {
|
||||
return talonData.pages[talonData.current_page]
|
||||
} else {
|
||||
return {
|
||||
name: "#" + talonData.current_version,
|
||||
path: "&v/" + talonData.current_version,
|
||||
color: "#7935df",
|
||||
visibility: TalonVisibility.HIDDEN,
|
||||
image: undefined,
|
||||
source: undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
// const isPresent = isTalonData(talonData) && isTalonVersion(currentVersion)
|
||||
|
||||
const currentPage: TalonPage = getCurrentPage()
|
||||
const currentPageId = talonData.current_page
|
||||
|
||||
const isPresent = isTalonData(talonData) && isTalonVersion(currentVersion)
|
||||
|
||||
const versions = talonData.versions
|
||||
const pages = talonData.pages
|
||||
|
||||
export {
|
||||
rootPath,
|
||||
isPresent,
|
||||
currentVersion,
|
||||
currentVersionId,
|
||||
currentPage,
|
||||
currentPageId,
|
||||
versions,
|
||||
pages,
|
||||
}
|
||||
// const versions = talonData.versions
|
||||
// const pages = talonData.pages
|
||||
|
|
|
@ -1,54 +1,15 @@
|
|||
export interface TalonData {
|
||||
root_path: string
|
||||
current_page: string | null
|
||||
current_version: string
|
||||
versions: {[key: string]: TalonVersion}
|
||||
pages: {[key: string]: TalonPage}
|
||||
export interface TalonConfig {
|
||||
api: string;
|
||||
version: string;
|
||||
root_domain: string;
|
||||
}
|
||||
|
||||
export interface TalonPage {
|
||||
name: string
|
||||
path: string
|
||||
color: string
|
||||
visibility: TalonVisibility
|
||||
image: string | undefined
|
||||
source: TalonLink | undefined
|
||||
}
|
||||
|
||||
export enum TalonVisibility {
|
||||
FEATURED = "featured",
|
||||
SEARCHABLE = "searchable",
|
||||
HIDDEN = "hidden",
|
||||
}
|
||||
|
||||
export interface TalonLink {
|
||||
url: string
|
||||
type: TalonLinkType
|
||||
}
|
||||
|
||||
export enum TalonLinkType {
|
||||
LINK = "link",
|
||||
GIT = "git",
|
||||
GITHUB = "github",
|
||||
GITLAB = "gitlab",
|
||||
GITEA = "gitea",
|
||||
BITBUCKET = "bitbucket",
|
||||
}
|
||||
|
||||
export interface TalonVersion {
|
||||
date: string
|
||||
name: string | undefined
|
||||
user: string
|
||||
tags: {[key: string]: string} | undefined
|
||||
}
|
||||
|
||||
export interface Focusable {
|
||||
focus(): void
|
||||
blur(): void
|
||||
focus(): void;
|
||||
blur(): void;
|
||||
}
|
||||
|
||||
export interface SvelteActionRes {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
update?: (parameters: any) => void
|
||||
destroy?: () => void
|
||||
update?: (parameters: any) => void;
|
||||
destroy?: () => void;
|
||||
}
|
||||
|
|
90
ui/menu/testdata/test.json
vendored
90
ui/menu/testdata/test.json
vendored
|
@ -1,90 +0,0 @@
|
|||
{
|
||||
"root_path": "/",
|
||||
"current_page": null,
|
||||
"current_version": "6",
|
||||
"versions": {
|
||||
"3": {
|
||||
"date": "2021-06-15T11:12:21+00:00",
|
||||
"name": "0.1.0",
|
||||
"user": "ThetaDev",
|
||||
"tags": {
|
||||
"commit": "ec55eba5ae45640c3d225c2471920fd3c9a36489"
|
||||
}
|
||||
},
|
||||
"4": {
|
||||
"date": "2021-06-20T16:48:21+00:00",
|
||||
"name": "0.1.1",
|
||||
"user": "ThetaDev",
|
||||
"tags": {
|
||||
"commit": "ec55eba5ae45640c3d225c2471920fd3c9a36489"
|
||||
}
|
||||
},
|
||||
"5": {
|
||||
"date": "2021-06-22T12:08:21+00:00",
|
||||
"name": "0.1.2",
|
||||
"user": "ThetaDev",
|
||||
"tags": {
|
||||
"commit": "ec55eba5ae45640c3d225c2471920fd3c9a36489"
|
||||
}
|
||||
},
|
||||
"6": {
|
||||
"date": "2021-07-02T16:12:40+00:00",
|
||||
"name": "0.1.3",
|
||||
"user": "ThetaDev",
|
||||
"tags": {
|
||||
"commit": "bbc7342580b48433481857bfe95e58784a508275"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pages": {
|
||||
"1": {
|
||||
"name": "ThetaDev",
|
||||
"path": "",
|
||||
"color": "#1f91ee",
|
||||
"visibility": "featured",
|
||||
"source": {
|
||||
"url": "https://github.com/Theta-Dev",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"name": "Talon",
|
||||
"path": "Talon",
|
||||
"color": "#4b228a",
|
||||
"visibility": "featured",
|
||||
"source": {
|
||||
"url": "https://github.com/Theta-Dev/Talon",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"name": "Spotify-Gender-Ex",
|
||||
"path": "Spotify-Gender-Ex",
|
||||
"color": "#1DB954",
|
||||
"image": "https://raw.githubusercontent.com/Theta-Dev/Spotify-Gender-Ex/master/assets/logo_square.svg",
|
||||
"visibility": "featured",
|
||||
"source": {
|
||||
"url": "https://github.com/Theta-Dev/Spotify-Gender-Ex",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"4": {
|
||||
"name": "A1",
|
||||
"path": "tests/a1",
|
||||
"color": "#ff0000",
|
||||
"visibility": "searchable"
|
||||
},
|
||||
"5": {
|
||||
"name": "B1",
|
||||
"path": "tests/b1",
|
||||
"color": "#00ff00",
|
||||
"visibility": "searchable"
|
||||
},
|
||||
"6": {
|
||||
"name": "C1",
|
||||
"path": "tests/c1",
|
||||
"color": "#0000ff",
|
||||
"visibility": "searchable"
|
||||
}
|
||||
}
|
||||
}
|
99
ui/menu/testdata/testSchema.json
vendored
99
ui/menu/testdata/testSchema.json
vendored
|
@ -1,99 +0,0 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Talon menu data",
|
||||
"definitions": {
|
||||
"Version": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"date": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
},
|
||||
"tags": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["date", "user"]
|
||||
},
|
||||
"Link": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["link", "git", "github", "gitlab", "gitea", "bitbucket"]
|
||||
}
|
||||
},
|
||||
"required": ["url", "type"]
|
||||
},
|
||||
"Page": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"maxLength": 100
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"color": {
|
||||
"type": "string",
|
||||
"format": "regex",
|
||||
"pattern": "^#[A-z\\d]{6}$"
|
||||
},
|
||||
"image": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"visibility": {
|
||||
"type": "string",
|
||||
"enum": ["featured", "searchable", "hidden"]
|
||||
},
|
||||
"source": {
|
||||
"$ref": "#/definitions/Link"
|
||||
}
|
||||
},
|
||||
"required": ["name", "path", "color", "visibility"]
|
||||
}
|
||||
},
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"root_path": {
|
||||
"type": "string"
|
||||
},
|
||||
"current_page": {
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"current_version": {
|
||||
"type": "string"
|
||||
},
|
||||
"versions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/Version"
|
||||
}
|
||||
},
|
||||
"pages": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/Page"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["root_path", "current_page", "current_version", "versions", "pages"]
|
||||
}
|
||||
]
|
||||
}
|
4
ui/talon-client/.gitignore
vendored
Normal file
4
ui/talon-client/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
wwwroot/*.js
|
||||
node_modules
|
||||
typings
|
||||
dist
|
1
ui/talon-client/.npmignore
Normal file
1
ui/talon-client/.npmignore
Normal file
|
@ -0,0 +1 @@
|
|||
README.md
|
23
ui/talon-client/.openapi-generator-ignore
Normal file
23
ui/talon-client/.openapi-generator-ignore
Normal file
|
@ -0,0 +1,23 @@
|
|||
# OpenAPI Generator Ignore
|
||||
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
|
||||
|
||||
# Use this file to prevent files from being overwritten by the generator.
|
||||
# The patterns follow closely to .gitignore or .dockerignore.
|
||||
|
||||
# As an example, the C# client generator defines ApiClient.cs.
|
||||
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
|
||||
#ApiClient.cs
|
||||
|
||||
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
|
||||
#foo/*/qux
|
||||
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
|
||||
|
||||
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
|
||||
#foo/**/qux
|
||||
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
|
||||
|
||||
# You can also negate patterns with an exclamation (!).
|
||||
# For example, you can ignore all files in a docs folder with the file extension .md:
|
||||
#docs/*.md
|
||||
# Then explicitly reverse the ignore rule for a single file:
|
||||
#!docs/README.md
|
23
ui/talon-client/.openapi-generator/FILES
Normal file
23
ui/talon-client/.openapi-generator/FILES
Normal file
|
@ -0,0 +1,23 @@
|
|||
.gitignore
|
||||
.npmignore
|
||||
.openapi-generator-ignore
|
||||
README.md
|
||||
package.json
|
||||
src/apis/DefaultApi.ts
|
||||
src/apis/index.ts
|
||||
src/index.ts
|
||||
src/models/Info.ts
|
||||
src/models/InfoStats.ts
|
||||
src/models/InfoVersion.ts
|
||||
src/models/SourceIcon.ts
|
||||
src/models/Stats.ts
|
||||
src/models/Version.ts
|
||||
src/models/VersionFile.ts
|
||||
src/models/VersionInfo.ts
|
||||
src/models/Visibility.ts
|
||||
src/models/Website.ts
|
||||
src/models/WebsiteNew.ts
|
||||
src/models/WebsiteUpdate.ts
|
||||
src/models/index.ts
|
||||
src/runtime.ts
|
||||
tsconfig.json
|
1
ui/talon-client/.openapi-generator/VERSION
Normal file
1
ui/talon-client/.openapi-generator/VERSION
Normal file
|
@ -0,0 +1 @@
|
|||
6.4.0
|
45
ui/talon-client/README.md
Normal file
45
ui/talon-client/README.md
Normal file
|
@ -0,0 +1,45 @@
|
|||
## talon-client@0.1.0
|
||||
|
||||
This generator creates TypeScript/JavaScript client that utilizes [Fetch API](https://fetch.spec.whatwg.org/). The generated Node module can be used in the following environments:
|
||||
|
||||
Environment
|
||||
* Node.js
|
||||
* Webpack
|
||||
* Browserify
|
||||
|
||||
Language level
|
||||
* ES5 - you must have a Promises/A+ library installed
|
||||
* ES6
|
||||
|
||||
Module system
|
||||
* CommonJS
|
||||
* ES6 module system
|
||||
|
||||
It can be used in both TypeScript and JavaScript. In TypeScript, the definition should be automatically resolved via `package.json`. ([Reference](http://www.typescriptlang.org/docs/handbook/typings-for-npm-packages.html))
|
||||
|
||||
### Building
|
||||
|
||||
To build and compile the typescript sources to javascript use:
|
||||
```
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Publishing
|
||||
|
||||
First build the package then run ```npm publish```
|
||||
|
||||
### Consuming
|
||||
|
||||
navigate to the folder of your consuming project and run one of the following commands.
|
||||
|
||||
_published:_
|
||||
|
||||
```
|
||||
npm install talon-client@0.1.0 --save
|
||||
```
|
||||
|
||||
_unPublished (not recommended):_
|
||||
|
||||
```
|
||||
npm install PATH_TO_GENERATED_PACKAGE --save
|
28
ui/talon-client/package-lock.json
generated
Normal file
28
ui/talon-client/package-lock.json
generated
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "talon-client",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "talon-client",
|
||||
"version": "0.1.0",
|
||||
"devDependencies": {
|
||||
"typescript": "^4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
ui/talon-client/package.json
Normal file
19
ui/talon-client/package.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "talon-client",
|
||||
"version": "0.1.0",
|
||||
"description": "OpenAPI client for talon-client",
|
||||
"author": "OpenAPI-Generator",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/GIT_USER_ID/GIT_REPO_ID.git"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"typings": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"prepare": "npm run build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^4.0"
|
||||
}
|
||||
}
|
559
ui/talon-client/src/apis/DefaultApi.ts
Normal file
559
ui/talon-client/src/apis/DefaultApi.ts
Normal file
|
@ -0,0 +1,559 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Talon
|
||||
* API for the Talon static site management system
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
import * as runtime from '../runtime';
|
||||
import type {
|
||||
Info,
|
||||
Version,
|
||||
VersionFile,
|
||||
Visibility,
|
||||
Website,
|
||||
WebsiteNew,
|
||||
WebsiteUpdate,
|
||||
} from '../models';
|
||||
import {
|
||||
InfoFromJSON,
|
||||
InfoToJSON,
|
||||
VersionFromJSON,
|
||||
VersionToJSON,
|
||||
VersionFileFromJSON,
|
||||
VersionFileToJSON,
|
||||
VisibilityFromJSON,
|
||||
VisibilityToJSON,
|
||||
WebsiteFromJSON,
|
||||
WebsiteToJSON,
|
||||
WebsiteNewFromJSON,
|
||||
WebsiteNewToJSON,
|
||||
WebsiteUpdateFromJSON,
|
||||
WebsiteUpdateToJSON,
|
||||
} from '../models';
|
||||
|
||||
export interface FileHashGetRequest {
|
||||
hash: string;
|
||||
}
|
||||
|
||||
export interface WebsiteSubdomainDeleteRequest {
|
||||
subdomain: string;
|
||||
}
|
||||
|
||||
export interface WebsiteSubdomainGetRequest {
|
||||
subdomain: string;
|
||||
}
|
||||
|
||||
export interface WebsiteSubdomainPatchRequest {
|
||||
subdomain: string;
|
||||
websiteUpdate: WebsiteUpdate;
|
||||
}
|
||||
|
||||
export interface WebsiteSubdomainPutRequest {
|
||||
subdomain: string;
|
||||
websiteNew: WebsiteNew;
|
||||
}
|
||||
|
||||
export interface WebsiteSubdomainUploadPostRequest {
|
||||
subdomain: string;
|
||||
body: Blob;
|
||||
fallback?: string;
|
||||
spa?: boolean;
|
||||
versionData?: { [key: string]: string; };
|
||||
}
|
||||
|
||||
export interface WebsiteSubdomainVersionVersionDeleteRequest {
|
||||
subdomain: string;
|
||||
version: number;
|
||||
}
|
||||
|
||||
export interface WebsiteSubdomainVersionVersionFilesGetRequest {
|
||||
subdomain: string;
|
||||
version: number;
|
||||
}
|
||||
|
||||
export interface WebsiteSubdomainVersionVersionGetRequest {
|
||||
subdomain: string;
|
||||
version: number;
|
||||
}
|
||||
|
||||
export interface WebsiteSubdomainVersionsGetRequest {
|
||||
subdomain: string;
|
||||
}
|
||||
|
||||
export interface WebsitesAllGetRequest {
|
||||
visibility?: Visibility;
|
||||
}
|
||||
|
||||
export interface WebsitesGetRequest {
|
||||
visibility?: Visibility;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export class DefaultApi extends runtime.BaseAPI {
|
||||
|
||||
/**
|
||||
* Retrieve a file
|
||||
*/
|
||||
async fileHashGetRaw(requestParameters: FileHashGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Blob>> {
|
||||
if (requestParameters.hash === null || requestParameters.hash === undefined) {
|
||||
throw new runtime.RequiredError('hash','Required parameter requestParameters.hash was null or undefined when calling fileHashGet.');
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
const response = await this.request({
|
||||
path: `/file/{hash}`.replace(`{${"hash"}}`, encodeURIComponent(String(requestParameters.hash))),
|
||||
method: 'GET',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.BlobApiResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a file
|
||||
*/
|
||||
async fileHashGet(requestParameters: FileHashGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Blob> {
|
||||
const response = await this.fileHashGetRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about your server
|
||||
*/
|
||||
async infoGetRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Info>> {
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
const response = await this.request({
|
||||
path: `/info`,
|
||||
method: 'GET',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) => InfoFromJSON(jsonValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about your server
|
||||
*/
|
||||
async infoGet(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Info> {
|
||||
const response = await this.infoGetRaw(initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete website
|
||||
*/
|
||||
async websiteSubdomainDeleteRaw(requestParameters: WebsiteSubdomainDeleteRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>> {
|
||||
if (requestParameters.subdomain === null || requestParameters.subdomain === undefined) {
|
||||
throw new runtime.RequiredError('subdomain','Required parameter requestParameters.subdomain was null or undefined when calling websiteSubdomainDelete.');
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.apiKey) {
|
||||
headerParameters["X-API-Key"] = this.configuration.apiKey("X-API-Key"); // ApiKeyAuthorization authentication
|
||||
}
|
||||
|
||||
const response = await this.request({
|
||||
path: `/website/{subdomain}`.replace(`{${"subdomain"}}`, encodeURIComponent(String(requestParameters.subdomain))),
|
||||
method: 'DELETE',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.VoidApiResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete website
|
||||
*/
|
||||
async websiteSubdomainDelete(requestParameters: WebsiteSubdomainDeleteRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void> {
|
||||
await this.websiteSubdomainDeleteRaw(requestParameters, initOverrides);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a website
|
||||
*/
|
||||
async websiteSubdomainGetRaw(requestParameters: WebsiteSubdomainGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Website>> {
|
||||
if (requestParameters.subdomain === null || requestParameters.subdomain === undefined) {
|
||||
throw new runtime.RequiredError('subdomain','Required parameter requestParameters.subdomain was null or undefined when calling websiteSubdomainGet.');
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
const response = await this.request({
|
||||
path: `/website/{subdomain}`.replace(`{${"subdomain"}}`, encodeURIComponent(String(requestParameters.subdomain))),
|
||||
method: 'GET',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) => WebsiteFromJSON(jsonValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a website
|
||||
*/
|
||||
async websiteSubdomainGet(requestParameters: WebsiteSubdomainGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Website> {
|
||||
const response = await this.websiteSubdomainGetRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update website
|
||||
*/
|
||||
async websiteSubdomainPatchRaw(requestParameters: WebsiteSubdomainPatchRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>> {
|
||||
if (requestParameters.subdomain === null || requestParameters.subdomain === undefined) {
|
||||
throw new runtime.RequiredError('subdomain','Required parameter requestParameters.subdomain was null or undefined when calling websiteSubdomainPatch.');
|
||||
}
|
||||
|
||||
if (requestParameters.websiteUpdate === null || requestParameters.websiteUpdate === undefined) {
|
||||
throw new runtime.RequiredError('websiteUpdate','Required parameter requestParameters.websiteUpdate was null or undefined when calling websiteSubdomainPatch.');
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
headerParameters['Content-Type'] = 'application/json; charset=utf-8';
|
||||
|
||||
if (this.configuration && this.configuration.apiKey) {
|
||||
headerParameters["X-API-Key"] = this.configuration.apiKey("X-API-Key"); // ApiKeyAuthorization authentication
|
||||
}
|
||||
|
||||
const response = await this.request({
|
||||
path: `/website/{subdomain}`.replace(`{${"subdomain"}}`, encodeURIComponent(String(requestParameters.subdomain))),
|
||||
method: 'PATCH',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
body: WebsiteUpdateToJSON(requestParameters.websiteUpdate),
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.VoidApiResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update website
|
||||
*/
|
||||
async websiteSubdomainPatch(requestParameters: WebsiteSubdomainPatchRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void> {
|
||||
await this.websiteSubdomainPatchRaw(requestParameters, initOverrides);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new website
|
||||
*/
|
||||
async websiteSubdomainPutRaw(requestParameters: WebsiteSubdomainPutRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>> {
|
||||
if (requestParameters.subdomain === null || requestParameters.subdomain === undefined) {
|
||||
throw new runtime.RequiredError('subdomain','Required parameter requestParameters.subdomain was null or undefined when calling websiteSubdomainPut.');
|
||||
}
|
||||
|
||||
if (requestParameters.websiteNew === null || requestParameters.websiteNew === undefined) {
|
||||
throw new runtime.RequiredError('websiteNew','Required parameter requestParameters.websiteNew was null or undefined when calling websiteSubdomainPut.');
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
headerParameters['Content-Type'] = 'application/json; charset=utf-8';
|
||||
|
||||
if (this.configuration && this.configuration.apiKey) {
|
||||
headerParameters["X-API-Key"] = this.configuration.apiKey("X-API-Key"); // ApiKeyAuthorization authentication
|
||||
}
|
||||
|
||||
const response = await this.request({
|
||||
path: `/website/{subdomain}`.replace(`{${"subdomain"}}`, encodeURIComponent(String(requestParameters.subdomain))),
|
||||
method: 'PUT',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
body: WebsiteNewToJSON(requestParameters.websiteNew),
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.VoidApiResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new website
|
||||
*/
|
||||
async websiteSubdomainPut(requestParameters: WebsiteSubdomainPutRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void> {
|
||||
await this.websiteSubdomainPutRaw(requestParameters, initOverrides);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a new version
|
||||
*/
|
||||
async websiteSubdomainUploadPostRaw(requestParameters: WebsiteSubdomainUploadPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>> {
|
||||
if (requestParameters.subdomain === null || requestParameters.subdomain === undefined) {
|
||||
throw new runtime.RequiredError('subdomain','Required parameter requestParameters.subdomain was null or undefined when calling websiteSubdomainUploadPost.');
|
||||
}
|
||||
|
||||
if (requestParameters.body === null || requestParameters.body === undefined) {
|
||||
throw new runtime.RequiredError('body','Required parameter requestParameters.body was null or undefined when calling websiteSubdomainUploadPost.');
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
if (requestParameters.fallback !== undefined) {
|
||||
queryParameters['fallback'] = requestParameters.fallback;
|
||||
}
|
||||
|
||||
if (requestParameters.spa !== undefined) {
|
||||
queryParameters['spa'] = requestParameters.spa;
|
||||
}
|
||||
|
||||
if (requestParameters.versionData !== undefined) {
|
||||
queryParameters['version_data'] = requestParameters.versionData;
|
||||
}
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
headerParameters['Content-Type'] = 'application/octet-stream';
|
||||
|
||||
if (this.configuration && this.configuration.apiKey) {
|
||||
headerParameters["X-API-Key"] = this.configuration.apiKey("X-API-Key"); // ApiKeyAuthorization authentication
|
||||
}
|
||||
|
||||
const response = await this.request({
|
||||
path: `/website/{subdomain}/upload`.replace(`{${"subdomain"}}`, encodeURIComponent(String(requestParameters.subdomain))),
|
||||
method: 'POST',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
body: requestParameters.body as any,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.VoidApiResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a new version
|
||||
*/
|
||||
async websiteSubdomainUploadPost(requestParameters: WebsiteSubdomainUploadPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void> {
|
||||
await this.websiteSubdomainUploadPostRaw(requestParameters, initOverrides);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete version
|
||||
*/
|
||||
async websiteSubdomainVersionVersionDeleteRaw(requestParameters: WebsiteSubdomainVersionVersionDeleteRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>> {
|
||||
if (requestParameters.subdomain === null || requestParameters.subdomain === undefined) {
|
||||
throw new runtime.RequiredError('subdomain','Required parameter requestParameters.subdomain was null or undefined when calling websiteSubdomainVersionVersionDelete.');
|
||||
}
|
||||
|
||||
if (requestParameters.version === null || requestParameters.version === undefined) {
|
||||
throw new runtime.RequiredError('version','Required parameter requestParameters.version was null or undefined when calling websiteSubdomainVersionVersionDelete.');
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.apiKey) {
|
||||
headerParameters["X-API-Key"] = this.configuration.apiKey("X-API-Key"); // ApiKeyAuthorization authentication
|
||||
}
|
||||
|
||||
const response = await this.request({
|
||||
path: `/website/{subdomain}/version/{version}`.replace(`{${"subdomain"}}`, encodeURIComponent(String(requestParameters.subdomain))).replace(`{${"version"}}`, encodeURIComponent(String(requestParameters.version))),
|
||||
method: 'DELETE',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.VoidApiResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete version
|
||||
*/
|
||||
async websiteSubdomainVersionVersionDelete(requestParameters: WebsiteSubdomainVersionVersionDeleteRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void> {
|
||||
await this.websiteSubdomainVersionVersionDeleteRaw(requestParameters, initOverrides);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get version files
|
||||
*/
|
||||
async websiteSubdomainVersionVersionFilesGetRaw(requestParameters: WebsiteSubdomainVersionVersionFilesGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Array<VersionFile>>> {
|
||||
if (requestParameters.subdomain === null || requestParameters.subdomain === undefined) {
|
||||
throw new runtime.RequiredError('subdomain','Required parameter requestParameters.subdomain was null or undefined when calling websiteSubdomainVersionVersionFilesGet.');
|
||||
}
|
||||
|
||||
if (requestParameters.version === null || requestParameters.version === undefined) {
|
||||
throw new runtime.RequiredError('version','Required parameter requestParameters.version was null or undefined when calling websiteSubdomainVersionVersionFilesGet.');
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
const response = await this.request({
|
||||
path: `/website/{subdomain}/version/{version}/files`.replace(`{${"subdomain"}}`, encodeURIComponent(String(requestParameters.subdomain))).replace(`{${"version"}}`, encodeURIComponent(String(requestParameters.version))),
|
||||
method: 'GET',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(VersionFileFromJSON));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get version files
|
||||
*/
|
||||
async websiteSubdomainVersionVersionFilesGet(requestParameters: WebsiteSubdomainVersionVersionFilesGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Array<VersionFile>> {
|
||||
const response = await this.websiteSubdomainVersionVersionFilesGetRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get version
|
||||
*/
|
||||
async websiteSubdomainVersionVersionGetRaw(requestParameters: WebsiteSubdomainVersionVersionGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Version>> {
|
||||
if (requestParameters.subdomain === null || requestParameters.subdomain === undefined) {
|
||||
throw new runtime.RequiredError('subdomain','Required parameter requestParameters.subdomain was null or undefined when calling websiteSubdomainVersionVersionGet.');
|
||||
}
|
||||
|
||||
if (requestParameters.version === null || requestParameters.version === undefined) {
|
||||
throw new runtime.RequiredError('version','Required parameter requestParameters.version was null or undefined when calling websiteSubdomainVersionVersionGet.');
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
const response = await this.request({
|
||||
path: `/website/{subdomain}/version/{version}`.replace(`{${"subdomain"}}`, encodeURIComponent(String(requestParameters.subdomain))).replace(`{${"version"}}`, encodeURIComponent(String(requestParameters.version))),
|
||||
method: 'GET',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) => VersionFromJSON(jsonValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get version
|
||||
*/
|
||||
async websiteSubdomainVersionVersionGet(requestParameters: WebsiteSubdomainVersionVersionGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Version> {
|
||||
const response = await this.websiteSubdomainVersionVersionGetRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get website versions
|
||||
*/
|
||||
async websiteSubdomainVersionsGetRaw(requestParameters: WebsiteSubdomainVersionsGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Array<Version>>> {
|
||||
if (requestParameters.subdomain === null || requestParameters.subdomain === undefined) {
|
||||
throw new runtime.RequiredError('subdomain','Required parameter requestParameters.subdomain was null or undefined when calling websiteSubdomainVersionsGet.');
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
const response = await this.request({
|
||||
path: `/website/{subdomain}/versions`.replace(`{${"subdomain"}}`, encodeURIComponent(String(requestParameters.subdomain))),
|
||||
method: 'GET',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(VersionFromJSON));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get website versions
|
||||
*/
|
||||
async websiteSubdomainVersionsGet(requestParameters: WebsiteSubdomainVersionsGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Array<Version>> {
|
||||
const response = await this.websiteSubdomainVersionsGetRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all websites from Talon\'s database (including hidden ones, if the current user has access to them). This endpoint requires authentication (use the `/websites` endpoint for unauthenticated users).
|
||||
* Get all websites
|
||||
*/
|
||||
async websitesAllGetRaw(requestParameters: WebsitesAllGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Array<Website>>> {
|
||||
const queryParameters: any = {};
|
||||
|
||||
if (requestParameters.visibility !== undefined) {
|
||||
queryParameters['visibility'] = requestParameters.visibility;
|
||||
}
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.apiKey) {
|
||||
headerParameters["X-API-Key"] = this.configuration.apiKey("X-API-Key"); // ApiKeyAuthorization authentication
|
||||
}
|
||||
|
||||
const response = await this.request({
|
||||
path: `/websitesAll`,
|
||||
method: 'GET',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(WebsiteFromJSON));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all websites from Talon\'s database (including hidden ones, if the current user has access to them). This endpoint requires authentication (use the `/websites` endpoint for unauthenticated users).
|
||||
* Get all websites
|
||||
*/
|
||||
async websitesAllGet(requestParameters: WebsitesAllGetRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Array<Website>> {
|
||||
const response = await this.websitesAllGetRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all publicly listed websites (visibility != `hidden`)
|
||||
* Get all publicly listed websites
|
||||
*/
|
||||
async websitesGetRaw(requestParameters: WebsitesGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Array<Website>>> {
|
||||
const queryParameters: any = {};
|
||||
|
||||
if (requestParameters.visibility !== undefined) {
|
||||
queryParameters['visibility'] = requestParameters.visibility;
|
||||
}
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
const response = await this.request({
|
||||
path: `/websites`,
|
||||
method: 'GET',
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
}, initOverrides);
|
||||
|
||||
return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(WebsiteFromJSON));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all publicly listed websites (visibility != `hidden`)
|
||||
* Get all publicly listed websites
|
||||
*/
|
||||
async websitesGet(requestParameters: WebsitesGetRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Array<Website>> {
|
||||
const response = await this.websitesGetRaw(requestParameters, initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
}
|
3
ui/talon-client/src/apis/index.ts
Normal file
3
ui/talon-client/src/apis/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export * from './DefaultApi';
|
5
ui/talon-client/src/index.ts
Normal file
5
ui/talon-client/src/index.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export * from './runtime';
|
||||
export * from './apis';
|
||||
export * from './models';
|
96
ui/talon-client/src/models/Info.ts
Normal file
96
ui/talon-client/src/models/Info.ts
Normal file
|
@ -0,0 +1,96 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Talon
|
||||
* API for the Talon static site management system
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { exists, mapValues } from '../runtime';
|
||||
import type { InfoStats } from './InfoStats';
|
||||
import {
|
||||
InfoStatsFromJSON,
|
||||
InfoStatsFromJSONTyped,
|
||||
InfoStatsToJSON,
|
||||
} from './InfoStats';
|
||||
import type { InfoVersion } from './InfoVersion';
|
||||
import {
|
||||
InfoVersionFromJSON,
|
||||
InfoVersionFromJSONTyped,
|
||||
InfoVersionToJSON,
|
||||
} from './InfoVersion';
|
||||
|
||||
/**
|
||||
* Server information
|
||||
* @export
|
||||
* @interface Info
|
||||
*/
|
||||
export interface Info {
|
||||
/**
|
||||
*
|
||||
* @type {InfoStats}
|
||||
* @memberof Info
|
||||
*/
|
||||
stats: InfoStats;
|
||||
/**
|
||||
*
|
||||
* @type {InfoVersion}
|
||||
* @memberof Info
|
||||
*/
|
||||
version: InfoVersion;
|
||||
/**
|
||||
* Current uptime of the server in seconds
|
||||
* @type {number}
|
||||
* @memberof Info
|
||||
*/
|
||||
uptime: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the Info interface.
|
||||
*/
|
||||
export function instanceOfInfo(value: object): boolean {
|
||||
let isInstance = true;
|
||||
isInstance = isInstance && "stats" in value;
|
||||
isInstance = isInstance && "version" in value;
|
||||
isInstance = isInstance && "uptime" in value;
|
||||
|
||||
return isInstance;
|
||||
}
|
||||
|
||||
export function InfoFromJSON(json: any): Info {
|
||||
return InfoFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function InfoFromJSONTyped(json: any, ignoreDiscriminator: boolean): Info {
|
||||
if ((json === undefined) || (json === null)) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
|
||||
'stats': InfoStatsFromJSON(json['stats']),
|
||||
'version': InfoVersionFromJSON(json['version']),
|
||||
'uptime': json['uptime'],
|
||||
};
|
||||
}
|
||||
|
||||
export function InfoToJSON(value?: Info | null): any {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
|
||||
'stats': InfoStatsToJSON(value.stats),
|
||||
'version': InfoVersionToJSON(value.version),
|
||||
'uptime': value.uptime,
|
||||
};
|
||||
}
|
83
ui/talon-client/src/models/InfoStats.ts
Normal file
83
ui/talon-client/src/models/InfoStats.ts
Normal file
|
@ -0,0 +1,83 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Talon
|
||||
* API for the Talon static site management system
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { exists, mapValues } from '../runtime';
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface InfoStats
|
||||
*/
|
||||
export interface InfoStats {
|
||||
/**
|
||||
* Number of websites
|
||||
* @type {number}
|
||||
* @memberof InfoStats
|
||||
*/
|
||||
nWebsites: number;
|
||||
/**
|
||||
* Number of unique files
|
||||
* @type {number}
|
||||
* @memberof InfoStats
|
||||
*/
|
||||
nFiles: number;
|
||||
/**
|
||||
* Amount of used storage space (in bytes)
|
||||
* @type {number}
|
||||
* @memberof InfoStats
|
||||
*/
|
||||
storageUsed: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the InfoStats interface.
|
||||
*/
|
||||
export function instanceOfInfoStats(value: object): boolean {
|
||||
let isInstance = true;
|
||||
isInstance = isInstance && "nWebsites" in value;
|
||||
isInstance = isInstance && "nFiles" in value;
|
||||
isInstance = isInstance && "storageUsed" in value;
|
||||
|
||||
return isInstance;
|
||||
}
|
||||
|
||||
export function InfoStatsFromJSON(json: any): InfoStats {
|
||||
return InfoStatsFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function InfoStatsFromJSONTyped(json: any, ignoreDiscriminator: boolean): InfoStats {
|
||||
if ((json === undefined) || (json === null)) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
|
||||
'nWebsites': json['n_websites'],
|
||||
'nFiles': json['n_files'],
|
||||
'storageUsed': json['storage_used'],
|
||||
};
|
||||
}
|
||||
|
||||
export function InfoStatsToJSON(value?: InfoStats | null): any {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
|
||||
'n_websites': value.nWebsites,
|
||||
'n_files': value.nFiles,
|
||||
'storage_used': value.storageUsed,
|
||||
};
|
||||
}
|
110
ui/talon-client/src/models/InfoVersion.ts
Normal file
110
ui/talon-client/src/models/InfoVersion.ts
Normal file
|
@ -0,0 +1,110 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Talon
|
||||
* API for the Talon static site management system
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { exists, mapValues } from '../runtime';
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface InfoVersion
|
||||
*/
|
||||
export interface InfoVersion {
|
||||
/**
|
||||
* Talon version
|
||||
* @type {string}
|
||||
* @memberof InfoVersion
|
||||
*/
|
||||
version: string;
|
||||
/**
|
||||
* Commit hash
|
||||
* @type {string}
|
||||
* @memberof InfoVersion
|
||||
*/
|
||||
commit: string;
|
||||
/**
|
||||
* Commit date
|
||||
* @type {Date}
|
||||
* @memberof InfoVersion
|
||||
*/
|
||||
commitDate: Date;
|
||||
/**
|
||||
* Rust version
|
||||
* @type {string}
|
||||
* @memberof InfoVersion
|
||||
*/
|
||||
rustVersion: string;
|
||||
/**
|
||||
* Build target (OS and architecture)
|
||||
* @type {string}
|
||||
* @memberof InfoVersion
|
||||
*/
|
||||
buildTarget: string;
|
||||
/**
|
||||
* Rust build mode (`debug` / `release`)
|
||||
* @type {string}
|
||||
* @memberof InfoVersion
|
||||
*/
|
||||
buildMode: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the InfoVersion interface.
|
||||
*/
|
||||
export function instanceOfInfoVersion(value: object): boolean {
|
||||
let isInstance = true;
|
||||
isInstance = isInstance && "version" in value;
|
||||
isInstance = isInstance && "commit" in value;
|
||||
isInstance = isInstance && "commitDate" in value;
|
||||
isInstance = isInstance && "rustVersion" in value;
|
||||
isInstance = isInstance && "buildTarget" in value;
|
||||
isInstance = isInstance && "buildMode" in value;
|
||||
|
||||
return isInstance;
|
||||
}
|
||||
|
||||
export function InfoVersionFromJSON(json: any): InfoVersion {
|
||||
return InfoVersionFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function InfoVersionFromJSONTyped(json: any, ignoreDiscriminator: boolean): InfoVersion {
|
||||
if ((json === undefined) || (json === null)) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
|
||||
'version': json['version'],
|
||||
'commit': json['commit'],
|
||||
'commitDate': (new Date(json['commit_date'])),
|
||||
'rustVersion': json['rust_version'],
|
||||
'buildTarget': json['build_target'],
|
||||
'buildMode': json['build_mode'],
|
||||
};
|
||||
}
|
||||
|
||||
export function InfoVersionToJSON(value?: InfoVersion | null): any {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
|
||||
'version': value.version,
|
||||
'commit': value.commit,
|
||||
'commit_date': (value.commitDate.toISOString()),
|
||||
'rust_version': value.rustVersion,
|
||||
'build_target': value.buildTarget,
|
||||
'build_mode': value.buildMode,
|
||||
};
|
||||
}
|
41
ui/talon-client/src/models/SourceIcon.ts
Normal file
41
ui/talon-client/src/models/SourceIcon.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Talon
|
||||
* API for the Talon static site management system
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const SourceIcon = {
|
||||
Link: 'link',
|
||||
Git: 'git',
|
||||
Github: 'github',
|
||||
Gitlab: 'gitlab',
|
||||
Gitea: 'gitea',
|
||||
Bitbucket: 'bitbucket'
|
||||
} as const;
|
||||
export type SourceIcon = typeof SourceIcon[keyof typeof SourceIcon];
|
||||
|
||||
|
||||
export function SourceIconFromJSON(json: any): SourceIcon {
|
||||
return SourceIconFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function SourceIconFromJSONTyped(json: any, ignoreDiscriminator: boolean): SourceIcon {
|
||||
return json as SourceIcon;
|
||||
}
|
||||
|
||||
export function SourceIconToJSON(value?: SourceIcon | null): any {
|
||||
return value as any;
|
||||
}
|
83
ui/talon-client/src/models/Stats.ts
Normal file
83
ui/talon-client/src/models/Stats.ts
Normal file
|
@ -0,0 +1,83 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Talon
|
||||
* API for the Talon static site management system
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { exists, mapValues } from '../runtime';
|
||||
/**
|
||||
* Stats about your Talon instance
|
||||
* @export
|
||||
* @interface Stats
|
||||
*/
|
||||
export interface Stats {
|
||||
/**
|
||||
* Number of websites
|
||||
* @type {number}
|
||||
* @memberof Stats
|
||||
*/
|
||||
nWebsites: number;
|
||||
/**
|
||||
* Number of unique files
|
||||
* @type {number}
|
||||
* @memberof Stats
|
||||
*/
|
||||
nFiles: number;
|
||||
/**
|
||||
* Amount of used storage space (in bytes)
|
||||
* @type {number}
|
||||
* @memberof Stats
|
||||
*/
|
||||
storageUsed: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the Stats interface.
|
||||
*/
|
||||
export function instanceOfStats(value: object): boolean {
|
||||
let isInstance = true;
|
||||
isInstance = isInstance && "nWebsites" in value;
|
||||
isInstance = isInstance && "nFiles" in value;
|
||||
isInstance = isInstance && "storageUsed" in value;
|
||||
|
||||
return isInstance;
|
||||
}
|
||||
|
||||
export function StatsFromJSON(json: any): Stats {
|
||||
return StatsFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function StatsFromJSONTyped(json: any, ignoreDiscriminator: boolean): Stats {
|
||||
if ((json === undefined) || (json === null)) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
|
||||
'nWebsites': json['n_websites'],
|
||||
'nFiles': json['n_files'],
|
||||
'storageUsed': json['storage_used'],
|
||||
};
|
||||
}
|
||||
|
||||
export function StatsToJSON(value?: Stats | null): any {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
|
||||
'n_websites': value.nWebsites,
|
||||
'n_files': value.nFiles,
|
||||
'storage_used': value.storageUsed,
|
||||
};
|
||||
}
|
86
ui/talon-client/src/models/Version.ts
Normal file
86
ui/talon-client/src/models/Version.ts
Normal file
|
@ -0,0 +1,86 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Talon
|
||||
* API for the Talon static site management system
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { exists, mapValues } from '../runtime';
|
||||
/**
|
||||
* Website version
|
||||
* @export
|
||||
* @interface Version
|
||||
*/
|
||||
export interface Version {
|
||||
/**
|
||||
* Version ID
|
||||
* @type {number}
|
||||
* @memberof Version
|
||||
*/
|
||||
id: number;
|
||||
/**
|
||||
* Version creation date
|
||||
* @type {Date}
|
||||
* @memberof Version
|
||||
*/
|
||||
createdAt: Date;
|
||||
/**
|
||||
* Associated version data
|
||||
*
|
||||
* This is an arbitrary string map that can hold build information and other stuff
|
||||
* and will be displayed in the site info dialog.
|
||||
* @type {{ [key: string]: string; }}
|
||||
* @memberof Version
|
||||
*/
|
||||
data: { [key: string]: string; };
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the Version interface.
|
||||
*/
|
||||
export function instanceOfVersion(value: object): boolean {
|
||||
let isInstance = true;
|
||||
isInstance = isInstance && "id" in value;
|
||||
isInstance = isInstance && "createdAt" in value;
|
||||
isInstance = isInstance && "data" in value;
|
||||
|
||||
return isInstance;
|
||||
}
|
||||
|
||||
export function VersionFromJSON(json: any): Version {
|
||||
return VersionFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function VersionFromJSONTyped(json: any, ignoreDiscriminator: boolean): Version {
|
||||
if ((json === undefined) || (json === null)) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
|
||||
'id': json['id'],
|
||||
'createdAt': (new Date(json['created_at'])),
|
||||
'data': json['data'],
|
||||
};
|
||||
}
|
||||
|
||||
export function VersionToJSON(value?: Version | null): any {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
|
||||
'id': value.id,
|
||||
'created_at': (value.createdAt.toISOString()),
|
||||
'data': value.data,
|
||||
};
|
||||
}
|
82
ui/talon-client/src/models/VersionFile.ts
Normal file
82
ui/talon-client/src/models/VersionFile.ts
Normal file
|
@ -0,0 +1,82 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Talon
|
||||
* API for the Talon static site management system
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { exists, mapValues } from '../runtime';
|
||||
/**
|
||||
* Website file
|
||||
* @export
|
||||
* @interface VersionFile
|
||||
*/
|
||||
export interface VersionFile {
|
||||
/**
|
||||
* File path
|
||||
* @type {string}
|
||||
* @memberof VersionFile
|
||||
*/
|
||||
path: string;
|
||||
/**
|
||||
* File hash
|
||||
* @type {string}
|
||||
* @memberof VersionFile
|
||||
*/
|
||||
hash: string;
|
||||
/**
|
||||
* MIME file type
|
||||
* @type {string}
|
||||
* @memberof VersionFile
|
||||
*/
|
||||
mime?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the VersionFile interface.
|
||||
*/
|
||||
export function instanceOfVersionFile(value: object): boolean {
|
||||
let isInstance = true;
|
||||
isInstance = isInstance && "path" in value;
|
||||
isInstance = isInstance && "hash" in value;
|
||||
|
||||
return isInstance;
|
||||
}
|
||||
|
||||
export function VersionFileFromJSON(json: any): VersionFile {
|
||||
return VersionFileFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function VersionFileFromJSONTyped(json: any, ignoreDiscriminator: boolean): VersionFile {
|
||||
if ((json === undefined) || (json === null)) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
|
||||
'path': json['path'],
|
||||
'hash': json['hash'],
|
||||
'mime': !exists(json, 'mime') ? undefined : json['mime'],
|
||||
};
|
||||
}
|
||||
|
||||
export function VersionFileToJSON(value?: VersionFile | null): any {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
|
||||
'path': value.path,
|
||||
'hash': value.hash,
|
||||
'mime': value.mime,
|
||||
};
|
||||
}
|
110
ui/talon-client/src/models/VersionInfo.ts
Normal file
110
ui/talon-client/src/models/VersionInfo.ts
Normal file
|
@ -0,0 +1,110 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Talon
|
||||
* API for the Talon static site management system
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { exists, mapValues } from '../runtime';
|
||||
/**
|
||||
* Information about a Talon version
|
||||
* @export
|
||||
* @interface VersionInfo
|
||||
*/
|
||||
export interface VersionInfo {
|
||||
/**
|
||||
* Talon version
|
||||
* @type {string}
|
||||
* @memberof VersionInfo
|
||||
*/
|
||||
version: string;
|
||||
/**
|
||||
* Commit hash
|
||||
* @type {string}
|
||||
* @memberof VersionInfo
|
||||
*/
|
||||
commit: string;
|
||||
/**
|
||||
* Commit date
|
||||
* @type {Date}
|
||||
* @memberof VersionInfo
|
||||
*/
|
||||
commitDate: Date;
|
||||
/**
|
||||
* Rust version
|
||||
* @type {string}
|
||||
* @memberof VersionInfo
|
||||
*/
|
||||
rustVersion: string;
|
||||
/**
|
||||
* Build target (OS and architecture)
|
||||
* @type {string}
|
||||
* @memberof VersionInfo
|
||||
*/
|
||||
buildTarget: string;
|
||||
/**
|
||||
* Rust build mode (`debug` / `release`)
|
||||
* @type {string}
|
||||
* @memberof VersionInfo
|
||||
*/
|
||||
buildMode: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the VersionInfo interface.
|
||||
*/
|
||||
export function instanceOfVersionInfo(value: object): boolean {
|
||||
let isInstance = true;
|
||||
isInstance = isInstance && "version" in value;
|
||||
isInstance = isInstance && "commit" in value;
|
||||
isInstance = isInstance && "commitDate" in value;
|
||||
isInstance = isInstance && "rustVersion" in value;
|
||||
isInstance = isInstance && "buildTarget" in value;
|
||||
isInstance = isInstance && "buildMode" in value;
|
||||
|
||||
return isInstance;
|
||||
}
|
||||
|
||||
export function VersionInfoFromJSON(json: any): VersionInfo {
|
||||
return VersionInfoFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function VersionInfoFromJSONTyped(json: any, ignoreDiscriminator: boolean): VersionInfo {
|
||||
if ((json === undefined) || (json === null)) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
|
||||
'version': json['version'],
|
||||
'commit': json['commit'],
|
||||
'commitDate': (new Date(json['commit_date'])),
|
||||
'rustVersion': json['rust_version'],
|
||||
'buildTarget': json['build_target'],
|
||||
'buildMode': json['build_mode'],
|
||||
};
|
||||
}
|
||||
|
||||
export function VersionInfoToJSON(value?: VersionInfo | null): any {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
|
||||
'version': value.version,
|
||||
'commit': value.commit,
|
||||
'commit_date': (value.commitDate.toISOString()),
|
||||
'rust_version': value.rustVersion,
|
||||
'build_target': value.buildTarget,
|
||||
'build_mode': value.buildMode,
|
||||
};
|
||||
}
|
38
ui/talon-client/src/models/Visibility.ts
Normal file
38
ui/talon-client/src/models/Visibility.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Talon
|
||||
* API for the Talon static site management system
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const Visibility = {
|
||||
Featured: 'featured',
|
||||
Searchable: 'searchable',
|
||||
Hidden: 'hidden'
|
||||
} as const;
|
||||
export type Visibility = typeof Visibility[keyof typeof Visibility];
|
||||
|
||||
|
||||
export function VisibilityFromJSON(json: any): Visibility {
|
||||
return VisibilityFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function VisibilityFromJSONTyped(json: any, ignoreDiscriminator: boolean): Visibility {
|
||||
return json as Visibility;
|
||||
}
|
||||
|
||||
export function VisibilityToJSON(value?: Visibility | null): any {
|
||||
return value as any;
|
||||
}
|
139
ui/talon-client/src/models/Website.ts
Normal file
139
ui/talon-client/src/models/Website.ts
Normal file
|
@ -0,0 +1,139 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Talon
|
||||
* API for the Talon static site management system
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { exists, mapValues } from '../runtime';
|
||||
import type { SourceIcon } from './SourceIcon';
|
||||
import {
|
||||
SourceIconFromJSON,
|
||||
SourceIconFromJSONTyped,
|
||||
SourceIconToJSON,
|
||||
} from './SourceIcon';
|
||||
import type { Visibility } from './Visibility';
|
||||
import {
|
||||
VisibilityFromJSON,
|
||||
VisibilityFromJSONTyped,
|
||||
VisibilityToJSON,
|
||||
} from './Visibility';
|
||||
|
||||
/**
|
||||
* Website
|
||||
* @export
|
||||
* @interface Website
|
||||
*/
|
||||
export interface Website {
|
||||
/**
|
||||
* Website subdomain
|
||||
* @type {string}
|
||||
* @memberof Website
|
||||
*/
|
||||
subdomain: string;
|
||||
/**
|
||||
* Website name
|
||||
* @type {string}
|
||||
* @memberof Website
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Website creation date
|
||||
* @type {Date}
|
||||
* @memberof Website
|
||||
*/
|
||||
createdAt: Date;
|
||||
/**
|
||||
* Latest version ID
|
||||
* @type {number}
|
||||
* @memberof Website
|
||||
*/
|
||||
latestVersion?: number;
|
||||
/**
|
||||
* Color of the page icon
|
||||
*
|
||||
* Format: `#7935df`
|
||||
* @type {string}
|
||||
* @memberof Website
|
||||
*/
|
||||
color?: string;
|
||||
/**
|
||||
*
|
||||
* @type {Visibility}
|
||||
* @memberof Website
|
||||
*/
|
||||
visibility: Visibility;
|
||||
/**
|
||||
* Link to the source of the page
|
||||
* @type {string}
|
||||
* @memberof Website
|
||||
*/
|
||||
sourceUrl?: string;
|
||||
/**
|
||||
*
|
||||
* @type {SourceIcon}
|
||||
* @memberof Website
|
||||
*/
|
||||
sourceIcon?: SourceIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the Website interface.
|
||||
*/
|
||||
export function instanceOfWebsite(value: object): boolean {
|
||||
let isInstance = true;
|
||||
isInstance = isInstance && "subdomain" in value;
|
||||
isInstance = isInstance && "name" in value;
|
||||
isInstance = isInstance && "createdAt" in value;
|
||||
isInstance = isInstance && "visibility" in value;
|
||||
|
||||
return isInstance;
|
||||
}
|
||||
|
||||
export function WebsiteFromJSON(json: any): Website {
|
||||
return WebsiteFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function WebsiteFromJSONTyped(json: any, ignoreDiscriminator: boolean): Website {
|
||||
if ((json === undefined) || (json === null)) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
|
||||
'subdomain': json['subdomain'],
|
||||
'name': json['name'],
|
||||
'createdAt': (new Date(json['created_at'])),
|
||||
'latestVersion': !exists(json, 'latest_version') ? undefined : json['latest_version'],
|
||||
'color': !exists(json, 'color') ? undefined : json['color'],
|
||||
'visibility': VisibilityFromJSON(json['visibility']),
|
||||
'sourceUrl': !exists(json, 'source_url') ? undefined : json['source_url'],
|
||||
'sourceIcon': !exists(json, 'source_icon') ? undefined : SourceIconFromJSON(json['source_icon']),
|
||||
};
|
||||
}
|
||||
|
||||
export function WebsiteToJSON(value?: Website | null): any {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
|
||||
'subdomain': value.subdomain,
|
||||
'name': value.name,
|
||||
'created_at': (value.createdAt.toISOString()),
|
||||
'latest_version': value.latestVersion,
|
||||
'color': value.color,
|
||||
'visibility': VisibilityToJSON(value.visibility),
|
||||
'source_url': value.sourceUrl,
|
||||
'source_icon': SourceIconToJSON(value.sourceIcon),
|
||||
};
|
||||
}
|
110
ui/talon-client/src/models/WebsiteNew.ts
Normal file
110
ui/talon-client/src/models/WebsiteNew.ts
Normal file
|
@ -0,0 +1,110 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Talon
|
||||
* API for the Talon static site management system
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { exists, mapValues } from '../runtime';
|
||||
import type { SourceIcon } from './SourceIcon';
|
||||
import {
|
||||
SourceIconFromJSON,
|
||||
SourceIconFromJSONTyped,
|
||||
SourceIconToJSON,
|
||||
} from './SourceIcon';
|
||||
import type { Visibility } from './Visibility';
|
||||
import {
|
||||
VisibilityFromJSON,
|
||||
VisibilityFromJSONTyped,
|
||||
VisibilityToJSON,
|
||||
} from './Visibility';
|
||||
|
||||
/**
|
||||
* Create a new website
|
||||
* @export
|
||||
* @interface WebsiteNew
|
||||
*/
|
||||
export interface WebsiteNew {
|
||||
/**
|
||||
* Website name
|
||||
* @type {string}
|
||||
* @memberof WebsiteNew
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Color of the page icon
|
||||
* @type {string}
|
||||
* @memberof WebsiteNew
|
||||
*/
|
||||
color?: string;
|
||||
/**
|
||||
*
|
||||
* @type {Visibility}
|
||||
* @memberof WebsiteNew
|
||||
*/
|
||||
visibility?: Visibility;
|
||||
/**
|
||||
* Link to the source of the page
|
||||
* @type {string}
|
||||
* @memberof WebsiteNew
|
||||
*/
|
||||
sourceUrl?: string;
|
||||
/**
|
||||
*
|
||||
* @type {SourceIcon}
|
||||
* @memberof WebsiteNew
|
||||
*/
|
||||
sourceIcon?: SourceIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the WebsiteNew interface.
|
||||
*/
|
||||
export function instanceOfWebsiteNew(value: object): boolean {
|
||||
let isInstance = true;
|
||||
isInstance = isInstance && "name" in value;
|
||||
|
||||
return isInstance;
|
||||
}
|
||||
|
||||
export function WebsiteNewFromJSON(json: any): WebsiteNew {
|
||||
return WebsiteNewFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function WebsiteNewFromJSONTyped(json: any, ignoreDiscriminator: boolean): WebsiteNew {
|
||||
if ((json === undefined) || (json === null)) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
|
||||
'name': json['name'],
|
||||
'color': !exists(json, 'color') ? undefined : json['color'],
|
||||
'visibility': !exists(json, 'visibility') ? undefined : VisibilityFromJSON(json['visibility']),
|
||||
'sourceUrl': !exists(json, 'source_url') ? undefined : json['source_url'],
|
||||
'sourceIcon': !exists(json, 'source_icon') ? undefined : SourceIconFromJSON(json['source_icon']),
|
||||
};
|
||||
}
|
||||
|
||||
export function WebsiteNewToJSON(value?: WebsiteNew | null): any {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
|
||||
'name': value.name,
|
||||
'color': value.color,
|
||||
'visibility': VisibilityToJSON(value.visibility),
|
||||
'source_url': value.sourceUrl,
|
||||
'source_icon': SourceIconToJSON(value.sourceIcon),
|
||||
};
|
||||
}
|
111
ui/talon-client/src/models/WebsiteUpdate.ts
Normal file
111
ui/talon-client/src/models/WebsiteUpdate.ts
Normal file
|
@ -0,0 +1,111 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Talon
|
||||
* API for the Talon static site management system
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { exists, mapValues } from '../runtime';
|
||||
import type { SourceIcon } from './SourceIcon';
|
||||
import {
|
||||
SourceIconFromJSON,
|
||||
SourceIconFromJSONTyped,
|
||||
SourceIconToJSON,
|
||||
} from './SourceIcon';
|
||||
import type { Visibility } from './Visibility';
|
||||
import {
|
||||
VisibilityFromJSON,
|
||||
VisibilityFromJSONTyped,
|
||||
VisibilityToJSON,
|
||||
} from './Visibility';
|
||||
|
||||
/**
|
||||
* Update a website with the contained values
|
||||
*
|
||||
* Values set to `None` remain unchanged.
|
||||
* @export
|
||||
* @interface WebsiteUpdate
|
||||
*/
|
||||
export interface WebsiteUpdate {
|
||||
/**
|
||||
* Website name
|
||||
* @type {string}
|
||||
* @memberof WebsiteUpdate
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* Color of the page icon
|
||||
* @type {string}
|
||||
* @memberof WebsiteUpdate
|
||||
*/
|
||||
color?: string;
|
||||
/**
|
||||
*
|
||||
* @type {Visibility}
|
||||
* @memberof WebsiteUpdate
|
||||
*/
|
||||
visibility?: Visibility;
|
||||
/**
|
||||
* Link to the source of the page
|
||||
* @type {string}
|
||||
* @memberof WebsiteUpdate
|
||||
*/
|
||||
sourceUrl?: string;
|
||||
/**
|
||||
*
|
||||
* @type {SourceIcon}
|
||||
* @memberof WebsiteUpdate
|
||||
*/
|
||||
sourceIcon?: SourceIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the WebsiteUpdate interface.
|
||||
*/
|
||||
export function instanceOfWebsiteUpdate(value: object): boolean {
|
||||
let isInstance = true;
|
||||
|
||||
return isInstance;
|
||||
}
|
||||
|
||||
export function WebsiteUpdateFromJSON(json: any): WebsiteUpdate {
|
||||
return WebsiteUpdateFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function WebsiteUpdateFromJSONTyped(json: any, ignoreDiscriminator: boolean): WebsiteUpdate {
|
||||
if ((json === undefined) || (json === null)) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
|
||||
'name': !exists(json, 'name') ? undefined : json['name'],
|
||||
'color': !exists(json, 'color') ? undefined : json['color'],
|
||||
'visibility': !exists(json, 'visibility') ? undefined : VisibilityFromJSON(json['visibility']),
|
||||
'sourceUrl': !exists(json, 'source_url') ? undefined : json['source_url'],
|
||||
'sourceIcon': !exists(json, 'source_icon') ? undefined : SourceIconFromJSON(json['source_icon']),
|
||||
};
|
||||
}
|
||||
|
||||
export function WebsiteUpdateToJSON(value?: WebsiteUpdate | null): any {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
|
||||
'name': value.name,
|
||||
'color': value.color,
|
||||
'visibility': VisibilityToJSON(value.visibility),
|
||||
'source_url': value.sourceUrl,
|
||||
'source_icon': SourceIconToJSON(value.sourceIcon),
|
||||
};
|
||||
}
|
14
ui/talon-client/src/models/index.ts
Normal file
14
ui/talon-client/src/models/index.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export * from './Info';
|
||||
export * from './InfoStats';
|
||||
export * from './InfoVersion';
|
||||
export * from './SourceIcon';
|
||||
export * from './Stats';
|
||||
export * from './Version';
|
||||
export * from './VersionFile';
|
||||
export * from './VersionInfo';
|
||||
export * from './Visibility';
|
||||
export * from './Website';
|
||||
export * from './WebsiteNew';
|
||||
export * from './WebsiteUpdate';
|
407
ui/talon-client/src/runtime.ts
Normal file
407
ui/talon-client/src/runtime.ts
Normal file
|
@ -0,0 +1,407 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Talon
|
||||
* API for the Talon static site management system
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
export const BASE_PATH = "http://localhost".replace(/\/+$/, "");
|
||||
|
||||
export interface ConfigurationParameters {
|
||||
basePath?: string; // override base path
|
||||
fetchApi?: FetchAPI; // override for fetch implementation
|
||||
middleware?: Middleware[]; // middleware to apply before/after fetch requests
|
||||
queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings
|
||||
username?: string; // parameter for basic security
|
||||
password?: string; // parameter for basic security
|
||||
apiKey?: string | ((name: string) => string); // parameter for apiKey security
|
||||
accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string | Promise<string>); // parameter for oauth2 security
|
||||
headers?: HTTPHeaders; //header params we want to use on every request
|
||||
credentials?: RequestCredentials; //value for the credentials param we want to use on each request
|
||||
}
|
||||
|
||||
export class Configuration {
|
||||
constructor(private configuration: ConfigurationParameters = {}) {}
|
||||
|
||||
set config(configuration: Configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
get basePath(): string {
|
||||
return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH;
|
||||
}
|
||||
|
||||
get fetchApi(): FetchAPI | undefined {
|
||||
return this.configuration.fetchApi;
|
||||
}
|
||||
|
||||
get middleware(): Middleware[] {
|
||||
return this.configuration.middleware || [];
|
||||
}
|
||||
|
||||
get queryParamsStringify(): (params: HTTPQuery) => string {
|
||||
return this.configuration.queryParamsStringify || querystring;
|
||||
}
|
||||
|
||||
get username(): string | undefined {
|
||||
return this.configuration.username;
|
||||
}
|
||||
|
||||
get password(): string | undefined {
|
||||
return this.configuration.password;
|
||||
}
|
||||
|
||||
get apiKey(): ((name: string) => string) | undefined {
|
||||
const apiKey = this.configuration.apiKey;
|
||||
if (apiKey) {
|
||||
return typeof apiKey === 'function' ? apiKey : () => apiKey;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
get accessToken(): ((name?: string, scopes?: string[]) => string | Promise<string>) | undefined {
|
||||
const accessToken = this.configuration.accessToken;
|
||||
if (accessToken) {
|
||||
return typeof accessToken === 'function' ? accessToken : async () => accessToken;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
get headers(): HTTPHeaders | undefined {
|
||||
return this.configuration.headers;
|
||||
}
|
||||
|
||||
get credentials(): RequestCredentials | undefined {
|
||||
return this.configuration.credentials;
|
||||
}
|
||||
}
|
||||
|
||||
export const DefaultConfig = new Configuration();
|
||||
|
||||
/**
|
||||
* This is the base class for all generated API classes.
|
||||
*/
|
||||
export class BaseAPI {
|
||||
|
||||
private middleware: Middleware[];
|
||||
|
||||
constructor(protected configuration = DefaultConfig) {
|
||||
this.middleware = configuration.middleware;
|
||||
}
|
||||
|
||||
withMiddleware<T extends BaseAPI>(this: T, ...middlewares: Middleware[]) {
|
||||
const next = this.clone<T>();
|
||||
next.middleware = next.middleware.concat(...middlewares);
|
||||
return next;
|
||||
}
|
||||
|
||||
withPreMiddleware<T extends BaseAPI>(this: T, ...preMiddlewares: Array<Middleware['pre']>) {
|
||||
const middlewares = preMiddlewares.map((pre) => ({ pre }));
|
||||
return this.withMiddleware<T>(...middlewares);
|
||||
}
|
||||
|
||||
withPostMiddleware<T extends BaseAPI>(this: T, ...postMiddlewares: Array<Middleware['post']>) {
|
||||
const middlewares = postMiddlewares.map((post) => ({ post }));
|
||||
return this.withMiddleware<T>(...middlewares);
|
||||
}
|
||||
|
||||
protected async request(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction): Promise<Response> {
|
||||
const { url, init } = await this.createFetchParams(context, initOverrides);
|
||||
const response = await this.fetchApi(url, init);
|
||||
if (response && (response.status >= 200 && response.status < 300)) {
|
||||
return response;
|
||||
}
|
||||
throw new ResponseError(response, 'Response returned an error code');
|
||||
}
|
||||
|
||||
private async createFetchParams(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction) {
|
||||
let url = this.configuration.basePath + context.path;
|
||||
if (context.query !== undefined && Object.keys(context.query).length !== 0) {
|
||||
// only add the querystring to the URL if there are query parameters.
|
||||
// this is done to avoid urls ending with a "?" character which buggy webservers
|
||||
// do not handle correctly sometimes.
|
||||
url += '?' + this.configuration.queryParamsStringify(context.query);
|
||||
}
|
||||
|
||||
const headers = Object.assign({}, this.configuration.headers, context.headers);
|
||||
Object.keys(headers).forEach(key => headers[key] === undefined ? delete headers[key] : {});
|
||||
|
||||
const initOverrideFn =
|
||||
typeof initOverrides === "function"
|
||||
? initOverrides
|
||||
: async () => initOverrides;
|
||||
|
||||
const initParams = {
|
||||
method: context.method,
|
||||
headers,
|
||||
body: context.body,
|
||||
credentials: this.configuration.credentials,
|
||||
};
|
||||
|
||||
const overriddenInit: RequestInit = {
|
||||
...initParams,
|
||||
...(await initOverrideFn({
|
||||
init: initParams,
|
||||
context,
|
||||
}))
|
||||
};
|
||||
|
||||
const init: RequestInit = {
|
||||
...overriddenInit,
|
||||
body:
|
||||
isFormData(overriddenInit.body) ||
|
||||
overriddenInit.body instanceof URLSearchParams ||
|
||||
isBlob(overriddenInit.body)
|
||||
? overriddenInit.body
|
||||
: JSON.stringify(overriddenInit.body),
|
||||
};
|
||||
|
||||
return { url, init };
|
||||
}
|
||||
|
||||
private fetchApi = async (url: string, init: RequestInit) => {
|
||||
let fetchParams = { url, init };
|
||||
for (const middleware of this.middleware) {
|
||||
if (middleware.pre) {
|
||||
fetchParams = await middleware.pre({
|
||||
fetch: this.fetchApi,
|
||||
...fetchParams,
|
||||
}) || fetchParams;
|
||||
}
|
||||
}
|
||||
let response: Response | undefined = undefined;
|
||||
try {
|
||||
response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init);
|
||||
} catch (e) {
|
||||
for (const middleware of this.middleware) {
|
||||
if (middleware.onError) {
|
||||
response = await middleware.onError({
|
||||
fetch: this.fetchApi,
|
||||
url: fetchParams.url,
|
||||
init: fetchParams.init,
|
||||
error: e,
|
||||
response: response ? response.clone() : undefined,
|
||||
}) || response;
|
||||
}
|
||||
}
|
||||
if (response === undefined) {
|
||||
if (e instanceof Error) {
|
||||
throw new FetchError(e, 'The request failed and the interceptors did not return an alternative response');
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const middleware of this.middleware) {
|
||||
if (middleware.post) {
|
||||
response = await middleware.post({
|
||||
fetch: this.fetchApi,
|
||||
url: fetchParams.url,
|
||||
init: fetchParams.init,
|
||||
response: response.clone(),
|
||||
}) || response;
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a shallow clone of `this` by constructing a new instance
|
||||
* and then shallow cloning data members.
|
||||
*/
|
||||
private clone<T extends BaseAPI>(this: T): T {
|
||||
const constructor = this.constructor as any;
|
||||
const next = new constructor(this.configuration);
|
||||
next.middleware = this.middleware.slice();
|
||||
return next;
|
||||
}
|
||||
};
|
||||
|
||||
function isBlob(value: any): value is Blob {
|
||||
return typeof Blob !== 'undefined' && value instanceof Blob;
|
||||
}
|
||||
|
||||
function isFormData(value: any): value is FormData {
|
||||
return typeof FormData !== "undefined" && value instanceof FormData;
|
||||
}
|
||||
|
||||
export class ResponseError extends Error {
|
||||
override name: "ResponseError" = "ResponseError";
|
||||
constructor(public response: Response, msg?: string) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class FetchError extends Error {
|
||||
override name: "FetchError" = "FetchError";
|
||||
constructor(public cause: Error, msg?: string) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class RequiredError extends Error {
|
||||
override name: "RequiredError" = "RequiredError";
|
||||
constructor(public field: string, msg?: string) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export const COLLECTION_FORMATS = {
|
||||
csv: ",",
|
||||
ssv: " ",
|
||||
tsv: "\t",
|
||||
pipes: "|",
|
||||
};
|
||||
|
||||
export type FetchAPI = WindowOrWorkerGlobalScope['fetch'];
|
||||
|
||||
export type Json = any;
|
||||
export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
|
||||
export type HTTPHeaders = { [key: string]: string };
|
||||
export type HTTPQuery = { [key: string]: string | number | null | boolean | Array<string | number | null | boolean> | Set<string | number | null | boolean> | HTTPQuery };
|
||||
export type HTTPBody = Json | FormData | URLSearchParams;
|
||||
export type HTTPRequestInit = { headers?: HTTPHeaders; method: HTTPMethod; credentials?: RequestCredentials; body?: HTTPBody };
|
||||
export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original';
|
||||
|
||||
export type InitOverrideFunction = (requestContext: { init: HTTPRequestInit, context: RequestOpts }) => Promise<RequestInit>
|
||||
|
||||
export interface FetchParams {
|
||||
url: string;
|
||||
init: RequestInit;
|
||||
}
|
||||
|
||||
export interface RequestOpts {
|
||||
path: string;
|
||||
method: HTTPMethod;
|
||||
headers: HTTPHeaders;
|
||||
query?: HTTPQuery;
|
||||
body?: HTTPBody;
|
||||
}
|
||||
|
||||
export function exists(json: any, key: string) {
|
||||
const value = json[key];
|
||||
return value !== null && value !== undefined;
|
||||
}
|
||||
|
||||
export function querystring(params: HTTPQuery, prefix: string = ''): string {
|
||||
return Object.keys(params)
|
||||
.map(key => querystringSingleKey(key, params[key], prefix))
|
||||
.filter(part => part.length > 0)
|
||||
.join('&');
|
||||
}
|
||||
|
||||
function querystringSingleKey(key: string, value: string | number | null | undefined | boolean | Array<string | number | null | boolean> | Set<string | number | null | boolean> | HTTPQuery, keyPrefix: string = ''): string {
|
||||
const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key);
|
||||
if (value instanceof Array) {
|
||||
const multiValue = value.map(singleValue => encodeURIComponent(String(singleValue)))
|
||||
.join(`&${encodeURIComponent(fullKey)}=`);
|
||||
return `${encodeURIComponent(fullKey)}=${multiValue}`;
|
||||
}
|
||||
if (value instanceof Set) {
|
||||
const valueAsArray = Array.from(value);
|
||||
return querystringSingleKey(key, valueAsArray, keyPrefix);
|
||||
}
|
||||
if (value instanceof Date) {
|
||||
return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`;
|
||||
}
|
||||
if (value instanceof Object) {
|
||||
return querystring(value as HTTPQuery, fullKey);
|
||||
}
|
||||
return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`;
|
||||
}
|
||||
|
||||
export function mapValues(data: any, fn: (item: any) => any) {
|
||||
return Object.keys(data).reduce(
|
||||
(acc, key) => ({ ...acc, [key]: fn(data[key]) }),
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
export function canConsumeForm(consumes: Consume[]): boolean {
|
||||
for (const consume of consumes) {
|
||||
if ('multipart/form-data' === consume.contentType) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export interface Consume {
|
||||
contentType: string;
|
||||
}
|
||||
|
||||
export interface RequestContext {
|
||||
fetch: FetchAPI;
|
||||
url: string;
|
||||
init: RequestInit;
|
||||
}
|
||||
|
||||
export interface ResponseContext {
|
||||
fetch: FetchAPI;
|
||||
url: string;
|
||||
init: RequestInit;
|
||||
response: Response;
|
||||
}
|
||||
|
||||
export interface ErrorContext {
|
||||
fetch: FetchAPI;
|
||||
url: string;
|
||||
init: RequestInit;
|
||||
error: unknown;
|
||||
response?: Response;
|
||||
}
|
||||
|
||||
export interface Middleware {
|
||||
pre?(context: RequestContext): Promise<FetchParams | void>;
|
||||
post?(context: ResponseContext): Promise<Response | void>;
|
||||
onError?(context: ErrorContext): Promise<Response | void>;
|
||||
}
|
||||
|
||||
export interface ApiResponse<T> {
|
||||
raw: Response;
|
||||
value(): Promise<T>;
|
||||
}
|
||||
|
||||
export interface ResponseTransformer<T> {
|
||||
(json: any): T;
|
||||
}
|
||||
|
||||
export class JSONApiResponse<T> {
|
||||
constructor(public raw: Response, private transformer: ResponseTransformer<T> = (jsonValue: any) => jsonValue) {}
|
||||
|
||||
async value(): Promise<T> {
|
||||
return this.transformer(await this.raw.json());
|
||||
}
|
||||
}
|
||||
|
||||
export class VoidApiResponse {
|
||||
constructor(public raw: Response) {}
|
||||
|
||||
async value(): Promise<void> {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export class BlobApiResponse {
|
||||
constructor(public raw: Response) {}
|
||||
|
||||
async value(): Promise<Blob> {
|
||||
return await this.raw.blob();
|
||||
};
|
||||
}
|
||||
|
||||
export class TextApiResponse {
|
||||
constructor(public raw: Response) {}
|
||||
|
||||
async value(): Promise<string> {
|
||||
return await this.raw.text();
|
||||
};
|
||||
}
|
20
ui/talon-client/tsconfig.json
Normal file
20
ui/talon-client/tsconfig.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "dist",
|
||||
"lib": [
|
||||
"es6",
|
||||
"dom"
|
||||
],
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
"node_modules"
|
||||
]
|
||||
}
|
Loading…
Reference in a new issue