├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ └── build-packages.yml ├── .gitignore ├── .gitmodules ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── bootstrap.sh ├── docs ├── create-samba-share-example.gif └── refactor.puml ├── file-sharing-old ├── .gitignore ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── public │ ├── assets │ │ └── images │ │ │ ├── 45d-fan-dark.svg │ │ │ └── 45d-fan-light.svg │ └── manifest.json ├── src │ ├── App.vue │ ├── components │ │ ├── CephShareOptions.vue │ │ ├── Config.vue │ │ ├── DropdownSelector.vue │ │ ├── FileModeMatrix.vue │ │ ├── FilePermissions.vue │ │ ├── HoustonHeader.vue │ │ ├── InfoTip.vue │ │ ├── LabelledSwitch.vue │ │ ├── LoadingSpinner.vue │ │ ├── ModalPopup.vue │ │ ├── NfsManager.vue │ │ ├── NfsShare.vue │ │ ├── NfsShareEditor.vue │ │ ├── Notifications.vue │ │ ├── PillList.vue │ │ ├── PillListItem.vue │ │ ├── SambaGlobalManagement.vue │ │ ├── SambaManager.vue │ │ ├── SambaShare.vue │ │ ├── SambaShareEditor.vue │ │ ├── SambaShareManagement.vue │ │ ├── TabbedComponentSwitcher.vue │ │ ├── Table.vue │ │ └── WhatsNew.vue │ ├── composables │ │ └── useUserGroupLists.js │ ├── functions.js │ ├── keys.js │ └── main.js ├── tailwind.config.js ├── vite.config.js └── yarn.lock ├── file-sharing ├── .eslintrc.cjs ├── .gitignore ├── .prettierrc.json ├── .vscode │ └── extensions.json ├── README.md ├── env.d.ts ├── index.html ├── package.json ├── postcss.config.cjs ├── public │ ├── favicon.ico │ └── manifest.json ├── src │ ├── App.vue │ ├── common │ │ ├── ceph-option-manager.ts │ │ ├── hooks.ts │ │ ├── injectionKeys.ts │ │ ├── systemd-manager.ts │ │ ├── ui │ │ │ ├── CephOptions.vue │ │ │ ├── ShareDirectoryInputAndOptions.vue │ │ │ └── UserSettingsView.vue │ │ ├── useMountpointInfo.ts │ │ └── user-settings.ts │ ├── main.ts │ ├── tabs │ │ ├── iSCSI │ │ │ ├── types │ │ │ │ ├── CHAPConfiguration.ts │ │ │ │ ├── ConfigurationManager.ts │ │ │ │ ├── Connection.ts │ │ │ │ ├── Initiator.ts │ │ │ │ ├── InitiatorGroup.ts │ │ │ │ ├── LogicalUnitNumber.ts │ │ │ │ ├── Portal.ts │ │ │ │ ├── Session.ts │ │ │ │ ├── Target.ts │ │ │ │ ├── VirtualDevice.ts │ │ │ │ ├── cluster │ │ │ │ │ ├── LogicalVolume.ts │ │ │ │ │ ├── PCSResource.ts │ │ │ │ │ ├── PCSResourceGroup.ts │ │ │ │ │ ├── PCSResourceManager.ts │ │ │ │ │ ├── PhysicalVolume.ts │ │ │ │ │ ├── Pool.ts │ │ │ │ │ ├── RBDManager.ts │ │ │ │ │ ├── RadosBlockDevice.ts │ │ │ │ │ └── VolumeGroup.ts │ │ │ │ └── drivers │ │ │ │ │ ├── ISCSIDriver.ts │ │ │ │ │ ├── ISCSIDriverClusteredServer.ts │ │ │ │ │ └── ISCSIDriverSingleServer.ts │ │ │ └── ui │ │ │ │ ├── ISCSITabMain.vue │ │ │ │ └── screens │ │ │ │ ├── chapConfigurations │ │ │ │ ├── ChapConfigurationEditor.vue │ │ │ │ ├── ChapConfigurationEntry.vue │ │ │ │ └── ChapConfigurationTable.vue │ │ │ │ ├── config │ │ │ │ └── ConfigurationEditor.vue │ │ │ │ ├── connections │ │ │ │ ├── ConnectionEntry.vue │ │ │ │ └── ConnectionTable.vue │ │ │ │ ├── initiatorGroups │ │ │ │ ├── InitiatorGroupEditor.vue │ │ │ │ ├── InitiatorGroupEntry.vue │ │ │ │ └── InitiatorGroupTable.vue │ │ │ │ ├── initiators │ │ │ │ ├── InitiatorEditor.vue │ │ │ │ ├── InitiatorEntry.vue │ │ │ │ └── InitiatorTable.vue │ │ │ │ ├── logicalUnitNumbers │ │ │ │ ├── LogicalUnitNumberEditor.vue │ │ │ │ ├── LogicalUnitNumberEntry.vue │ │ │ │ └── LogicalUnitNumberTable.vue │ │ │ │ ├── portal │ │ │ │ ├── PortalEditor.vue │ │ │ │ ├── PortalEntry.vue │ │ │ │ └── PortalTable.vue │ │ │ │ ├── radosBlockDeviceManagement │ │ │ │ ├── RBDDeviceCreationScreen.vue │ │ │ │ ├── RBDExpansionScreen.vue │ │ │ │ ├── RBDManagementScreen.vue │ │ │ │ ├── RBDTable.vue │ │ │ │ └── RBDTableEntry.vue │ │ │ │ ├── sessions │ │ │ │ ├── SessionEntry.vue │ │ │ │ └── SessionTable.vue │ │ │ │ ├── target │ │ │ │ ├── TargetEditor.vue │ │ │ │ ├── TargetEntry.vue │ │ │ │ └── TargetTable.vue │ │ │ │ └── virtualDevice │ │ │ │ ├── FileIOCreationPrompt.vue │ │ │ │ ├── VirtualDeviceEditor.vue │ │ │ │ ├── VirtualDeviceEntry.vue │ │ │ │ └── VirtualDeviceTable.vue │ │ ├── nfs │ │ │ ├── data-types.ts │ │ │ ├── exports-parser.test.ts │ │ │ ├── exports-parser.ts │ │ │ ├── nfs-manager.ts │ │ │ └── ui │ │ │ │ ├── NFSExportEditor.vue │ │ │ │ ├── NFSExportListView.vue │ │ │ │ ├── NFSTabMain.vue │ │ │ │ └── index.ts │ │ └── samba │ │ │ ├── samba-manager.ts │ │ │ └── ui │ │ │ ├── BooleanKeyValueSuite.ts │ │ │ ├── GlobalConfigEditor.vue │ │ │ ├── SambaTabMain.vue │ │ │ ├── ShareEditor.vue │ │ │ ├── ShareListView.vue │ │ │ └── index.ts │ └── version.js ├── tailwind.config.cjs ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.node.json ├── tsconfig.vitest.json ├── vite.config.ts ├── vite.config.ts.timestamp-1740513605019-f275d5647a579.mjs └── vitest.config.ts ├── img ├── nfs │ ├── nfs.gif │ └── nfs_interface.png └── samba │ ├── samba_add.png │ ├── samba_advanced.png │ ├── samba_global.png │ ├── samba_group.gif │ ├── samba_interface.png │ ├── samba_password.png │ ├── samba_privilege.gif │ ├── samba_remove.gif │ └── samba_user.gif ├── json ├── manifest.json ├── os ├── package.json ├── packaging ├── debian-bookworm │ ├── changelog │ ├── control.j2 │ ├── copyright.j2 │ ├── rules │ └── source │ │ └── format ├── rocky-el8 │ └── main.spec.j2 ├── rocky-el9 │ └── main.spec.j2 ├── ubuntu-focal │ ├── changelog │ ├── control.j2 │ ├── copyright.j2 │ ├── rules │ └── source │ │ └── format └── ubuntu-jammy │ ├── changelog │ ├── control.j2 │ ├── copyright.j2 │ ├── rules │ └── source │ └── format ├── re ├── system_dependencies.txt ├── time ├── vetur.config.js └── yarn.lock /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Bug Info 11 | 12 | **Describe the bug** 13 | A clear and concise description of what the bug is. 14 | 15 | **To Reproduce** 16 | Steps to reproduce the behavior: 17 | 1. Go to '...' 18 | 2. Click on '....' 19 | 3. Scroll down to '....' 20 | 4. See error 21 | 22 | **Expected behavior** 23 | A clear and concise description of what you expected to happen. 24 | 25 | **Screenshots** 26 | If applicable, add screenshots to help explain your problem. 27 | 28 | **Console Output** 29 | - Firefox: 30 | - Go to the menu dropdown > Web Developer > Web Developer Tools > Console 31 | - Expand relevent error messages 32 | - Copy and paste console log 33 | - Chrome 34 | - Press Ctrl+Shift+J 35 | - Expand relevent error messages 36 | - Copy and paste console log 37 | - Safari 38 | - [Open console](https://balsamiq.com/support/faqs/browserconsole/#apple-safari) 39 | - Expand relevent error messages 40 | - Copy and paste console log 41 | - Internet Explorer 42 | - Good luck 43 | 44 | ## Client Side 45 | 46 | **Desktop (please complete the following information):** 47 | - OS: [e.g. iOS] 48 | - Browser [e.g. chrome, safari] 49 | - Version [e.g. 22] 50 | 51 | **Smartphone (please complete the following information):** 52 | - Device: [e.g. iPhone6] 53 | - OS: [e.g. iOS8.1] 54 | - Browser [e.g. stock browser, safari] 55 | - Version [e.g. 22] 56 | 57 | ## Server Side 58 | - OS: [e.g. Ubuntu 20.04, Rocky 8] 59 | - Cockpit Version: [e.g. 235] 60 | 61 | **Additional context** 62 | Add any other context about the problem here. 63 | -------------------------------------------------------------------------------- /.github/workflows/build-packages.yml: -------------------------------------------------------------------------------- 1 | name: Build Packages 2 | on: 3 | push: 4 | branches: 5 | - build 6 | tags: 7 | - 'v*.*.*' 8 | jobs: 9 | prebuild: 10 | runs-on: buildinator-5 11 | outputs: 12 | manifest: ${{ steps.prebuild.outputs.manifest }} 13 | matrix: ${{ steps.prebuild.outputs.matrix }} 14 | uuid: ${{ steps.prebuild.outputs.uuid }} 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v3 18 | 19 | - id: prebuild 20 | name: Prebuild flow 21 | uses: 45drives/actions/prebuild-flow@main 22 | with: 23 | directory: ${{ github.workspace }} 24 | 25 | build: 26 | needs: prebuild 27 | runs-on: buildinator-group 28 | strategy: 29 | matrix: 30 | build: ${{ fromJSON(needs.prebuild.outputs.matrix).include }} 31 | steps: 32 | - name: Build package 33 | uses: 45drives/actions/build-package@main 34 | with: 35 | manifest: ${{ needs.prebuild.outputs.manifest }} 36 | build: ${{ toJSON(matrix.build) }} 37 | uuid: ${{ needs.prebuild.outputs.uuid }} 38 | 39 | sign: 40 | needs: 41 | - prebuild 42 | - build 43 | runs-on: buildinator-5 44 | steps: 45 | - name: Run sign playbook 46 | uses: 45drives/actions/ansible-playbook@main 47 | with: 48 | playbook: /root/git/auto-packaging/actions/ansible/sign.yml 49 | directory: ${{ github.workspace }}/packaging 50 | group_vars_directory: ${{ github.workspace }}/packaging/group_vars/ 51 | inventory: | 52 | [ci] 53 | localhost 54 | become: true 55 | host_key_checking: true 56 | extra_vars: sign_key_name=prod 57 | 58 | update_repositories: 59 | needs: 60 | - prebuild 61 | - build 62 | - sign 63 | runs-on: buildinator-5 64 | steps: 65 | - id: push_local 66 | name: Push packages into local repository 67 | uses: 45drives/actions/update-repo@main 68 | with: 69 | directory: ${{ github.workspace }} 70 | 71 | create_release: 72 | needs: 73 | - prebuild 74 | - build 75 | - sign 76 | - update_repositories 77 | runs-on: buildinator-5 78 | steps: 79 | - name: Set Variables 80 | if: startsWith(github.ref, 'refs/tags/') 81 | run: | 82 | echo "PRERELEASE=$([ "$(cat ${{github.workspace}}/manifest.json | jq --raw-output '.stable')" = "true" ] && echo false || echo "true")" >> $GITHUB_ENV 83 | echo "TITLE=$(cat ${{github.workspace}}/manifest.json | jq --raw-output '.title')" >> $GITHUB_ENV 84 | echo "VERSION=$(cat ${{github.workspace}}/manifest.json | jq --raw-output '.version')" >> $GITHUB_ENV 85 | echo "REVISION=$(cat ${{github.workspace}}/manifest.json | jq --raw-output '.build_number')" >> $GITHUB_ENV 86 | - name: GitHub Release 87 | if: startsWith(github.ref, 'refs/tags/') 88 | uses: softprops/action-gh-release@v1 89 | with: 90 | name: ${{env.TITLE}} ${{env.VERSION}} 91 | prerelease: ${{env.PRERELEASE}} 92 | body_path: ${{github.workspace}}/CHANGELOG.md 93 | env: 94 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 95 | 96 | sync_repositories: 97 | needs: 98 | - prebuild 99 | - build 100 | - sign 101 | - update_repositories 102 | - create_release 103 | runs-on: buildinator-5 104 | steps: 105 | - id: push_remote 106 | name: Sync local repository with remote repository 107 | if: startsWith(github.ref, 'refs/tags/') 108 | uses: 45drives/actions/sync-repo@main 109 | with: 110 | directory: ${{ github.workspace }} 111 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | TODO 2 | node_modules/ 3 | .yarn/ 4 | .yarnrc.yml 5 | *_generic.tar.gz 6 | *_generic.zip 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "houston-common"] 2 | path = houston-common 3 | url = ../houston-common.git 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "tailwindCSS.classAttributes": [ 3 | "class", 4 | "className", 5 | "ngClass", 6 | "enter-active-class", 7 | "enter-from-class", 8 | "enter-to-class", 9 | "leave-active-class", 10 | "leave-from-class", 11 | "leave-to-class" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Cockpit File Sharing 4.2.12-1 2 | 3 | * ISCSI - Shows all virtual devices and whether in use or not -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # bootstrap.sh 3 | 4 | set -e 5 | set -o pipefail 6 | set -x 7 | 8 | command -v sponge >/dev/null || { echo "Missing 'sponge'. Please install moreutils." >&2 ; exit 1 ; } 9 | command -v yarn >/dev/null || { echo "Missing 'yarn'. Please install yarn." >&2 ; exit 1 ; } 10 | 11 | jq 'del(.packageManager)' ./package.json | sponge ./package.json 12 | 13 | rm .yarnrc.yml .yarn -rf 14 | 15 | yarn set version stable 16 | 17 | yarn config set nodeLinker node-modules 18 | -------------------------------------------------------------------------------- /docs/create-samba-share-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/docs/create-samba-share-example.gif -------------------------------------------------------------------------------- /file-sharing-old/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | # Generated files 27 | src/version.js 28 | -------------------------------------------------------------------------------- /file-sharing-old/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | File Sharing 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /file-sharing-old/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "file-sharing", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "vue": "^3.2.37" 12 | }, 13 | "devDependencies": { 14 | "@45drives/cockpit-css": "^0.1.12", 15 | "@45drives/cockpit-helpers": "^0.1.19", 16 | "@45drives/cockpit-syntaxes": "^0.1.6", 17 | "@45drives/regex": "^0.1.0", 18 | "@fontsource/red-hat-text": "^4.5.9", 19 | "@headlessui/vue": "^1.6.5", 20 | "@heroicons/vue": "^1.0.6", 21 | "@tailwindcss/forms": "^0.5.2", 22 | "@vitejs/plugin-vue": "^2.3.3", 23 | "autoprefixer": "^10.4.7", 24 | "postcss": "^8.4.14", 25 | "source-sans-pro": "^3.6.0", 26 | "tailwindcss": "^3.1.4", 27 | "vite": "^2.9.13" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /file-sharing-old/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'tailwindcss/nesting': {}, 4 | tailwindcss: {}, 5 | autoprefixer: {}, 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /file-sharing-old/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2.0, 3 | 4 | "menu": { 5 | "file-sharing": { 6 | "label": "File Sharing", 7 | "path": "index.html", 8 | "order": 116, 9 | "docs": [ 10 | { 11 | "label": "Managing SMB/CIFS", 12 | "url": "http://knowledgebase.45drives.com/kb/kb045281-managing-smb-cifs-in-houston-ui/" 13 | }, 14 | { 15 | "label": "Managing NFS", 16 | "url": "http://knowledgebase.45drives.com/kb/kb045282-managing-nfs-in-houston-ui/" 17 | } 18 | ] 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /file-sharing-old/src/App.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 58 | 59 | 83 | -------------------------------------------------------------------------------- /file-sharing-old/src/components/DropdownSelector.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 28 | 29 | 49 | -------------------------------------------------------------------------------- /file-sharing-old/src/components/FileModeMatrix.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 44 | 45 | 99 | -------------------------------------------------------------------------------- /file-sharing-old/src/components/FilePermissions.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 56 | 57 | 145 | -------------------------------------------------------------------------------- /file-sharing-old/src/components/InfoTip.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 26 | 27 | 44 | -------------------------------------------------------------------------------- /file-sharing-old/src/components/LabelledSwitch.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 40 | 41 | 62 | -------------------------------------------------------------------------------- /file-sharing-old/src/components/LoadingSpinner.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /file-sharing-old/src/components/ModalPopup.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 58 | 59 | 95 | -------------------------------------------------------------------------------- /file-sharing-old/src/components/NfsShare.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 70 | 71 | 139 | -------------------------------------------------------------------------------- /file-sharing-old/src/components/PillList.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | 24 | 33 | 34 | 36 | -------------------------------------------------------------------------------- /file-sharing-old/src/components/PillListItem.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 29 | 30 | 41 | 42 | 44 | -------------------------------------------------------------------------------- /file-sharing-old/src/components/SambaShare.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 69 | 70 | 140 | -------------------------------------------------------------------------------- /file-sharing-old/src/components/TabbedComponentSwitcher.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 43 | 44 | 81 | -------------------------------------------------------------------------------- /file-sharing-old/src/components/Table.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 51 | 52 | 90 | 91 | 116 | -------------------------------------------------------------------------------- /file-sharing-old/src/components/WhatsNew.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 37 | -------------------------------------------------------------------------------- /file-sharing-old/src/composables/useUserGroupLists.js: -------------------------------------------------------------------------------- 1 | import { ref, onBeforeMount, onUnmounted, watch } from 'vue'; 2 | import { useConfig } from '../components/Config.vue'; 3 | import { useSpawn, errorString } from '@45drives/cockpit-helpers'; 4 | 5 | const spawnOpts = { 6 | superuser: 'try', 7 | }; 8 | 9 | /** 10 | * Get bash script for json ouput of users or groups 11 | * 12 | * @param {"passwd" | "group"} db - getent database to query 13 | * @param {boolean} isDomain - if true, pull from domain users, else local users 14 | * @param {boolean} includeSystemAccounts - keep users/groups with uid/gid between 0 and 1000 15 | * @returns {string} bash script 16 | */ 17 | const getentScript = (db, isDomain, includeSystemAccounts) => { 18 | const [nameKey, idKey, wbinfoFlag] = { 19 | 'passwd': ['user', 'uid', '-u'], 20 | 'group': ['group', 'gid', '-g'] 21 | }[db]; 22 | const getent = isDomain ? `wbinfo ${wbinfoFlag} | xargs -d'\n' -r getent` : `getent -s files`; 23 | const printFilter = includeSystemAccounts ? '' : '$3 >= 1000 || $3 == 0'; 24 | return `set -o pipefail; ${getent} ${db} | awk -F: ' 25 | BEGIN { printf "[" } 26 | ${printFilter} { 27 | printf sep; 28 | printf "{"; 29 | printf "\\"${nameKey}\\":\\"%s\\",",$1; 30 | printf "\\"${idKey}\\":%d,",$3; 31 | printf "\\"domain\\":${isDomain.toString()},"; 32 | printf "\\"pretty\\":\\"%s${isDomain ? ' (domain)' : ''}\\"",$1; 33 | printf "}"; 34 | sep = ","; 35 | } 36 | END { printf "]" } 37 | '`; 38 | }; 39 | 40 | const getent = (db, includeSystemAccounts) => { 41 | return Promise.all([ 42 | useSpawn(['bash', '-c', getentScript(db, false, includeSystemAccounts)], spawnOpts).promise() 43 | .then(({ stdout }) => { 44 | return JSON.parse(stdout); 45 | }), 46 | // useSpawn(['bash', '-c', getentScript(db, true, includeSystemAccounts)], spawnOpts).promise() 47 | // .catch(state => { 48 | // console.warn(`Error getting users/groups from AD/domain:`, errorString(state)); 49 | // return state; 50 | // }) 51 | // .then(({ stdout }) => JSON.parse(stdout)), 52 | ]).then(nested => nested.flat()); 53 | }; 54 | 55 | /** 56 | * @typedef User 57 | * @property {string} name - system name of user 58 | * @property {number} uid - user id 59 | * @property {boolean} domain - if true, user is from AD 60 | * @property {string} pretty - 61 | */ 62 | 63 | export default function useUserGroupLists() { 64 | const users = ref([]); 65 | const groups = ref([]); 66 | const processingUsersList = ref(false); 67 | const processingGroupsList = ref(false); 68 | const config = useConfig(); 69 | 70 | const cockpitWatchHandles = []; 71 | 72 | async function aquireUserList() { 73 | processingUsersList.value = true; 74 | try { 75 | users.value = await getent('passwd', config.includeSystemAccounts ?? false); 76 | } catch (state) { 77 | console.error('Failed to get user list:', errorString(state)); 78 | } finally { 79 | processingUsersList.value = false; 80 | } 81 | } 82 | 83 | async function aquireGroupList() { 84 | processingGroupsList.value = true; 85 | try { 86 | groups.value = await getent('group', config.includeSystemAccounts ?? false); 87 | } catch (state) { 88 | console.error('Failed to get group list:', errorString(state)); 89 | } finally { 90 | processingGroupsList.value = false; 91 | } 92 | } 93 | 94 | onBeforeMount(() => { 95 | // cockpit.file.watch callbacks are always called once when initialized 96 | cockpitWatchHandles.push( 97 | cockpit.file('/etc/passwd', { superuser: 'try' }).watch(() => aquireUserList(), { read: false }), 98 | cockpit.file('/etc/group', { superuser: 'try' }).watch(() => aquireGroupList(), { read: false }) 99 | ); 100 | watch(() => config.includeSystemAccounts, () => { 101 | aquireUserList(); 102 | aquireGroupList(); 103 | }); 104 | }); 105 | 106 | onUnmounted(() => { 107 | cockpitWatchHandles.forEach(handle => handle?.remove?.()); 108 | }); 109 | 110 | return { 111 | users, 112 | groups, 113 | processingUsersList, 114 | processingGroupsList, 115 | }; 116 | } 117 | -------------------------------------------------------------------------------- /file-sharing-old/src/keys.js: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Josh Boudreau 2 | * 3 | * This file is part of Cockpit File Sharing. 4 | * 5 | * Cockpit File Sharing is free software: you can redistribute it and/or modify it under the terms 6 | * of the GNU General Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Cockpit File Sharing is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 10 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with Cockpit File Sharing. 14 | * If not, see . 15 | */ 16 | 17 | /** 18 | * Notifications component instance to add notifs 19 | */ 20 | export const notificationsInjectionKey = Symbol(); 21 | 22 | export const usersInjectionKey = Symbol(); 23 | export const groupsInjectionKey = Symbol(); 24 | export const processingUsersListInjectionKey = Symbol(); 25 | export const processingGroupsListInjectionKey = Symbol(); 26 | -------------------------------------------------------------------------------- /file-sharing-old/src/main.js: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Josh Boudreau 2 | * 3 | * This file is part of Cockpit File Sharing. 4 | * 5 | * Cockpit File Sharing is free software: you can redistribute it and/or modify it under the terms 6 | * of the GNU General Public License as published by the Free Software Foundation, either version 3 7 | * of the License, or (at your option) any later version. 8 | * 9 | * Cockpit File Sharing is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 10 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with Cockpit File Sharing. 14 | * If not, see . 15 | */ 16 | 17 | import { createApp, reactive } from 'vue'; 18 | import App from './App.vue'; 19 | import { FIFO } from '@45drives/cockpit-helpers'; 20 | import '@45drives/cockpit-css/src/index.css'; 21 | 22 | const notificationFIFO = reactive(new FIFO()); 23 | 24 | const errorHandler = (error) => { 25 | console.error(error); 26 | const notificationObj = { 27 | title: "System Error", 28 | body: "", 29 | show: true, 30 | timeout: 10000, 31 | actions: [], 32 | level: "error", 33 | } 34 | if (error instanceof Error && error?.message) { 35 | notificationObj.body = error.message; 36 | } else if (typeof error === "string") { 37 | notificationObj.body = error; 38 | } else if (error?.stderr) { 39 | notificationObj.body = error.stderr; 40 | } else { 41 | notificationObj.body = "An error occured, check the system console (CTRL+SHIFT+J) for more information."; 42 | } 43 | if (notificationFIFO.getLen() < 10) 44 | notificationFIFO.push(notificationObj); 45 | else 46 | throw error; 47 | } 48 | 49 | const app = createApp(App, { notificationFIFO }) 50 | 51 | app.config.errorHandler = (error) => errorHandler(error); 52 | 53 | window.onerror = (...args) => errorHandler(args[4] ?? args[0]); 54 | 55 | app.mount('#app'); 56 | -------------------------------------------------------------------------------- /file-sharing-old/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./index.html", 4 | "./src/**/*.{vue,js,ts,jsx,tsx}", 5 | ], 6 | theme: { 7 | extend: { 8 | fontFamily: { 9 | redhat: ['Red Hat Text', 'open-sans', 'sans-serif'], 10 | "source-sans-pro": ["Source Sans Pro", 'open-sans', 'sans-serif'], 11 | }, 12 | colors: { 13 | neutral: { 14 | 850: "#222222", 15 | } 16 | } 17 | }, 18 | }, 19 | plugins: [ 20 | require('@tailwindcss/forms'), 21 | ], 22 | darkMode: "class", 23 | } 24 | -------------------------------------------------------------------------------- /file-sharing-old/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [vue()], 7 | base: "./", 8 | build: { 9 | target: [ 10 | "chrome87", "edge88", "firefox78", "safari14" 11 | ], 12 | }, 13 | }) 14 | -------------------------------------------------------------------------------- /file-sharing/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | require('@rushstack/eslint-patch/modern-module-resolution') 3 | 4 | module.exports = { 5 | root: true, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/eslint-config-typescript', 10 | '@vue/eslint-config-prettier/skip-formatting' 11 | ], 12 | parserOptions: { 13 | ecmaVersion: 'latest' 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /file-sharing/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | 30 | *.tsbuildinfo 31 | -------------------------------------------------------------------------------- /file-sharing/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/prettierrc", 3 | "semi": true, 4 | "tabWidth": 2, 5 | "singleQuote": false, 6 | "printWidth": 100, 7 | "trailingComma": "es5" 8 | } 9 | -------------------------------------------------------------------------------- /file-sharing/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "Vue.volar", 4 | "dbaeumer.vscode-eslint", 5 | "esbenp.prettier-vscode" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /file-sharing/README.md: -------------------------------------------------------------------------------- 1 | # file-sharing 2 | 3 | This template should help get you started developing with Vue 3 in Vite. 4 | 5 | ## Recommended IDE Setup 6 | 7 | [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). 8 | 9 | ## Type Support for `.vue` Imports in TS 10 | 11 | TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types. 12 | 13 | ## Customize configuration 14 | 15 | See [Vite Configuration Reference](https://vitejs.dev/config/). 16 | 17 | ## Project Setup 18 | 19 | ```sh 20 | yarn 21 | ``` 22 | 23 | ### Compile and Hot-Reload for Development 24 | 25 | ```sh 26 | yarn dev 27 | ``` 28 | 29 | ### Type-Check, Compile and Minify for Production 30 | 31 | ```sh 32 | yarn build 33 | ``` 34 | 35 | ### Run Unit Tests with [Vitest](https://vitest.dev/) 36 | 37 | ```sh 38 | yarn test:unit 39 | ``` 40 | 41 | ### Lint with [ESLint](https://eslint.org/) 42 | 43 | ```sh 44 | yarn lint 45 | ``` 46 | -------------------------------------------------------------------------------- /file-sharing/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | declare const __APP_VERSION__: string; 3 | -------------------------------------------------------------------------------- /file-sharing/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | File Sharing 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /file-sharing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "file-sharing", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build:app": "run-p type-check \"build-only {@}\" --", 9 | "build": "run-p build:app", 10 | "preview": "vite preview", 11 | "test:app": "vitest run", 12 | "test": "vitest run", 13 | "build-only": "vite build", 14 | "type-check": "vue-tsc --skipLibCheck", 15 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", 16 | "format": "prettier --write src/" 17 | }, 18 | "dependencies": { 19 | "@45drives/houston-common-css": "workspace:^", 20 | "@45drives/houston-common-lib": "workspace:^", 21 | "@45drives/houston-common-ui": "workspace:^", 22 | "@headlessui/vue": "^1.7.22", 23 | "@heroicons/vue": "^2.0.18", 24 | "monet": "^0.9.3", 25 | "neverthrow": "^6.2.1", 26 | "vue": "^3.5", 27 | "vue-router": "^4.3.2", 28 | "zod": "^3.23.8" 29 | }, 30 | "devDependencies": { 31 | "@fontsource/red-hat-text": "^5.0.18", 32 | "@rushstack/eslint-patch": "^1.10.2", 33 | "@tailwindcss/forms": "^0.5.7", 34 | "@tsconfig/node20": "^20.1.4", 35 | "@types/deep-equal": "^1.0.4", 36 | "@types/jsdom": "^21.1.6", 37 | "@types/node": "^20.12.12", 38 | "@vitejs/plugin-vue": "^5.2.1", 39 | "@vue/eslint-config-prettier": "^9.0.0", 40 | "@vue/eslint-config-typescript": "^14.3.0", 41 | "@vue/test-utils": "^2.4.6", 42 | "@vue/tsconfig": "^0.5.1", 43 | "autoprefixer": "^10.4.19", 44 | "deep-equal": "^2.2.3", 45 | "eslint": "^9.19.0", 46 | "eslint-plugin-vue": "^9.32.0", 47 | "jsdom": "^24.0.0", 48 | "npm-run-all2": "^6.1.2", 49 | "postcss": "^8.4.38", 50 | "postcss-modules": "^6.0.0", 51 | "tailwindcss": "^3.4.3", 52 | "ts-node": "^10.9.2", 53 | "typescript": "~5.4.5", 54 | "vite": "^6.2.0", 55 | "vite-plugin-vue-devtools": "^7.2.0", 56 | "vitest": "^1.6.0", 57 | "vue-tsc": "^2.0.19" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /file-sharing/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "plugins": { 3 | "tailwindcss/nesting": {}, 4 | "tailwindcss": {}, 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /file-sharing/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/file-sharing/public/favicon.ico -------------------------------------------------------------------------------- /file-sharing/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2.0, 3 | 4 | "menu": { 5 | "file-sharing": { 6 | "label": "File Sharing", 7 | "path": "index.html", 8 | "order": 116, 9 | "docs": [ 10 | { 11 | "label": "Managing SMB/CIFS", 12 | "url": "http://knowledgebase.45drives.com/kb/kb045281-managing-smb-cifs-in-houston-ui/" 13 | }, 14 | { 15 | "label": "Managing NFS", 16 | "url": "http://knowledgebase.45drives.com/kb/kb045282-managing-nfs-in-houston-ui/" 17 | } 18 | ] 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /file-sharing/src/common/hooks.ts: -------------------------------------------------------------------------------- 1 | import { onMounted, onUnmounted } from "vue"; 2 | import { ResultAsync, ok, err } from "neverthrow"; 3 | import { type NFSExport } from "@/tabs/nfs/data-types"; 4 | import { type Server, type SambaShareConfig } from "@45drives/houston-common-lib"; 5 | 6 | type Common = { 7 | [P in keyof A & keyof B]: A[P] | B[P]; 8 | }; 9 | 10 | export type Share = Common; 11 | 12 | export type HookCallback = (server: Server, share: Share) => ResultAsync; 13 | 14 | export const Hooks = { 15 | BeforeAddShare: Symbol("BeforeAddShare"), 16 | AfterAddShare: Symbol("AfterAddShare"), 17 | BeforeEditShare: Symbol("BeforeEditShare"), 18 | AfterEditShare: Symbol("AfterEditShare"), 19 | BeforeRemoveShare: Symbol("BeforeRemoveShare"), 20 | AfterRemoveShare: Symbol("AfterRemoveShare"), 21 | }; 22 | 23 | export type Hook = (typeof Hooks)[keyof typeof Hooks]; 24 | 25 | const hookCallbacks = new Map>( 26 | Object.entries(Hooks).map(([_, hook]) => [hook, new Set()]) 27 | ); 28 | 29 | const getHookCallbacks = (hook: Hook) => { 30 | const callbacks = hookCallbacks.get(hook); 31 | return callbacks ? ok(callbacks) : err(new Error(`Hooks not in Map for ${String(hook)}`)); 32 | }; 33 | 34 | function runInSequence( 35 | ...args: Args 36 | ): (fns: ReadonlyArray<(...args: Args) => ResultAsync>) => ResultAsync, E> { 37 | return (fns) => { 38 | const run = async () => { 39 | const okValues: Array = []; 40 | 41 | for (const fn of fns) { 42 | const result = await fn(...args); 43 | if (result.isErr()) { 44 | return err(result.error); 45 | } 46 | okValues.push(result.value); 47 | } 48 | 49 | return ok(okValues); 50 | }; 51 | 52 | return new ResultAsync(run()); 53 | }; 54 | } 55 | 56 | export const executeHookCallbacks = (hook: Hook, server: Server, share: Share) => 57 | getHookCallbacks(hook) 58 | .map((callbackSet) => [...callbackSet.values()]) 59 | .asyncAndThen(runInSequence(server, share)); 60 | // getHookCallbacks(hook).asyncAndThen((hooks) => 61 | // ResultAsync.combine( 62 | // [...hooks.values()] 63 | // .map((callback) => callback(server, share)) 64 | // .filter((result): result is ResultAsync => result instanceof ResultAsync) 65 | // ) 66 | // ); 67 | 68 | export const registerHookCallback = (hook: Hook, callback: HookCallback) => 69 | getHookCallbacks(hook).map((callbacks) => callbacks.add(callback)); 70 | export const unregisterHookCallback = (hook: Hook, callback: HookCallback) => 71 | getHookCallbacks(hook).map((callbacks) => callbacks.delete(callback)); 72 | 73 | export const useHookCallback = (hook: Hook | Hook[], callback: HookCallback) => { 74 | const hooks = [hook].flat(); 75 | for (const hook of hooks) { 76 | onMounted(() => registerHookCallback(hook, callback)); 77 | onUnmounted(() => unregisterHookCallback(hook, callback)); 78 | } 79 | }; 80 | -------------------------------------------------------------------------------- /file-sharing/src/common/injectionKeys.ts: -------------------------------------------------------------------------------- 1 | import { type InjectionKey, type Ref } from "vue"; 2 | import { getServerCluster } from "@45drives/houston-common-lib"; 3 | 4 | export const serverClusterInjectionKey = Symbol() as InjectionKey< 5 | ReturnType 6 | >; 7 | export const cephClientNameInjectionKey = Symbol() as InjectionKey>; 8 | -------------------------------------------------------------------------------- /file-sharing/src/common/useMountpointInfo.ts: -------------------------------------------------------------------------------- 1 | import { ref, watchEffect, type Ref } from "vue"; 2 | 3 | import { 4 | Server, 5 | type CommandOptions, 6 | FileSystemNode, 7 | type FilesystemMount, 8 | } from "@45drives/houston-common-lib"; 9 | import { ResultAsync, okAsync } from "neverthrow"; 10 | 11 | export const useMountpointInfo = ( 12 | path: Ref, 13 | server: Server | ResultAsync, 14 | commandOptions: CommandOptions = { superuser: "try" } 15 | ) => { 16 | if (server instanceof Server) { 17 | server = okAsync(server); 18 | } 19 | 20 | const fsNode = (path: string) => server.map((server) => new FileSystemNode(server, path)); 21 | 22 | const mountpointInfo = ref(); 23 | const updateMountpointInfo = () => { 24 | fsNode(path.value) 25 | .andThen((node) => node.findLongestExistingStem(commandOptions)) 26 | .andThen((node) => node.getFilesystemMount(commandOptions)) 27 | .map((mi) => (mountpointInfo.value = mi)); 28 | }; 29 | watchEffect(updateMountpointInfo); 30 | 31 | return { 32 | mountpointInfo, 33 | updateMountpointInfo, 34 | }; 35 | }; 36 | -------------------------------------------------------------------------------- /file-sharing/src/common/user-settings.ts: -------------------------------------------------------------------------------- 1 | import { computed, ref, type Ref, type WritableComputedRef } from "vue"; 2 | 3 | export type TabVisibility = "auto" | "always" | "never"; 4 | 5 | export type UserSettings = { 6 | /** 7 | * Samba-specific settings 8 | */ 9 | samba: { 10 | /** 11 | * Path to smb.conf 12 | */ 13 | confPath: string; 14 | tabVisibility: TabVisibility; 15 | }; 16 | /** 17 | * NFS-specific settings 18 | */ 19 | nfs: { 20 | /** 21 | * Path to cockpit-file-sharing.exports or equivalent 22 | */ 23 | confPath: string; 24 | tabVisibility: TabVisibility; 25 | }; 26 | /** 27 | * iSCSI-specific settings 28 | */ 29 | iscsi: { 30 | /** 31 | * Path to iSCSI configuration path. 32 | */ 33 | confPath: string; 34 | clusteredServerChecked: boolean; 35 | clusteredServer: boolean; 36 | subnetMask: number; 37 | tabVisibility: TabVisibility; 38 | }; 39 | /** 40 | * Include users and groups with uid and gid from 1 to 999 41 | */ 42 | includeSystemAccounts: boolean; 43 | }; 44 | 45 | const defaultSettings = (): UserSettings => ({ 46 | samba: { 47 | confPath: "/etc/samba/smb.conf", 48 | tabVisibility: "auto", 49 | }, 50 | nfs: { 51 | confPath: "/etc/exports.d/cockpit-file-sharing.exports", 52 | tabVisibility: "auto", 53 | }, 54 | iscsi: { 55 | // confPath: "/etc/scst/cockpit-iscsi.conf", 56 | confPath: "/etc/scst.conf", 57 | clusteredServerChecked: false, 58 | clusteredServer: false, 59 | subnetMask: 16, 60 | tabVisibility: "auto", 61 | }, 62 | includeSystemAccounts: false, 63 | }); 64 | 65 | const configPath = "/etc/cockpit-file-sharing.conf.json"; 66 | 67 | const configFile = cockpit.file(configPath, { 68 | superuser: "try", 69 | syntax: JSON, 70 | }); 71 | 72 | const config = ref(defaultSettings()); 73 | 74 | const configFileReadPromise = new Promise>((resolve) => { 75 | configFile.watch( 76 | (contents: Partial | null) => { 77 | if (contents !== null) { 78 | config.value = { 79 | samba: { 80 | confPath: contents.samba?.confPath || defaultSettings().samba.confPath, 81 | tabVisibility: contents.samba?.tabVisibility || defaultSettings().samba.tabVisibility, 82 | }, 83 | nfs: { 84 | confPath: contents.nfs?.confPath || defaultSettings().nfs.confPath, 85 | tabVisibility: contents.nfs?.tabVisibility || defaultSettings().nfs.tabVisibility, 86 | }, 87 | iscsi: { 88 | // confPath: contents.iscsi?.confPath || defaultSettings().iscsi.confPath, 89 | confPath: defaultSettings().iscsi.confPath, 90 | clusteredServer: contents.iscsi?.clusteredServer || defaultSettings().iscsi.clusteredServer, 91 | clusteredServerChecked: contents.iscsi?.clusteredServerChecked ?? defaultSettings().iscsi.clusteredServerChecked, 92 | subnetMask: contents.iscsi?.subnetMask || defaultSettings().iscsi.subnetMask, 93 | tabVisibility: contents.iscsi?.tabVisibility || defaultSettings().iscsi.tabVisibility, 94 | }, 95 | includeSystemAccounts: 96 | contents.includeSystemAccounts ?? defaultSettings().includeSystemAccounts, 97 | }; 98 | } 99 | resolve(config); 100 | }, 101 | { read: true } 102 | ); 103 | }); 104 | 105 | const computedSettingsRef = computed({ 106 | get: () => config.value, 107 | set: (newConfig) => configFile.replace(newConfig), 108 | }); 109 | 110 | export function useUserSettings(waitUntilRead?: false): WritableComputedRef; 111 | export function useUserSettings(waitUntilRead: true): Promise>; 112 | export function useUserSettings(waitUntilRead: boolean = false) { 113 | return waitUntilRead 114 | ? configFileReadPromise.then(() => computedSettingsRef) 115 | : computedSettingsRef; 116 | } 117 | -------------------------------------------------------------------------------- /file-sharing/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | 3 | import App from "./App.vue"; 4 | 5 | import "@45drives/houston-common-css/src/index.css"; 6 | import "../../houston-common/houston-common-ui/dist/style.css"; 7 | 8 | const app = createApp(App); 9 | 10 | app.mount("#app"); 11 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/CHAPConfiguration.ts: -------------------------------------------------------------------------------- 1 | export class CHAPConfiguration { 2 | username: string; 3 | password: string; 4 | chapType: CHAPType; 5 | 6 | constructor(username: string, password: string, chapType: CHAPType) { 7 | this.username = username; 8 | this.password = password; 9 | this.chapType = chapType; 10 | } 11 | } 12 | 13 | export namespace CHAPConfiguration { 14 | export function empty() : CHAPConfiguration { 15 | return { 16 | username: "", 17 | password: "", 18 | chapType: CHAPType.IncomingUser, 19 | } 20 | } 21 | } 22 | 23 | export enum CHAPType { 24 | IncomingUser = "IncomingUser", 25 | OutgoingUser = "OutgoingUser" 26 | } -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/ConfigurationManager.ts: -------------------------------------------------------------------------------- 1 | import { useUserSettings } from "@/common/user-settings"; 2 | import { BashCommand, ProcessError, type Server, File } from "@45drives/houston-common-lib"; 3 | import { ResultAsync } from "neverthrow"; 4 | 5 | const userSettingsResult = ResultAsync.fromSafePromise(useUserSettings(true)); 6 | 7 | export class ConfigurationManager { 8 | server: Server; 9 | 10 | constructor(server: Server) { 11 | this.server = server; 12 | } 13 | 14 | exportConfiguration(): ResultAsync { 15 | return userSettingsResult.andThen((userSettings) => { 16 | return this.server 17 | .execute( 18 | new BashCommand(`scstadmin -write_config ${userSettings.value.iscsi.confPath}`) 19 | ) 20 | .andThen(() => 21 | this.server 22 | .execute(new BashCommand(`cat ${userSettings.value.iscsi.confPath}`)) 23 | .map((proc) => proc.getStdout()) 24 | ); 25 | }); 26 | } 27 | 28 | importConfiguration(newConfig: string) { 29 | return userSettingsResult.andThen((userSettings) => { 30 | return new File(this.server, userSettings.value.iscsi.confPath) 31 | .create() 32 | .andThen((file) => file.write(newConfig)) 33 | .andThen(() => this.server.execute(new BashCommand(`scstadmin -check_config ${userSettings.value.iscsi.confPath}`))) 34 | .map(() => this.server.execute(new BashCommand(`scstadmin -config ${userSettings.value.iscsi.confPath} -force -noprompt`))) 35 | .mapErr(() => new ProcessError("Config file syntax validation failed.")) 36 | }); 37 | } 38 | 39 | saveCurrentConfiguration(): ResultAsync { 40 | return userSettingsResult.andThen((userSettings) => { 41 | return new File(this.server, userSettings.value.iscsi.confPath) 42 | .create(true) 43 | .andThen((file) => 44 | this.exportConfiguration() 45 | .map((config) => file.write(config)) 46 | .andThen(() => this.server.execute(new BashCommand(`systemctl enable scst`))) 47 | .andThen(() => this.server.execute(new BashCommand(`scstadmin -config ${userSettings.value.iscsi.confPath}`))) 48 | .map(() => file) 49 | ); 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/Connection.ts: -------------------------------------------------------------------------------- 1 | export interface Connection { 2 | connectionID: string; 3 | ipAddress: string; 4 | 5 | devicePath: string; 6 | } -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/Initiator.ts: -------------------------------------------------------------------------------- 1 | export class Initiator { 2 | name: string; 3 | 4 | constructor(name: string) { 5 | this.name = name; 6 | } 7 | } 8 | 9 | export namespace Initiator { 10 | export function empty() : Initiator { 11 | return { 12 | name: "", 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/InitiatorGroup.ts: -------------------------------------------------------------------------------- 1 | import type { Initiator } from "./Initiator"; 2 | import type { LogicalUnitNumber } from "./LogicalUnitNumber"; 3 | 4 | export class InitiatorGroup { 5 | name: string; 6 | logicalUnitNumbers: LogicalUnitNumber[]; 7 | initiators: Initiator[]; 8 | 9 | devicePath: string; 10 | 11 | constructor(name: string, logicalUnitNumber: LogicalUnitNumber[], initiators: Initiator[], devicePath: string) { 12 | this.name = name; 13 | this.logicalUnitNumbers = logicalUnitNumber; 14 | this.initiators = initiators; 15 | this.devicePath = devicePath; 16 | 17 | } 18 | } 19 | 20 | export namespace InitiatorGroup { 21 | export function empty() : InitiatorGroup { 22 | return { 23 | name: "", 24 | devicePath: "", 25 | logicalUnitNumbers: [], 26 | initiators: [], 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/LogicalUnitNumber.ts: -------------------------------------------------------------------------------- 1 | import type { VirtualDevice } from "./VirtualDevice"; 2 | 3 | export class LogicalUnitNumber { 4 | name: string; 5 | unitNumber: number; 6 | blockDevice?: VirtualDevice; 7 | 8 | constructor(name: string, unitNumber: number, blockDevice?: VirtualDevice) { 9 | this.name = name; 10 | this.unitNumber = unitNumber; 11 | this.blockDevice = blockDevice; 12 | } 13 | } 14 | 15 | export namespace LogicalUnitNumber { 16 | export function empty() : LogicalUnitNumber { 17 | return { 18 | name: "", 19 | unitNumber: 0, 20 | blockDevice: undefined, 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/Portal.ts: -------------------------------------------------------------------------------- 1 | export class Portal { 2 | address: string; 3 | 4 | constructor(address: string) { 5 | this.address = address; 6 | } 7 | } -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/Session.ts: -------------------------------------------------------------------------------- 1 | import type { Connection } from "./Connection"; 2 | 3 | export interface Session { 4 | initiatorName: string; 5 | connections: Connection[]; 6 | 7 | writeAmountKB: number; 8 | readAmountKB: number; 9 | 10 | devicePath: string; 11 | } -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/Target.ts: -------------------------------------------------------------------------------- 1 | import { CHAPConfiguration } from "./CHAPConfiguration"; 2 | import type { InitiatorGroup } from "./InitiatorGroup"; 3 | import type { Portal } from "./Portal"; 4 | import type { Session } from "./Session"; 5 | 6 | export interface Target { 7 | name: string; 8 | chapConfigurations: CHAPConfiguration[]; 9 | initiatorGroups: InitiatorGroup[]; 10 | portals: Portal[]; 11 | sessions: Session[]; 12 | 13 | devicePath: string; 14 | } 15 | 16 | export namespace Target { 17 | export function empty() : Target { 18 | return { 19 | name: "", 20 | chapConfigurations: [], 21 | portals: [], 22 | sessions: [], 23 | initiatorGroups: [], 24 | devicePath: "", 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/VirtualDevice.ts: -------------------------------------------------------------------------------- 1 | export class VirtualDevice { 2 | deviceName: string; 3 | filePath: string; 4 | blockSize: number; 5 | deviceType: DeviceType; 6 | assigned?: boolean; 7 | 8 | constructor(deviceName: string, filePath: string, blockSize: number, deviceType: DeviceType, assigned?: boolean) { 9 | this.deviceName = deviceName; 10 | this.filePath = filePath; 11 | this.blockSize = blockSize; 12 | this.deviceType = deviceType; 13 | this.assigned = assigned; 14 | } 15 | } 16 | 17 | export enum DeviceType { 18 | FileIO = "FileIO", 19 | BlockIO = "BlockIO" 20 | } -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/cluster/LogicalVolume.ts: -------------------------------------------------------------------------------- 1 | import type { RadosBlockDevice } from '@/tabs/iSCSI/types/cluster/RadosBlockDevice'; 2 | import type { VolumeGroup } from './VolumeGroup'; 3 | import { DeviceType, VirtualDevice } from '@/tabs/iSCSI/types/VirtualDevice'; 4 | 5 | export class LogicalVolume extends VirtualDevice{ 6 | volumeGroup: VolumeGroup; 7 | maximumSize: number; 8 | 9 | constructor(deviceName: string, blockSize: number, volumeGroup: VolumeGroup, maximumSize: number) { 10 | super(deviceName, `/dev/${volumeGroup.name}/${deviceName}`, blockSize, DeviceType.BlockIO); 11 | 12 | this.volumeGroup = volumeGroup; 13 | this.maximumSize = maximumSize; 14 | } 15 | } -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/cluster/PCSResource.ts: -------------------------------------------------------------------------------- 1 | import type { PCSResourceGroup } from "@/tabs/iSCSI/types/cluster/PCSResourceGroup"; 2 | import type { Command } from "@45drives/houston-common-lib"; 3 | 4 | export class PCSResource { 5 | name: string; 6 | generationCommand: Command; 7 | resourceType: PCSResourceType; 8 | resourceGroup: PCSResourceGroup | undefined; 9 | 10 | constructor(name: string, generationCommand: Command, type: PCSResourceType, group?: PCSResourceGroup) { 11 | this.name = name; 12 | this.generationCommand = generationCommand; 13 | this.resourceType = type 14 | this.resourceGroup = group; 15 | } 16 | } 17 | 18 | export enum PCSResourceType { 19 | PORTBLOCK_ON, 20 | VIP, 21 | LVM, 22 | TARGET, 23 | LUN , 24 | PORTBLOCK_OFF, 25 | RBD, 26 | } 27 | 28 | interface PCSResourceInfo { 29 | namePrefix: string, 30 | internalTypeName: string, 31 | orderInGroup: number; 32 | } 33 | 34 | export const PCSResourceTypeInfo: { [key in PCSResourceType]: PCSResourceInfo } = { 35 | [PCSResourceType.PORTBLOCK_ON]: { 36 | namePrefix: "iscsi_portblock_on", 37 | internalTypeName: "portblock", 38 | orderInGroup: 0, 39 | }, 40 | [PCSResourceType.VIP]: { 41 | namePrefix: "iscsi_vip", 42 | internalTypeName: "IPaddr2", 43 | orderInGroup: 1, 44 | }, 45 | [PCSResourceType.LVM]: { 46 | namePrefix: "iscsi_lvm", 47 | internalTypeName: "LVM_activate", 48 | orderInGroup: 2, 49 | }, 50 | [PCSResourceType.TARGET]: { 51 | namePrefix: "iscsi_target", 52 | internalTypeName: "iSCSITarget", 53 | orderInGroup: 3, 54 | }, 55 | [PCSResourceType.LUN]: { 56 | namePrefix: "iscsi_lun", 57 | internalTypeName: "iSCSILogicalUnit", 58 | orderInGroup: 4, 59 | }, 60 | [PCSResourceType.PORTBLOCK_OFF]: { 61 | namePrefix: "iscsi_portblock_off", 62 | internalTypeName: "portblock", 63 | orderInGroup: 5, 64 | }, 65 | [PCSResourceType.RBD]: { 66 | namePrefix: "rbd", 67 | internalTypeName: "rbd", 68 | orderInGroup: -1, 69 | }, 70 | } 71 | 72 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/cluster/PCSResourceGroup.ts: -------------------------------------------------------------------------------- 1 | import type { PCSResource } from "@/tabs/iSCSI/types/cluster/PCSResource"; 2 | 3 | export class PCSResourceGroup { 4 | name: string; 5 | 6 | constructor(name: string) { 7 | this.name = name; 8 | } 9 | } -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/cluster/PhysicalVolume.ts: -------------------------------------------------------------------------------- 1 | import type { RadosBlockDevice } from './RadosBlockDevice'; 2 | 3 | export class PhysicalVolume { 4 | rbd: RadosBlockDevice; 5 | 6 | constructor(rbd: RadosBlockDevice) { 7 | this.rbd = rbd; 8 | } 9 | } -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/cluster/Pool.ts: -------------------------------------------------------------------------------- 1 | export class Pool { 2 | name: string; 3 | poolType: PoolType; 4 | 5 | constructor(name: string, poolType: PoolType) { 6 | this.name = name; 7 | this.poolType = poolType; 8 | } 9 | } 10 | 11 | export enum PoolType { 12 | Replication = "Replication", 13 | ErasureCoded = "Erasure Coded" 14 | } -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/cluster/RadosBlockDevice.ts: -------------------------------------------------------------------------------- 1 | import type { Pool } from "@/tabs/iSCSI/types/cluster/Pool"; 2 | import { DeviceType, VirtualDevice } from "@/tabs/iSCSI/types/VirtualDevice"; 3 | 4 | export class RadosBlockDevice extends VirtualDevice { 5 | parentPool: Pool; 6 | dataPool: Pool | undefined; 7 | maximumSize: number; 8 | 9 | constructor(deviceName: string, filePath: string, blockSize: number, maximumSize: number, parentPool: Pool, dataPool?: Pool) { 10 | super(deviceName, filePath, blockSize, DeviceType.BlockIO); 11 | 12 | this.parentPool = parentPool; 13 | this.dataPool = dataPool; 14 | this.maximumSize = maximumSize; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/cluster/VolumeGroup.ts: -------------------------------------------------------------------------------- 1 | import type { PhysicalVolume } from "@/tabs/iSCSI/types/cluster/PhysicalVolume"; 2 | 3 | export class VolumeGroup { 4 | name: string; 5 | volumes: PhysicalVolume[]; 6 | 7 | constructor(name: string, volumes: PhysicalVolume[]) { 8 | this.name = name; 9 | this.volumes = volumes; 10 | } 11 | } -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/types/drivers/ISCSIDriver.ts: -------------------------------------------------------------------------------- 1 | import type { ResultAsync } from "neverthrow"; 2 | import type { DeviceType, VirtualDevice } from "@/tabs/iSCSI/types/VirtualDevice"; 3 | import type { CHAPConfiguration } from "@/tabs/iSCSI/types/CHAPConfiguration"; 4 | import type { Connection } from "@/tabs/iSCSI/types/Connection"; 5 | import type { Initiator } from "@/tabs/iSCSI/types/Initiator"; 6 | import type { InitiatorGroup } from "@/tabs/iSCSI/types/InitiatorGroup"; 7 | import type { LogicalUnitNumber } from "@/tabs/iSCSI/types/LogicalUnitNumber"; 8 | import type { Portal } from "@/tabs/iSCSI/types/Portal"; 9 | import type { Session } from "@/tabs/iSCSI/types/Session"; 10 | import type { Target } from "@/tabs/iSCSI/types/Target"; 11 | import type { ProcessError } from "@45drives/houston-common-lib"; 12 | 13 | export abstract class ISCSIDriver { 14 | abstract initialize(): ResultAsync; 15 | 16 | abstract getHandledDeviceTypes(): DeviceType[]; 17 | 18 | abstract addVirtualDevice(virtualDevice: VirtualDevice): ResultAsync; 19 | abstract removeVirtualDevice(virtualDevice: VirtualDevice): ResultAsync; 20 | 21 | abstract createTarget(target: Target): ResultAsync; 22 | abstract removeTarget(target: Target): ResultAsync; 23 | 24 | abstract addPortalToTarget(target: Target, portal: Portal): ResultAsync; 25 | abstract deletePortalFromTarget(target: Target, portal: Portal): ResultAsync; 26 | 27 | abstract addInitiatorGroupToTarget(target: Target, initiatorGroup: InitiatorGroup): ResultAsync; 28 | abstract deleteInitiatorGroupFromTarget(target: Target, initiatorGroup: InitiatorGroup): ResultAsync; 29 | 30 | abstract addInitiatorToGroup(initiatorGroup: InitiatorGroup, initiator: Initiator): ResultAsync; 31 | abstract removeInitiatorFromGroup(initiatorGroup: InitiatorGroup, initiator: Initiator): ResultAsync; 32 | 33 | abstract addLogicalUnitNumberToGroup(initiatorGroup: InitiatorGroup, logicalUnitNumber: LogicalUnitNumber): ResultAsync; 34 | abstract removeLogicalUnitNumberFromGroup(initiatorGroup: InitiatorGroup, logicalUnitNumber: LogicalUnitNumber): ResultAsync; 35 | 36 | abstract addCHAPConfigurationToTarget(target: Target, chapConfiguration: CHAPConfiguration): ResultAsync; 37 | abstract removeCHAPConfigurationFromTarget(target: Target, chapConfiguration: CHAPConfiguration): ResultAsync; 38 | 39 | abstract getVirtualDevices(): ResultAsync; 40 | abstract getTargets(): ResultAsync; 41 | 42 | abstract getPortalsOfTarget(target: Target): ResultAsync; 43 | abstract getInitatorGroupsOfTarget(target: Target): ResultAsync; 44 | abstract getSessionsOfTarget(target: Target): ResultAsync; 45 | abstract getCHAPConfigurationsOfTarget(target: Target): ResultAsync; 46 | 47 | abstract getConnectionsOfSession(session: Session): ResultAsync; 48 | 49 | abstract getLogicalUnitNumbersOfInitiatorGroup(initiatorGroup: InitiatorGroup): ResultAsync; 50 | abstract getInitiatorsOfInitiatorGroup(initiatorGroup: InitiatorGroup): ResultAsync; 51 | } -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/ISCSITabMain.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 117 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/chapConfigurations/ChapConfigurationEntry.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 60 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/chapConfigurations/ChapConfigurationTable.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 95 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/config/ConfigurationEditor.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/connections/ConnectionEntry.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/connections/ConnectionTable.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/initiatorGroups/InitiatorGroupEditor.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 87 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/initiatorGroups/InitiatorGroupEntry.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 67 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/initiatorGroups/InitiatorGroupTable.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 91 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/initiators/InitiatorEditor.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 97 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/initiators/InitiatorEntry.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 51 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/initiators/InitiatorTable.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 85 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/logicalUnitNumbers/LogicalUnitNumberEntry.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 57 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/logicalUnitNumbers/LogicalUnitNumberTable.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 107 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/portal/PortalEditor.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 127 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/portal/PortalEntry.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 47 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/portal/PortalTable.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 85 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDExpansionScreen.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 101 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDManagementScreen.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDTable.vue: -------------------------------------------------------------------------------- 1 | 2 | 60 | 61 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDTableEntry.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 57 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/sessions/SessionEntry.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 46 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/sessions/SessionTable.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 69 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/target/TargetEntry.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 68 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/target/TargetTable.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 96 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/virtualDevice/FileIOCreationPrompt.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 84 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/virtualDevice/VirtualDeviceEntry.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 68 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/iSCSI/ui/screens/virtualDevice/VirtualDeviceTable.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 84 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/nfs/ui/NFSExportListView.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 121 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/nfs/ui/NFSTabMain.vue: -------------------------------------------------------------------------------- 1 | 103 | 104 | 129 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/nfs/ui/index.ts: -------------------------------------------------------------------------------- 1 | export { default as NFSTabMain } from "./NFSTabMain.vue"; 2 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/samba/samba-manager.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SambaShareConfig, 3 | type ISambaManager, 4 | SambaManagerNet as SambaManager, 5 | } from "@45drives/houston-common-lib"; 6 | import { executeHookCallbacks, Hooks } from "@/common/hooks"; 7 | 8 | export class HookedSambaManager extends SambaManager implements ISambaManager { 9 | addShare(share: SambaShareConfig) { 10 | return executeHookCallbacks(Hooks.BeforeAddShare, this.server, share) 11 | .andThen(() => super.addShare(share)) 12 | .andThen(() => executeHookCallbacks(Hooks.AfterAddShare, this.server, share)) 13 | .map(() => share); 14 | } 15 | 16 | editShare(share: SambaShareConfig) { 17 | return executeHookCallbacks(Hooks.BeforeEditShare, this.server, share) 18 | .andThen(() => super.editShare(share)) 19 | .andThen(() => executeHookCallbacks(Hooks.AfterEditShare, this.server, share)) 20 | .map(() => share); 21 | } 22 | 23 | removeShare(share: SambaShareConfig) { 24 | return executeHookCallbacks(Hooks.BeforeRemoveShare, this.server, share) 25 | .andThen(() => super.removeShare(share)) 26 | .andThen(() => executeHookCallbacks(Hooks.AfterRemoveShare, this.server, share)) 27 | .map(() => share); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/samba/ui/BooleanKeyValueSuite.ts: -------------------------------------------------------------------------------- 1 | import { computed } from "vue"; 2 | 3 | export const BooleanKeyValueSuite = ( 4 | objGetter: () => Record, 5 | trueContents: { 6 | include: Record; 7 | exclude: Record; 8 | suggest?: Record; 9 | }, 10 | arraySplitter: RegExp = /\s+/, 11 | arrayJoiner: string = " " 12 | ) => { 13 | const includesOption = (obj: Record, key: string, value: string): boolean => { 14 | const current = obj[key]?.trim(); 15 | return current === value; 16 | }; 17 | 18 | const includesOptionAll = ( 19 | obj: Record, 20 | key: string, 21 | value: string | string[] 22 | ): boolean => { 23 | if (typeof value === "string") { 24 | return includesOption(obj, key, value); 25 | } 26 | const current = obj[key]?.split(arraySplitter); 27 | return current !== undefined && value.every((value) => current.includes(value)); 28 | }; 29 | 30 | const includesOptionAny = ( 31 | obj: Record, 32 | key: string, 33 | value: string | string[] 34 | ): boolean => { 35 | if (typeof value === "string") { 36 | return includesOption(obj, key, value); 37 | } 38 | const current = obj[key]?.split(arraySplitter); 39 | return current !== undefined && value.some((value) => current.includes(value)); 40 | }; 41 | 42 | const includesOptionsAll = ( 43 | obj: Record, 44 | options: [string, string | string[]][] 45 | ): boolean => { 46 | return options.every(([key, value]) => includesOptionAll(obj, key, value)); 47 | }; 48 | 49 | const includesOptionsAny = ( 50 | obj: Record, 51 | options: [string, string | string[]][] 52 | ): boolean => { 53 | return options.some(([key, value]) => includesOptionAny(obj, key, value)); 54 | }; 55 | 56 | const addOption = (obj: Record, key: string, value: string | string[]) => { 57 | if (Array.isArray(value)) { 58 | const currentValues = obj[key]?.split(arraySplitter) ?? []; 59 | obj[key] = [...new Set([...currentValues, ...value])].join(arrayJoiner); 60 | } else { 61 | obj[key] = value; 62 | } 63 | }; 64 | 65 | const addOptions = (obj: Record, options: [string, string | string[]][]) => { 66 | options.forEach(([key, value]) => addOption(obj, key, value)); 67 | }; 68 | 69 | const removeOption = (obj: Record, key: string, value: string | string[]) => { 70 | if (Array.isArray(value)) { 71 | const currentValues = obj[key]?.split(arraySplitter) ?? []; 72 | const filteredValues = currentValues.filter((v) => !value.includes(v)); 73 | if (filteredValues.length) { 74 | obj[key] = filteredValues.join(arrayJoiner); 75 | } else { 76 | delete obj[key]; 77 | } 78 | } else { 79 | const current = obj[key]?.trim(); 80 | if (current === value) { 81 | delete obj[key]; 82 | } 83 | } 84 | }; 85 | 86 | const removeOptions = (obj: Record, options: [string, string | string[]][]) => { 87 | options.forEach(([key, value]) => removeOption(obj, key, value)); 88 | }; 89 | 90 | const includedOptions = Object.entries(trueContents.include); 91 | const excludedOptions = Object.entries(trueContents.exclude); 92 | const suggestedOptions = Object.entries(trueContents.suggest ?? {}); 93 | 94 | return computed({ 95 | get() { 96 | const obj = objGetter(); 97 | return includesOptionsAll(obj, includedOptions) && !includesOptionsAny(obj, excludedOptions); 98 | }, 99 | set(value) { 100 | const obj = objGetter(); 101 | if (value) { 102 | removeOptions(obj, excludedOptions); 103 | addOptions(obj, [...includedOptions, ...suggestedOptions]); 104 | } else { 105 | removeOptions(obj, [...includedOptions, ...suggestedOptions]); 106 | } 107 | }, 108 | }); 109 | }; 110 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/samba/ui/GlobalConfigEditor.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 132 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/samba/ui/ShareListView.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 125 | -------------------------------------------------------------------------------- /file-sharing/src/tabs/samba/ui/index.ts: -------------------------------------------------------------------------------- 1 | export { default as SambaTabMain } from "./SambaTabMain.vue"; 2 | -------------------------------------------------------------------------------- /file-sharing/src/version.js: -------------------------------------------------------------------------------- 1 | export const pluginVersion = "-built_from_source"; 2 | -------------------------------------------------------------------------------- /file-sharing/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./index.html", 4 | "./src/**/*.{vue,js,ts,jsx,tsx}", 5 | ], 6 | theme: { 7 | extend: { 8 | fontFamily: { 9 | redhat: ['Red Hat Text', 'open-sans', 'sans-serif'], 10 | "source-sans-pro": ["Source Sans Pro", 'open-sans', 'sans-serif'], 11 | }, 12 | colors: { 13 | neutral: { 14 | 850: "#222222", 15 | } 16 | } 17 | }, 18 | }, 19 | plugins: [ 20 | require('@tailwindcss/forms'), 21 | ], 22 | darkMode: "class", 23 | } 24 | -------------------------------------------------------------------------------- /file-sharing/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.dom.json", 3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], 4 | "exclude": ["src/**/__tests__/*"], 5 | "compilerOptions": { 6 | "composite": true, 7 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 8 | 9 | "baseUrl": ".", 10 | "paths": { 11 | "@/*": ["./src/*"] 12 | }, 13 | "strict": true, 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "esModuleInterop": true, 17 | // "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "noImplicitReturns": true, 20 | "skipLibCheck": true, 21 | "alwaysStrict": true, 22 | "strictFunctionTypes": true, 23 | "strictNullChecks": true, 24 | "strictPropertyInitialization": true, 25 | "forceConsistentCasingInFileNames": true, 26 | "noImplicitAny": true, 27 | "noImplicitThis": true, 28 | "noFallthroughCasesInSwitch": true, 29 | "experimentalDecorators": true, 30 | "downlevelIteration": true, 31 | "pretty": true, 32 | "allowJs": false, 33 | "checkJs": false, 34 | "noUncheckedIndexedAccess": true, 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /file-sharing/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { 5 | "path": "./tsconfig.node.json" 6 | }, 7 | { 8 | "path": "./tsconfig.app.json" 9 | }, 10 | { 11 | "path": "./tsconfig.vitest.json" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /file-sharing/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node20/tsconfig.json", 3 | "include": [ 4 | "vite.config.*", 5 | "vitest.config.*", 6 | "cypress.config.*", 7 | "nightwatch.conf.*", 8 | "playwright.config.*" 9 | ], 10 | "compilerOptions": { 11 | "composite": true, 12 | "noEmit": true, 13 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 14 | 15 | "module": "ESNext", 16 | "moduleResolution": "Bundler", 17 | "types": ["node"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /file-sharing/tsconfig.vitest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.app.json", 3 | "exclude": [], 4 | "compilerOptions": { 5 | "composite": true, 6 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo", 7 | 8 | "lib": [], 9 | "types": ["node", "jsdom"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /file-sharing/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from "node:url"; 2 | 3 | import { defineConfig, PluginOption } from "vite"; 4 | import vue from "@vitejs/plugin-vue"; 5 | import VueDevTools from "vite-plugin-vue-devtools"; 6 | 7 | import manifest from "../manifest.json"; 8 | 9 | const getAppVersionDefine = () => { 10 | return `${manifest.version}-${manifest.build_number}${process.env.OS_PACKAGE_RELEASE ?? "built_from_source"}`; 11 | }; 12 | 13 | // https://vitejs.dev/config/ 14 | export default defineConfig({ 15 | plugins: [vue(), VueDevTools()] as PluginOption[], 16 | base: "./", 17 | resolve: { 18 | alias: { 19 | "@": fileURLToPath(new URL("./src", import.meta.url)), 20 | }, 21 | dedupe: ["vue"], 22 | }, 23 | build: { 24 | target: ["chrome87", "edge88", "firefox78", "safari14"], 25 | sourcemap: true, 26 | }, 27 | define: { 28 | __APP_VERSION__: JSON.stringify(getAppVersionDefine()), 29 | }, 30 | }); 31 | -------------------------------------------------------------------------------- /file-sharing/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'node:url' 2 | import { mergeConfig, defineConfig, configDefaults } from 'vitest/config' 3 | import viteConfig from './vite.config' 4 | 5 | export default mergeConfig( 6 | viteConfig, 7 | defineConfig({ 8 | test: { 9 | environment: 'jsdom', 10 | exclude: [...configDefaults.exclude, 'e2e/**'], 11 | root: fileURLToPath(new URL('./', import.meta.url)) 12 | } 13 | }) 14 | ) 15 | -------------------------------------------------------------------------------- /img/nfs/nfs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/img/nfs/nfs.gif -------------------------------------------------------------------------------- /img/nfs/nfs_interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/img/nfs/nfs_interface.png -------------------------------------------------------------------------------- /img/samba/samba_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/img/samba/samba_add.png -------------------------------------------------------------------------------- /img/samba/samba_advanced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/img/samba/samba_advanced.png -------------------------------------------------------------------------------- /img/samba/samba_global.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/img/samba/samba_global.png -------------------------------------------------------------------------------- /img/samba/samba_group.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/img/samba/samba_group.gif -------------------------------------------------------------------------------- /img/samba/samba_interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/img/samba/samba_interface.png -------------------------------------------------------------------------------- /img/samba/samba_password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/img/samba/samba_password.png -------------------------------------------------------------------------------- /img/samba/samba_privilege.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/img/samba/samba_privilege.gif -------------------------------------------------------------------------------- /img/samba/samba_remove.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/img/samba/samba_remove.gif -------------------------------------------------------------------------------- /img/samba/samba_user.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/img/samba/samba_user.gif -------------------------------------------------------------------------------- /json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/json -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema_version": "45D_AP_V2.0", 3 | "name": "cockpit-file-sharing", 4 | "title": "Cockpit File Sharing", 5 | "description": "A cockpit module to make file sharing with Samba and NFS easier.", 6 | "version": "4.2.12", 7 | "build_number": "1", 8 | "stable": true, 9 | "author": "Josh Boudreau ", 10 | "git_url": "https://github.com/45Drives/cockpit-file-sharing", 11 | "license": "GPL-3.0+", 12 | "architecture": { 13 | "ubuntu": "all", 14 | "rocky": "noarch" 15 | }, 16 | "defaults": { 17 | "change_urgency": "medium" 18 | }, 19 | "dependencies": { 20 | "ubuntu_common": [ 21 | "cockpit-bridge", 22 | "coreutils", 23 | "attr", 24 | "findutils", 25 | "hostname", 26 | "iproute2", 27 | "libc-bin", 28 | "systemd", 29 | "nfs-kernel-server", 30 | "samba-common-bin" 31 | ], 32 | "rocky_common": [ 33 | "cockpit-bridge", 34 | "coreutils", 35 | "attr", 36 | "findutils", 37 | "hostname", 38 | "iproute", 39 | "glibc-common", 40 | "systemd", 41 | "nfs-utils", 42 | "samba-common-tools" 43 | ] 44 | }, 45 | "builds": [ 46 | { 47 | "group": "ubuntu", 48 | "os_name": "ubuntu-focal", 49 | "image": "ci.cr.45d.io/ci/simple/ubuntu-focal:1" 50 | }, 51 | { 52 | "group": "debian", 53 | "os_name": "debian-bookworm", 54 | "image": "ci.cr.45d.io/ci/simple/ubuntu-jammy:1" 55 | }, 56 | { 57 | "group": "ubuntu", 58 | "os_name": "ubuntu-jammy", 59 | "image": "ci.cr.45d.io/ci/simple/ubuntu-jammy:1" 60 | }, 61 | { 62 | "group": "rocky", 63 | "os_name": "rocky-el8", 64 | "image": "ci.cr.45d.io/ci/simple/rocky-el8:1" 65 | }, 66 | { 67 | "group": "rocky", 68 | "os_name": "rocky-el9", 69 | "image": "ci.cr.45d.io/ci/simple/rocky-el9:1" 70 | } 71 | ], 72 | "repos": [ 73 | "community", 74 | "enterprise" 75 | ], 76 | "changelog": { 77 | "urgency": "medium", 78 | "version": "4.2.12", 79 | "build_number": "1", 80 | "date": null, 81 | "packager": "Josh Boudreau ", 82 | "changes": [] 83 | } 84 | } -------------------------------------------------------------------------------- /os: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/os -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "workspaces": [ 4 | "file-sharing", 5 | "houston-common", 6 | "houston-common/houston-common-*" 7 | ], 8 | "scripts": { 9 | "build": "yarn build:common && yarn build:app", 10 | "build:common": "yarn build:css && yarn build:lib && yarn build:ui" 11 | }, 12 | "packageManager": "yarn@4.7.0" 13 | } 14 | -------------------------------------------------------------------------------- /packaging/debian-bookworm/changelog: -------------------------------------------------------------------------------- 1 | cockpit-file-sharing (4.2.12-1bookworm) bookworm; urgency=medium 2 | 3 | * ISCSI - Shows all virtual devices and whether in use or not 4 | 5 | -- Jordan Keough Thu, 29 May 2025 11:03:26 -0300 6 | 7 | cockpit-file-sharing (4.2.11-1bookworm) bookworm; urgency=medium 8 | 9 | * remove acl_xattr:ignore 10 | * system 11 | * acls 12 | * = 13 | * yes option from windowsACLsOptions 14 | 15 | -- Jordan Keough Thu, 29 May 2025 05:17:16 -0300 16 | 17 | cockpit-file-sharing (4.2.10-4bookworm) bookworm; urgency=medium 18 | 19 | * Updates houston-common commit, fixes isDomainJoined function 20 | 21 | -- Jordan Keough Fri, 04 Apr 2025 05:55:47 -0300 22 | 23 | cockpit-file-sharing (4.2.10-3bookworm) bookworm; urgency=medium 24 | 25 | * Merges prerelease packages into stable 26 | 27 | -- Jordan Keough Wed, 02 Apr 2025 06:53:28 -0300 28 | 29 | cockpit-file-sharing (4.2.9-2bookworm) bookworm; urgency=medium 30 | 31 | * bump build rev 32 | 33 | -- Josh Boudreau Tue, 25 Mar 2025 12:28:20 -0300 34 | 35 | cockpit-file-sharing (4.2.9-1bookworm) bookworm; urgency=medium 36 | 37 | * Initial build for bookworm 38 | 39 | -- Brett Kelly Thu, 06 Feb 2025 14:10:25 -0400 -------------------------------------------------------------------------------- /packaging/debian-bookworm/control.j2: -------------------------------------------------------------------------------- 1 | Source: {{ name }} 2 | Section: utils 3 | Priority: optional 4 | Maintainer: {{ author }} 5 | Build-Depends: debhelper-compat (= 12) 6 | Standards-Version: 4.4.1 7 | Homepage: {{ git_url }} 8 | Vcs-Git: {{ git_url }} 9 | 10 | Package: {{ name }} 11 | Architecture: {{ architecture.ubuntu }} 12 | Depends: {{ dependencies.ubuntu_common | join(',') }} 13 | Description: {{ description }} -------------------------------------------------------------------------------- /packaging/debian-bookworm/copyright.j2: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: "{{ name }}" 3 | Upstream-Contact: "{{ author }}" 4 | Source: "{{ git_url }}" 5 | 6 | Files: * 7 | Copyright: 2021 Josh Boudreau 8 | License: GPL-3.0+ 9 | 10 | License: GPL-3.0+ 11 | This program is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU General Public License as published by 13 | the Free Software Foundation, either version 3 of the License, or 14 | (at your option) any later version. 15 | . 16 | This program is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU General Public License for more details. 20 | . 21 | You should have received a copy of the GNU General Public License 22 | along with the Onboard package. If not, please have a look at 23 | /usr/share/common-licenses or . 24 | -------------------------------------------------------------------------------- /packaging/debian-bookworm/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # See debhelper(7) (uncomment to enable) 3 | # output every command that modifies files on the build system. 4 | #export DH_VERBOSE = 1 5 | 6 | 7 | # see FEATURE AREAS in dpkg-buildflags(1) 8 | #export DEB_BUILD_MAINT_OPTIONS = hardening=+all 9 | 10 | # see ENVIRONMENT in dpkg-buildflags(1) 11 | # package maintainers to append CFLAGS 12 | #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic 13 | # package maintainers to append LDFLAGS 14 | #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 15 | 16 | export OS_PACKAGE_RELEASE := focal 17 | 18 | %: 19 | dh $@ 20 | 21 | 22 | # dh_make generated override targets 23 | # This is example for Cmake (See https://bugs.debian.org/641051 ) 24 | #override_dh_auto_configure: 25 | # dh_auto_configure -- # -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) 26 | -------------------------------------------------------------------------------- /packaging/debian-bookworm/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /packaging/rocky-el9/main.spec.j2: -------------------------------------------------------------------------------- 1 | Name: {{ name }} 2 | Version: {{ version }} 3 | Release: {{ build_number }}%{?dist} 4 | Summary: {{ description }} 5 | License: {{ license }} 6 | URL: {{ git_url }} 7 | Source0: %{name}-%{version}.tar.gz 8 | BuildArch: {{ architecture.rocky }} 9 | Requires: {{ dependencies.rocky_common | join(',') }} 10 | 11 | 12 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root 13 | 14 | %description 15 | {{ title }} 16 | {{ description }} 17 | 18 | %prep 19 | %setup -q 20 | 21 | %build 22 | make OS_PACKAGE_RELEASE=el9 23 | 24 | %install 25 | make DESTDIR=%{buildroot} install 26 | 27 | %files 28 | /usr/share/cockpit/file-sharing/* 29 | 30 | %changelog 31 | * Thu May 29 2025 Jordan Keough 4.2.12-1 32 | - ISCSI - Shows all virtual devices and whether in use or not 33 | * Thu May 29 2025 Jordan Keough 4.2.11-1 34 | - remove acl_xattr:ignore 35 | - system 36 | - acls 37 | - = 38 | - yes option from windowsACLsOptions 39 | * Fri Apr 04 2025 Jordan Keough 4.2.10-4 40 | - Updates houston-common commit, fixes isDomainJoined function 41 | * Wed Apr 02 2025 Jordan Keough 4.2.10-3 42 | - Merges prerelease packages into stable 43 | * Tue Mar 25 2025 Josh Boudreau 4.2.9-2 44 | - bump build rev 45 | * Thu Feb 06 2025 Brett Kelly 4.2.9-1 46 | - Initial build for el9 -------------------------------------------------------------------------------- /packaging/ubuntu-focal/control.j2: -------------------------------------------------------------------------------- 1 | Source: {{ name }} 2 | Section: utils 3 | Priority: optional 4 | Maintainer: {{ author }} 5 | Build-Depends: debhelper-compat (= 12) 6 | Standards-Version: 4.4.1 7 | Homepage: {{ git_url }} 8 | Vcs-Git: {{ git_url }} 9 | 10 | Package: {{ name }} 11 | Architecture: {{ architecture.ubuntu }} 12 | Depends: {{ dependencies.ubuntu_common | join(',') }} 13 | Description: {{ description }} -------------------------------------------------------------------------------- /packaging/ubuntu-focal/copyright.j2: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: "{{ name }}" 3 | Upstream-Contact: "{{ author }}" 4 | Source: "{{ git_url }}" 5 | 6 | Files: * 7 | Copyright: 2021 Josh Boudreau 8 | License: GPL-3.0+ 9 | 10 | License: GPL-3.0+ 11 | This program is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU General Public License as published by 13 | the Free Software Foundation, either version 3 of the License, or 14 | (at your option) any later version. 15 | . 16 | This program is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU General Public License for more details. 20 | . 21 | You should have received a copy of the GNU General Public License 22 | along with the Onboard package. If not, please have a look at 23 | /usr/share/common-licenses or . 24 | -------------------------------------------------------------------------------- /packaging/ubuntu-focal/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # See debhelper(7) (uncomment to enable) 3 | # output every command that modifies files on the build system. 4 | #export DH_VERBOSE = 1 5 | 6 | 7 | # see FEATURE AREAS in dpkg-buildflags(1) 8 | #export DEB_BUILD_MAINT_OPTIONS = hardening=+all 9 | 10 | # see ENVIRONMENT in dpkg-buildflags(1) 11 | # package maintainers to append CFLAGS 12 | #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic 13 | # package maintainers to append LDFLAGS 14 | #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 15 | 16 | export OS_PACKAGE_RELEASE := focal 17 | 18 | %: 19 | dh $@ 20 | 21 | 22 | # dh_make generated override targets 23 | # This is example for Cmake (See https://bugs.debian.org/641051 ) 24 | #override_dh_auto_configure: 25 | # dh_auto_configure -- # -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) 26 | -------------------------------------------------------------------------------- /packaging/ubuntu-focal/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /packaging/ubuntu-jammy/changelog: -------------------------------------------------------------------------------- 1 | cockpit-file-sharing (4.2.12-1jammy) jammy; urgency=medium 2 | 3 | * ISCSI - Shows all virtual devices and whether in use or not 4 | 5 | -- Jordan Keough Thu, 29 May 2025 11:03:26 -0300 6 | 7 | cockpit-file-sharing (4.2.11-1jammy) jammy; urgency=medium 8 | 9 | * remove acl_xattr:ignore 10 | * system 11 | * acls 12 | * = 13 | * yes option from windowsACLsOptions 14 | 15 | -- Jordan Keough Thu, 29 May 2025 05:17:16 -0300 16 | 17 | cockpit-file-sharing (4.2.10-4jammy) jammy; urgency=medium 18 | 19 | * Updates houston-common commit, fixes isDomainJoined function 20 | 21 | -- Jordan Keough Fri, 04 Apr 2025 05:55:47 -0300 22 | 23 | cockpit-file-sharing (4.2.10-3jammy) jammy; urgency=medium 24 | 25 | * Merges prerelease packages into stable 26 | 27 | -- Jordan Keough Wed, 02 Apr 2025 06:53:28 -0300 28 | 29 | cockpit-file-sharing (4.2.9-2jammy) jammy; urgency=medium 30 | 31 | * bump build rev 32 | 33 | -- Josh Boudreau Tue, 25 Mar 2025 12:28:20 -0300 34 | 35 | cockpit-file-sharing (4.2.9-1jammy) jammy; urgency=medium 36 | 37 | * Initial build for jammy 38 | 39 | -- Brett Kelly Thu, 06 Feb 2025 14:10:25 -0400 -------------------------------------------------------------------------------- /packaging/ubuntu-jammy/control.j2: -------------------------------------------------------------------------------- 1 | Source: {{ name }} 2 | Section: utils 3 | Priority: optional 4 | Maintainer: {{ author }} 5 | Build-Depends: debhelper-compat (= 12) 6 | Standards-Version: 4.4.1 7 | Homepage: {{ git_url }} 8 | Vcs-Git: {{ git_url }} 9 | 10 | Package: {{ name }} 11 | Architecture: {{ architecture.ubuntu }} 12 | Depends: {{ dependencies.ubuntu_common | join(',') }} 13 | Description: {{ description }} -------------------------------------------------------------------------------- /packaging/ubuntu-jammy/copyright.j2: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: "{{ name }}" 3 | Upstream-Contact: "{{ author }}" 4 | Source: "{{ git_url }}" 5 | 6 | Files: * 7 | Copyright: 2021 Josh Boudreau 8 | License: GPL-3.0+ 9 | 10 | License: GPL-3.0+ 11 | This program is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU General Public License as published by 13 | the Free Software Foundation, either version 3 of the License, or 14 | (at your option) any later version. 15 | . 16 | This program is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU General Public License for more details. 20 | . 21 | You should have received a copy of the GNU General Public License 22 | along with the Onboard package. If not, please have a look at 23 | /usr/share/common-licenses or . 24 | -------------------------------------------------------------------------------- /packaging/ubuntu-jammy/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # See debhelper(7) (uncomment to enable) 3 | # output every command that modifies files on the build system. 4 | #export DH_VERBOSE = 1 5 | 6 | 7 | # see FEATURE AREAS in dpkg-buildflags(1) 8 | #export DEB_BUILD_MAINT_OPTIONS = hardening=+all 9 | 10 | # see ENVIRONMENT in dpkg-buildflags(1) 11 | # package maintainers to append CFLAGS 12 | #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic 13 | # package maintainers to append LDFLAGS 14 | #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 15 | 16 | export OS_PACKAGE_RELEASE := jammy 17 | 18 | %: 19 | dh $@ 20 | 21 | 22 | # dh_make generated override targets 23 | # This is example for Cmake (See https://bugs.debian.org/641051 ) 24 | #override_dh_auto_configure: 25 | # dh_auto_configure -- # -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) 26 | -------------------------------------------------------------------------------- /packaging/ubuntu-jammy/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /re: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/re -------------------------------------------------------------------------------- /system_dependencies.txt: -------------------------------------------------------------------------------- 1 | all of houston-common/system_dependencies.txt, plus: 2 | 3 | commands: 4 | systemctl 5 | exportfs 6 | net 7 | 8 | RHEL 8: 9 | systemd - systemctl 10 | nfs-utils - exportfs 11 | samba-common-tools - net 12 | 13 | Ubuntu Focal: 14 | systemd - systemctl 15 | nfs-kernel-server - exportfs 16 | samba-common-bin - net 17 | -------------------------------------------------------------------------------- /time: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/45Drives/cockpit-file-sharing/e6afc175951877b6801d2053fdda27d209715eb1/time -------------------------------------------------------------------------------- /vetur.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | settings: { 3 | "vetur.useWorkspaceDependencies": true, 4 | "vetur.experimental.templateInterpolationService": true 5 | }, 6 | projects: [ 7 | "./file-sharing-vue", 8 | ] 9 | } 10 | --------------------------------------------------------------------------------