├── .cirrus.yml ├── .codesandbox ├── Dockerfile └── tailscaled ├── .eslintignore ├── .eslintrc.json ├── .fmf └── version ├── .github └── workflows │ ├── cockpit-lib-update.yml │ ├── npm-update-pf.yml │ ├── npm-update.yml │ └── release.yml.disabled ├── .gitignore ├── .gitpod.yml ├── .gitpod └── Dockerfile ├── .stylelintrc.json ├── @types └── cockpitjs │ └── index.d.ts ├── LICENSE ├── Makefile ├── README.md ├── docs └── screenshot.png ├── org.cockpit-project.tailscale.metainfo.xml ├── package.json ├── packaging └── cockpit-tailscale.spec.in ├── packit.yaml ├── plans └── all.fmf ├── po └── de.po ├── src ├── app.scss ├── app.tsx ├── index.html ├── index.tsx ├── manifest.json └── types.ts ├── stylePaths.js ├── test ├── browser │ ├── browser.sh │ ├── main.fmf │ └── run-test.sh ├── check-application ├── reference-image ├── run └── vm.install ├── tsconfig.json ├── webpack.config.js └── yarn.lock /.cirrus.yml: -------------------------------------------------------------------------------- 1 | container: 2 | # official cockpit CI container, with cockpit related build and test dependencies 3 | # if you want to use your own, see the documentation about required packages: 4 | # https://github.com/cockpit-project/cockpit/blob/main/HACKING.md#getting-the-development-dependencies 5 | image: quay.io/cockpit/tasks 6 | kvm: true 7 | # increase this if you have many tests that benefit from parallelism 8 | cpu: 1 9 | 10 | test_task: 11 | env: 12 | matrix: 13 | - TEST_OS: fedora-37 14 | - TEST_OS: centos-8-stream 15 | 16 | fix_kvm_script: sudo chmod 666 /dev/kvm 17 | 18 | # test PO template generation 19 | pot_build_script: make po/tailscale.pot 20 | 21 | # chromium has too little /dev/shm, and we can't make that bigger 22 | check_script: TEST_BROWSER=firefox TEST_JOBS=$(nproc) TEST_OS=$TEST_OS make check 23 | -------------------------------------------------------------------------------- /.codesandbox/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=linux/amd64 ghcr.io/gbraad-devenv/fedora/base:38 2 | 3 | USER root 4 | 5 | RUN dnf install -y \ 6 | docker \ 7 | cockpit \ 8 | passwd \ 9 | make \ 10 | npm \ 11 | rpm-build \ 12 | && dnf clean all \ 13 | && rm -rf /var/cache/yum 14 | 15 | RUN npm install -g yarn 16 | 17 | COPY .codesandbox/tailscaled /etc/init.d/taiscaled 18 | 19 | USER gbraad 20 | 21 | RUN git clone https://github.com/gbraad/dotfiles ~/.dotfiles \ 22 | && ~/.dotfiles/install.sh 23 | -------------------------------------------------------------------------------- /.codesandbox/tailscaled: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | test -x /usr/sbin/tailscaled || exit 0 5 | umask 022 6 | 7 | export PATH="${PATH:+$PATH:}/usr/sbin:/sbin" 8 | 9 | case "$1" in 10 | start) 11 | echo "Starting Tailscale VPN" 12 | tailscaled --tun=userspace-networking \ 13 | --socks5-server=localhost:3215 \ 14 | --outbound-http-proxy-listen=localhost:3214 \ 15 | --state=/var/lib/tailscale/tailscaled.state \ 16 | --socket=/run/tailscale/tailscaled.sock \ 17 | --port 41641 \ 18 | 2>/dev/null & 19 | tailscale up --authkey=${TAILSCALE_AUTHKEY} \ 20 | --netfilter-mode=off \ 21 | --ssh 22 | ;; 23 | *) 24 | echo "Usage: /etc/init.d/tailscaled {start}" 25 | exit 1 26 | esac 27 | 28 | exit 0 -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | pkg/lib/* 3 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "browser": true, 5 | "es6": true 6 | }, 7 | "extends": ["eslint:recommended", "standard", "standard-jsx", "standard-react"], 8 | "parserOptions": { 9 | "ecmaVersion": "2022", 10 | "sourceType": "module" 11 | }, 12 | "plugins": ["flowtype", "react", "react-hooks"], 13 | "rules": { 14 | "indent": ["error", 4, 15 | { 16 | "ObjectExpression": "first", 17 | "CallExpression": {"arguments": "first"}, 18 | "MemberExpression": 2, 19 | "ignoredNodes": [ "JSXAttribute" ] 20 | }], 21 | "newline-per-chained-call": ["error", { "ignoreChainWithDepth": 2 }], 22 | "no-var": "error", 23 | "lines-between-class-members": ["error", "always", { "exceptAfterSingleLine": true }], 24 | "prefer-promise-reject-errors": ["error", { "allowEmptyReject": true }], 25 | "react/jsx-indent": ["error", 4], 26 | "semi": ["error", "always", { "omitLastInOneLineBlock": true }], 27 | 28 | "react-hooks/rules-of-hooks": "error", 29 | "react-hooks/exhaustive-deps": "error", 30 | 31 | "camelcase": "off", 32 | "comma-dangle": "off", 33 | "curly": "off", 34 | "jsx-quotes": "off", 35 | "key-spacing": "off", 36 | "no-console": "off", 37 | "quotes": "off", 38 | "react/jsx-curly-spacing": "off", 39 | "react/jsx-indent-props": "off", 40 | "react/prop-types": "off", 41 | "space-before-function-paren": "off", 42 | "standard/no-callback-literal": "off" 43 | }, 44 | "globals": { 45 | "require": false, 46 | "module": false 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /.fmf/version: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /.github/workflows/cockpit-lib-update.yml: -------------------------------------------------------------------------------- 1 | name: cockpit-lib-update 2 | on: 3 | schedule: 4 | - cron: '0 2 * * 4' 5 | # can be run manually on https://github.com/cockpit-project/starter-kit/actions 6 | workflow_dispatch: 7 | jobs: 8 | cockpit-lib-update: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | pull-requests: write 12 | contents: write 13 | steps: 14 | - name: Set up dependencies 15 | run: | 16 | sudo apt update 17 | sudo apt install -y make 18 | 19 | - name: Set up configuration and secrets 20 | run: | 21 | printf '[user]\n\tname = Cockpit Project\n\temail=cockpituous@gmail.com\n' > ~/.gitconfig 22 | echo '${{ secrets.GITHUB_TOKEN }}' > ~/.config/github-token 23 | 24 | - name: Clone repository 25 | uses: actions/checkout@v3 26 | 27 | - name: Run cockpit-lib-update 28 | run: | 29 | make bots 30 | bots/cockpit-lib-update 31 | -------------------------------------------------------------------------------- /.github/workflows/npm-update-pf.yml: -------------------------------------------------------------------------------- 1 | name: npm-update-pf 2 | on: 3 | schedule: 4 | - cron: '0 2 * * 1' 5 | # can be run manually on https://github.com/cockpit-project/starter-kit/actions 6 | workflow_dispatch: 7 | jobs: 8 | npm-update: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | pull-requests: write 12 | contents: write 13 | steps: 14 | - name: Set up dependencies 15 | run: | 16 | sudo apt update 17 | sudo apt install -y npm make 18 | 19 | - name: Set up configuration and secrets 20 | run: | 21 | printf '[user]\n\tname = Cockpit Project\n\temail=cockpituous@gmail.com\n' > ~/.gitconfig 22 | echo '${{ secrets.GITHUB_TOKEN }}' > ~/.config/github-token 23 | 24 | - name: Clone repository 25 | uses: actions/checkout@v3 26 | 27 | - name: Run npm-update bot 28 | run: | 29 | make bots 30 | bots/npm-update @patternfly 31 | -------------------------------------------------------------------------------- /.github/workflows/npm-update.yml: -------------------------------------------------------------------------------- 1 | name: npm-update 2 | on: 3 | schedule: 4 | - cron: '0 2 * * 2,4,6' 5 | # can be run manually on https://github.com/cockpit-project/starter-kit/actions 6 | workflow_dispatch: 7 | jobs: 8 | npm-update: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | pull-requests: write 12 | contents: write 13 | steps: 14 | - name: Set up dependencies 15 | run: | 16 | sudo apt update 17 | sudo apt install -y npm make 18 | 19 | - name: Set up configuration and secrets 20 | run: | 21 | printf '[user]\n\tname = Cockpit Project\n\temail=cockpituous@gmail.com\n' > ~/.gitconfig 22 | echo '${{ secrets.GITHUB_TOKEN }}' > ~/.config/github-token 23 | 24 | - name: Clone repository 25 | uses: actions/checkout@v3 26 | 27 | - name: Run npm-update bot 28 | run: | 29 | make bots 30 | bots/npm-update ~@patternfly 31 | -------------------------------------------------------------------------------- /.github/workflows/release.yml.disabled: -------------------------------------------------------------------------------- 1 | # Create a GitHub upstream release. Replace "TARNAME" with your project tarball 2 | # name and enable this by dropping the ".disabled" suffix from the file name. 3 | # See README.md. 4 | name: release 5 | on: 6 | push: 7 | tags: 8 | # this is a glob, not a regexp 9 | - '[0-9]*' 10 | jobs: 11 | source: 12 | runs-on: ubuntu-latest 13 | container: 14 | image: ghcr.io/cockpit-project/unit-tests 15 | options: --user root 16 | permissions: 17 | # create GitHub release 18 | contents: write 19 | steps: 20 | - name: Clone repository 21 | uses: actions/checkout@v3 22 | with: 23 | fetch-depth: 0 24 | 25 | # https://github.blog/2022-04-12-git-security-vulnerability-announced/ 26 | - name: Pacify git's permission check 27 | run: git config --global --add safe.directory /__w/ 28 | 29 | - name: Workaround for https://github.com/actions/checkout/pull/697 30 | run: git fetch --force origin $(git describe --tags):refs/tags/$(git describe --tags) 31 | 32 | - name: Build release 33 | run: make dist 34 | 35 | - name: Publish GitHub release 36 | uses: cockpit-project/action-release@88d994da62d1451c7073e26748c18413fcdf46e9 37 | with: 38 | filename: "TARNAME-${{ github.ref_name }}.tar.xz" 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.retry 3 | *.tar.xz 4 | *.rpm 5 | node_modules/ 6 | dist/ 7 | /*.spec 8 | /.vagrant 9 | package-lock.json 10 | Test*FAIL* 11 | /bots 12 | test/common/ 13 | test/images/ 14 | pkg 15 | *.pot 16 | POTFILES* 17 | tmp/ 18 | /po/LINGUAS 19 | /tools 20 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: /.gitpod/Dockerfile 3 | 4 | tasks: 5 | - name: dotfiles 6 | command: | 7 | if [ ! -d "~/.dotfiles" ]; then 8 | cd /tmp 9 | curl -sSL https://raw.githubusercontent.com/gbraad/dotfiles/master/install.sh -o /tmp/install.sh && 10 | rm -f ~/.zshrc && 11 | sh /tmp/install.sh 12 | fi 13 | mv ~/.bashrc-nochsh ~/.bashrc 14 | - name: sshd 15 | command: | 16 | sudo ssh-keygen -A && sudo /usr/sbin/sshd 17 | curl https://github.com/gbraad.keys | tee -a ~/.ssh/authorized_keys 18 | - name: tailscale 19 | command: | 20 | sudo --preserve-env=TAILSCALE_AUTHKEY /etc/init.d/tailscaled start 21 | 22 | ports: 23 | - port: 22 24 | onOpen: ignore 25 | - port: 6080 26 | onOpen: open-preview 27 | - port: 9090 28 | onOpen: open-preview 29 | 30 | vscode: 31 | extensions: 32 | - ms-vscode.Theme-TomorrowKit 33 | - tailscale.vscode-tailscale -------------------------------------------------------------------------------- /.gitpod/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=linux/amd64 ghcr.io/gbraad-devenv/fedora/base:38 2 | 3 | USER root 4 | 5 | # Add gitpod user with the expected ID (automated setup does not work atm) 6 | RUN useradd -l -u 33333 -G wheel -md /home/gitpod -s /usr/bin/zsh -p gitpod gitpod 7 | 8 | RUN dnf install -y \ 9 | docker \ 10 | cockpit \ 11 | passwd \ 12 | make \ 13 | npm \ 14 | rpm-build \ 15 | && dnf clean all \ 16 | && rm -rf /var/cache/yum 17 | 18 | USER gitpod 19 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard-scss", 3 | "rules": { 4 | "declaration-colon-newline-after": null, 5 | "selector-list-comma-newline-after": null, 6 | 7 | "at-rule-empty-line-before": null, 8 | "declaration-colon-space-before": null, 9 | "declaration-empty-line-before": null, 10 | "custom-property-empty-line-before": null, 11 | "comment-empty-line-before": null, 12 | "scss/double-slash-comment-empty-line-before": null, 13 | "scss/dollar-variable-colon-space-after": null, 14 | 15 | "custom-property-pattern": null, 16 | "declaration-block-no-duplicate-properties": null, 17 | "declaration-block-no-redundant-longhand-properties": null, 18 | "declaration-block-no-shorthand-property-overrides": null, 19 | "declaration-block-single-line-max-declarations": null, 20 | "font-family-no-duplicate-names": null, 21 | "function-url-quotes": null, 22 | "indentation": null, 23 | "keyframes-name-pattern": null, 24 | "max-line-length": null, 25 | "no-descending-specificity": null, 26 | "no-duplicate-selectors": null, 27 | "scss/at-extend-no-missing-placeholder": null, 28 | "scss/at-import-partial-extension": null, 29 | "scss/at-mixin-pattern": null, 30 | "scss/comment-no-empty": null, 31 | "scss/dollar-variable-pattern": null, 32 | "scss/double-slash-comment-whitespace-inside": null, 33 | "scss/no-global-function-names": null, 34 | "scss/operator-no-unspaced": null, 35 | "selector-class-pattern": null, 36 | "selector-id-pattern": null 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /@types/cockpitjs/index.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace Cockpit { 2 | /********************************** 3 | * Some helpful primitive typedefs 4 | *********************************/ 5 | 6 | type integer = number; //A typedef for an integer. Doesn't actually prevent compilation, but provides an IDE hint 7 | 8 | /********************************** 9 | * Cockpit D-Bus 10 | * http://cockpit-project.org/guide/latest/cockpit-dbus.html 11 | *********************************/ 12 | 13 | type BYTE = number; 14 | type BOOLEAN = boolean; 15 | type INT16 = number; 16 | type UINT16 = number; 17 | type INT32 = number; 18 | type UINT32 = number; 19 | type INT64 = number; 20 | type UINT64 = number; 21 | type DOUBLE = number; 22 | type STRING = string; 23 | type OBJECT_PATH = string; 24 | type SIGNATURE = string; 25 | type ARRAY_BYTE = string[]; 26 | type ARRAY_DICT_ENTRY_STRING = object; 27 | type ARRAY_DICT_ENTRY_OTHER = object; 28 | type ARRAY_OTHER = any[]; 29 | interface VARIANT { 30 | "t": STRING, 31 | "v": any 32 | } 33 | //TODO - Not sure on specifics for handle 34 | type HANDLE = object; 35 | 36 | interface DBusOptions { 37 | "bus" : string 38 | "host" : string 39 | "superuser" : string 40 | "track" : string 41 | } 42 | 43 | interface DBusProxy { 44 | client : string 45 | path : string 46 | iface : string 47 | valid : boolean 48 | data : object 49 | 50 | } 51 | 52 | //Todo unfinished 53 | interface DBusClient { 54 | 55 | } 56 | 57 | /********************************** 58 | * Cockpit File Access 59 | * http://cockpit-project.org/guide/latest/cockpit-file.html 60 | **********************************/ 61 | 62 | interface ParsingFunction { 63 | (data: string) : string 64 | } 65 | 66 | interface StringifyingFunction { 67 | (data: string) : string 68 | } 69 | 70 | interface SyntaxObject { 71 | parse: ParsingFunction 72 | stringify: StringifyingFunction 73 | } 74 | 75 | interface FileAccessOptions { 76 | syntax?: SyntaxObject, 77 | binary?: boolean, 78 | max_read_size?: integer, 79 | superuser?: string, 80 | host?: string 81 | } 82 | 83 | interface FileReadDoneCallback { 84 | (content: string, tag: string) : void 85 | } 86 | 87 | interface FileReadFailCallback { 88 | (error: string) : void 89 | } 90 | 91 | interface FileReadPromise { 92 | done (callback : FileReadDoneCallback) : FileReadPromise 93 | fail (callback : FileReadFailCallback) : FileReadPromise 94 | } 95 | 96 | interface FileReplaceDoneCallback { 97 | (newTag: string) : void 98 | } 99 | 100 | interface FileReplaceFailCallback { 101 | (error: string) : void 102 | } 103 | 104 | interface FileReplacePromise { 105 | done (callback : FileReplaceDoneCallback) : FileReplacePromise 106 | fail (callback : FileReplaceFailCallback) : FileReplacePromise 107 | } 108 | 109 | interface FileModifyDoneCallback { 110 | (newContent : string, newTag: string) : void 111 | } 112 | 113 | interface FileModifyFailCallback { 114 | (error: string) : void 115 | } 116 | 117 | interface FileModifyPromise { 118 | done (callback : FileModifyDoneCallback) : FileModifyPromise 119 | fail (callback : FileModifyFailCallback) : FileModifyPromise 120 | } 121 | 122 | interface FileWatchCallback { 123 | content : string, 124 | tag : string, 125 | error? : any //TODO - what is the error content? 126 | } 127 | 128 | interface File { 129 | read () : FileReadPromise 130 | replace (content : string, expected_tag?: string) : FileReplacePromise 131 | modify (callback : any, initial_content?: string, initial_tag?: string) : FileModifyPromise 132 | watch (callback : FileWatchCallback) : void 133 | close () : void 134 | } 135 | 136 | /********************************** 137 | * Cockpit Processes 138 | * http://cockpit-project.org/guide/latest/cockpit-spawn.html 139 | **********************************/ 140 | 141 | interface ProcessFailureException { 142 | message?: string 143 | problem?: string 144 | exit_status?: integer 145 | exit_signal?: string 146 | } 147 | 148 | enum ProcessProblemCodes { 149 | "access-denied", //"The user is not permitted to perform the action in question." 150 | "authentication-failed", //"User authentication failed." 151 | "internal-error", //"An unexpected internal error without further info. This should not happen during the normal course of operations." 152 | "no-cockpit", //"The system does not have a compatible version of Cockpit installed or installed properly." 153 | "no-session", //"Cockpit is not logged in." 154 | "not-found", //"Something specifically requested was not found, such as a file, executable etc." 155 | "terminated", //"Something was terminated forcibly, such as a connection, process session, etc." 156 | "timeout", //"Something timed out." 157 | "unknown-hostkey", //"The remote host had an unexpected or unknown key." 158 | "no-forwarding" //"Could not forward authentication credentials to the remote host." 159 | } 160 | 161 | interface ProcessPromiseDoneCallback { 162 | (data: string, message?: string) : void 163 | } 164 | 165 | interface ProcessPromiseFailCallback { 166 | (exception: ProcessFailureException, data?: string) : void 167 | } 168 | 169 | interface ProcessPromiseStreamCallback { 170 | (data: string) : void 171 | } 172 | 173 | interface ProcessPromise { 174 | done( callback: ProcessPromiseDoneCallback ) : ProcessPromise, 175 | fail( callback: ProcessPromiseFailCallback ) : ProcessPromise, 176 | stream( callback: ProcessPromiseStreamCallback ) : ProcessPromise, 177 | input( data: string, stream?: boolean ) : ProcessPromise, 178 | close( problem?: ProcessProblemCodes ) : ProcessPromise, 179 | } 180 | 181 | /********************************** 182 | * Cockpit User Session 183 | * http://cockpit-project.org/guide/latest/cockpit-login.html 184 | **********************************/ 185 | 186 | interface UserSessionPermission { 187 | allowed : boolean 188 | onChanged : any //TODO need to see how to do events in TS 189 | close() : void 190 | } 191 | 192 | interface UserSessionObject { 193 | onchanged : any 194 | } 195 | 196 | interface UserSessionDetails { 197 | "id" : string //This is unix user id. 198 | "name" : string //This is the unix user name like "root". 199 | "full_name" : string //This is a readable name for the user. 200 | "groups" : string //This is an array of group names to which the user belongs. 201 | "home" : string //This is user's home directory. 202 | "shell" : string //This is unix user shell. 203 | } 204 | 205 | interface UserSessionPromiseDoneCallback { 206 | (user: UserSessionDetails) : void 207 | } 208 | 209 | interface UserSessionPromiseFailCallback { //Todo - is this defined? 210 | 211 | } 212 | 213 | interface UserSessionPromise { 214 | 215 | } 216 | 217 | /********************************** 218 | * Cockpit Object 219 | * Generally brought into your app in the root HTML file via a 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 |