Compare commits

..

No commits in common. "7f055c98bec70cb6873a6b580af3919f9e78e1af" and "f4a37d6851c52a8d5a32350cd5526d68f8fa1556" have entirely different histories.

58 changed files with 910 additions and 3910 deletions

39
Cargo.lock generated
View file

@ -1776,44 +1776,6 @@ dependencies = [
"unicode-ident", "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]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.4.0" version = "0.4.0"
@ -2089,7 +2051,6 @@ dependencies = [
"regex", "regex",
"rmp-serde", "rmp-serde",
"rstest", "rstest",
"rust-embed",
"serde", "serde",
"serde_json", "serde_json",
"sha2", "sha2",

View file

@ -7,7 +7,6 @@ license = "MIT"
description = "Static site management system" description = "Static site management system"
build = "build.rs" build = "build.rs"
default-run = "talon"
[dependencies] [dependencies]
poem = "1.3.55" poem = "1.3.55"
@ -52,7 +51,6 @@ async-compression = { version = "0.3.15", features = [
clap = { version = "4.1.8", features = ["derive"] } clap = { version = "4.1.8", features = ["derive"] }
shadow-rs = "0.21.0" shadow-rs = "0.21.0"
walkdir = "2.3.2" walkdir = "2.3.2"
rust-embed = { version = "6.6.1", features = ["poem-ex"] }
[dev-dependencies] [dev-dependencies]
rstest = "0.16.0" rstest = "0.16.0"

View file

@ -1,28 +0,0 @@
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

View file

@ -1,836 +0,0 @@
{
"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"
}
}
}
}

View file

@ -1,7 +0,0 @@
{
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "6.4.0"
}
}

View file

@ -11,7 +11,7 @@ use poem_openapi::{
auth::ApiKey, auth::ApiKey,
param::{Path, Query}, param::{Path, Query},
payload::{Binary, Html, Json, Response}, payload::{Binary, Html, Json, Response},
LicenseObject, OpenApi, OpenApiService, SecurityScheme, OpenApi, SecurityScheme,
}; };
use crate::{ use crate::{
@ -388,9 +388,3 @@ impl TalonApi {
Ok(Json(talon.info()?)) 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"))
}

View file

@ -1,69 +0,0 @@
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()),
}
}

View file

@ -1,8 +0,0 @@
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())
}

View file

@ -1,7 +1,6 @@
#![warn(clippy::todo, clippy::dbg_macro)] #![warn(clippy::todo, clippy::dbg_macro)]
pub mod api; pub mod api;
pub mod assets;
pub mod config; pub mod config;
pub mod db; pub mod db;
pub mod model; pub mod model;
@ -22,8 +21,6 @@ use time::OffsetDateTime;
shadow_rs::shadow!(build); shadow_rs::shadow!(build);
pub const API_VERSION: &str = "0.1.0";
pub static LAST_COMMIT_DATE: Lazy<SystemTime> = Lazy::new(|| { pub static LAST_COMMIT_DATE: Lazy<SystemTime> = Lazy::new(|| {
OffsetDateTime::parse( OffsetDateTime::parse(
build::COMMIT_DATE_3339, build::COMMIT_DATE_3339,

View file

@ -131,14 +131,6 @@ pub struct Stats {
pub storage_used: u64, 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( #[derive(
Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Enum, Serialize, Deserialize, Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Enum, Serialize, Deserialize,
)] )]

View file

@ -1,7 +1,7 @@
use std::{ops::Deref, path::Path, sync::Arc}; use std::{ops::Deref, path::Path, sync::Arc};
use crate::{ use crate::{
assets, api::TalonApi,
config::Config, config::Config,
db::Db, db::Db,
model::{Info, VersionInfo}, model::{Info, VersionInfo},
@ -14,6 +14,7 @@ use poem::{
http::header, listener::TcpListener, middleware, Endpoint, EndpointExt, Route, RouteDomain, http::header, listener::TcpListener, middleware, Endpoint, EndpointExt, Route, RouteDomain,
Server, Server,
}; };
use poem_openapi::OpenApiService;
use time::OffsetDateTime; use time::OffsetDateTime;
#[derive(Clone)] #[derive(Clone)]
@ -73,8 +74,8 @@ impl Talon {
} }
pub fn endpoint(&self) -> impl Endpoint { pub fn endpoint(&self) -> impl Endpoint {
let api_service = let api_service = OpenApiService::new(TalonApi, "Talon", "0.1.0")
crate::api::api_service().server(format!("{}/api", self.i.cfg.server.internal_url)); .server(format!("{}/api", self.i.cfg.server.internal_url));
let swagger_ui = api_service.swagger_ui(); let swagger_ui = api_service.swagger_ui();
let spec = api_service.spec(); let spec = api_service.spec();
@ -83,18 +84,14 @@ impl Talon {
"/", "/",
poem::endpoint::make_sync(|_| "Hello World, I am Talon"), poem::endpoint::make_sync(|_| "Hello World, I am Talon"),
) )
.nest( .nest("/api", api_service)
"/api",
api_service
.with(middleware::Cors::new())
.with(crate::middleware::LastModified),
)
.nest("/api/swagger", swagger_ui) .nest("/api/swagger", swagger_ui)
.at( .at(
"/api/spec", "/api/spec",
poem::endpoint::make_sync(move |_| spec.clone()), poem::endpoint::make_sync(move |_| spec.clone()),
) )
.at("/assets/menu/*path", assets::menu_assets); .with(middleware::Cors::new())
.with(crate::middleware::LastModified);
let internal_domain = format!( let internal_domain = format!(
"{}.{}", "{}.{}",

View file

@ -24,7 +24,7 @@ use zip::ZipArchive;
use crate::{ use crate::{
config::Config, config::Config,
db::{Db, DbError}, db::{Db, DbError},
model::{Stats, TalonConfig}, model::Stats,
util, util,
}; };
@ -32,7 +32,6 @@ pub struct Storage {
path: PathBuf, path: PathBuf,
db: Db, db: Db,
cfg: Config, cfg: Config,
to_inject: String,
} }
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -45,7 +44,7 @@ pub enum CompressionAlg {
impl CompressionAlg { impl CompressionAlg {
/// Get value of the http encoding header /// Get value of the http encoding header
pub fn encoding(&self) -> Option<&'static str> { fn encoding(&self) -> Option<&'static str> {
match self { match self {
CompressionAlg::None => None, CompressionAlg::None => None,
CompressionAlg::Gzip => Some("gzip"), CompressionAlg::Gzip => Some("gzip"),
@ -118,28 +117,10 @@ const TMPDIR_PREFIX: &str = "talon";
impl Storage { impl Storage {
/// Create a new file storage using the root folder and the database /// 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 { 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 { Self {
path: path.into(), path: path.into(),
db, db,
cfg, cfg,
to_inject,
} }
} }
@ -491,13 +472,15 @@ impl Storage {
// HTML files are not precompressed and need to have UI code injected // HTML files are not precompressed and need to have UI code injected
if is_html { if is_html {
// Inject UI code into HTML // Inject UI code into HTML
let to_inject = "<!-- Hello World -->\n";
let mut html = String::with_capacity(metadata.len() as usize); let mut html = String::with_capacity(metadata.len() as usize);
tokio::fs::File::from_std(file) tokio::fs::File::from_std(file)
.read_to_string(&mut html) .read_to_string(&mut html)
.await?; .await?;
if let Some(ctag_pos) = html.rfind("</html>") { if let Some(ctag_pos) = html.rfind("</html>") {
html.insert_str(ctag_pos, &self.to_inject); html.insert_str(ctag_pos, to_inject);
} }
// Compress response if possible // Compress response if possible

View file

@ -426,7 +426,7 @@ mod storage {
let resp = tokio_test::block_on(tln.storage.file_to_response(gf, &HeaderMap::new(), true)) let resp = tokio_test::block_on(tln.storage.file_to_response(gf, &HeaderMap::new(), true))
.unwrap(); .unwrap();
let body = tokio_test::block_on(resp.into_body().into_string()).unwrap(); let body = tokio_test::block_on(resp.into_body().into_string()).unwrap();
assert!(body.contains("<!-- INJECTED BY TALON -->\n")); assert!(body.contains("<!-- Hello World -->\n"));
} }
#[rstest] #[rstest]

View file

@ -1,40 +1,40 @@
module.exports = { module.exports = {
extends: ["eslint:recommended"], extends: ["eslint:recommended"],
env: { browser: true, es6: true, node: true }, env: {browser: true, es6: true, node: true},
parserOptions: { parserOptions: {
sourceType: "module", sourceType: "module",
}, },
overrides: [ overrides: [
{ {
files: ["*.ts", "*.svelte"], files: ["*.ts", "*.svelte"],
extends: [ extends: [
"eslint:recommended", "eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
], ],
globals: { globals: {
Atomics: "readonly", Atomics: "readonly",
SharedArrayBuffer: "readonly", SharedArrayBuffer: "readonly",
}, },
parser: "@typescript-eslint/parser", parser: "@typescript-eslint/parser",
parserOptions: { parserOptions: {
project: "./tsconfig.json", project: "./tsconfig.json",
}, },
plugins: ["@typescript-eslint"], plugins: ["@typescript-eslint"],
}, },
{ {
files: ["*.svelte"], files: ["*.svelte"],
processor: "svelte3/svelte3", processor: "svelte3/svelte3",
parserOptions: { parserOptions: {
extraFileExtensions: [".svelte"], extraFileExtensions: [".svelte"],
}, },
plugins: ["svelte3", "@typescript-eslint"], plugins: ["svelte3", "@typescript-eslint"],
settings: { settings: {
"svelte3/typescript": true, "svelte3/typescript": true,
"svelte3/ignore-styles": () => true, "svelte3/ignore-styles": () => true,
}, },
}, },
], ],
rules: {}, rules: {},
ignorePatterns: [".rollup/**", "public/**", "dist/**"], ignorePatterns: [".rollup/**", "public/**", "dist/**"],
}; }

View file

@ -9,8 +9,7 @@
"dependencies": { "dependencies": {
"@fortawesome/free-brands-svg-icons": "^5.15.4", "@fortawesome/free-brands-svg-icons": "^5.15.4",
"@fortawesome/free-solid-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": { "devDependencies": {
"@babel/core": "^7.15.5", "@babel/core": "^7.15.5",
@ -18,7 +17,7 @@
"@babel/preset-typescript": "^7.15.0", "@babel/preset-typescript": "^7.15.0",
"@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^20.0.0", "@rollup/plugin-commonjs": "^20.0.0",
"@rollup/plugin-node-resolve": "^13.3.0", "@rollup/plugin-node-resolve": "^13.0.5",
"@rollup/plugin-replace": "^3.0.0", "@rollup/plugin-replace": "^3.0.0",
"@rollup/plugin-typescript": "^8.2.5", "@rollup/plugin-typescript": "^8.2.5",
"@rollup/pluginutils": "^4.1.1", "@rollup/pluginutils": "^4.1.1",
@ -30,7 +29,6 @@
"prettier": "^2.2.1", "prettier": "^2.2.1",
"prettier-plugin-svelte": "^1.2.0", "prettier-plugin-svelte": "^1.2.0",
"rollup": "^2.57.0", "rollup": "^2.57.0",
"rollup-plugin-brotli": "^3.1.0",
"rollup-plugin-copy": "^3.4.0", "rollup-plugin-copy": "^3.4.0",
"rollup-plugin-html-minifier": "^2.0.0", "rollup-plugin-html-minifier": "^2.0.0",
"rollup-plugin-livereload": "^2.0.5", "rollup-plugin-livereload": "^2.0.5",
@ -43,8 +41,7 @@
"svelte-keydown": "^0.3.1", "svelte-keydown": "^0.3.1",
"svelte-modals": "^1.0.4", "svelte-modals": "^1.0.4",
"svelte-preprocess": "^4.9.5", "svelte-preprocess": "^4.9.5",
"tslib": "^2.5.0", "typescript": "^4.4.3"
"typescript": "^4.9.5"
} }
}, },
"node_modules/@ampproject/remapping": { "node_modules/@ampproject/remapping": {
@ -2761,9 +2758,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001462", "version": "1.0.30001460",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001462.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001460.tgz",
"integrity": "sha512-PDd20WuOBPiasZ7KbFnmQRyuLE7cFXW2PVd7dmALzbkUXEP46upAuCDm9eY9vho8fgNMGmbAX92QBZHzcnWIqw==", "integrity": "sha512-Bud7abqjvEjipUkpLs4D7gR0l8hBYBHoa+tGtKJHvT2AYzLp1z7EmVkUT4ERpVUfca8S2HGIVs883D8pUH1ZzQ==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@ -2976,9 +2973,9 @@
} }
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.4.325", "version": "1.4.320",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.325.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.320.tgz",
"integrity": "sha512-K1C03NT4I7BuzsRdCU5RWkgZxtswnKDYM6/eMhkEXqKu4e5T+ck610x3FPzu1y7HVFSiQKZqP16gnJzPpji1TQ==", "integrity": "sha512-h70iRscrNluMZPVICXYl5SSB+rBKo22XfuIS1ER0OQxQZpKTnFpuS6coj7wY9M/3trv7OR88rRMOlKmRvDty7Q==",
"dev": true "dev": true
}, },
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
@ -4492,9 +4489,9 @@
} }
}, },
"node_modules/resolve.exports": { "node_modules/resolve.exports": {
"version": "2.0.1", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.1.tgz", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.0.tgz",
"integrity": "sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw==", "integrity": "sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
@ -4540,15 +4537,6 @@
"fsevents": "~2.3.2" "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": { "node_modules/rollup-plugin-copy": {
"version": "3.4.0", "version": "3.4.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.4.0.tgz", "resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.4.0.tgz",
@ -5162,10 +5150,6 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true "dev": true
}, },
"node_modules/talon-client": {
"version": "0.1.0",
"resolved": "file:../talon-client"
},
"node_modules/terser": { "node_modules/terser": {
"version": "5.16.5", "version": "5.16.5",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.16.5.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.5.tgz",
@ -5242,9 +5226,9 @@
} }
}, },
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.5.0", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true "dev": true
}, },
"node_modules/tsutils": { "node_modules/tsutils": {
@ -5262,12 +5246,6 @@
"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" "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": { "node_modules/type-check": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",

View file

@ -9,7 +9,7 @@
"start": "sirv public --single", "start": "sirv public --single",
"lint": "eslint .", "lint": "eslint .",
"fix": "eslint . --fix", "fix": "eslint . --fix",
"check": "svelte-check --tsconfig ../../tsconfig.json", "check": "svelte-check --tsconfig tsconfig.json",
"format": "prettier --plugin=./node_modules/prettier-plugin-svelte --write .", "format": "prettier --plugin=./node_modules/prettier-plugin-svelte --write .",
"pc": "npm run fix & npm run check & npm run format", "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 ." "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", "@babel/preset-typescript": "^7.15.0",
"@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^20.0.0", "@rollup/plugin-commonjs": "^20.0.0",
"@rollup/plugin-node-resolve": "^13.3.0", "@rollup/plugin-node-resolve": "^13.0.5",
"@rollup/plugin-replace": "^3.0.0", "@rollup/plugin-replace": "^3.0.0",
"@rollup/plugin-typescript": "^8.2.5", "@rollup/plugin-typescript": "^8.2.5",
"@rollup/pluginutils": "^4.1.1", "@rollup/pluginutils": "^4.1.1",
@ -32,7 +32,6 @@
"prettier": "^2.2.1", "prettier": "^2.2.1",
"prettier-plugin-svelte": "^1.2.0", "prettier-plugin-svelte": "^1.2.0",
"rollup": "^2.57.0", "rollup": "^2.57.0",
"rollup-plugin-brotli": "^3.1.0",
"rollup-plugin-copy": "^3.4.0", "rollup-plugin-copy": "^3.4.0",
"rollup-plugin-html-minifier": "^2.0.0", "rollup-plugin-html-minifier": "^2.0.0",
"rollup-plugin-livereload": "^2.0.5", "rollup-plugin-livereload": "^2.0.5",
@ -45,13 +44,11 @@
"svelte-keydown": "^0.3.1", "svelte-keydown": "^0.3.1",
"svelte-modals": "^1.0.4", "svelte-modals": "^1.0.4",
"svelte-preprocess": "^4.9.5", "svelte-preprocess": "^4.9.5",
"tslib": "^2.5.0", "typescript": "^4.4.3"
"typescript": "^4.9.5"
}, },
"dependencies": { "dependencies": {
"@fortawesome/free-brands-svg-icons": "^5.15.4", "@fortawesome/free-brands-svg-icons": "^5.15.4",
"@fortawesome/free-solid-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"
} }
} }

View file

@ -1,199 +1,296 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="title" content="Talon" /> <meta name="title" content="Talon" />
<meta name="description" content="Talon menu development page" /> <meta name="description" content="Talon menu development page" />
<style> <style>
body { body {
background-color: #161b22; background-color: #161b22;
font-family: sans-serif; font-family: sans-serif;
color: #fff; color: #fff;
} }
h1 { h1 {
color: #7935df; color: #7935df;
font-size: 3em; font-size: 3em;
font-weight: lighter; font-weight: lighter;
text-shadow: 0 0 30px #7935df, 0 0 40px #7935df; text-shadow: 0 0 30px #7935df, 0 0 40px #7935df;
} }
#main { #main {
text-align: center; text-align: center;
margin: 0 10%; margin: 0 10%;
} }
#main p { #main p {
text-align: justify; text-align: justify;
background-color: #1f242b; background-color: #1f242b;
padding: 20px; padding: 20px;
border-radius: 15px; border-radius: 15px;
} }
</style> </style>
<title>Talon</title> <title>Talon</title>
</head> </head>
<body> <body>
<div id="main"> <div id="main">
<div style="width: 100%; max-width: 450px; display: inline-block"> <div style="width: 100%; max-width: 450px; display: inline-block">
<svg <svg
version="1.1" version="1.1"
viewBox="0 0 119.06 34.396" viewBox="0 0 119.06 34.396"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
aria-label="Talon" aria-label="Talon"
> >
<defs> <defs>
<filter <filter
id="filter106970" id="filter106970"
x="-.069601" x="-.069601"
y="-.32851" y="-.32851"
width="1.1682" width="1.1682"
height="1.7939" height="1.7939"
color-interpolation-filters="sRGB" color-interpolation-filters="sRGB"
> >
<feOffset dx="3" dy="3" /> <feOffset dx="3" dy="3" />
<feGaussianBlur result="blur" stdDeviation="3" /> <feGaussianBlur result="blur" stdDeviation="3" />
<feFlood flood-color="rgb(121,53,223)" result="flood" /> <feFlood flood-color="rgb(121,53,223)" result="flood" />
<feComposite <feComposite
in="flood" in="flood"
in2="SourceGraphic" in2="SourceGraphic"
operator="in" operator="in"
result="composite" result="composite"
/> />
<feBlend in="blur" in2="composite" /> <feBlend in="blur" in2="composite" />
</filter> </filter>
</defs> </defs>
<g transform="translate(-5.1682 -.21323)"> <g transform="translate(-5.1682 -.21323)">
<text <text
transform="scale(.26458)" transform="scale(.26458)"
fill="#000000" fill="#000000"
font-family="sans-serif" font-family="sans-serif"
font-size="40px" font-size="40px"
style="line-height: 1.25; shape-inside: url(#rect3620); white-space: pre" style="
xml:space="preserve" line-height: 1.25;
/> shape-inside: url(#rect3620);
<g white-space: pre;
fill="#7935df" "
filter="url(#filter106970)" xml:space="preserve"
stroke-width=".72282" />
aria-label="Talon" <g
> fill="#7935df"
<path filter="url(#filter106970)"
d="m11.476 6.1383q2.9788 0.11294 8.7599 0.23294 6.1905-0.12 9.6282-0.23294v1.6165q-1.327-0.05647-4.1647-0.05647-1.4188-0.05647-4.1929-0.05647 0.05647 2.9506 0.05647 4.9411 0 2.0259 0.03529 4.8353 0 2.8023 0.05647 5.0541 0.02824 2.287 0.0847 3.8188h-2.0823q0.17647-5.4352 0.17647-13.56v-5.0894q-1.3623 0.028235-3.8188 0.028235t-4.5388 0.084705z" stroke-width=".72282"
/> aria-label="Talon"
<path >
d="m30.323 25.945q0.89646-1.7365 2.4282-4.8 1.5035-3.0353 2.8094-5.6682 1.2988-2.6047 2.5976-5.2305 1.2706-2.6329 2.5764-5.2376h0.05647q1.6165 2.9788 2.9506 5.4988 1.2988 2.5412 2.6894 5.1741 1.3835 2.6612 2.9435 5.5764 1.5388 2.9223 2.4917 4.567l-1.6518 1.0447-4.1294-7.807-10.673 0.34588q-0.60705 1.2423-1.7365 3.7341-1.1223 2.4847-1.7012 3.727zm5.7811-7.8917 9.374-0.35294q-2.4635-4.7082-4.7435-9.4235-1.1294 2.3153-2.52 5.3505-1.4118 3.0635-2.1106 4.4258z" <path
/> d="m11.476 6.1383q2.9788 0.11294 8.7599 0.23294 6.1905-0.12 9.6282-0.23294v1.6165q-1.327-0.05647-4.1647-0.05647-1.4188-0.05647-4.1929-0.05647 0.05647 2.9506 0.05647 4.9411 0 2.0259 0.03529 4.8353 0 2.8023 0.05647 5.0541 0.02824 2.287 0.0847 3.8188h-2.0823q0.17647-5.4352 0.17647-13.56v-5.0894q-1.3623 0.028235-3.8188 0.028235t-4.5388 0.084705z"
<path />
d="m66.005 26.58q-2.6612-0.11294-4.7153-0.17647-2.0753-0.02824-4.4188-0.11294 0.16941-3.9882 0.16941-8.4705 0-2.9506-0.11294-11.739h1.9623q-0.11294 5.407-0.14118 8.2376 0.02824 3.5011 0.02824 10.553 2.4 0 3.6141 0.05647 1.2423-0.05647 3.6141-0.05647z" <path
/> d="m30.323 25.945q0.89646-1.7365 2.4282-4.8 1.5035-3.0353 2.8094-5.6682 1.2988-2.6047 2.5976-5.2305 1.2706-2.6329 2.5764-5.2376h0.05647q1.6165 2.9788 2.9506 5.4988 1.2988 2.5412 2.6894 5.1741 1.3835 2.6612 2.9435 5.5764 1.5388 2.9223 2.4917 4.567l-1.6518 1.0447-4.1294-7.807-10.673 0.34588q-0.60705 1.2423-1.7365 3.7341-1.1223 2.4847-1.7012 3.727zm5.7811-7.8917 9.374-0.35294q-2.4635-4.7082-4.7435-9.4235-1.1294 2.3153-2.52 5.3505-1.4118 3.0635-2.1106 4.4258z"
<path />
d="m79.65 7.0348q-2.4282 0.17647-4.3341 1.5882-1.9341 1.4188-2.9506 3.5859-1.0094 2.167-1.0094 4.4823 0 3.7623 2.1953 6.247 2.0823 2.4 5.7529 2.4 0.17647 0 0.34588 0 3.6141-0.0847 5.9858-2.5694 2.3717-2.4917 2.6047-6.0776 0.02823-0.43058 0.02823-0.86823 0-2.5694-0.89646-4.4823-1.0376-2.1953-2.9506-3.2611-1.9341-1.0447-4.2211-1.0447zm0-1.4188h1.0729q1.8494 0 2.8659 0.35294 2.0188 0.54352 3.5788 1.8776 1.567 1.3553 2.3435 3.5576 0.60705 1.7576 0.60705 4.0447 0 0.60705-0.02823 1.2423-0.2047 2.4-1.5953 4.8564-1.3835 2.4565-3.84 3.847-1.9059 1.1859-5.0047 1.3623-0.25412 0-0.51529 0-2.4564 0-4.567-1.0165-2.3435-1.1294-3.7341-3.4094-1.3835-2.2588-1.4188-5.3505 0.06353-0.16941 0.06353-0.28941 0.02824-2.9223 1.4118-5.407 1.3906-2.4564 3.7059-3.9882 2.3082-1.5035 5.0541-1.68z" <path
/> d="m66.005 26.58q-2.6612-0.11294-4.7153-0.17647-2.0753-0.02824-4.4188-0.11294 0.16941-3.9882 0.16941-8.4705 0-2.9506-0.11294-11.739h1.9623q-0.11294 5.407-0.14118 8.2376 0.02824 3.5011 0.02824 10.553 2.4 0 3.6141 0.05647 1.2423-0.05647 3.6141-0.05647z"
<path />
d="m96.739 4.9524h0.11294q2.6612 3.007 5.2941 5.9576 2.6047 2.9506 3.4094 3.8188 0.78352 0.89646 2.9788 3.3811 2.167 2.5129 4.1364 4.9129h0.0565q0-0.95293 0-1.9059-0.0282-0.98117 0-1.9694 0.0282-1.9623 0.17647-6.6494 0.14117-4.6517 0.16941-6.4729h1.8494q-0.0847 2.3082-0.28941 6.6776-0.22588 4.3906-0.34588 7.9482-0.14117 3.5576-0.19764 6.2188h-0.0635q-1.7294-2.0259-5.407-6.127-3.6988-4.1082-5.6894-6.3952-2.0259-2.28-4.8353-5.6047h-0.05647l-0.37412 17.661-1.8776-0.05647z" <path
/> d="m79.65 7.0348q-2.4282 0.17647-4.3341 1.5882-1.9341 1.4188-2.9506 3.5859-1.0094 2.167-1.0094 4.4823 0 3.7623 2.1953 6.247 2.0823 2.4 5.7529 2.4 0.17647 0 0.34588 0 3.6141-0.0847 5.9858-2.5694 2.3717-2.4917 2.6047-6.0776 0.02823-0.43058 0.02823-0.86823 0-2.5694-0.89646-4.4823-1.0376-2.1953-2.9506-3.2611-1.9341-1.0447-4.2211-1.0447zm0-1.4188h1.0729q1.8494 0 2.8659 0.35294 2.0188 0.54352 3.5788 1.8776 1.567 1.3553 2.3435 3.5576 0.60705 1.7576 0.60705 4.0447 0 0.60705-0.02823 1.2423-0.2047 2.4-1.5953 4.8564-1.3835 2.4565-3.84 3.847-1.9059 1.1859-5.0047 1.3623-0.25412 0-0.51529 0-2.4564 0-4.567-1.0165-2.3435-1.1294-3.7341-3.4094-1.3835-2.2588-1.4188-5.3505 0.06353-0.16941 0.06353-0.28941 0.02824-2.9223 1.4118-5.407 1.3906-2.4564 3.7059-3.9882 2.3082-1.5035 5.0541-1.68z"
</g> />
</g> <path
</svg> d="m96.739 4.9524h0.11294q2.6612 3.007 5.2941 5.9576 2.6047 2.9506 3.4094 3.8188 0.78352 0.89646 2.9788 3.3811 2.167 2.5129 4.1364 4.9129h0.0565q0-0.95293 0-1.9059-0.0282-0.98117 0-1.9694 0.0282-1.9623 0.17647-6.6494 0.14117-4.6517 0.16941-6.4729h1.8494q-0.0847 2.3082-0.28941 6.6776-0.22588 4.3906-0.34588 7.9482-0.14117 3.5576-0.19764 6.2188h-0.0635q-1.7294-2.0259-5.407-6.127-3.6988-4.1082-5.6894-6.3952-2.0259-2.28-4.8353-5.6047h-0.05647l-0.37412 17.661-1.8776-0.05647z"
</div> />
<h1>development</h1> </g>
<p> </g>
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar plum </svg>
pudding halvah chocolate. Cotton candy tart cake bonbon tart. Shortbread jelly </div>
fruitcake icing pastry. Dragée dessert cupcake cake sesame snaps toffee pie. <h1>development</h1>
Sweet roll sweet roll chupa chups jelly-o gummies tootsie roll sweet halvah oat <p>
cake. Carrot cake carrot cake muffin bonbon sesame snaps brownie. Bonbon candy Carrot cake biscuit icing pudding danish topping powder. Croissant sugar
macaroon fruitcake candy canes. Cake pudding danish liquorice cupcake jelly-o plum pudding halvah chocolate. Cotton candy tart cake bonbon tart.
ice cream. Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate Shortbread jelly fruitcake icing pastry. Dragée dessert cupcake cake
candy canes donut lemon drops apple pie danish bear claw. Caramels cake jelly sesame snaps toffee pie. Sweet roll sweet roll chupa chups jelly-o
jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop pudding gummies tootsie roll sweet halvah oat cake. Carrot cake carrot cake
marshmallow candy canes tootsie roll danish. muffin bonbon sesame snaps brownie. Bonbon candy macaroon fruitcake
</p> candy canes. Cake pudding danish liquorice cupcake jelly-o ice cream.
<p> Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar plum candy canes donut lemon drops apple pie danish bear claw. Caramels cake
pudding halvah chocolate. Cotton candy tart cake bonbon tart. Shortbread jelly jelly jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop
fruitcake icing pastry. Dragée dessert cupcake cake sesame snaps toffee pie. pudding marshmallow candy canes tootsie roll danish.
Sweet roll sweet roll chupa chups jelly-o gummies tootsie roll sweet halvah oat </p>
cake. Carrot cake carrot cake muffin bonbon sesame snaps brownie. Bonbon candy <p>
macaroon fruitcake candy canes. Cake pudding danish liquorice cupcake jelly-o Carrot cake biscuit icing pudding danish topping powder. Croissant sugar
ice cream. Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate plum pudding halvah chocolate. Cotton candy tart cake bonbon tart.
candy canes donut lemon drops apple pie danish bear claw. Caramels cake jelly Shortbread jelly fruitcake icing pastry. Dragée dessert cupcake cake
jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop pudding sesame snaps toffee pie. Sweet roll sweet roll chupa chups jelly-o
marshmallow candy canes tootsie roll danish. gummies tootsie roll sweet halvah oat cake. Carrot cake carrot cake
</p> muffin bonbon sesame snaps brownie. Bonbon candy macaroon fruitcake
<p> candy canes. Cake pudding danish liquorice cupcake jelly-o ice cream.
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar plum Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
pudding halvah chocolate. Cotton candy tart cake bonbon tart. Shortbread jelly candy canes donut lemon drops apple pie danish bear claw. Caramels cake
fruitcake icing pastry. Dragée dessert cupcake cake sesame snaps toffee pie. jelly jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop
Sweet roll sweet roll chupa chups jelly-o gummies tootsie roll sweet halvah oat pudding marshmallow candy canes tootsie roll danish.
cake. Carrot cake carrot cake muffin bonbon sesame snaps brownie. Bonbon candy </p>
macaroon fruitcake candy canes. Cake pudding danish liquorice cupcake jelly-o <p>
ice cream. Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate Carrot cake biscuit icing pudding danish topping powder. Croissant sugar
candy canes donut lemon drops apple pie danish bear claw. Caramels cake jelly plum pudding halvah chocolate. Cotton candy tart cake bonbon tart.
jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop pudding Shortbread jelly fruitcake icing pastry. Dragée dessert cupcake cake
marshmallow candy canes tootsie roll danish. sesame snaps toffee pie. Sweet roll sweet roll chupa chups jelly-o
</p> gummies tootsie roll sweet halvah oat cake. Carrot cake carrot cake
<p> muffin bonbon sesame snaps brownie. Bonbon candy macaroon fruitcake
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar plum candy canes. Cake pudding danish liquorice cupcake jelly-o ice cream.
pudding halvah chocolate. Cotton candy tart cake bonbon tart. Shortbread jelly Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
fruitcake icing pastry. Dragée dessert cupcake cake sesame snaps toffee pie. candy canes donut lemon drops apple pie danish bear claw. Caramels cake
Sweet roll sweet roll chupa chups jelly-o gummies tootsie roll sweet halvah oat jelly jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop
cake. Carrot cake carrot cake muffin bonbon sesame snaps brownie. Bonbon candy pudding marshmallow candy canes tootsie roll danish.
macaroon fruitcake candy canes. Cake pudding danish liquorice cupcake jelly-o </p>
ice cream. Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate <p>
candy canes donut lemon drops apple pie danish bear claw. Caramels cake jelly Carrot cake biscuit icing pudding danish topping powder. Croissant sugar
jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop pudding plum pudding halvah chocolate. Cotton candy tart cake bonbon tart.
marshmallow candy canes tootsie roll danish. Shortbread jelly fruitcake icing pastry. Dragée dessert cupcake cake
</p> sesame snaps toffee pie. Sweet roll sweet roll chupa chups jelly-o
<p> gummies tootsie roll sweet halvah oat cake. Carrot cake carrot cake
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar plum muffin bonbon sesame snaps brownie. Bonbon candy macaroon fruitcake
pudding halvah chocolate. Cotton candy tart cake bonbon tart. Shortbread jelly candy canes. Cake pudding danish liquorice cupcake jelly-o ice cream.
fruitcake icing pastry. Dragée dessert cupcake cake sesame snaps toffee pie. Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
Sweet roll sweet roll chupa chups jelly-o gummies tootsie roll sweet halvah oat candy canes donut lemon drops apple pie danish bear claw. Caramels cake
cake. Carrot cake carrot cake muffin bonbon sesame snaps brownie. Bonbon candy jelly jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop
macaroon fruitcake candy canes. Cake pudding danish liquorice cupcake jelly-o pudding marshmallow candy canes tootsie roll danish.
ice cream. Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate </p>
candy canes donut lemon drops apple pie danish bear claw. Caramels cake jelly <p>
jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop pudding Carrot cake biscuit icing pudding danish topping powder. Croissant sugar
marshmallow candy canes tootsie roll danish. plum pudding halvah chocolate. Cotton candy tart cake bonbon tart.
</p> Shortbread jelly fruitcake icing pastry. Dragée dessert cupcake cake
<p> sesame snaps toffee pie. Sweet roll sweet roll chupa chups jelly-o
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar plum gummies tootsie roll sweet halvah oat cake. Carrot cake carrot cake
pudding halvah chocolate. Cotton candy tart cake bonbon tart. Shortbread jelly muffin bonbon sesame snaps brownie. Bonbon candy macaroon fruitcake
fruitcake icing pastry. Dragée dessert cupcake cake sesame snaps toffee pie. candy canes. Cake pudding danish liquorice cupcake jelly-o ice cream.
Sweet roll sweet roll chupa chups jelly-o gummies tootsie roll sweet halvah oat Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
cake. Carrot cake carrot cake muffin bonbon sesame snaps brownie. Bonbon candy candy canes donut lemon drops apple pie danish bear claw. Caramels cake
macaroon fruitcake candy canes. Cake pudding danish liquorice cupcake jelly-o jelly jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop
ice cream. Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate pudding marshmallow candy canes tootsie roll danish.
candy canes donut lemon drops apple pie danish bear claw. Caramels cake jelly </p>
jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop pudding <p>
marshmallow candy canes tootsie roll danish. Carrot cake biscuit icing pudding danish topping powder. Croissant sugar
</p> plum pudding halvah chocolate. Cotton candy tart cake bonbon tart.
<p> Shortbread jelly fruitcake icing pastry. Dragée dessert cupcake cake
Carrot cake biscuit icing pudding danish topping powder. Croissant sugar plum sesame snaps toffee pie. Sweet roll sweet roll chupa chups jelly-o
pudding halvah chocolate. Cotton candy tart cake bonbon tart. Shortbread jelly gummies tootsie roll sweet halvah oat cake. Carrot cake carrot cake
fruitcake icing pastry. Dragée dessert cupcake cake sesame snaps toffee pie. muffin bonbon sesame snaps brownie. Bonbon candy macaroon fruitcake
Sweet roll sweet roll chupa chups jelly-o gummies tootsie roll sweet halvah oat candy canes. Cake pudding danish liquorice cupcake jelly-o ice cream.
cake. Carrot cake carrot cake muffin bonbon sesame snaps brownie. Bonbon candy Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate
macaroon fruitcake candy canes. Cake pudding danish liquorice cupcake jelly-o candy canes donut lemon drops apple pie danish bear claw. Caramels cake
ice cream. Liquorice lollipop danish tootsie roll toffee. Gingerbread chocolate jelly jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop
candy canes donut lemon drops apple pie danish bear claw. Caramels cake jelly pudding marshmallow candy canes tootsie roll danish.
jelly sweet chocolate bar gingerbread icing. Cake soufflé lollipop pudding </p>
marshmallow candy canes tootsie roll danish. <p>
</p> Carrot cake biscuit icing pudding danish topping powder. Croissant sugar
</div> plum pudding halvah chocolate. Cotton candy tart cake bonbon tart.
</body> 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-config" type="application/json"> <script id="talon-data" type="application/json">
{ {
"api": "http://talon.localhost:3000/api", "root_path": "/",
"version": "0.1.0", "current_page": "2",
"root_domain": "localhost:3000" "current_version": "6",
} "versions": {
</script> "3": {
<script src="./talon.js"></script> "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"
}
}
}
</script>
<script src="./talon.js"></script>
</html> </html>

View file

@ -13,7 +13,6 @@ import sveltePreprocess from "svelte-preprocess"
import typescript from "@rollup/plugin-typescript" import typescript from "@rollup/plugin-typescript"
import replace from "@rollup/plugin-replace" import replace from "@rollup/plugin-replace"
import babel from "@rollup/plugin-babel" import babel from "@rollup/plugin-babel"
import brotli from "rollup-plugin-brotli"
import css from ".rollup/css-only" import css from ".rollup/css-only"
import {serve} from ".rollup/serve" import {serve} from ".rollup/serve"
@ -149,8 +148,6 @@ export default {
], ],
babelHelpers: "bundled", babelHelpers: "bundled",
}), }),
brotli(),
], ],
watch: { watch: {
chokidar: true, chokidar: true,

View file

@ -1,32 +1,16 @@
<script lang="ts"> <script lang="ts">
import { onMount } from "svelte"; import {closeModal, Modals} from "svelte-modals"
import { closeModal, Modals } from "svelte-modals";
import type { Website } from "ui/talon-client/src";
import Menu from "./components/Menu.svelte"; import Menu from "./components/Menu.svelte"
import { currentWebsiteStore, fetchWebsites } from "./util/api"; import {currentPage, isPresent} from "./util/talonData"
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> </script>
<style lang="sass"> <style lang="sass">
// Default theme
.wrapper
--talon-color: #7935df
.backdrop .backdrop
position: fixed position: fixed
top: 0 top: 0
@ -36,13 +20,12 @@
background: rgba(0, 0, 0, 0.6) background: rgba(0, 0, 0, 0.6)
</style> </style>
<div class="wrapper" style={`--talon-color: ${color}`}> <div class="wrapper" style={`--talon-color: ${currentPage.color}`}>
{#if currentWebsite} {#if isPresent}
<Menu /> <Menu />
{/if} {/if}
<Modals> <Modals>
<!-- svelte-ignore a11y-click-events-have-key-events --> <div class="backdrop" slot="backdrop" on:click={closeModal} />
<div class="backdrop" slot="backdrop" on:click={closeModal} /> </Modals>
</Modals>
</div> </div>

View file

@ -1,9 +1,9 @@
<script lang="ts"> <script lang="ts">
export let imageSrc: string; export let imageSrc: string
export let size = 32; export let size = 32
export let scale = 1; export let scale = 1
export let alt = "??"; export let alt = "??"
export let color = "#4b228a"; export let color = "#4b228a"
</script> </script>
@ -19,13 +19,13 @@
</style> </style>
<span class="icon" style="width: {size}px; height: {size}px;"> <span class="icon" style="width: {size}px; height: {size}px;">
{#if imageSrc} {#if imageSrc}
<span <span
style="width: {size * scale}px; height: {size * scale}px; background-image: url('{imageSrc}')" /> style="width: {size * scale}px; height: {size * scale}px; background-image: url('{imageSrc}')" />
{:else} {:else}
<span <span
style="width: {size * scale}px; height: {size * scale}px; font-size: {size * scale * 0.55}px; background: {color}"> style="width: {size * scale}px; height: {size * scale}px; font-size: {size * scale * 0.55}px; background: {color}">
{alt} {alt}
</span> </span>
{/if} {/if}
</span> </span>

View file

@ -1,38 +1,63 @@
<script lang="ts"> <script lang="ts">
import { fly } from "svelte/transition"; import {fly} from "svelte/transition"
import { closeModal } from "svelte-modals"; import {closeModal} from "svelte-modals"
import Keydown from "svelte-keydown"; import Keydown from "svelte-keydown"
import PageIcon from "./PageIcon.svelte"; import type {TalonVersion} from "../util/types"
import Icon from "./Icon.svelte"; import PageIcon from "./PageIcon.svelte"
import { formatDate, getWebsiteVersionUrl } from "../util/functions"; import Icon from "./Icon.svelte"
import InlineIcon from "./InlineIcon.svelte"; import {formatDate} from "../util/functions"
import Tag from "./Tag.svelte"; import InlineIcon from "./InlineIcon.svelte"
import type { Version, Website } from "talon-client"; import Tag from "./Tag.svelte"
import { client, currentWebsiteStore } from "../util/api"; import {
import { talonConfig } from "../util/talonData"; currentPage,
currentVersion,
currentVersionId,
rootPath,
versions,
} from "../util/talonData"
let currentWebsite: Website; export let isOpen: boolean
currentWebsiteStore.subscribe((ws) => {
currentWebsite = ws;
});
export let isOpen: boolean; function getVersionName(versionId: string, version: TalonVersion): string {
$: { return version.name ? version.name : "#" + versionId
if (isOpen && currentWebsite) { }
client
.websiteSubdomainVersionsGet({ subdomain: currentWebsite.subdomain }) function getVersionUrl(versionId: string, version: TalonVersion): string {
.then((v) => { return (
versions = v; rootPath +
if (v && v.length > 0) { (currentPage && version.name
currentVersion = v[v.length - 1]; ? 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> </script>
<style lang="sass"> <style lang="sass">
@ -118,58 +143,65 @@
<Keydown paused={!isOpen} on:Escape={closeModal} /> <Keydown paused={!isOpen} on:Escape={closeModal} />
{#if isOpen && currentWebsite} {#if isOpen}
<div class="modal" role="dialog" transition:fly={{ y: 50 }} on:introstart on:outroend> <div class="modal" role="dialog" transition:fly={{y: 50}} on:introstart on:outroend>
<div> <div>
<div class="tag"> <div class="tag">
<PageIcon website={currentWebsite} size={60} scale={0.8} /> <PageIcon page={currentPage} size={60} scale={0.8} />
<span>{currentWebsite.name}</span> <span>{currentPage ? currentPage.name : 'v' + currentVersionId}</span>
</div> </div>
{#if currentVersion} {#if !currentPage}
<p class="dhead"> <p>
<InlineIcon iconName="question" /> This is a dangling version, i.e. it does not belong to a page.
Current version #{currentVersion.id} Assign it to a page or it will be purged within 24 hours.
</p> </p>
<Tag key="Upload date" value={formatDate(currentVersion.createdAt)} /> {/if}
<!--<Tag key="Uploaded by" value={currentVersion.user} />-->
{#each Object.entries(currentVersion.data) as [key, value]} <p class="dhead">
<Tag {key} {value} /> <InlineIcon iconName="question" />
{/each} Current version
</p>
{#if versions && versions.length} <Tag key="Version" value={versionName} href={versionUrl} />
<p class="dhead"> <Tag key="Upload date" value={uploadDate} />
<InlineIcon iconName="history" /> <Tag key="Uploaded by" value={currentVersion.user} />
History
</p>
{#each versions as version} {#each pageTags as [key, value]}
<p class="smalltag"> <Tag {key} {value} />
<a href={getWebsiteVersionUrl(currentWebsite.subdomain, version.id)}> {/each}
<span>#{version.id}</span>
<span>{formatDate(version.createdAt)}</span>
</a>
</p>
{/each}
{/if}
{/if}
<p class="dhead" /> {#if history.length}
<p class="dhead">
<InlineIcon iconName="history" />
History
</p>
<div> {#each history as [date, name, url]}
Powered by <p class="smalltag">
<a <a href={url}> <span>{name}</span> <span>{date}</span> </a>
href="https://code.thetadev.de/ThetaDev/Talon/src/tag/{talonConfig.version}" </p>
target="_blank" {/each}
rel="noreferrer" {/if}
referrerpolicy="no-referrer">Talon
{talonConfig.version}</a> <p class="dhead" />
</div>
<!--<p><a href="" target="_blank">View licenses</a></p>--> <div>
<button on:click={closeModal}> This site is powered by
<Icon iconName="close" size={40} scale={0.6} transparent={true} /> <a
</button> href="https://github.com/Theta-Dev/Talon/tree/__VERSION__"
</div> target="_blank"
</div> 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>
</div>
<p><a href={rootPath + '&credits'} target="_blank">View licenses</a></p>
<button on:click={closeModal}>
<Icon iconName="close" size={40} scale={0.6} transparent={true} />
</button>
</div>
</div>
{/if} {/if}

View file

@ -1,97 +1,87 @@
<script lang="ts"> <script lang="ts">
import { openModal } from "svelte-modals"; import {openModal} from "svelte-modals"
import Icon from "./Icon.svelte"; import Icon from "./Icon.svelte"
import MenuItem from "./MenuItem.svelte"; import MenuItem from "./MenuItem.svelte"
import MenuItemPage from "./MenuItemPage.svelte"; import MenuItemPage from "./MenuItemPage.svelte"
import InfoModal from "./InfoModal.svelte"; import InfoModal from "./InfoModal.svelte"
import FloatingButton from "./FloatingButton.svelte"; import FloatingButton from "./FloatingButton.svelte"
import type { Focusable } from "../util/types"; import type {Focusable, TalonPage} from "../util/types"
import PageIcon from "./PageIcon.svelte"; import {TalonVisibility} from "../util/types"
import MenuItemInput from "./MenuItemInput.svelte"; import PageIcon from "./PageIcon.svelte"
import { currentWebsiteStore, websitesStore } from "../util/api"; import MenuItemInput from "./MenuItemInput.svelte"
import { Visibility, Website } from "talon-client"; import {currentPage, currentPageId, pages, rootPath} from "../util/talonData"
import { getWebsiteUrl } from "../util/functions";
let currentWebsite: Website; function showSidebar(): void {
let websites: Website[]; sidebarShown = true
}
currentWebsiteStore.subscribe((ws) => { function hideSidebar(): void {
currentWebsite = ws; sidebarShown = false
}); }
websitesStore.subscribe((ws) => {
websites = ws;
});
function showSidebar(): void { function isMobile(): boolean {
sidebarShown = true; return window.innerWidth < 768
} }
function hideSidebar(): void { function openSearch(): void {
sidebarShown = false; searchOpen = true
} searchInput.focus()
}
function isMobile(): boolean { function closeSearch() {
return window.innerWidth < 768; searchOpen = false
} searchInput.blur()
function openSearch(): void { if (displayedPages.length === 0) searchText = ""
searchOpen = true; }
searchInput.focus();
}
function closeSearch() { function clearSearch() {
searchOpen = false; searchText = ""
searchInput.blur(); closeSearch()
}
if (displayedWebsites.length === 0) searchText = ""; function searchKeypress(e: KeyboardEvent) {
} switch (e.key) {
case "Enter":
if (!searchText) {
closeSearch()
} else if (displayedPages.length) {
window.location.href = rootPath + displayedPages[0].path
} else {
closeSearch()
}
break
case "Escape":
clearSearch()
break
}
}
function clearSearch() { function openInfo() {
searchText = ""; openModal(InfoModal)
closeSearch(); }
}
function searchKeypress(e: KeyboardEvent) { let sidebarShown = !isMobile()
switch (e.key) { let searchInput: Focusable
case "Enter": let searchOpen = false
if (!searchText) { let searchText = ""
closeSearch();
} else if (displayedWebsites.length) {
window.location.href = getWebsiteUrl(displayedWebsites[0].subdomain);
} else {
closeSearch();
}
break;
case "Escape":
clearSearch();
break;
}
}
function openInfo() { let displayedPages: TalonPage[]
openModal(InfoModal); $: displayedPages = Object.entries(pages)
} .filter(([id, page]) => {
if (id === currentPageId) return false
let sidebarShown = !isMobile(); if (searchText) {
let searchInput: Focusable; return (
let searchOpen = false; page.visibility !== TalonVisibility.HIDDEN &&
let searchText = ""; page.name.toLowerCase().includes(searchText.toLowerCase())
)
let displayedWebsites: Website[]; }
return page.visibility === TalonVisibility.FEATURED
$: displayedWebsites = websites.filter((ws) => { })
if (ws.subdomain == currentWebsite.subdomain) return false; .map(([, page]) => page)
if (searchText) {
return (
ws.visibility !== Visibility.Hidden &&
ws.name.toLowerCase().includes(searchText.toLowerCase())
);
}
return ws.visibility === Visibility.Featured;
});
</script> </script>
@ -128,37 +118,40 @@
</style> </style>
<nav class:hide={!sidebarShown}> <nav class:hide={!sidebarShown}>
<div> <div>
<MenuItemInput <MenuItemInput
active={searchOpen || Boolean(searchText).valueOf()} active={searchOpen || Boolean(searchText).valueOf()}
on:click={openSearch} on:click={openSearch}
on:focusout={closeSearch} on:focusout={closeSearch}
on:keyup={searchKeypress} on:keyup={searchKeypress}
bind:input={searchInput} bind:input={searchInput}
bind:text={searchText} /> bind:text={searchText} />
</div> </div>
<div> <div>
{#each displayedWebsites as website, i} {#each displayedPages as page, i}
<MenuItemPage {website} active={searchOpen && searchText && i === 0} /> <MenuItemPage
{/each} {page}
</div> {rootPath}
<div> active={searchOpen && searchText && i === 0} />
{#if currentWebsite && currentWebsite.sourceUrl} {/each}
<MenuItem </div>
text="View source" <div>
link={currentWebsite.sourceUrl} {#if currentPage && currentPage.source}
newTab={true} <MenuItem
privacy={true}> text="View source"
<Icon iconName={currentWebsite.sourceIcon} size={40} scale={0.6} /> link={currentPage.source.url}
</MenuItem> newTab={true}
{/if} privacy={true}>
<MenuItem text="Info" on:click={openInfo}> <Icon iconName={currentPage.source.type} size={40} scale={0.6} />
<PageIcon website={currentWebsite} /> </MenuItem>
</MenuItem> {/if}
<MenuItem text="Hide sidebar" on:click={hideSidebar}> <MenuItem text="Info" on:click={openInfo}>
<Icon iconName="arrowRight" size={40} scale={0.6} /> <PageIcon page={currentPage} />
</MenuItem> </MenuItem>
</div> <MenuItem text="Hide sidebar" on:click={hideSidebar}>
<Icon iconName="arrowRight" size={40} scale={0.6} />
</MenuItem>
</div>
</nav> </nav>
<FloatingButton hide={sidebarShown} on:click={showSidebar} /> <FloatingButton hide={sidebarShown} on:click={showSidebar} />

View file

@ -1,22 +1,22 @@
<script lang="ts"> <script lang="ts">
import PageIcon from "./PageIcon.svelte"; import type {TalonPage} from "../util/types"
import MenuItem from "./MenuItem.svelte"; import PageIcon from "./PageIcon.svelte"
import type { Website } from "talon-client"; import MenuItem from "./MenuItem.svelte"
import { getWebsiteUrl } from "../util/functions";
export let website: Website; export let page: TalonPage
export let active = false; export let rootPath = "/"
export let active = false
const MAX_TEXT_LEN = 20; const MAX_TEXT_LEN = 20
let text: string; let text: string
$: text = $: text =
website.name.length > MAX_TEXT_LEN page.name.length > MAX_TEXT_LEN
? website.name.substring(0, 20).trim() + "..." ? page.name.substr(0, 20).trim() + "..."
: website.name; : page.name
</script> </script>
<MenuItem {active} {text} link={getWebsiteUrl(website.subdomain)}> <MenuItem {active} {text} link={rootPath + page.path}>
<PageIcon {website} /> <PageIcon {page} />
</MenuItem> </MenuItem>

View file

@ -1,19 +1,19 @@
<script lang="ts"> <script lang="ts">
import ImageIcon from "./ImageIcon.svelte" import ImageIcon from "./ImageIcon.svelte"
import Icon from "./Icon.svelte" import Icon from "./Icon.svelte"
import type { Website } from "ui/apiclient"; import type {TalonPage} from "../util/types"
export let website: Website export let page: TalonPage
export let size = 40 export let size = 40
export let scale = 0.8 export let scale = 0.8
</script> </script>
{#if website} {#if page}
<ImageIcon <ImageIcon
imageSrc="" imageSrc={page.image}
color={website.color} color={page.color}
alt={website.name.substring(0, 2)} alt={page.name.substr(0, 2)}
{size} {size}
{scale} /> {scale} />
{:else} {:else}

View file

@ -1,4 +1,4 @@
const sidebar = document.createElement("talon-sidebar"); const sidebar = document.createElement("talon-sidebar")
document.body.append(sidebar); document.body.append(sidebar)
export { default as default } from "./App.svelte"; export {default as default} from "./App.svelte"

View file

@ -1,18 +0,0 @@
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))
})
}

View file

@ -1,41 +1,5 @@
import {talonConfig} from "./talonData" function formatDate(dateString: string): string {
return new Date(dateString).toLocaleString(navigator.language)
export function formatDate(date: Date): string {
return date.toLocaleString(navigator.language)
} }
export function getSubdomain(): string { export {formatDate}
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}`
}
}

View file

@ -1,33 +1,54 @@
import type {TalonConfig} from "./types" import type {TalonData, TalonPage, TalonVersion} from "./types"
import {TalonVisibility} from "./types"
export const talonConfig: TalonConfig = JSON.parse( const talonData: TalonData = JSON.parse(
document.getElementById("talon-config").textContent document.getElementById("talon-data").textContent
) as TalonConfig ) as TalonData
// const rootPath = talonData.root_path const isTalonData = (obj: TalonData) =>
"root_path" in obj &&
"current_page" in obj &&
"current_version" in obj &&
"versions" in obj &&
"pages" in obj
// const currentVersion = talonData.versions[talonData.current_version] const isTalonVersion = (obj: TalonVersion) => "date" in obj && "user" in obj
// const currentVersionId = talonData.current_version
// const getCurrentPage = () => { const rootPath = talonData.root_path
// 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 currentPage: TalonPage = getCurrentPage() const currentVersion = talonData.versions[talonData.current_version]
// const currentPageId = talonData.current_page const currentVersionId = talonData.current_version
// const isPresent = isTalonData(talonData) && isTalonVersion(currentVersion) 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 versions = talonData.versions const currentPage: TalonPage = getCurrentPage()
// const pages = talonData.pages 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,
}

View file

@ -1,15 +1,54 @@
export interface TalonConfig { export interface TalonData {
api: string; root_path: string
version: string; current_page: string | null
root_domain: string; current_version: string
versions: {[key: string]: TalonVersion}
pages: {[key: string]: TalonPage}
} }
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 { export interface Focusable {
focus(): void; focus(): void
blur(): void; blur(): void
} }
export interface SvelteActionRes { export interface SvelteActionRes {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
update?: (parameters: any) => void; update?: (parameters: any) => void
destroy?: () => void; destroy?: () => void
} }

90
ui/menu/testdata/test.json vendored Normal file
View file

@ -0,0 +1,90 @@
{
"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 Normal file
View file

@ -0,0 +1,99 @@
{
"$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"]
}
]
}

View file

@ -32,10 +32,5 @@
* Use globals.d.ts instead of compilerOptions.types * Use globals.d.ts instead of compilerOptions.types
* to avoid limiting type declarations. * to avoid limiting type declarations.
*/ */
"include": [ "include": ["globals.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
"ui/*/globals.d.ts",
"ui/*/src/**/*.ts",
"ui/*/src/**/*.js",
"ui/*/src/**/*.svelte"
]
} }

View file

@ -1,4 +0,0 @@
wwwroot/*.js
node_modules
typings
dist

View file

@ -1 +0,0 @@
README.md

View file

@ -1,23 +0,0 @@
# 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

View file

@ -1,23 +0,0 @@
.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

View file

@ -1 +0,0 @@
6.4.0

View file

@ -1,45 +0,0 @@
## 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

View file

@ -1,28 +0,0 @@
{
"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"
}
}
}
}

View file

@ -1,19 +0,0 @@
{
"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"
}
}

View file

@ -1,559 +0,0 @@
/* 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();
}
}

View file

@ -1,3 +0,0 @@
/* tslint:disable */
/* eslint-disable */
export * from './DefaultApi';

View file

@ -1,5 +0,0 @@
/* tslint:disable */
/* eslint-disable */
export * from './runtime';
export * from './apis';
export * from './models';

View file

@ -1,96 +0,0 @@
/* 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,
};
}

View file

@ -1,83 +0,0 @@
/* 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,
};
}

View file

@ -1,110 +0,0 @@
/* 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,
};
}

View file

@ -1,41 +0,0 @@
/* 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;
}

View file

@ -1,83 +0,0 @@
/* 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,
};
}

View file

@ -1,86 +0,0 @@
/* 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,
};
}

View file

@ -1,82 +0,0 @@
/* 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,
};
}

View file

@ -1,110 +0,0 @@
/* 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,
};
}

View file

@ -1,38 +0,0 @@
/* 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;
}

View file

@ -1,139 +0,0 @@
/* 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),
};
}

View file

@ -1,110 +0,0 @@
/* 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),
};
}

View file

@ -1,111 +0,0 @@
/* 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),
};
}

View file

@ -1,14 +0,0 @@
/* 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';

View file

@ -1,407 +0,0 @@
/* 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();
};
}

View file

@ -1,20 +0,0 @@
{
"compilerOptions": {
"declaration": true,
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"outDir": "dist",
"lib": [
"es6",
"dom"
],
"typeRoots": [
"node_modules/@types"
]
},
"exclude": [
"dist",
"node_modules"
]
}