more stuff working
This commit is contained in:
parent
98001c9273
commit
89e152c715
32 changed files with 1337 additions and 140 deletions
1
.tokeignore
Normal file
1
.tokeignore
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package-lock.json
|
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -13,6 +13,7 @@ dependencies = [
|
||||||
"bollard",
|
"bollard",
|
||||||
"clap",
|
"clap",
|
||||||
"console-subscriber",
|
"console-subscriber",
|
||||||
|
"deadqueue",
|
||||||
"entity",
|
"entity",
|
||||||
"futures",
|
"futures",
|
||||||
"migration",
|
"migration",
|
||||||
|
@ -23,6 +24,7 @@ dependencies = [
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
"tower",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -812,6 +814,16 @@ dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deadqueue"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "16a2561fd313df162315935989dceb8c99db4ee1933358270a57a3cfb8c957f3"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-queue",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "der"
|
name = "der"
|
||||||
version = "0.7.7"
|
version = "0.7.7"
|
||||||
|
@ -3269,6 +3281,7 @@ version = "1.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
|
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -29,3 +29,5 @@ tokio = { version = "1.29.1", features = ["full"] }
|
||||||
tower = { version = "0.4.13", features = ["full"] }
|
tower = { version = "0.4.13", features = ["full"] }
|
||||||
base64 = "0.21.2"
|
base64 = "0.21.2"
|
||||||
console-subscriber = "0.1.10"
|
console-subscriber = "0.1.10"
|
||||||
|
deadqueue = "0.2.4"
|
||||||
|
uuid = { version = "1.4.1", features = ["v4"] }
|
||||||
|
|
|
@ -1,5 +1,36 @@
|
||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
|
import { RouterProvider } from "react-router";
|
||||||
|
import { createBrowserRouter } from "react-router-dom";
|
||||||
|
|
||||||
|
import "normalize.css";
|
||||||
|
import "@blueprintjs/core/lib/css/blueprint.css";
|
||||||
|
import "@blueprintjs/icons/lib/css/blueprint-icons.css";
|
||||||
|
import "@blueprintjs/icons/lib/css/blueprint-icons.css";
|
||||||
|
|
||||||
|
import Home from "./pages/Home";
|
||||||
|
import Service from "./pages/Service";
|
||||||
|
import Root from "./Root";
|
||||||
|
import { QueryClient, QueryClientProvider } from "react-query";
|
||||||
|
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
|
const router = createBrowserRouter([
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
Component: Root,
|
||||||
|
children: [
|
||||||
|
{ path: "/", Component: Home },
|
||||||
|
{ path: "/service/:id", Component: Service },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return <StrictMode></StrictMode>;
|
return (
|
||||||
|
<StrictMode>
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<RouterProvider router={router} />
|
||||||
|
</QueryClientProvider>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
11
client/Root.module.scss
Normal file
11
client/Root.module.scss
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
.container {
|
||||||
|
max-width: 980px;
|
||||||
|
margin: auto;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
flex-basis: 270px;
|
||||||
|
}
|
44
client/Root.tsx
Normal file
44
client/Root.tsx
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { Link, Outlet, useNavigate } from "react-router-dom";
|
||||||
|
import styles from "./Root.module.scss";
|
||||||
|
import { useQuery } from "react-query";
|
||||||
|
import { MenuItem } from "@blueprintjs/core";
|
||||||
|
import { getServices } from "./lib/api";
|
||||||
|
|
||||||
|
export default function Root() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const servicesQuery = useQuery("service/list", getServices);
|
||||||
|
|
||||||
|
if (servicesQuery.status !== "success") {
|
||||||
|
console.log("SHIET", servicesQuery);
|
||||||
|
return <>Loading...</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<div className={styles.sidebar}>
|
||||||
|
<h1 style={{ margin: "6px" }}>
|
||||||
|
<Link to="/">aah</Link>
|
||||||
|
</h1>
|
||||||
|
<small>"no honey, we have aws at home" ~mom</small>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<p>Services:</p>
|
||||||
|
{servicesQuery.data.map((service) => {
|
||||||
|
let icon;
|
||||||
|
if (service.status === "New") {
|
||||||
|
icon = "clean";
|
||||||
|
}
|
||||||
|
const text = `${service.name} (${service.id})`;
|
||||||
|
const goto = () => navigate(`/service/${service.id}`);
|
||||||
|
return (
|
||||||
|
<MenuItem icon={icon} text={text} key={service.id} onClick={goto} />
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className={styles.mainContent}>
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
13
client/lib/api.ts
Normal file
13
client/lib/api.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
export async function getServices() {
|
||||||
|
const response = await fetch("/api/service/list");
|
||||||
|
const body = await response.json();
|
||||||
|
console.log(body);
|
||||||
|
const { services } = body;
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getService(id: number) {
|
||||||
|
const response = await fetch(`/api/service/${id}`);
|
||||||
|
const body = await response.json();
|
||||||
|
return body;
|
||||||
|
}
|
45
client/pages/Home.tsx
Normal file
45
client/pages/Home.tsx
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import { FormEvent, useState } from "react";
|
||||||
|
import { Button, InputGroup, TextArea } from "@blueprintjs/core";
|
||||||
|
import { useNavigate } from "react-router";
|
||||||
|
import { useMutation, useQueryClient } from "react-query";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const [name, setName] = useState("");
|
||||||
|
const [content, setContent] = useState("");
|
||||||
|
|
||||||
|
const submitCreate = async (evt: FormEvent) => {
|
||||||
|
evt.stopPropagation();
|
||||||
|
evt.preventDefault();
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("name", name);
|
||||||
|
formData.append("config", content);
|
||||||
|
|
||||||
|
const result = await fetch("/api/upload", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
const { id } = await result.json();
|
||||||
|
queryClient.invalidateQueries("service/list");
|
||||||
|
navigate(`/service/${id}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<form onSubmit={submitCreate}>
|
||||||
|
<InputGroup
|
||||||
|
onChange={(evt) => setName(evt.target.value)}
|
||||||
|
placeholder="Service name..."
|
||||||
|
/>
|
||||||
|
<TextArea
|
||||||
|
onChange={(evt) => setContent(evt.target.value)}
|
||||||
|
placeholder="Service config (YML)..."
|
||||||
|
/>
|
||||||
|
<Button type="submit">Submit</Button>
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
15
client/pages/Service.tsx
Normal file
15
client/pages/Service.tsx
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { useQuery } from "react-query";
|
||||||
|
import { getService } from "../lib/api";
|
||||||
|
import { useParams } from "react-router";
|
||||||
|
|
||||||
|
export default function Service() {
|
||||||
|
const { id } = useParams();
|
||||||
|
const serviceQuery = useQuery(["service/get", id], () => getService(id));
|
||||||
|
|
||||||
|
if (serviceQuery.status !== "success") {
|
||||||
|
console.log("SHIET", serviceQuery);
|
||||||
|
return <>Loading...</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <pre>{JSON.stringify(serviceQuery.data, null, 2)}</pre>;
|
||||||
|
}
|
1
docs/.gitignore
vendored
Normal file
1
docs/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
book
|
6
docs/book.toml
Normal file
6
docs/book.toml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[book]
|
||||||
|
authors = ["Michael Zhang"]
|
||||||
|
language = "en"
|
||||||
|
multilingual = false
|
||||||
|
src = "src"
|
||||||
|
title = "aah documentation"
|
5
docs/src/SUMMARY.md
Normal file
5
docs/src/SUMMARY.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Summary
|
||||||
|
|
||||||
|
- [Intro](./intro.md)
|
||||||
|
- [Setup](./setup.md)
|
||||||
|
- [Dev Notes](./dev-notes.md)
|
22
docs/src/dev-notes.md
Normal file
22
docs/src/dev-notes.md
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# Dev Notes
|
||||||
|
|
||||||
|
#### Task queue
|
||||||
|
|
||||||
|
Simple task queue built on top of [deadqueue]. It needs to support the following interface:
|
||||||
|
|
||||||
|
[deadqueue]: https://docs.rs/deadqueue
|
||||||
|
|
||||||
|
- schedule a task NOW
|
||||||
|
- schedule a task later
|
||||||
|
- schedule a repeating task
|
||||||
|
|
||||||
|
This is used for healthchecks
|
||||||
|
|
||||||
|
#### Healthchecks
|
||||||
|
|
||||||
|
There's a variety of health and status checks that needs to be done
|
||||||
|
|
||||||
|
- is there anything in the database that isn't "healthy" or "needs attention"? we need to build those
|
||||||
|
- just query all of the docker containers to make sure they're still running and stuff
|
||||||
|
- are all the ones marked healthy actually reachable?
|
||||||
|
- how's our acme status? (maybe don't need this one, we know the expiry status)
|
7
docs/src/intro.md
Normal file
7
docs/src/intro.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Intro
|
||||||
|
|
||||||
|
aah is a self-hosting platform.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- HTTP basic auth only for the control server
|
1
docs/src/setup.md
Normal file
1
docs/src/setup.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# Setup
|
|
@ -2,5 +2,6 @@
|
||||||
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
|
||||||
|
pub mod process;
|
||||||
pub mod secrets;
|
pub mod secrets;
|
||||||
pub mod service;
|
pub mod service;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2
|
||||||
|
|
||||||
|
pub use super::process::Entity as Process;
|
||||||
pub use super::secrets::Entity as Secrets;
|
pub use super::secrets::Entity as Secrets;
|
||||||
pub use super::service::Entity as Service;
|
pub use super::service::Entity as Service;
|
||||||
|
|
37
entity/src/process.rs
Normal file
37
entity/src/process.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize,
|
||||||
|
)]
|
||||||
|
#[sea_orm(table_name = "process")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key)]
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub service_id: Option<i32>,
|
||||||
|
pub container_id: Option<String>,
|
||||||
|
pub status: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::service::Entity",
|
||||||
|
from = "Column::ServiceId",
|
||||||
|
to = "super::service::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
Service,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::service::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Service.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -12,15 +12,24 @@ pub struct Model {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub config: String,
|
pub config: String,
|
||||||
pub overall_status: String,
|
pub status: String,
|
||||||
|
pub in_progress_since: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
pub enum Relation {
|
pub enum Relation {
|
||||||
|
#[sea_orm(has_many = "super::process::Entity")]
|
||||||
|
Process,
|
||||||
#[sea_orm(has_many = "super::secrets::Entity")]
|
#[sea_orm(has_many = "super::secrets::Entity")]
|
||||||
Secrets,
|
Secrets,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Related<super::process::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Process.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Related<super::secrets::Entity> for Entity {
|
impl Related<super::secrets::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::Secrets.def()
|
Relation::Secrets.def()
|
||||||
|
|
|
@ -9,7 +9,7 @@ service:
|
||||||
- db
|
- db
|
||||||
entry: |
|
entry: |
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
export DB_HOST="postgres://name2"
|
export DB_HOST="postgres://db"
|
||||||
exec npm run start
|
exec npm run start
|
||||||
|
|
||||||
db:
|
db:
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="client/index.ts"></script>
|
<script type="module" src="/client/index.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use sea_orm_migration::prelude::*;
|
use sea_orm_migration::{
|
||||||
|
prelude::*,
|
||||||
|
sea_orm::{EnumIter, Iterable},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(DeriveMigrationName)]
|
#[derive(DeriveMigrationName)]
|
||||||
pub struct Migration;
|
pub struct Migration;
|
||||||
|
@ -21,18 +24,47 @@ impl MigrationTrait for Migration {
|
||||||
.col(ColumnDef::new(Service::Name).string().not_null())
|
.col(ColumnDef::new(Service::Name).string().not_null())
|
||||||
.col(ColumnDef::new(Service::Config).string().not_null())
|
.col(ColumnDef::new(Service::Config).string().not_null())
|
||||||
.col(
|
.col(
|
||||||
ColumnDef::new(Service::OverallStatus)
|
ColumnDef::new(Service::Status)
|
||||||
.enumeration(
|
.enumeration(Service::Status, OverallStatus::iter())
|
||||||
Service::OverallStatus,
|
|
||||||
vec![
|
|
||||||
OverallStatus::New,
|
|
||||||
OverallStatus::NeedsAttention,
|
|
||||||
OverallStatus::Healthy,
|
|
||||||
OverallStatus::InProgress,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.not_null(),
|
.not_null(),
|
||||||
)
|
)
|
||||||
|
.col(ColumnDef::new(Service::InProgressSince).date_time())
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(Process::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(Process::Id)
|
||||||
|
.integer()
|
||||||
|
.not_null()
|
||||||
|
.auto_increment()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(Process::Name).string().not_null())
|
||||||
|
.col(ColumnDef::new(Process::ServiceId).integer())
|
||||||
|
.col(ColumnDef::new(Process::ContainerId).string())
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(Process::Status)
|
||||||
|
.enumeration(Process::Status, ProcessStatus::iter())
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.foreign_key(
|
||||||
|
ForeignKey::create()
|
||||||
|
.from(Process::Table, Process::ServiceId)
|
||||||
|
.to(Service::Table, Service::Id)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade),
|
||||||
|
)
|
||||||
|
.index(
|
||||||
|
Index::create()
|
||||||
|
.unique()
|
||||||
|
.col(Process::ServiceId)
|
||||||
|
.col(Process::Name),
|
||||||
|
)
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -67,7 +99,14 @@ impl MigrationTrait for Migration {
|
||||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
manager
|
manager
|
||||||
.drop_table(Table::drop().table(Service::Table).to_owned())
|
.drop_table(Table::drop().table(Service::Table).to_owned())
|
||||||
.await
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(Process::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(Secrets::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,11 +117,31 @@ enum Service {
|
||||||
Name,
|
Name,
|
||||||
Config,
|
Config,
|
||||||
|
|
||||||
OverallStatus,
|
Status,
|
||||||
|
InProgressSince,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden, EnumIter)]
|
||||||
|
enum OverallStatus {
|
||||||
|
New,
|
||||||
|
Healthy,
|
||||||
|
NeedsAttention,
|
||||||
|
InProgress,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(DeriveIden)]
|
#[derive(DeriveIden)]
|
||||||
enum OverallStatus {
|
enum Process {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
ServiceId,
|
||||||
|
ContainerId,
|
||||||
|
|
||||||
|
Status,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden, EnumIter)]
|
||||||
|
enum ProcessStatus {
|
||||||
New,
|
New,
|
||||||
Healthy,
|
Healthy,
|
||||||
NeedsAttention,
|
NeedsAttention,
|
||||||
|
|
768
package-lock.json
generated
768
package-lock.json
generated
|
@ -5,15 +5,89 @@
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@blueprintjs/core": "^5.2.0",
|
||||||
|
"@blueprintjs/icons": "^5.1.5",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0",
|
||||||
|
"react-query": "^3.39.3",
|
||||||
|
"react-router": "^6.14.2",
|
||||||
|
"react-router-dom": "^6.14.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react-dom": "^18.2.7",
|
"@types/react-dom": "^18.2.7",
|
||||||
"@vitejs/plugin-react-swc": "^3.3.2",
|
"@vitejs/plugin-react-swc": "^3.3.2",
|
||||||
|
"sass": "^1.64.2",
|
||||||
"vite": "^4.4.8"
|
"vite": "^4.4.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/runtime": {
|
||||||
|
"version": "7.22.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz",
|
||||||
|
"integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"regenerator-runtime": "^0.13.11"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@blueprintjs/colors": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@blueprintjs/colors/-/colors-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-Lz3TSHJBL7+Ar/pQS15ewbNMMZc9p0CyBTPvICx4sWVH2STIJrvh05f+rdOBsKyx+FdTGTBoISyw67ZOSXvWVA==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "~2.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@blueprintjs/core": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@blueprintjs/core/-/core-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-GsYm/O12GxszkFBuwFUckc0O8RjN2LlZ+3Tp0rHvAtgLLH813nS+shCDGaVmd9ns4YKNfDMymEHgaoambaS75g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@blueprintjs/colors": "^5.0.1",
|
||||||
|
"@blueprintjs/icons": "^5.1.5",
|
||||||
|
"@popperjs/core": "^2.11.7",
|
||||||
|
"classnames": "^2.3.1",
|
||||||
|
"normalize.css": "^8.0.1",
|
||||||
|
"react-popper": "^2.3.0",
|
||||||
|
"react-transition-group": "^4.4.5",
|
||||||
|
"tslib": "~2.5.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"upgrade-blueprint-2.0.0-rename": "scripts/upgrade-blueprint-2.0.0-rename.sh",
|
||||||
|
"upgrade-blueprint-3.0.0-rename": "scripts/upgrade-blueprint-3.0.0-rename.sh"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^16.14.32 || 17 || 18",
|
||||||
|
"react": "^16.8 || 17 || 18",
|
||||||
|
"react-dom": "^16.8 || 17 || 18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@blueprintjs/icons": {
|
||||||
|
"version": "5.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@blueprintjs/icons/-/icons-5.1.5.tgz",
|
||||||
|
"integrity": "sha512-Fvtmehxgff59OvPmtnws18wk1HJENQZj31HuxvAlZWIU/0XGEGqumUyCCCFQ8zHsM9G9D6Glg+BCjiX2qCcFIg==",
|
||||||
|
"dependencies": {
|
||||||
|
"change-case": "^4.1.2",
|
||||||
|
"classnames": "^2.3.1",
|
||||||
|
"tslib": "~2.5.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^16.14.32 || 17 || 18",
|
||||||
|
"react": "^16.8 || 17 || 18",
|
||||||
|
"react-dom": "^16.8 || 17 || 18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@esbuild/android-arm": {
|
"node_modules/@esbuild/android-arm": {
|
||||||
"version": "0.18.19",
|
"version": "0.18.19",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.19.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.19.tgz",
|
||||||
|
@ -366,6 +440,23 @@
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@popperjs/core": {
|
||||||
|
"version": "2.11.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||||
|
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/popperjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@remix-run/router": {
|
||||||
|
"version": "1.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.2.tgz",
|
||||||
|
"integrity": "sha512-7Lcn7IqGMV+vizMPoEl5F0XDshcdDYtMI6uJLQdQz5CfZAwy3vvGKYSUk789qndt5dEC4HfSjviSYlSoHGL2+A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@swc/core": {
|
"node_modules/@swc/core": {
|
||||||
"version": "1.3.74",
|
"version": "1.3.74",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.74.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.74.tgz",
|
||||||
|
@ -564,13 +655,13 @@
|
||||||
"version": "15.7.5",
|
"version": "15.7.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
|
||||||
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
|
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "18.2.18",
|
"version": "18.2.18",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.18.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.18.tgz",
|
||||||
"integrity": "sha512-da4NTSeBv/P34xoZPhtcLkmZuJ+oYaCxHmyHzwaDQo9RQPBeXV+06gEk2FpqEcsX9XrnNLvRpVh6bdavDSjtiQ==",
|
"integrity": "sha512-da4NTSeBv/P34xoZPhtcLkmZuJ+oYaCxHmyHzwaDQo9RQPBeXV+06gEk2FpqEcsX9XrnNLvRpVh6bdavDSjtiQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"@types/scheduler": "*",
|
"@types/scheduler": "*",
|
||||||
|
@ -590,7 +681,7 @@
|
||||||
"version": "0.16.3",
|
"version": "0.16.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
|
||||||
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
|
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/@vitejs/plugin-react-swc": {
|
"node_modules/@vitejs/plugin-react-swc": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
|
@ -604,11 +695,189 @@
|
||||||
"vite": "^4"
|
"vite": "^4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/anymatch": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"normalize-path": "^3.0.0",
|
||||||
|
"picomatch": "^2.0.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/balanced-match": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||||
|
},
|
||||||
|
"node_modules/big-integer": {
|
||||||
|
"version": "1.6.51",
|
||||||
|
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
|
||||||
|
"integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/binary-extensions": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/braces": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fill-range": "^7.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/broadcast-channel": {
|
||||||
|
"version": "3.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz",
|
||||||
|
"integrity": "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.7.2",
|
||||||
|
"detect-node": "^2.1.0",
|
||||||
|
"js-sha3": "0.8.0",
|
||||||
|
"microseconds": "0.2.0",
|
||||||
|
"nano-time": "1.0.0",
|
||||||
|
"oblivious-set": "1.0.0",
|
||||||
|
"rimraf": "3.0.2",
|
||||||
|
"unload": "2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/camel-case": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
|
||||||
|
"dependencies": {
|
||||||
|
"pascal-case": "^3.1.2",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/capital-case": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==",
|
||||||
|
"dependencies": {
|
||||||
|
"no-case": "^3.0.4",
|
||||||
|
"tslib": "^2.0.3",
|
||||||
|
"upper-case-first": "^2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/change-case": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==",
|
||||||
|
"dependencies": {
|
||||||
|
"camel-case": "^4.1.2",
|
||||||
|
"capital-case": "^1.0.4",
|
||||||
|
"constant-case": "^3.0.4",
|
||||||
|
"dot-case": "^3.0.4",
|
||||||
|
"header-case": "^2.0.4",
|
||||||
|
"no-case": "^3.0.4",
|
||||||
|
"param-case": "^3.0.4",
|
||||||
|
"pascal-case": "^3.1.2",
|
||||||
|
"path-case": "^3.0.4",
|
||||||
|
"sentence-case": "^3.0.4",
|
||||||
|
"snake-case": "^3.0.4",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chokidar": {
|
||||||
|
"version": "3.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
|
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"anymatch": "~3.1.2",
|
||||||
|
"braces": "~3.0.2",
|
||||||
|
"glob-parent": "~5.1.2",
|
||||||
|
"is-binary-path": "~2.1.0",
|
||||||
|
"is-glob": "~4.0.1",
|
||||||
|
"normalize-path": "~3.0.0",
|
||||||
|
"readdirp": "~3.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8.10.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/classnames": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
|
||||||
|
},
|
||||||
|
"node_modules/concat-map": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||||
|
},
|
||||||
|
"node_modules/constant-case": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"no-case": "^3.0.4",
|
||||||
|
"tslib": "^2.0.3",
|
||||||
|
"upper-case": "^2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/csstype": {
|
"node_modules/csstype": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
|
||||||
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
|
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
|
||||||
"dev": true
|
},
|
||||||
|
"node_modules/detect-node": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="
|
||||||
|
},
|
||||||
|
"node_modules/dom-helpers": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.8.7",
|
||||||
|
"csstype": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dot-case": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
|
||||||
|
"dependencies": {
|
||||||
|
"no-case": "^3.0.4",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.18.19",
|
"version": "0.18.19",
|
||||||
|
@ -647,6 +916,23 @@
|
||||||
"@esbuild/win32-x64": "0.18.19"
|
"@esbuild/win32-x64": "0.18.19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fill-range": {
|
||||||
|
"version": "7.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"to-regex-range": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs.realpath": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
||||||
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
@ -661,6 +947,113 @@
|
||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/glob-parent": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"is-glob": "^4.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/header-case": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"capital-case": "^1.0.4",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/immutable": {
|
||||||
|
"version": "4.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.2.tgz",
|
||||||
|
"integrity": "sha512-oGXzbEDem9OOpDWZu88jGiYCvIsLHMvGw+8OXlpsvTFvIQplQbjg1B1cvKg8f7Hoch6+NGjpPsH1Fr+Mc2D1aA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/inflight": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||||
|
"dependencies": {
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
|
},
|
||||||
|
"node_modules/is-binary-path": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"binary-extensions": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-extglob": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-glob": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"is-extglob": "^2.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-number": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/js-sha3": {
|
||||||
|
"version": "0.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
|
||||||
|
"integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="
|
||||||
|
},
|
||||||
"node_modules/js-tokens": {
|
"node_modules/js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
|
@ -677,6 +1070,47 @@
|
||||||
"loose-envify": "cli.js"
|
"loose-envify": "cli.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lower-case": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/match-sorter": {
|
||||||
|
"version": "6.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz",
|
||||||
|
"integrity": "sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.12.5",
|
||||||
|
"remove-accents": "0.4.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/microseconds": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA=="
|
||||||
|
},
|
||||||
|
"node_modules/minimatch": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/nano-time": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==",
|
||||||
|
"dependencies": {
|
||||||
|
"big-integer": "^1.6.16"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.6",
|
"version": "3.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
|
||||||
|
@ -695,12 +1129,103 @@
|
||||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/no-case": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
|
||||||
|
"dependencies": {
|
||||||
|
"lower-case": "^2.0.2",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/normalize-path": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/normalize.css": {
|
||||||
|
"version": "8.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz",
|
||||||
|
"integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="
|
||||||
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/oblivious-set": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw=="
|
||||||
|
},
|
||||||
|
"node_modules/once": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||||
|
"dependencies": {
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/param-case": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
|
||||||
|
"dependencies": {
|
||||||
|
"dot-case": "^3.0.4",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pascal-case": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
|
||||||
|
"dependencies": {
|
||||||
|
"no-case": "^3.0.4",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-case": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==",
|
||||||
|
"dependencies": {
|
||||||
|
"dot-case": "^3.0.4",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-is-absolute": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/picomatch": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.27",
|
"version": "8.4.27",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz",
|
||||||
|
@ -729,6 +1254,16 @@
|
||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prop-types": {
|
||||||
|
"version": "15.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
|
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||||
|
"dependencies": {
|
||||||
|
"loose-envify": "^1.4.0",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"react-is": "^16.13.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react": {
|
"node_modules/react": {
|
||||||
"version": "18.2.0",
|
"version": "18.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||||
|
@ -752,6 +1287,136 @@
|
||||||
"react": "^18.2.0"
|
"react": "^18.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-fast-compare": {
|
||||||
|
"version": "3.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
|
||||||
|
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
|
||||||
|
},
|
||||||
|
"node_modules/react-is": {
|
||||||
|
"version": "16.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
|
},
|
||||||
|
"node_modules/react-popper": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"react-fast-compare": "^3.0.1",
|
||||||
|
"warning": "^4.0.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@popperjs/core": "^2.0.0",
|
||||||
|
"react": "^16.8.0 || ^17 || ^18",
|
||||||
|
"react-dom": "^16.8.0 || ^17 || ^18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-query": {
|
||||||
|
"version": "3.39.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz",
|
||||||
|
"integrity": "sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.5.5",
|
||||||
|
"broadcast-channel": "^3.4.1",
|
||||||
|
"match-sorter": "^6.0.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-native": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-router": {
|
||||||
|
"version": "6.14.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.2.tgz",
|
||||||
|
"integrity": "sha512-09Zss2dE2z+T1D03IheqAFtK4UzQyX8nFPWx6jkwdYzGLXd5ie06A6ezS2fO6zJfEb/SpG6UocN2O1hfD+2urQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@remix-run/router": "1.7.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-router-dom": {
|
||||||
|
"version": "6.14.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.14.2.tgz",
|
||||||
|
"integrity": "sha512-5pWX0jdKR48XFZBuJqHosX3AAHjRAzygouMTyimnBPOLdY3WjzUSKhus2FVMihUFWzeLebDgr4r8UeQFAct7Bg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@remix-run/router": "1.7.2",
|
||||||
|
"react-router": "6.14.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8",
|
||||||
|
"react-dom": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-transition-group": {
|
||||||
|
"version": "4.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||||
|
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.5.5",
|
||||||
|
"dom-helpers": "^5.0.1",
|
||||||
|
"loose-envify": "^1.4.0",
|
||||||
|
"prop-types": "^15.6.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.6.0",
|
||||||
|
"react-dom": ">=16.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/readdirp": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"picomatch": "^2.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/regenerator-runtime": {
|
||||||
|
"version": "0.13.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||||
|
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
|
||||||
|
},
|
||||||
|
"node_modules/remove-accents": {
|
||||||
|
"version": "0.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz",
|
||||||
|
"integrity": "sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA=="
|
||||||
|
},
|
||||||
|
"node_modules/rimraf": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||||
|
"dependencies": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rimraf": "bin.js"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "3.27.2",
|
"version": "3.27.2",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.27.2.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.27.2.tgz",
|
||||||
|
@ -768,6 +1433,23 @@
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sass": {
|
||||||
|
"version": "1.64.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.64.2.tgz",
|
||||||
|
"integrity": "sha512-TnDlfc+CRnUAgLO9D8cQLFu/GIjJIzJCGkE7o4ekIGQOH7T3GetiRR/PsTWJUHhkzcSPrARkPI+gNWn5alCzDg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"chokidar": ">=3.0.0 <4.0.0",
|
||||||
|
"immutable": "^4.0.0",
|
||||||
|
"source-map-js": ">=0.6.2 <2.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"sass": "sass.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/scheduler": {
|
"node_modules/scheduler": {
|
||||||
"version": "0.23.0",
|
"version": "0.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
||||||
|
@ -776,6 +1458,25 @@
|
||||||
"loose-envify": "^1.1.0"
|
"loose-envify": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sentence-case": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==",
|
||||||
|
"dependencies": {
|
||||||
|
"no-case": "^3.0.4",
|
||||||
|
"tslib": "^2.0.3",
|
||||||
|
"upper-case-first": "^2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/snake-case": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==",
|
||||||
|
"dependencies": {
|
||||||
|
"dot-case": "^3.0.4",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||||
|
@ -785,6 +1486,48 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/to-regex-range": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"is-number": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",
|
||||||
|
"integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w=="
|
||||||
|
},
|
||||||
|
"node_modules/unload": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.6.2",
|
||||||
|
"detect-node": "^2.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/upper-case": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/upper-case-first": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "4.4.8",
|
"version": "4.4.8",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.8.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.8.tgz",
|
||||||
|
@ -839,6 +1582,19 @@
|
||||||
"optional": true
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/warning": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
|
||||||
|
"dependencies": {
|
||||||
|
"loose-envify": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wrappy": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,16 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react-dom": "^18.2.7",
|
"@types/react-dom": "^18.2.7",
|
||||||
"@vitejs/plugin-react-swc": "^3.3.2",
|
"@vitejs/plugin-react-swc": "^3.3.2",
|
||||||
|
"sass": "^1.64.2",
|
||||||
"vite": "^4.4.8"
|
"vite": "^4.4.8"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@blueprintjs/core": "^5.2.0",
|
||||||
|
"@blueprintjs/icons": "^5.1.5",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0",
|
||||||
|
"react-query": "^3.39.3",
|
||||||
|
"react-router": "^6.14.2",
|
||||||
|
"react-router-dom": "^6.14.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1
src/api/mod.rs
Normal file
1
src/api/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod service;
|
83
src/api/service.rs
Normal file
83
src/api/service.rs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use axum::extract::{Path, State};
|
||||||
|
use axum::Json;
|
||||||
|
use entity::{process, service};
|
||||||
|
use sea_orm::{
|
||||||
|
EntityTrait, ModelTrait,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::error::Result;
|
||||||
|
use crate::AppState;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct ListResult {
|
||||||
|
services: Vec<ServiceShort>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct ServiceShort {
|
||||||
|
id: i32,
|
||||||
|
name: String,
|
||||||
|
status: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn list(state: State<AppState>) -> Result<Json<ListResult>> {
|
||||||
|
let services = service::Entity::find().all(&state.conn).await?;
|
||||||
|
let services = services
|
||||||
|
.into_iter()
|
||||||
|
.map(|model| ServiceShort {
|
||||||
|
id: model.id,
|
||||||
|
name: model.name,
|
||||||
|
status: model.status,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Ok(Json(ListResult { services }))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct ServiceInfo {
|
||||||
|
name: String,
|
||||||
|
status: String,
|
||||||
|
processes: HashMap<String, ProcessInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct ProcessInfo {
|
||||||
|
id: i32,
|
||||||
|
status: String,
|
||||||
|
container_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get(
|
||||||
|
state: State<AppState>,
|
||||||
|
Path(id): Path<i32>,
|
||||||
|
) -> Result<Json<ServiceInfo>> {
|
||||||
|
let service = service::Entity::find_by_id(id).one(&state.conn).await?;
|
||||||
|
let service = match service {
|
||||||
|
Some(v) => v,
|
||||||
|
None => bail!("get fucked"),
|
||||||
|
};
|
||||||
|
let processes = service
|
||||||
|
.find_related(process::Entity)
|
||||||
|
.all(&state.conn)
|
||||||
|
.await?;
|
||||||
|
let processes = processes
|
||||||
|
.into_iter()
|
||||||
|
.map(|process| {
|
||||||
|
(
|
||||||
|
process.name,
|
||||||
|
ProcessInfo {
|
||||||
|
id: process.id,
|
||||||
|
container_id: process.container_id,
|
||||||
|
status: process.status,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Ok(Json(ServiceInfo {
|
||||||
|
name: service.name,
|
||||||
|
status: service.status,
|
||||||
|
processes,
|
||||||
|
}))
|
||||||
|
}
|
19
src/error.rs
19
src/error.rs
|
@ -3,9 +3,24 @@ use axum::{
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bail {
|
||||||
|
($msg:literal $(,)?) => {
|
||||||
|
return Err(anyhow::anyhow!($msg).into())
|
||||||
|
};
|
||||||
|
($err:expr $(,)?) => {
|
||||||
|
return Err(anyhow::anyhow!($err).into())
|
||||||
|
};
|
||||||
|
($fmt:expr, $($arg:tt)*) => {
|
||||||
|
return Err(anyhow::anyhow!($fmt, $($arg)*).into())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T, E = AppError> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct AppError(anyhow::Error);
|
pub struct AppError(anyhow::Error);
|
||||||
|
|
||||||
// Tell axum how to convert `AppError` into a response.
|
|
||||||
impl IntoResponse for AppError {
|
impl IntoResponse for AppError {
|
||||||
fn into_response(self) -> Response {
|
fn into_response(self) -> Response {
|
||||||
(
|
(
|
||||||
|
@ -16,8 +31,6 @@ impl IntoResponse for AppError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into
|
|
||||||
// `Result<_, AppError>`. That way you don't need to do that manually.
|
|
||||||
impl<E> From<E> for AppError
|
impl<E> From<E> for AppError
|
||||||
where
|
where
|
||||||
E: Into<anyhow::Error>,
|
E: Into<anyhow::Error>,
|
||||||
|
|
118
src/jobs.rs
118
src/jobs.rs
|
@ -1,19 +1,13 @@
|
||||||
use std::{
|
use std::{pin::Pin, sync::Arc};
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
pin::Pin,
|
use futures::Future;
|
||||||
sync::Arc,
|
use tokio::sync::{
|
||||||
time::Duration,
|
mpsc::{self, UnboundedReceiver, UnboundedSender},
|
||||||
|
RwLock,
|
||||||
};
|
};
|
||||||
|
|
||||||
use futures::{future::pending, Future, FutureExt};
|
pub type TaskQueue = deadqueue::limited::Queue<Job>;
|
||||||
use tokio::{
|
const WORKER_COUNT: usize = 4;
|
||||||
select,
|
|
||||||
sync::{
|
|
||||||
mpsc::{self, UnboundedReceiver, UnboundedSender},
|
|
||||||
RwLock,
|
|
||||||
},
|
|
||||||
time::sleep,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Job {
|
pub struct Job {
|
||||||
pub code: Pin<Box<dyn Future<Output = ()> + Send>>,
|
pub code: Pin<Box<dyn Future<Output = ()> + Send>>,
|
||||||
|
@ -26,85 +20,45 @@ pub enum Schedule {
|
||||||
|
|
||||||
pub type JobSender = UnboundedSender<Job>;
|
pub type JobSender = UnboundedSender<Job>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Scheduler {
|
pub struct Scheduler {
|
||||||
rx: Arc<RwLock<UnboundedReceiver<Job>>>,
|
rx: Arc<RwLock<UnboundedReceiver<Job>>>,
|
||||||
|
queue: Arc<TaskQueue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scheduler {
|
impl Scheduler {
|
||||||
pub fn new() -> (Self, JobSender) {
|
pub fn new() -> (Self, Arc<TaskQueue>) {
|
||||||
let (tx, rx) = mpsc::unbounded_channel();
|
let (_tx, rx) = mpsc::unbounded_channel();
|
||||||
let rx = Arc::new(RwLock::new(rx));
|
let rx = Arc::new(RwLock::new(rx));
|
||||||
let scheduler = Scheduler { rx };
|
let queue = Arc::new(TaskQueue::new(100));
|
||||||
(scheduler, tx)
|
let scheduler = Scheduler {
|
||||||
|
rx,
|
||||||
|
queue: queue.clone(),
|
||||||
|
};
|
||||||
|
(scheduler, queue.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(self) {
|
pub async fn run(self) {
|
||||||
let mut id: usize = 1;
|
for worker in 0..WORKER_COUNT {
|
||||||
let mut jobs = HashMap::<usize, Job>::new();
|
let queue = self.queue.clone();
|
||||||
let mut remove_set = HashSet::new();
|
let handle = tokio::spawn(async move {
|
||||||
|
loop {
|
||||||
|
let task = queue.pop().await;
|
||||||
|
println!("worker[{}] processing task ...", worker);
|
||||||
|
|
||||||
loop {
|
task.code.await;
|
||||||
// Calculate the next job
|
println!("worker[{}] done", worker);
|
||||||
let mut get_next_job = || {
|
|
||||||
let mut next_job = pending().boxed();
|
|
||||||
let next_job_time = jobs
|
|
||||||
.iter()
|
|
||||||
.map(|(job_id, job)| {
|
|
||||||
let time = match job.schedule {
|
|
||||||
Schedule::ASAP => Duration::ZERO,
|
|
||||||
};
|
|
||||||
(job_id, time)
|
|
||||||
})
|
|
||||||
.min_by_key(|(_, time)| *time);
|
|
||||||
if let Some((job_id, time)) = next_job_time {
|
|
||||||
let job_id = *job_id;
|
|
||||||
let job = jobs.remove(&job_id).unwrap();
|
|
||||||
if time.is_zero() {
|
|
||||||
next_job = async {
|
|
||||||
job.code.await;
|
|
||||||
}
|
|
||||||
.boxed();
|
|
||||||
} else {
|
|
||||||
next_job = sleep(time).then(move |_| job.code).boxed();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
next_job
|
println!("Spawned worker thread [{}]: {handle:?}", worker);
|
||||||
};
|
|
||||||
|
|
||||||
let next_job = get_next_job();
|
|
||||||
|
|
||||||
let mut write_lock = select! {
|
|
||||||
rx = self.rx.write() => rx,
|
|
||||||
_ = next_job => {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let next_job = get_next_job();
|
|
||||||
|
|
||||||
select! {
|
|
||||||
// Did a new job just come in?
|
|
||||||
job = write_lock.recv() => {
|
|
||||||
let job = match job {
|
|
||||||
Some(v) => v,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
let this_id = id;
|
|
||||||
id += 1;
|
|
||||||
|
|
||||||
jobs.insert(this_id, job);
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = next_job => {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for x in remove_set.drain() {
|
|
||||||
jobs.remove(x);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokio::spawn(async {
|
||||||
|
loop {
|
||||||
|
// TODO: Implement
|
||||||
|
// wait 30 seconds
|
||||||
|
// query docker container status
|
||||||
|
// add new incomplete containers to the worker queue
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -1,17 +1,23 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
pub mod error;
|
||||||
|
|
||||||
|
pub mod api;
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod error;
|
|
||||||
pub mod jobs;
|
pub mod jobs;
|
||||||
pub mod upload;
|
pub mod upload;
|
||||||
|
|
||||||
use jobs::JobSender;
|
use std::sync::{atomic::AtomicPtr, Arc};
|
||||||
|
|
||||||
|
use jobs::TaskQueue;
|
||||||
use sea_orm::DatabaseConnection;
|
use sea_orm::DatabaseConnection;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub conn: DatabaseConnection,
|
pub conn: DatabaseConnection,
|
||||||
pub tx: JobSender,
|
// pub tx: JobSender,
|
||||||
|
pub queue: Arc<TaskQueue>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,9 @@ async fn main() -> Result<()> {
|
||||||
Migrator::up(&conn, None).await?;
|
Migrator::up(&conn, None).await?;
|
||||||
|
|
||||||
// Run a web server
|
// Run a web server
|
||||||
let (sched, tx) = Scheduler::new();
|
let (sched, queue) = Scheduler::new();
|
||||||
let sched_handle = tokio::spawn(sched.run());
|
let sched_handle = tokio::spawn(sched.run());
|
||||||
let state = AppState { conn, tx };
|
let state = AppState { conn, queue };
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route(
|
.route(
|
||||||
"/",
|
"/",
|
||||||
|
@ -31,6 +31,8 @@ async fn main() -> Result<()> {
|
||||||
Html::from("<form action='/upload' method=POST><input type='text' name='name' /><textarea name='config'></textarea><input type='submit' /></form>")
|
Html::from("<form action='/upload' method=POST><input type='text' name='name' /><textarea name='config'></textarea><input type='submit' /></form>")
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
.route("/service/list", get(aah::api::service::list))
|
||||||
|
.route("/service/:id", get(aah::api::service::get))
|
||||||
.route("/upload", post(handle_upload_configuration))
|
.route("/upload", post(handle_upload_configuration))
|
||||||
.layer(from_fn(http_basic_auth))
|
.layer(from_fn(http_basic_auth))
|
||||||
.with_state(state);
|
.with_state(state);
|
||||||
|
|
100
src/upload.rs
100
src/upload.rs
|
@ -1,19 +1,27 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::anyhow;
|
||||||
use axum::extract::{Multipart, State};
|
use axum::{
|
||||||
|
extract::{Multipart, State},
|
||||||
|
Json,
|
||||||
|
};
|
||||||
use bollard::{
|
use bollard::{
|
||||||
container::{
|
container::{
|
||||||
Config as ContainerConfig, CreateContainerOptions, StartContainerOptions,
|
Config as ContainerConfig, CreateContainerOptions, StartContainerOptions,
|
||||||
},
|
},
|
||||||
image::CreateImageOptions,
|
image::CreateImageOptions,
|
||||||
|
network::CreateNetworkOptions,
|
||||||
Docker,
|
Docker,
|
||||||
};
|
};
|
||||||
use entity::service;
|
use entity::{process, service};
|
||||||
use futures::{FutureExt, TryStreamExt};
|
use futures::{FutureExt, TryStreamExt};
|
||||||
use sea_orm::{ActiveModelTrait, Set};
|
use migration::OnConflict;
|
||||||
|
use sea_orm::{ActiveModelTrait, DatabaseConnection, EntityTrait, Set};
|
||||||
|
use serde_json::{json, Value};
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::error::Result;
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{Config, SourceConfig},
|
config::{Config, SourceConfig},
|
||||||
error::AppError,
|
error::AppError,
|
||||||
|
@ -24,34 +32,41 @@ use crate::{
|
||||||
pub async fn handle_upload_configuration(
|
pub async fn handle_upload_configuration(
|
||||||
state: State<AppState>,
|
state: State<AppState>,
|
||||||
form: Multipart,
|
form: Multipart,
|
||||||
) -> Result<(), AppError> {
|
) -> Result<Json<Value>, AppError> {
|
||||||
let upload_form = extract_multipart(form).await.unwrap();
|
let upload_form = extract_multipart(form).await.unwrap();
|
||||||
let config_string = serde_json::to_string(&upload_form.config).unwrap();
|
let config_string = serde_json::to_string(&upload_form.config).unwrap();
|
||||||
|
|
||||||
state
|
let mut result = service::ActiveModel {
|
||||||
.tx
|
|
||||||
.send(Job {
|
|
||||||
code: async move {
|
|
||||||
spawn_docker(&upload_form.config).await.unwrap();
|
|
||||||
println!("done with job");
|
|
||||||
}
|
|
||||||
.boxed(),
|
|
||||||
schedule: Schedule::ASAP,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
println!("spawned job");
|
|
||||||
|
|
||||||
service::ActiveModel {
|
|
||||||
name: Set(upload_form.name),
|
name: Set(upload_form.name),
|
||||||
config: Set(config_string),
|
config: Set(config_string),
|
||||||
overall_status: Set("New".into()),
|
status: Set("New".into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.save(&state.conn)
|
.save(&state.conn)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Ok(())
|
let id = result.id.take().unwrap();
|
||||||
|
|
||||||
|
let conn = state.conn.clone();
|
||||||
|
let job = Job {
|
||||||
|
code: async move {
|
||||||
|
spawn_docker(SpawnDockerOpts {
|
||||||
|
service_id: id,
|
||||||
|
conn,
|
||||||
|
config: &upload_form.config,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
println!("done with job");
|
||||||
|
}
|
||||||
|
.boxed(),
|
||||||
|
schedule: Schedule::ASAP,
|
||||||
|
};
|
||||||
|
state.queue.push(job).await;
|
||||||
|
println!("spawned job");
|
||||||
|
|
||||||
|
Ok(Json(json! ({"id": id})))
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UploadForm {
|
struct UploadForm {
|
||||||
|
@ -79,14 +94,50 @@ async fn extract_multipart(mut form: Multipart) -> Result<UploadForm> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn spawn_docker(config: &Config) -> Result<()> {
|
struct SpawnDockerOpts<'a> {
|
||||||
println!("Loading config: {config:?}");
|
conn: DatabaseConnection,
|
||||||
|
config: &'a Config,
|
||||||
|
service_id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn spawn_docker(opts: SpawnDockerOpts<'_>) -> Result<()> {
|
||||||
|
println!("Loading config: {:?}", opts.config);
|
||||||
|
|
||||||
let docker = Docker::connect_with_local_defaults().unwrap();
|
let docker = Docker::connect_with_local_defaults().unwrap();
|
||||||
println!("connected to docker {docker:?}");
|
println!("connected to docker {docker:?}");
|
||||||
sleep(Duration::from_secs(1)).await;
|
sleep(Duration::from_secs(1)).await;
|
||||||
|
|
||||||
for (service_name, service_config) in config.service.iter() {
|
// Create a bridge network
|
||||||
|
let network_id = format!("aah-network-{}", Uuid::new_v4());
|
||||||
|
docker
|
||||||
|
.create_network(CreateNetworkOptions {
|
||||||
|
name: network_id.clone(),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
println!("Created network.");
|
||||||
|
|
||||||
|
// Create all the services
|
||||||
|
for (service_name, service_config) in opts.config.service.iter() {
|
||||||
|
// Insert metadata into the db
|
||||||
|
let new_process = process::ActiveModel {
|
||||||
|
service_id: Set(Some(opts.service_id)),
|
||||||
|
name: Set(service_name.clone()),
|
||||||
|
status: Set("New".to_owned()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
process::Entity::insert(new_process)
|
||||||
|
.on_conflict(
|
||||||
|
OnConflict::columns([
|
||||||
|
process::Column::ServiceId,
|
||||||
|
process::Column::Name,
|
||||||
|
])
|
||||||
|
.update_column(process::Column::Status)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.exec(&opts.conn)
|
||||||
|
.await?;
|
||||||
|
|
||||||
println!("preparing config for {}", service_name);
|
println!("preparing config for {}", service_name);
|
||||||
let SourceConfig::Image { image: from_image } = &service_config.source;
|
let SourceConfig::Image { image: from_image } = &service_config.source;
|
||||||
|
|
||||||
|
@ -120,6 +171,7 @@ async fn spawn_docker(config: &Config) -> Result<()> {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
// Start the container
|
||||||
docker
|
docker
|
||||||
.start_container(&result.id, None::<StartContainerOptions<String>>)
|
.start_container(&result.id, None::<StartContainerOptions<String>>)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{ "compilerOptions": { "jsx": "react-jsx" } }
|
{ "compilerOptions": { "lib": ["ESNext", "DOM"], "jsx": "react-jsx" } }
|
||||||
|
|
Loading…
Reference in a new issue