mirror of
https://github.com/Ravinou/borgwarehouse
synced 2024-06-06 16:02:53 +02:00
feat: add borgbackup "append-only" mode as option to repo ! #160
This commit is contained in:
parent
6b43c38cc2
commit
76d11d83f7
|
@ -7,6 +7,7 @@ import {
|
|||
IconChevronDown,
|
||||
IconChevronUp,
|
||||
IconBellOff,
|
||||
IconLockPlus,
|
||||
} from '@tabler/icons-react';
|
||||
import timestampConverter from '../../helpers/functions/timestampConverter';
|
||||
import StorageBar from '../UI/StorageBar/StorageBar';
|
||||
|
@ -72,6 +73,16 @@ export default function Repo(props) {
|
|||
}
|
||||
};
|
||||
|
||||
const appendOnlyModeIndicator = () => {
|
||||
if (props.appendOnlyMode) {
|
||||
return (
|
||||
<div className={classes.appendOnlyModeIcon}>
|
||||
<IconLockPlus size={16} color='grey' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{displayDetails ? (
|
||||
|
@ -80,6 +91,7 @@ export default function Repo(props) {
|
|||
<div className={classes.openFlex}>
|
||||
<div className={statusIndicator()} />
|
||||
<div className={classes.alias}>{props.alias}</div>
|
||||
{appendOnlyModeIndicator()}
|
||||
{alertIndicator()}
|
||||
{props.comment && (
|
||||
<div className={classes.comment}>
|
||||
|
@ -155,6 +167,7 @@ export default function Repo(props) {
|
|||
<div className={classes.closeFlex}>
|
||||
<div className={statusIndicator()} />
|
||||
<div className={classes.alias}>{props.alias}</div>
|
||||
{appendOnlyModeIndicator()}
|
||||
{alertIndicator()}
|
||||
{props.comment && (
|
||||
<div className={classes.comment}>
|
||||
|
|
|
@ -157,6 +157,13 @@
|
|||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.appendOnlyModeIcon {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
/* GENERAL */
|
||||
.alias {
|
||||
font-weight: bold;
|
||||
|
|
|
@ -123,6 +123,7 @@ export default function RepoList() {
|
|||
sshPublicKey={repo.sshPublicKey}
|
||||
comment={repo.comment}
|
||||
lanCommand={repo.lanCommand}
|
||||
appendOnlyMode={repo.appendOnlyMode}
|
||||
repoManageEditHandler={() => repoManageEditHandler(repo.id)}
|
||||
wizardEnv={wizardEnv}
|
||||
></Repo>
|
||||
|
|
|
@ -170,6 +170,7 @@ export default function RepoManage(props) {
|
|||
comment: dataForm.comment,
|
||||
alert: dataForm.alert.value,
|
||||
lanCommand: dataForm.lanCommand,
|
||||
appendOnlyMode: dataForm.appendOnlyMode,
|
||||
};
|
||||
//POST API to send new repo
|
||||
await fetch('/api/repo/add', {
|
||||
|
@ -210,6 +211,7 @@ export default function RepoManage(props) {
|
|||
comment: dataForm.comment,
|
||||
alert: dataForm.alert.value,
|
||||
lanCommand: dataForm.lanCommand,
|
||||
appendOnlyMode: dataForm.appendOnlyMode,
|
||||
};
|
||||
await fetch('/api/repo/id/' + router.query.slug + '/edit', {
|
||||
method: 'PUT',
|
||||
|
@ -426,7 +428,7 @@ export default function RepoManage(props) {
|
|||
</span>
|
||||
)}
|
||||
{/* LAN COMMAND GENERATION */}
|
||||
<div className={classes.lanCommandWrapper}>
|
||||
<div className={classes.optionCommandWrapper}>
|
||||
<input
|
||||
type='checkbox'
|
||||
name='lanCommand'
|
||||
|
@ -455,6 +457,36 @@ export default function RepoManage(props) {
|
|||
/>
|
||||
</Link>
|
||||
</div>
|
||||
{/* APPEND-ONLY MODE */}
|
||||
<div className={classes.optionCommandWrapper}>
|
||||
<input
|
||||
type='checkbox'
|
||||
name='appendOnlyMode'
|
||||
defaultChecked={
|
||||
props.mode == 'edit'
|
||||
? targetRepo.appendOnlyMode
|
||||
: false
|
||||
}
|
||||
{...register('appendOnlyMode')}
|
||||
/>
|
||||
<label htmlFor='appendOnlyMode'>
|
||||
Enable append-only mode.
|
||||
</label>
|
||||
<Link
|
||||
style={{
|
||||
alignSelf: 'baseline',
|
||||
marginLeft: '5px',
|
||||
}}
|
||||
href='https://borgwarehouse.com/docs/user-manual/repositories/#append-only-mode'
|
||||
rel='noreferrer'
|
||||
target='_blank'
|
||||
>
|
||||
<IconExternalLink
|
||||
size={16}
|
||||
color='#6c737f'
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
{/* ALERT */}
|
||||
<label
|
||||
style={{ margin: '25px auto 10px auto' }}
|
||||
|
|
|
@ -126,23 +126,23 @@
|
|||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.lanCommandWrapper {
|
||||
.optionCommandWrapper {
|
||||
display: flex;
|
||||
margin-top: 20px;
|
||||
color: #494b7a;
|
||||
}
|
||||
|
||||
.lanCommandWrapper label {
|
||||
.optionCommandWrapper label {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.lanCommandWrapper input[type='checkbox'] {
|
||||
.optionCommandWrapper input[type='checkbox'] {
|
||||
width: auto;
|
||||
margin-right: 8px;
|
||||
cursor: pointer;
|
||||
accent-color: #6d4aff;
|
||||
}
|
||||
.lanCommandWrapper input[type='checkbox']:focus {
|
||||
.optionCommandWrapper input[type='checkbox']:focus {
|
||||
outline: 0;
|
||||
box-shadow: none;
|
||||
accent-color: #6d4aff;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Shell created by Raven for BorgWarehouse.
|
||||
# This shell takes 2 arguments : [SSH pub key] X [quota]
|
||||
# This shell takes 2 arguments : [SSH pub key] X [quota] x [append only mode (boolean)]
|
||||
# Main steps are :
|
||||
# - check if args are present
|
||||
# - check the ssh pub key format
|
||||
|
@ -31,8 +31,8 @@ pool="${home}/repos"
|
|||
authorized_keys="${home}/.ssh/authorized_keys"
|
||||
|
||||
# Check args
|
||||
if [ "$1" == "" ] || [ "$2" == "" ];then
|
||||
echo -n "This shell takes 2 arguments : SSH Public Key, Quota in Go [e.g. : 10] "
|
||||
if [ "$1" == "" ] || [ "$2" == "" ] || [ "$3" != "true" ] && [ "$3" != "false" ];then
|
||||
echo -n "This shell takes 3 arguments : SSH Public Key, Quota in Go [e.g. : 10], Append only mode [true|false]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
@ -45,6 +45,12 @@ then
|
|||
exit 2
|
||||
fi
|
||||
|
||||
## Check if authorized_keys exists
|
||||
if [ ! -f "${authorized_keys}" ];then
|
||||
echo -n "${authorized_keys} must be present"
|
||||
exit 5
|
||||
fi
|
||||
|
||||
# Check if SSH pub key is already present in authorized_keys
|
||||
if grep -q "$1" "$authorized_keys"; then
|
||||
echo -n "SSH pub key already present in authorized_keys"
|
||||
|
@ -63,14 +69,15 @@ randRepositoryName () {
|
|||
}
|
||||
repositoryName=$(randRepositoryName)
|
||||
|
||||
## Check if authorized_keys exists
|
||||
if [ ! -f "${authorized_keys}" ];then
|
||||
echo -n "${authorized_keys} must be present"
|
||||
exit 5
|
||||
# Append only mode
|
||||
if [ "$3" == "true" ]; then
|
||||
appendOnlyMode=" --append-only"
|
||||
else
|
||||
appendOnlyMode=""
|
||||
fi
|
||||
|
||||
## Add ssh public key in authorized_keys with borg restriction for only 1 repository and storage quota
|
||||
restricted_authkeys="command=\"cd ${pool};borg serve --restrict-to-path ${pool}/${repositoryName} --storage-quota $2G\",restrict $1"
|
||||
restricted_authkeys="command=\"cd ${pool};borg serve${appendOnlyMode} --restrict-to-path ${pool}/${repositoryName} --storage-quota $2G\",restrict $1"
|
||||
echo "$restricted_authkeys" | tee -a "${authorized_keys}" >/dev/null
|
||||
|
||||
## Return the repositoryName
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Shell created by Raven for BorgWarehouse.
|
||||
# This shell takes 3 args: [repositoryName] [new SSH pub key] [quota]
|
||||
# This shell takes 4 args: [repositoryName] [new SSH pub key] [quota] [append-only mode (boolean)]
|
||||
# This shell updates the SSH key and the quota for a repository.
|
||||
|
||||
# Exit when any command fails
|
||||
|
@ -16,8 +16,8 @@ fi
|
|||
: "${home:=/home/borgwarehouse}"
|
||||
|
||||
# Check args
|
||||
if [ "$1" == "" ] || [ "$2" == "" ] || [ "$3" == "" ]; then
|
||||
echo -n "This shell takes 3 args: [repositoryName] [new SSH pub key] [quota]"
|
||||
if [ "$1" == "" ] || [ "$2" == "" ] || [ "$3" == "" ] || [ "$4" != "true" ] && [ "$4" != "false" ]; then
|
||||
echo -n "This shell takes 4 args: [repositoryName] [new SSH pub key] [quota] [Append only mode [true|false]]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
@ -68,5 +68,12 @@ if [ "$found" = true ]; then
|
|||
exit 5
|
||||
fi
|
||||
|
||||
# Append only mode
|
||||
if [ "$4" == "true" ]; then
|
||||
sed -ri "/command=\".*${repositoryName}.*\",restrict/ {/borg serve .*--append-only /! s|(borg serve )|\1--append-only |}" "$home/.ssh/authorized_keys"
|
||||
elif [ "$4" == "false" ]; then
|
||||
sed -ri "/command=\".*${repositoryName}.*\",restrict/ s|(--append-only )||g" "$home/.ssh/authorized_keys"
|
||||
fi
|
||||
|
||||
# Modify authorized_keys for the repositoryName: update the line with the quota and the SSH pub key
|
||||
sed -ri "s|(command=\".*${repositoryName}.*--storage-quota ).*G\",restrict .*|\\1$3G\",restrict $2|g" "$home/.ssh/authorized_keys"
|
||||
sed -ri "s|(command=\".*${repositoryName}.*--storage-quota ).*G\",restrict .*|\\1$3G\",restrict $2|g" "$home/.ssh/authorized_keys"
|
||||
|
|
|
@ -16,10 +16,23 @@ export default async function handler(req, res) {
|
|||
}
|
||||
|
||||
//The data we expect to receive
|
||||
const { alias, sshPublicKey, size, comment, alert, lanCommand } =
|
||||
req.body;
|
||||
const {
|
||||
alias,
|
||||
sshPublicKey,
|
||||
size,
|
||||
comment,
|
||||
alert,
|
||||
lanCommand,
|
||||
appendOnlyMode,
|
||||
} = req.body;
|
||||
//We check that we receive data for each variable. Only "comment" and "lanCommand" are optional in the form.
|
||||
if (!alias || !sshPublicKey || !size || (!alert && alert !== 0)) {
|
||||
if (
|
||||
!alias ||
|
||||
!sshPublicKey ||
|
||||
!size ||
|
||||
typeof appendOnlyMode !== 'boolean' ||
|
||||
(!alert && alert !== 0)
|
||||
) {
|
||||
//If a variable is empty.
|
||||
res.status(422).json({
|
||||
message: 'Unexpected data',
|
||||
|
@ -60,6 +73,7 @@ export default async function handler(req, res) {
|
|||
comment: comment,
|
||||
displayDetails: true,
|
||||
lanCommand: lanCommand,
|
||||
appendOnlyMode: appendOnlyMode,
|
||||
};
|
||||
|
||||
////Call the shell : createRepo.sh
|
||||
|
@ -67,7 +81,7 @@ export default async function handler(req, res) {
|
|||
const shellsDirectory = path.join(process.cwd(), '/helpers');
|
||||
//Exec the shell
|
||||
const { stdout } = await exec(
|
||||
`${shellsDirectory}/shells/createRepo.sh "${newRepo.sshPublicKey}" ${newRepo.storageSize}`
|
||||
`${shellsDirectory}/shells/createRepo.sh "${newRepo.sshPublicKey}" ${newRepo.storageSize} ${newRepo.appendOnlyMode}`
|
||||
);
|
||||
|
||||
newRepo.repositoryName = stdout.trim();
|
||||
|
|
|
@ -16,15 +16,26 @@ export default async function handler(req, res) {
|
|||
}
|
||||
|
||||
//The data we expect to receive
|
||||
const { alias, sshPublicKey, size, comment, alert, lanCommand } =
|
||||
req.body;
|
||||
//We check that we receive data for each variable. Only "comment" and "lanCommand" are optional in the form.
|
||||
if (!alias || !sshPublicKey || !size || (!alert && alert !== 0)) {
|
||||
//If a variable is empty.
|
||||
const {
|
||||
alias,
|
||||
sshPublicKey,
|
||||
size,
|
||||
comment,
|
||||
alert,
|
||||
lanCommand,
|
||||
appendOnlyMode,
|
||||
} = req.body;
|
||||
//Only "comment" and "lanCommand" are optional in the form.
|
||||
if (
|
||||
!alias ||
|
||||
!sshPublicKey ||
|
||||
!size ||
|
||||
typeof appendOnlyMode !== 'boolean' ||
|
||||
(!alert && alert !== 0)
|
||||
) {
|
||||
res.status(422).json({
|
||||
message: 'Unexpected data',
|
||||
});
|
||||
//A return to make sure we don't go any further if data are incorrect.
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -49,7 +60,7 @@ export default async function handler(req, res) {
|
|||
const shellsDirectory = path.join(process.cwd(), '/helpers');
|
||||
// //Exec the shell
|
||||
await exec(
|
||||
`${shellsDirectory}/shells/updateRepo.sh ${repoList[repoIndex].repositoryName} "${sshPublicKey}" ${size}`
|
||||
`${shellsDirectory}/shells/updateRepo.sh ${repoList[repoIndex].repositoryName} "${sshPublicKey}" ${size} ${appendOnlyMode}`
|
||||
);
|
||||
|
||||
//Find the ID in the data and change the values transmitted by the form
|
||||
|
@ -63,6 +74,7 @@ export default async function handler(req, res) {
|
|||
comment: comment,
|
||||
alert: alert,
|
||||
lanCommand: lanCommand,
|
||||
appendOnlyMode: appendOnlyMode,
|
||||
}
|
||||
: repo
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue