almost done

This commit is contained in:
Daniel LaCosse 2025-02-26 15:56:53 -05:00
parent 12631d3727
commit 1bc144f44e
15 changed files with 1921 additions and 46 deletions

1788
package-lock.json generated

File diff suppressed because it is too large Load diff

50
src/sdk/README.md Normal file
View file

@ -0,0 +1,50 @@
# Manager SDK Prototype
This is a prototype of a Manager SDK for the Outline Server.
It uses `ngrok` to allow the user to access the Management API from the browser.
It uses the `lit` library to create a web component that displays the server details.
It also uses `@hey-api/openapi-ts` to generate a TypeScript library from the Outline Server API definition.
## Setup
Get a`NGROK_TOKEN` from https://dashboard.ngrok.com/get-started/your-authtoken and a `NGROK_DOMAIN` from https://dashboard.ngrok.com/domains.
```sh
export NGROK_TOKEN=...
export NGROK_DOMAIN=...
# Start the local outline server
task shadowbox:start &
# Start the web app demo
npm run example --workspace sdk
```
Result:
<img src="screenshots/results.png" alt="results" width="320"/>
## Library Generation
Generated library from `src/shadowbox/server/api.yml` with `@hey-api/openapi-ts`:
```bash
# run in project root
npx @hey-api/openapi-ts \
-i src/shadowbox/server/api.yml \
-o src/sdk/library/generated \
-c @hey-api/client-fetch
```
Intellisense example:
<img src="screenshots/intellisense.png" alt="intellisense" width="320"/>
## TODOs
- [ ] Why does Prometheus continuously crash (had to comment it out)
- [ ] Generate Library on API change
- [ ] ...

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Manager SDK Demo</title>
<script type="module" src="/src/server-details.ts"></script>
</head>
<body>
<server-details url="%VITE_SERVER_DETAIL_URL%"></server-details>
</body>
</html>

View file

@ -0,0 +1,53 @@
import { LitElement, html, css } from 'lit'
import { customElement, state, property } from 'lit/decorators.js';
import makeLibrary, { type Server } from '../../library';
@customElement('server-details')
export class MainPage extends LitElement {
@property({ type: String }) url: string;
@state() server?: Server;
constructor(url: string) {
super();
this.url = url;
}
get client() {
return makeLibrary(this.url);
}
static styles = css`
:host {
font-family: system-ui;
}
dt {
font-weight: bold;
}
`;
async connectedCallback() {
super.connectedCallback();
const { data } = await this.client.getServer();
this.server = data;
}
render() {
if (!this.server) {
return html`Loading...`;
}
return html`
<dl>
${Object.entries(this.server).map(([key, value]) => html`
<dt>${key}</dt>
<dd>${value}</dd>
`)}
</dl>
`;
}
}

1
src/sdk/example/src/vite-env.d.ts vendored Normal file
View file

@ -0,0 +1 @@
/// <reference types="vite/client" />

View file

@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "ES2020",
"experimentalDecorators": true,
"useDefineForClassFields": false,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}

11
src/sdk/library/index.ts Normal file
View file

@ -0,0 +1,11 @@
import { client } from './generated/client.gen';
import * as generated from './generated';
export type * from './generated';
export default (baseUrl: string) => {
client.setConfig({ baseUrl, headers: { "ngrok-skip-browser-warning": "69420" } });
return generated;
}

16
src/sdk/package.json Normal file
View file

@ -0,0 +1,16 @@
{
"name": "sdk",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"example": "VITE_SERVER_DETAIL_URL=https://${NGROK_DOMAIN}/TestApiPrefix vite example"
},
"dependencies": {
"lit": "^3.2.1"
},
"devDependencies": {
"typescript": "~5.7.2",
"vite": "^6.2.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View file

@ -283,17 +283,26 @@ process.on('unhandledRejection', (error: Error) => {
main()
.then(async () => {
if (!process.env.NGROK_TOKEN || !process.env.NGROK_DOMAIN) {
return;
}
process.env.SSL_CERT_FILE = process.env.SB_CERTIFICATE_FILE;
process.env.SSL_KEY_FILE = process.env.SB_PRIVATE_KEY_FILE;
const listener = await ngrok.forward({
domain: process.env.NGROK_DOMAIN,
// ipv6 loopback address doesn't work
addr: `https://127.0.0.1:${process.env.SB_API_PORT || 8081}`,
authtoken_from_env: true,
verify_upstream_tls: false,
response_header_add: [
'Allow-Access-Control-Origin: *',
'Access-Control-Allow-Headers: *'
],
});
console.log(`Listening to the Manager API on ${listener.url()}`);
console.log(`Listening to the Manager API on '${listener.url()}'`);
})
.catch((error) => {
logging.error(error.stack);