Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
{
"name": "biigle/user-disks",
"description": "BIIGLE module to offer private storage disks for users.",
"keywords": ["biigle", "biigle-module"],
"keywords": [
"biigle",
"biigle-module"
],
"license": "GPL-3.0-only",
"support": {
"source": "https://github.com/biigle/user-disks",
"issues": "https://github.com/biigle/user-disks/issues"
"source": "https://github.com/biigle/user-disks",
"issues": "https://github.com/biigle/user-disks/issues"
},
"homepage": "https://biigle.de",
"authors": [
{
"name": "Martin Zurowietz",
"email": "[email protected]"
}
{
"name": "Martin Zurowietz",
"email": "[email protected]"
}
],
"require": {
"biigle/laravel-elements-storage": "^2.2",
"league/flysystem-aws-s3-v3": "^3.12",
"league/flysystem-read-only": "^3.3",
"biigle/laravel-webdav": "^1.0"
"biigle/laravel-webdav": "^1.0",
"azure-oss/storage-blob-laravel": "^1.4"
},
"autoload": {
"psr-4": {
"Biigle\\Modules\\UserDisks\\": "src"
}
"psr-4": {
"Biigle\\Modules\\UserDisks\\": "src"
}
},
"extra": {
"laravel": {
Expand Down
1 change: 1 addition & 0 deletions src/UserDisk.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class UserDisk extends Model
'webdav' => 'WebDAV',
'elements' => 'Elements',
'aruna' => 'Aruna',
'azure' => 'Azure Blob Storage',
];

/**
Expand Down
19 changes: 18 additions & 1 deletion src/config/user_disks.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

return [
/*
| Available types for new storage disks. Supported are: s3, webdav, elements, aruna.
| Available types for new storage disks. Supported are: s3, webdav, elements, aruna,
| azure.
*/
'types' => array_filter(explode(',', env('USER_DISKS_TYPES', 's3'))),

Expand Down Expand Up @@ -62,6 +63,12 @@
'secret' => '',
'endpoint' => '',
],

'azure' => [
'driver' => 'azure-storage-blob',
'connection_string' => '',
'container' => '',
],
],

/*
Expand Down Expand Up @@ -95,6 +102,11 @@
'key' => 'required',
'secret' => 'required',
],

'azure' => [
'connection_string' => 'required',
'container' => 'required',
],
],

/*
Expand Down Expand Up @@ -128,6 +140,11 @@
'key' => 'filled',
'secret' => 'filled',
],

'azure' => [
'connection_string' => 'filled',
'container' => 'filled',
],
],

/*
Expand Down
9 changes: 9 additions & 0 deletions src/resources/views/manual/tutorials/about.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@
<a href="#aruna">Aruna</a>
</li>
@endif
@if(in_array('azure', config('user_disks.types')))
<li>
<a href="#azure">Azure Blob Storage</a>
</li>
@endif
@if(empty(config('user_disks.types')))
<li class="text-muted">
No types are available. Please ask your administrator for help.
Expand All @@ -87,5 +92,9 @@
@if(in_array('aruna', config('user_disks.types')))
@include("user-disks::manual.types.aruna")
@endif

@if(in_array('azure', config('user_disks.types')))
@include("user-disks::manual.types.azure")
@endif
</div>
@endsection
39 changes: 39 additions & 0 deletions src/resources/views/manual/types/azure.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<h4><a name="azure"></a>Azure Blob Storage</h4>

<p>
Azure Blob Storage is Microsoft's object storage solution for the cloud. An Azure storage disk can connect to one storage container in Azure.
</p>

<p>
An Azure Blob Storage disk has the following options:
</p>

<dl>
<dt>SAS URL</dt>
<dd>
<p>
If you provide a SAS URL, BIIGLE will auto-fill the connection string and container options (see below). Alternatively, you can set these options directly.
</p>
</dd>
<dt>Connection String</dt>
<dd>
<p>
The Azure Storage connection string. You can find this in the Azure Portal under your Storage Account → Access keys.
</p>
<p>
Example:
<pre>DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=...;EndpointSuffix=core.windows.net</pre>
</p>
<p>
For local development with Azurite, use:
<pre>DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;</pre>
</p>
</dd>

<dt>Container</dt>
<dd>
<p>
The name of the container where your files are stored.
</p>
</dd>
</dl>
108 changes: 108 additions & 0 deletions src/resources/views/store/azure.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<div id="azure-disk-store-form">
<div class="col-xs-12">
<div class="form-group @error('sas_url') has-error @enderror">
<label>SAS URL (optional)</label>
<input v-model="url" type="text" class="form-control" placeholder="http://host:port/account/container?sv=...">
<p class="help-block">
Paste your full SAS URL here to auto-fill the connection string and container fields below.
</p>
@error('sas_url')
<p class="help-block">{{$message}}</p>
@enderror
</div>
</div>

<div class="col-xs-12">
<div class="form-group @error('connection_string') has-error @enderror">
<label>Connection String <span class="text-danger">*</span></label>
<textarea v-model="connectionString" name="connection_string" class="form-control" rows="3" placeholder="DefaultEndpointsProtocol=http;BlobEndpoint=...;SharedAccessSignature=..." :readonly="url.length > 0" required>{{old('connection_string')}}</textarea>
<p class="help-block">
Will be autofilled if SAS URL is given. You can find the connection string in the Azure Portal under your Storage Account → Security + networking → Access keys.
</p>
@error('connection_string')
<p class="help-block">{{$message}}</p>
@enderror
</div>
</div>

<div class="col-xs-12">
<div class="form-group @error('container') has-error @enderror">
<label>Container <span class="text-danger">*</span></label>
<input v-model="containerName" type="text" name="container" class="form-control" value="{{old('container')}}" placeholder="Container Name" :readonly="url.length > 0" required>
@error('container')
<p class="help-block">{{$message}}</p>
@enderror
</div>
</div>
</div>

@push('scripts')
<script type="module">
biigle.$mount('azure-disk-store-form', {
data() {
return {
url: '',
connectionString: '',
containerName: '',
};
},
computed: {
parsedUrl() {
let url = this.url.trim();

if (!url) {
return null;
}

url = new URL(url);
const pathParts = url.pathname.split('/').filter(p => p);

let containerName = '';
let blobEndpoint = '';
let protocol = url.protocol.replace(':', '');

// Check if it's standard Azure or Azurite format
if (url.hostname.endsWith('.blob.core.windows.net')) {
// Standard Azure: https://account.blob.core.windows.net/container?sas
containerName = pathParts[0] || '';
blobEndpoint = `${url.protocol}//${url.hostname}`;
} else {
// Azurite or custom: http://host:port/account/container?sas
if (pathParts.length >= 2) {
let accountName = pathParts[0];
containerName = pathParts[1];
blobEndpoint = `${url.protocol}//${url.host}/${accountName}`;
} else if (pathParts.length === 1) {
// Could be just container
containerName = pathParts[0];
blobEndpoint = `${url.protocol}//${url.host}`;
}
}

const sasToken = url.search ? url.search.substring(1) : '';

let connectionString = '';
if (blobEndpoint && sasToken) {
connectionString = `DefaultEndpointsProtocol=${protocol};BlobEndpoint=${blobEndpoint};SharedAccessSignature=${sasToken}`;
}

return [connectionString, containerName];
},
},
watch: {
parsedUrl(parsedUrl) {
this.connectionString = parsedUrl?.[0] || this.connectionString;
this.containerName = parsedUrl?.[1] || this.containerName;
},
},
created() {
this.connectionString = biigle.$require('azure.connectionString');
this.containerName = biigle.$require('azure.containerName');
}
});
</script>
<script type="module">
biigle.$declare('azure.connectionString', '{!!old('connection_string')!!}');
biigle.$declare('azure.containerName', '{!!old('container')!!}');
</script>
@endpush
109 changes: 109 additions & 0 deletions src/resources/views/update/azure.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<div id="azure-disk-update-form">
<div class="col-xs-12">
<div class="form-group @error('sas_url') has-error @enderror">
<label>SAS URL (optional)</label>
<input v-model="url" type="text" class="form-control" placeholder="http://host:port/account/container?sv=...">
<p class="help-block">
Paste your full SAS URL here to auto-fill the connection string and container fields below.
</p>
@error('sas_url')
<p class="help-block">{{$message}}</p>
@enderror
</div>
</div>

<div class="col-xs-12">
<div class="form-group @error('connection_string') has-error @enderror">
<label>Connection String</label>
<textarea v-model="connectionString" name="connection_string" class="form-control" rows="3" placeholder="DefaultEndpointsProtocol=http;BlobEndpoint=...;SharedAccessSignature=..." :readonly="url.length > 0">{{old('connection_string')}}</textarea>
<p class="help-block">
Will be autofilled if SAS URL is given. You can find the connection string in the Azure Portal under your Storage Account → Security + networking → Access keys. Leave empty to keep the current value.
</p>
@error('connection_string')
<p class="help-block">{{$message}}</p>
@enderror
</div>
</div>

<div class="col-xs-12">
<div class="form-group @error('container') has-error @enderror">
<label>Container <span class="text-danger">*</span></label>
<input v-model="containerName" type="text" name="container" class="form-control" value="{{old('container')}}" placeholder="Container Name" :readonly="url.length > 0" required>
@error('container')
<p class="help-block">{{$message}}</p>
@enderror
</div>
</div>
</div>

@push('scripts')
<script type="module">
biigle.$mount('azure-disk-update-form', {
data() {
return {
url: '',
connectionString: '',
containerName: '',
};
},
computed: {
parsedUrl() {
let url = this.url.trim();

if (!url) {
return null;
}

url = new URL(url);
const pathParts = url.pathname.split('/').filter(p => p);

let containerName = '';
let blobEndpoint = '';
let protocol = url.protocol.replace(':', '');

// Check if it's standard Azure or Azurite format
if (url.hostname.endsWith('.blob.core.windows.net')) {
// Standard Azure: https://account.blob.core.windows.net/container?sas
containerName = pathParts[0] || '';
blobEndpoint = `${url.protocol}//${url.hostname}`;
} else {
// Azurite or custom: http://host:port/account/container?sas
if (pathParts.length >= 2) {
let accountName = pathParts[0];
containerName = pathParts[1];
blobEndpoint = `${url.protocol}//${url.host}/${accountName}`;
} else if (pathParts.length === 1) {
// Could be just container
containerName = pathParts[0];
blobEndpoint = `${url.protocol}//${url.host}`;
}
}

const sasToken = url.search ? url.search.substring(1) : '';

let connectionString = '';
if (blobEndpoint && sasToken) {
connectionString = `DefaultEndpointsProtocol=${protocol};BlobEndpoint=${blobEndpoint};SharedAccessSignature=${sasToken}`;
}

return [connectionString, containerName];
},
},
watch: {
parsedUrl(parsedUrl) {
this.connectionString = parsedUrl?.[0] || this.connectionString;
this.containerName = parsedUrl?.[1] || this.containerName;
},
},
created() {
this.connectionString = biigle.$require('azure.connectionString');
this.containerName = biigle.$require('azure.containerName');
}
});
</script>
<script type="module">
biigle.$declare('azure.connectionString', '{!!old('connection_string')!!}');
biigle.$declare('azure.containerName', '{!!old('container', $disk->options['container'])!!}');
</script>
@endpush

Loading