Use typescript
parent
ff1323b4a6
commit
f473967016
|
@ -0,0 +1,242 @@
|
||||||
|
declare namespace Cockpit {
|
||||||
|
/**********************************
|
||||||
|
* Some helpful primitive typedefs
|
||||||
|
*********************************/
|
||||||
|
|
||||||
|
type integer = number; //A typedef for an integer. Doesn't actually prevent compilation, but provides an IDE hint
|
||||||
|
|
||||||
|
/**********************************
|
||||||
|
* Cockpit D-Bus
|
||||||
|
* http://cockpit-project.org/guide/latest/cockpit-dbus.html
|
||||||
|
*********************************/
|
||||||
|
|
||||||
|
type BYTE = number;
|
||||||
|
type BOOLEAN = boolean;
|
||||||
|
type INT16 = number;
|
||||||
|
type UINT16 = number;
|
||||||
|
type INT32 = number;
|
||||||
|
type UINT32 = number;
|
||||||
|
type INT64 = number;
|
||||||
|
type UINT64 = number;
|
||||||
|
type DOUBLE = number;
|
||||||
|
type STRING = string;
|
||||||
|
type OBJECT_PATH = string;
|
||||||
|
type SIGNATURE = string;
|
||||||
|
type ARRAY_BYTE = string[];
|
||||||
|
type ARRAY_DICT_ENTRY_STRING = object;
|
||||||
|
type ARRAY_DICT_ENTRY_OTHER = object;
|
||||||
|
type ARRAY_OTHER = any[];
|
||||||
|
interface VARIANT {
|
||||||
|
"t": STRING,
|
||||||
|
"v": any
|
||||||
|
}
|
||||||
|
//TODO - Not sure on specifics for handle
|
||||||
|
type HANDLE = object;
|
||||||
|
|
||||||
|
interface DBusOptions {
|
||||||
|
"bus" : string
|
||||||
|
"host" : string
|
||||||
|
"superuser" : string
|
||||||
|
"track" : string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DBusProxy {
|
||||||
|
client : string
|
||||||
|
path : string
|
||||||
|
iface : string
|
||||||
|
valid : boolean
|
||||||
|
data : object
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Todo unfinished
|
||||||
|
interface DBusClient {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************
|
||||||
|
* Cockpit File Access
|
||||||
|
* http://cockpit-project.org/guide/latest/cockpit-file.html
|
||||||
|
**********************************/
|
||||||
|
|
||||||
|
interface ParsingFunction {
|
||||||
|
(data: string) : string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StringifyingFunction {
|
||||||
|
(data: string) : string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SyntaxObject {
|
||||||
|
parse: ParsingFunction
|
||||||
|
stringify: StringifyingFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileAccessOptions {
|
||||||
|
syntax?: SyntaxObject,
|
||||||
|
binary?: boolean,
|
||||||
|
max_read_size?: integer,
|
||||||
|
superuser?: string,
|
||||||
|
host?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileReadDoneCallback {
|
||||||
|
(content: string, tag: string) : void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileReadFailCallback {
|
||||||
|
(error: string) : void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileReadPromise {
|
||||||
|
done (callback : FileReadDoneCallback) : FileReadPromise
|
||||||
|
fail (callback : FileReadFailCallback) : FileReadPromise
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileReplaceDoneCallback {
|
||||||
|
(newTag: string) : void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileReplaceFailCallback {
|
||||||
|
(error: string) : void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileReplacePromise {
|
||||||
|
done (callback : FileReplaceDoneCallback) : FileReplacePromise
|
||||||
|
fail (callback : FileReplaceFailCallback) : FileReplacePromise
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileModifyDoneCallback {
|
||||||
|
(newContent : string, newTag: string) : void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileModifyFailCallback {
|
||||||
|
(error: string) : void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileModifyPromise {
|
||||||
|
done (callback : FileModifyDoneCallback) : FileModifyPromise
|
||||||
|
fail (callback : FileModifyFailCallback) : FileModifyPromise
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileWatchCallback {
|
||||||
|
content : string,
|
||||||
|
tag : string,
|
||||||
|
error? : any //TODO - what is the error content?
|
||||||
|
}
|
||||||
|
|
||||||
|
interface File {
|
||||||
|
read () : FileReadPromise
|
||||||
|
replace (content : string, expected_tag?: string) : FileReplacePromise
|
||||||
|
modify (callback : any, initial_content?: string, initial_tag?: string) : FileModifyPromise
|
||||||
|
watch (callback : FileWatchCallback) : void
|
||||||
|
close () : void
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************
|
||||||
|
* Cockpit Processes
|
||||||
|
* http://cockpit-project.org/guide/latest/cockpit-spawn.html
|
||||||
|
**********************************/
|
||||||
|
|
||||||
|
interface ProcessFailureException {
|
||||||
|
message?: string
|
||||||
|
problem?: string
|
||||||
|
exit_status?: integer
|
||||||
|
exit_signal?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ProcessProblemCodes {
|
||||||
|
"access-denied", //"The user is not permitted to perform the action in question."
|
||||||
|
"authentication-failed", //"User authentication failed."
|
||||||
|
"internal-error", //"An unexpected internal error without further info. This should not happen during the normal course of operations."
|
||||||
|
"no-cockpit", //"The system does not have a compatible version of Cockpit installed or installed properly."
|
||||||
|
"no-session", //"Cockpit is not logged in."
|
||||||
|
"not-found", //"Something specifically requested was not found, such as a file, executable etc."
|
||||||
|
"terminated", //"Something was terminated forcibly, such as a connection, process session, etc."
|
||||||
|
"timeout", //"Something timed out."
|
||||||
|
"unknown-hostkey", //"The remote host had an unexpected or unknown key."
|
||||||
|
"no-forwarding" //"Could not forward authentication credentials to the remote host."
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProcessPromiseDoneCallback {
|
||||||
|
(data: string, message?: string) : void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProcessPromiseFailCallback {
|
||||||
|
(exception: ProcessFailureException, data?: string) : void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProcessPromiseStreamCallback {
|
||||||
|
(data: string) : void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProcessPromise {
|
||||||
|
done( callback: ProcessPromiseDoneCallback ) : ProcessPromise,
|
||||||
|
fail( callback: ProcessPromiseFailCallback ) : ProcessPromise,
|
||||||
|
stream( callback: ProcessPromiseStreamCallback ) : ProcessPromise,
|
||||||
|
input( data: string, stream?: boolean ) : ProcessPromise,
|
||||||
|
close( problem?: ProcessProblemCodes ) : ProcessPromise,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************
|
||||||
|
* Cockpit User Session
|
||||||
|
* http://cockpit-project.org/guide/latest/cockpit-login.html
|
||||||
|
**********************************/
|
||||||
|
|
||||||
|
interface UserSessionPermission {
|
||||||
|
allowed : boolean
|
||||||
|
onChanged : any //TODO need to see how to do events in TS
|
||||||
|
close() : void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserSessionObject {
|
||||||
|
onchanged : any
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserSessionDetails {
|
||||||
|
"id" : string //This is unix user id.
|
||||||
|
"name" : string //This is the unix user name like "root".
|
||||||
|
"full_name" : string //This is a readable name for the user.
|
||||||
|
"groups" : string //This is an array of group names to which the user belongs.
|
||||||
|
"home" : string //This is user's home directory.
|
||||||
|
"shell" : string //This is unix user shell.
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserSessionPromiseDoneCallback {
|
||||||
|
(user: UserSessionDetails) : void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserSessionPromiseFailCallback { //Todo - is this defined?
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserSessionPromise {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************
|
||||||
|
* Cockpit Object
|
||||||
|
* Generally brought into your app in the root HTML file via a <script>
|
||||||
|
**********************************/
|
||||||
|
|
||||||
|
interface CockpitObject {
|
||||||
|
|
||||||
|
//File system
|
||||||
|
file(path : string, options? : FileAccessOptions) : File
|
||||||
|
|
||||||
|
//Processes
|
||||||
|
spawn(
|
||||||
|
arguments: Array<string>,
|
||||||
|
parameters?: object
|
||||||
|
): ProcessPromise;
|
||||||
|
|
||||||
|
//User Session
|
||||||
|
logout(reload? : boolean) : void
|
||||||
|
user() : UserSessionPromise
|
||||||
|
//user : UserSessionObject // TODO ts doesn't like this
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
declare var cockpit : Cockpit.CockpitObject;
|
4
Makefile
4
Makefile
|
@ -79,8 +79,8 @@ $(SPEC): packaging/$(SPEC).in $(NODE_MODULES_TEST)
|
||||||
provides=$$(npm ls --omit dev --package-lock-only --depth=Infinity | grep -Eo '[^[:space:]]+@[^[:space:]]+' | sort -u | sed 's/^/Provides: bundled(npm(/; s/\(.*\)@/\1)) = /'); \
|
provides=$$(npm ls --omit dev --package-lock-only --depth=Infinity | grep -Eo '[^[:space:]]+@[^[:space:]]+' | sort -u | sed 's/^/Provides: bundled(npm(/; s/\(.*\)@/\1)) = /'); \
|
||||||
awk -v p="$$provides" '{gsub(/%{VERSION}/, "$(VERSION)"); gsub(/%{NPM_PROVIDES}/, p)}1' $< > $@
|
awk -v p="$$provides" '{gsub(/%{VERSION}/, "$(VERSION)"); gsub(/%{NPM_PROVIDES}/, p)}1' $< > $@
|
||||||
|
|
||||||
$(DIST_TEST): $(NODE_MODULES_TEST) $(COCKPIT_REPO_STAMP) $(shell find src/ -type f) package.json build.js
|
$(DIST_TEST): $(NODE_MODULES_TEST) $(COCKPIT_REPO_STAMP) $(shell find src/ -type f) package.json
|
||||||
NODE_ENV=$(NODE_ENV) ./build.js
|
NODE_ENV=$(NODE_ENV) npm run build
|
||||||
|
|
||||||
watch: $(NODE_MODULES_TEST) $(COCKPIT_REPO_STAMP)
|
watch: $(NODE_MODULES_TEST) $(COCKPIT_REPO_STAMP)
|
||||||
NODE_ENV=$(NODE_ENV) npm run watch
|
NODE_ENV=$(NODE_ENV) npm run watch
|
||||||
|
|
136
build.js
136
build.js
|
@ -1,136 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
import fs from 'node:fs';
|
|
||||||
import path from 'node:path';
|
|
||||||
import os from 'node:os';
|
|
||||||
|
|
||||||
import copy from 'esbuild-plugin-copy';
|
|
||||||
|
|
||||||
import { cleanPlugin } from './pkg/lib/esbuild-cleanup-plugin.js';
|
|
||||||
import { cockpitCompressPlugin } from './pkg/lib/esbuild-compress-plugin.js';
|
|
||||||
import { cockpitPoEsbuildPlugin } from './pkg/lib/cockpit-po-plugin.js';
|
|
||||||
import { cockpitRsyncEsbuildPlugin } from './pkg/lib/cockpit-rsync-plugin.js';
|
|
||||||
import { esbuildStylesPlugins } from './pkg/lib/esbuild-common.js';
|
|
||||||
import { eslintPlugin } from './pkg/lib/esbuild-eslint-plugin.js';
|
|
||||||
import { stylelintPlugin } from './pkg/lib/esbuild-stylelint-plugin.js';
|
|
||||||
|
|
||||||
const useWasm = os.arch() !== 'x64';
|
|
||||||
const esbuild = (await import(useWasm ? 'esbuild-wasm' : 'esbuild')).default;
|
|
||||||
|
|
||||||
const production = process.env.NODE_ENV === 'production';
|
|
||||||
const watchMode = process.env.ESBUILD_WATCH === "true";
|
|
||||||
// linters dominate the build time, so disable them for production builds by default, but enable in watch mode
|
|
||||||
const lint = process.env.LINT ? (process.env.LINT !== 0) : (watchMode || !production);
|
|
||||||
// List of directories to use when using import statements
|
|
||||||
const nodePaths = ['pkg/lib'];
|
|
||||||
const outdir = 'dist';
|
|
||||||
|
|
||||||
// Obtain package name from package.json
|
|
||||||
const packageJson = JSON.parse(fs.readFileSync('package.json'));
|
|
||||||
|
|
||||||
function notifyEndPlugin() {
|
|
||||||
return {
|
|
||||||
name: 'notify-end',
|
|
||||||
setup(build) {
|
|
||||||
let startTime;
|
|
||||||
|
|
||||||
build.onStart(() => {
|
|
||||||
startTime = new Date();
|
|
||||||
});
|
|
||||||
|
|
||||||
build.onEnd(() => {
|
|
||||||
const endTime = new Date();
|
|
||||||
const timeStamp = endTime.toTimeString().split(' ')[0];
|
|
||||||
console.log(`${timeStamp}: Build finished in ${endTime - startTime} ms`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const cwd = process.cwd();
|
|
||||||
|
|
||||||
// similar to fs.watch(), but recursively watches all subdirectories
|
|
||||||
function watch_dirs(dir, on_change) {
|
|
||||||
const callback = (ev, dir, fname) => {
|
|
||||||
// only listen for "change" events, as renames are noisy
|
|
||||||
// ignore hidden files
|
|
||||||
const isHidden = /^\./.test(fname);
|
|
||||||
if (ev !== "change" || isHidden) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
on_change(path.join(dir, fname));
|
|
||||||
};
|
|
||||||
|
|
||||||
fs.watch(dir, {}, (ev, path) => callback(ev, dir, path));
|
|
||||||
|
|
||||||
// watch all subdirectories in dir
|
|
||||||
const d = fs.opendirSync(dir);
|
|
||||||
let dirent;
|
|
||||||
|
|
||||||
while ((dirent = d.readSync()) !== null) {
|
|
||||||
if (dirent.isDirectory())
|
|
||||||
watch_dirs(path.join(dir, dirent.name), on_change);
|
|
||||||
}
|
|
||||||
d.closeSync();
|
|
||||||
}
|
|
||||||
|
|
||||||
const context = await esbuild.context({
|
|
||||||
...!production ? { sourcemap: "linked" } : {},
|
|
||||||
bundle: true,
|
|
||||||
entryPoints: ['./src/index.js'],
|
|
||||||
external: ['*.woff', '*.woff2', '*.jpg', '*.svg', '../../assets*'], // Allow external font files which live in ../../static/fonts
|
|
||||||
legalComments: 'external', // Move all legal comments to a .LEGAL.txt file
|
|
||||||
loader: { ".js": "jsx" },
|
|
||||||
minify: production,
|
|
||||||
nodePaths,
|
|
||||||
outdir,
|
|
||||||
target: ['es2020'],
|
|
||||||
plugins: [
|
|
||||||
cleanPlugin(),
|
|
||||||
...lint
|
|
||||||
? [
|
|
||||||
stylelintPlugin({ filter: new RegExp(cwd + '\/src\/.*\.(css?|scss?)$') }),
|
|
||||||
eslintPlugin({ filter: new RegExp(cwd + '\/src\/.*\.(jsx?|js?)$') })
|
|
||||||
]
|
|
||||||
: [],
|
|
||||||
// Esbuild will only copy assets that are explicitly imported and used
|
|
||||||
// in the code. This is a problem for index.html and manifest.json which are not imported
|
|
||||||
copy({
|
|
||||||
assets: [
|
|
||||||
{ from: ['./src/manifest.json'], to: ['./manifest.json'] },
|
|
||||||
{ from: ['./src/index.html'], to: ['./index.html'] },
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
...esbuildStylesPlugins,
|
|
||||||
cockpitPoEsbuildPlugin(),
|
|
||||||
...production ? [cockpitCompressPlugin()] : [],
|
|
||||||
cockpitRsyncEsbuildPlugin({ dest: packageJson.name }),
|
|
||||||
notifyEndPlugin(),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
await context.rebuild();
|
|
||||||
} catch (e) {
|
|
||||||
if (!watchMode)
|
|
||||||
process.exit(1);
|
|
||||||
// ignore errors in watch mode
|
|
||||||
}
|
|
||||||
|
|
||||||
if (watchMode) {
|
|
||||||
const on_change = async path => {
|
|
||||||
console.log("change detected:", path);
|
|
||||||
await context.cancel();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await context.rebuild();
|
|
||||||
} catch (e) {} // ignore in watch mode
|
|
||||||
};
|
|
||||||
|
|
||||||
watch_dirs('src', on_change);
|
|
||||||
|
|
||||||
// wait forever until Control-C
|
|
||||||
await new Promise(() => {});
|
|
||||||
}
|
|
||||||
|
|
||||||
context.dispose();
|
|
39
package.json
39
package.json
|
@ -1,48 +1,31 @@
|
||||||
{
|
{
|
||||||
"name": "tailscale",
|
"name": "tailscale",
|
||||||
"description": "Tailscale application for Cockpit",
|
"description": "Tailscale application for Cockpit",
|
||||||
"type": "module",
|
"main": "dist/index.js",
|
||||||
"main": "index.js",
|
|
||||||
"repository": "git@github.com:spotsnel/tailscale-cockpit.git",
|
"repository": "git@github.com:spotsnel/tailscale-cockpit.git",
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "LGPL-2.1",
|
"license": "LGPL-2.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch": "ESBUILD_WATCH='true' ./build.js",
|
"build": "webpack --mode production",
|
||||||
"build": "./build.js",
|
"watch": ""
|
||||||
"eslint": "eslint --ext .js --ext .jsx src/",
|
|
||||||
"eslint:fix": "eslint --fix --ext .js --ext .jsx src/",
|
|
||||||
"stylelint": "stylelint src/*{.css,scss}",
|
|
||||||
"stylelint:fix": "stylelint --fix src/*{.css,scss}"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.2.12",
|
||||||
|
"@types/react-dom": "^18.2.5",
|
||||||
"argparse": "^2.0.1",
|
"argparse": "^2.0.1",
|
||||||
|
"babel-loader": "^9.1.2",
|
||||||
"chrome-remote-interface": "^0.32.1",
|
"chrome-remote-interface": "^0.32.1",
|
||||||
"esbuild": "^0.17.15",
|
|
||||||
"esbuild-plugin-copy": "^2.1.1",
|
|
||||||
"esbuild-plugin-replace": "^1.3.0",
|
|
||||||
"esbuild-sass-plugin": "^2.8.0",
|
|
||||||
"esbuild-wasm": "^0.17.16",
|
|
||||||
"eslint": "^8.13.0",
|
|
||||||
"eslint-config-standard": "^17.0.0-1",
|
|
||||||
"eslint-config-standard-jsx": "^11.0.0-1",
|
|
||||||
"eslint-config-standard-react": "^13.0.0",
|
|
||||||
"eslint-plugin-flowtype": "^8.0.3",
|
|
||||||
"eslint-plugin-import": "^2.26.0",
|
|
||||||
"eslint-plugin-node": "^11.1.0",
|
|
||||||
"eslint-plugin-promise": "^6.0.0",
|
|
||||||
"eslint-plugin-react": "^7.29.4",
|
|
||||||
"eslint-plugin-react-hooks": "^4.4.0",
|
|
||||||
"eslint-plugin-standard": "^5.0.0",
|
|
||||||
"htmlparser": "^1.7.7",
|
"htmlparser": "^1.7.7",
|
||||||
"jed": "^1.1.1",
|
"jed": "^1.1.1",
|
||||||
"po2json": "^1.0.0-alpha",
|
"po2json": "^1.0.0-alpha",
|
||||||
"qunit": "^2.9.3",
|
"qunit": "^2.9.3",
|
||||||
"sass": "^1.61.0",
|
"sass": "^1.61.0",
|
||||||
"sizzle": "^2.3.3",
|
"sizzle": "^2.3.3",
|
||||||
"stylelint": "^14.9.1",
|
"ts-loader": "^9.4.3",
|
||||||
"stylelint-config-standard": "^25.0.0",
|
"typescript": "^4.8.4",
|
||||||
"stylelint-config-standard-scss": "^5.0.0",
|
"webpack": "^5.87.0",
|
||||||
"stylelint-formatter-pretty": "^3.2.0"
|
"webpack-cli": "^5.1.4",
|
||||||
|
"webpack-dev-server": "^4.15.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@patternfly/patternfly": "5.0.0-alpha.64",
|
"@patternfly/patternfly": "5.0.0-alpha.64",
|
||||||
|
|
31
src/app.jsx
31
src/app.jsx
|
@ -1,31 +0,0 @@
|
||||||
import cockpit from 'cockpit';
|
|
||||||
import React from 'react';
|
|
||||||
import { Alert } from "@patternfly/react-core/dist/esm/components/Alert/index.js";
|
|
||||||
import { Card, CardBody, CardTitle } from "@patternfly/react-core/dist/esm/components/Card/index.js";
|
|
||||||
|
|
||||||
const _ = cockpit.gettext;
|
|
||||||
|
|
||||||
export class Application extends React.Component {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.state = { hostname: _("Unknown") };
|
|
||||||
|
|
||||||
cockpit.file('/etc/hostname').watch(content => {
|
|
||||||
this.setState({ hostname: content.trim() });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Card>
|
|
||||||
<CardTitle>Tailscale</CardTitle>
|
|
||||||
<CardBody>
|
|
||||||
<Alert
|
|
||||||
variant="info"
|
|
||||||
title={ cockpit.format(_("Running on $0"), this.state.hostname) }
|
|
||||||
/>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
//import { Alert } from "@patternfly/react-core/dist/esm/components/Alert/index.js";
|
||||||
|
//import { Card, CardBody, CardTitle } from "@patternfly/react-core/dist/esm/components/Card/index.js";
|
||||||
|
|
||||||
|
type ApplicationProps = {
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApplicationState = {
|
||||||
|
response: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Application extends React.Component<ApplicationProps, ApplicationState> {
|
||||||
|
state: ApplicationState = {
|
||||||
|
response: ""
|
||||||
|
}
|
||||||
|
constructor(props: ApplicationProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
cockpit.spawn(['tailscale', 'status']).done(content => {
|
||||||
|
this.setState(state => ({response: content.trim()}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<pre>
|
||||||
|
{ this.state.response }
|
||||||
|
</pre>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,8 +7,8 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<link rel="stylesheet" href="index.css">
|
<link rel="stylesheet" href="index.css">
|
||||||
|
<script type="text/javascript" src="./base1/cockpit.js"></script>
|
||||||
<script type="text/javascript" src="index.js"></script>
|
<script type="text/javascript" src="bundle.js"></script>
|
||||||
<script type="text/javascript" src="po.js"></script>
|
<script type="text/javascript" src="po.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
11
src/index.js
11
src/index.js
|
@ -1,11 +0,0 @@
|
||||||
import "cockpit-dark-theme";
|
|
||||||
import "patternfly/patternfly-5-cockpit.scss";
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import { Application } from './app.jsx';
|
|
||||||
import './app.scss';
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
|
||||||
ReactDOM.render(React.createElement(Application, {}), document.getElementById('app'));
|
|
||||||
});
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
//import "cockpit-dark-theme";
|
||||||
|
//import "patternfly/patternfly-5-cockpit.scss";
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { Application } from './app';
|
||||||
|
//import './app.scss';
|
||||||
|
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
ReactDOM.render(
|
||||||
|
<Application />,
|
||||||
|
document.getElementById("app"),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||||
|
|
||||||
|
/* Projects */
|
||||||
|
// "incremental": true, /* Enable incremental compilation */
|
||||||
|
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||||
|
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
|
||||||
|
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
|
||||||
|
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||||
|
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||||
|
|
||||||
|
/* Language and Environment */
|
||||||
|
"target": "esnext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||||
|
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||||
|
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||||
|
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||||
|
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||||
|
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
|
||||||
|
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||||
|
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
|
||||||
|
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
|
||||||
|
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||||
|
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||||
|
|
||||||
|
/* Modules */
|
||||||
|
"module": "commonjs", /* Specify what module code is generated. */
|
||||||
|
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||||
|
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||||
|
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||||
|
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||||
|
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||||
|
"typeRoots": ["@types"], /* Specify multiple folders that act like `./node_modules/@types`. */
|
||||||
|
//"types": ["src/types"], /* Specify type package names to be included without being referenced in a source file. */
|
||||||
|
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||||
|
// "resolveJsonModule": true, /* Enable importing .json files */
|
||||||
|
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
|
||||||
|
|
||||||
|
/* JavaScript Support */
|
||||||
|
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
|
||||||
|
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||||
|
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
|
||||||
|
|
||||||
|
/* Emit */
|
||||||
|
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||||
|
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||||
|
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||||
|
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||||
|
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
|
||||||
|
"outDir": "dist", /* Specify an output folder for all emitted files. */
|
||||||
|
// "removeComments": true, /* Disable emitting comments. */
|
||||||
|
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||||
|
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||||
|
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
|
||||||
|
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||||
|
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||||
|
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
|
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||||
|
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||||
|
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||||
|
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||||
|
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
|
||||||
|
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
|
||||||
|
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||||
|
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
|
||||||
|
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||||
|
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||||
|
|
||||||
|
/* Interop Constraints */
|
||||||
|
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||||
|
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||||
|
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
|
||||||
|
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||||
|
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||||
|
|
||||||
|
/* Type Checking */
|
||||||
|
"strict": false, /* Enable all strict type-checking options. */
|
||||||
|
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
|
||||||
|
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
|
||||||
|
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||||
|
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
|
||||||
|
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||||
|
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
|
||||||
|
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
|
||||||
|
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||||
|
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
|
||||||
|
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
|
||||||
|
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||||
|
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||||
|
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||||
|
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||||
|
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||||
|
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
|
||||||
|
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||||
|
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||||
|
|
||||||
|
/* Completeness */
|
||||||
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||||
|
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
|
||||||
|
|
||||||
|
"jsx": "react"
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
devtool: "source-map",
|
||||||
|
entry: './src/index.tsx',
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.(ts|tsx)$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.tsx', '.ts', '.js'],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: 'bundle.js',
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
static: path.join(__dirname, "dist"),
|
||||||
|
compress: true,
|
||||||
|
port: 4000,
|
||||||
|
},
|
||||||
|
};
|
Loading…
Reference in New Issue