Browse Source

Added max upload storage limits for user accounts

master
Teknikode 9 months ago
parent
commit
8954102306

+ 6
- 0
Configuration/UploadConfig.cs View File

public long MaxUploadSizePremium { get; set; } public long MaxUploadSizePremium { get; set; }
// Gets the maximum download size before they are forced to the download page // Gets the maximum download size before they are forced to the download page
public long MaxDownloadSize { get; set; } public long MaxDownloadSize { get; set; }
// Maximum total size for basic users
public long MaxTotalSizeBasic { get; set; }
// Maximum total size for basic users
public long MaxTotalSizePremium { get; set; }
// Location of the upload directory // Location of the upload directory
public string UploadDirectory { get; set; } public string UploadDirectory { get; set; }
// File Extension for saved files // File Extension for saved files
MaxUploadSizeBasic = 100000000; MaxUploadSizeBasic = 100000000;
MaxUploadSizePremium = 100000000; MaxUploadSizePremium = 100000000;
MaxDownloadSize = 100000000; MaxDownloadSize = 100000000;
MaxTotalSizeBasic = 1000000000;
MaxTotalSizePremium = 5000000000;
UploadDirectory = Directory.GetCurrentDirectory(); UploadDirectory = Directory.GetCurrentDirectory();
FileExtension = "enc"; FileExtension = "enc";
UrlLength = 5; UrlLength = 5;

+ 13
- 1
Teknik/Areas/API/V1/Controllers/UploadAPIv1Controller.cs View File

if (User.Identity.IsAuthenticated) if (User.Identity.IsAuthenticated)
{ {
maxUploadSize = _config.UploadConfig.MaxUploadSizeBasic; maxUploadSize = _config.UploadConfig.MaxUploadSizeBasic;
long maxTotalSize = _config.UploadConfig.MaxTotalSizeBasic;
IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, User.Identity.Name); IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, User.Identity.Name);
if (userInfo.AccountType == AccountType.Premium) if (userInfo.AccountType == AccountType.Premium)
{ {
maxUploadSize = _config.UploadConfig.MaxUploadSizePremium; maxUploadSize = _config.UploadConfig.MaxUploadSizePremium;
maxTotalSize = _config.UploadConfig.MaxTotalSizePremium;
}

// Check account total limits
var user = UserHelper.GetUser(_dbContext, User.Identity.Name);
if (user.UploadSettings.MaxUploadStorage != null)
maxTotalSize = user.UploadSettings.MaxUploadStorage.Value;
var userUploadSize = user.Uploads.Sum(u => u.ContentLength);
if (userUploadSize + model.file.Length > maxTotalSize)
{
return Json(new { error = new { message = string.Format("Account storage limit exceeded: {0} / {1}", StringHelper.GetBytesReadable(userUploadSize + model.file.Length), StringHelper.GetBytesReadable(maxTotalSize)) } });
} }
} }
else else
} }
else else
{ {
return Json(new { error = new { message = "File Too Large" } });
return Json(new { error = new { message = "File Too Large. Max file size is " + StringHelper.GetBytesReadable(maxUploadSize) } });
} }
} }
return Json(new { error = new { message = "Invalid Upload Request" } }); return Json(new { error = new { message = "Invalid Upload Request" } });

+ 6
- 0
Teknik/Areas/About/Views/About/Index.cshtml View File

<td class="text-center">@StringHelper.GetBytesReadable(Config.UploadConfig.MaxDownloadSize)</td> <td class="text-center">@StringHelper.GetBytesReadable(Config.UploadConfig.MaxDownloadSize)</td>
<td class="text-center">@StringHelper.GetBytesReadable(Config.UploadConfig.MaxDownloadSize)</td> <td class="text-center">@StringHelper.GetBytesReadable(Config.UploadConfig.MaxDownloadSize)</td>
</tr> </tr>
<tr>
<td class="text-left">Upload Storage</td>
<td class="text-center">@StringHelper.GetBytesReadable(Config.UploadConfig.MaxTotalSizePremium)</td>
<td class="text-center">@StringHelper.GetBytesReadable(Config.UploadConfig.MaxTotalSizeBasic)</td>
<td class="text-center">No Limit</td>
</tr>
<tr> <tr>
<td class="text-left">Upload Expiration</td> <td class="text-left">Upload Expiration</td>
<td class="text-center">Never</td> <td class="text-center">Never</td>

+ 12
- 14
Teknik/Areas/TOS/Views/TOS/Index.cshtml View File

<h3>User Accounts</h3> <h3>User Accounts</h3>
<ul> <ul>
<li>You will not display or host links to Malware in your user profile</li> <li>You will not display or host links to Malware in your user profile</li>
<li>Inactive Basic Accounts with no data will be deleted after 365 days (1 Year)
<li>
Inactive Basic Accounts with no data may be deleted after 365 days (1 Year)
<ul> <ul>
<li>Activity is defined as logging into the Teknik Website, into the Git website, into the mail service (Either through webmail or via another client), or Modifying/Adding a Git Repository</li> <li>Activity is defined as logging into the Teknik Website, into the Git website, into the mail service (Either through webmail or via another client), or Modifying/Adding a Git Repository</li>
<li>Data is defined as Blog Posts/Comments, Podcast Comments, Emails, and Git Repositories</li> <li>Data is defined as Blog Posts/Comments, Podcast Comments, Emails, and Git Repositories</li>
</ul> </ul>
</li> </li>
<li>Creating multiple accounts to get around account limits will result in the account(s) being banned without notice</li>
<li>If your account has been banned, and you create a new account to get around said ban, the new account will be banned without notice</li>
</ul> </ul>
<h3>Premium Accounts</h3> <h3>Premium Accounts</h3>
<ul> <ul>
</ul> </ul>
<h3>Email</h3> <h3>Email</h3>
<ul> <ul>
<li>Email is limited to a maximum of 100 outbound email messages per day. This is to prevent spam accounts. If your account is flagged as spamming, it will be deleted without notice.</li>
<li>Premium Accounts are allowed unlimited outbound email messages, but need to follow the same policy of no spam.</li>
<li>Email is limited to a maximum of 100 outbound email messages per day. This is to prevent spam accounts. If your account is flagged as spamming, it will be banned without notice</li>
<li>Premium Accounts are allowed unlimited outbound email messages, but need to follow the same policy of no spam</li>
</ul> </ul>
<h3>Uploads</h3>
<h3>
Services<br />
<small>This includes but does not limit to: Uploads, Pastes, Vaults, Blogging, and Shortened URLs</small>
</h3>
<ul> <ul>
<li>This site may contain material your region may consider illegal. If you are viewing content, be mindful of the laws in your region</li> <li>This site may contain material your region may consider illegal. If you are viewing content, be mindful of the laws in your region</li>
<li>Any Malware uploads will be removed without notice</li>
<li>Any content containing or linking to malware/phishing sites will be removed without notice</li>
<li>Copyrighted or Personal content will only be removed if it is deemed necessary for a person(s) safety</li> <li>Copyrighted or Personal content will only be removed if it is deemed necessary for a person(s) safety</li>
</ul> </ul>
<h3>Pastebin</h3>
<ul>
<li>Copyrighted or Personal content will only be removed if it is demed necessary for a person(s) safety</li>
</ul>
<h3>Shortened URLs</h3>
<ul>
<li>No linking to Phishing Sites or Malware</li>
</ul>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
gitFullUrl = gitUrl.ToString(); gitFullUrl = gitUrl.ToString();
} }
} }
<p><i>Last Modified July 11, 2017 - <a href="@gitFullUrl">View History</a></i></p>
<p><i>Last Modified January 20, 2020 - <a href="@gitFullUrl">View History</a></i></p>
</div> </div>
</div> </div>
</div> </div>

+ 26
- 1
Teknik/Areas/Upload/Controllers/UploadController.cs View File

model.ExpirationUnit = user.UploadSettings.ExpirationUnit; model.ExpirationUnit = user.UploadSettings.ExpirationUnit;
model.Vaults = user.Vaults.ToList(); model.Vaults = user.Vaults.ToList();


model.CurrentTotalSize = user.Uploads.Sum(u => u.ContentLength);

model.MaxUploadSize = _config.UploadConfig.MaxUploadSizeBasic; model.MaxUploadSize = _config.UploadConfig.MaxUploadSizeBasic;
model.MaxTotalSize = _config.UploadConfig.MaxTotalSizeBasic;
IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, User.Identity.Name); IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, User.Identity.Name);
if (userInfo.AccountType == AccountType.Premium) if (userInfo.AccountType == AccountType.Premium)
{ {
model.MaxUploadSize = _config.UploadConfig.MaxUploadSizePremium; model.MaxUploadSize = _config.UploadConfig.MaxUploadSizePremium;
model.MaxTotalSize = _config.UploadConfig.MaxTotalSizePremium;
}
if (user.UploadSettings.MaxUploadStorage != null)
model.MaxTotalSize = user.UploadSettings.MaxUploadStorage.Value;

if (model.CurrentTotalSize >= model.MaxTotalSize)
{
model.Error = true;
model.ErrorMessage = string.Format("Account storage limit exceeded: {0} / {1}", StringHelper.GetBytesReadable(model.CurrentTotalSize), StringHelper.GetBytesReadable(model.MaxTotalSize));
} }
} }
} }
if (User.Identity.IsAuthenticated) if (User.Identity.IsAuthenticated)
{ {
maxUploadSize = _config.UploadConfig.MaxUploadSizeBasic; maxUploadSize = _config.UploadConfig.MaxUploadSizeBasic;
long maxTotalSize = _config.UploadConfig.MaxTotalSizeBasic;
IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, User.Identity.Name); IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, User.Identity.Name);
if (userInfo.AccountType == AccountType.Premium) if (userInfo.AccountType == AccountType.Premium)
{ {
maxUploadSize = _config.UploadConfig.MaxUploadSizePremium; maxUploadSize = _config.UploadConfig.MaxUploadSizePremium;
maxTotalSize = _config.UploadConfig.MaxTotalSizePremium;
}

// Check account total limits
var user = UserHelper.GetUser(_dbContext, User.Identity.Name);
if (user.UploadSettings.MaxUploadStorage != null)
maxTotalSize = user.UploadSettings.MaxUploadStorage.Value;
var userUploadSize = user.Uploads.Sum(u => u.ContentLength);
if (userUploadSize + uploadFile.file.Length > maxTotalSize)
{
return Json(new { error = new { message = string.Format("Account storage limit exceeded. {0} / {1}", StringHelper.GetBytesReadable(userUploadSize + uploadFile.file.Length), StringHelper.GetBytesReadable(maxTotalSize)) } });
} }
} }
else else
url = Url.SubRouteUrl("u", "Upload.Download", new { file = upload.Url }), url = Url.SubRouteUrl("u", "Upload.Download", new { file = upload.Url }),
contentType = upload.ContentType, contentType = upload.ContentType,
contentLength = StringHelper.GetBytesReadable(upload.ContentLength), contentLength = StringHelper.GetBytesReadable(upload.ContentLength),
contentLengthRaw = upload.ContentLength,
deleteUrl = Url.SubRouteUrl("u", "Upload.DeleteByKey", new { file = upload.Url, key = upload.DeleteKey }), deleteUrl = Url.SubRouteUrl("u", "Upload.DeleteByKey", new { file = upload.Url, key = upload.DeleteKey }),
expirationUnit = uploadFile.options.ExpirationUnit.ToString(), expirationUnit = uploadFile.options.ExpirationUnit.ToString(),
expirationLength = uploadFile.options.ExpirationLength expirationLength = uploadFile.options.ExpirationLength
} }
else else
{ {
return Json(new { error = new { message = "File Too Large" } });
return Json(new { error = new { message = "File Too Large. Max file size is " + StringHelper.GetBytesReadable(maxUploadSize) } });
} }
} }
return Json(new { error = new { message = "Uploads are disabled" } }); return Json(new { error = new { message = "Uploads are disabled" } });

+ 6
- 0
Teknik/Areas/Upload/ViewModels/UploadViewModel.cs View File



public long MaxUploadSize { get; set; } public long MaxUploadSize { get; set; }


public long MaxTotalSize { get; set; }

public long CurrentTotalSize { get; set; }

public UploadViewModel() public UploadViewModel()
{ {
CurrentSub = string.Empty; CurrentSub = string.Empty;
ExpirationUnit = ExpirationUnit.Never; ExpirationUnit = ExpirationUnit.Never;
Vaults = new List<Vault.Models.Vault>(); Vaults = new List<Vault.Models.Vault>();
MaxUploadSize = 0; MaxUploadSize = 0;
MaxTotalSize = -1;
CurrentTotalSize = 0;
} }
} }
} }

+ 151
- 120
Teknik/Areas/Upload/Views/Upload/Index.cshtml View File

var aesScriptSrc = '@Url.FullURL("~/js/crypto.min.js")'; var aesScriptSrc = '@Url.FullURL("~/js/crypto.min.js")';
var uploadFileURL = '@Url.SubRouteUrl(Model.CurrentSub, "Upload.Action", new { action = "Upload" })'; var uploadFileURL = '@Url.SubRouteUrl(Model.CurrentSub, "Upload.Action", new { action = "Upload" })';
var maxUploadSize = @Model.MaxUploadSize; var maxUploadSize = @Model.MaxUploadSize;
var maxTotalSize = @Model.MaxTotalSize;
var curTotalSize = @Model.CurrentTotalSize;
var chunkSize = @Config.UploadConfig.ChunkSize; var chunkSize = @Config.UploadConfig.ChunkSize;
var keySize = @Config.UploadConfig.KeySize; var keySize = @Config.UploadConfig.KeySize;
var blockSize = @Config.UploadConfig.BlockSize; var blockSize = @Config.UploadConfig.BlockSize;


<bundle src="css/upload.min.css" append-version="true" /> <bundle src="css/upload.min.css" append-version="true" />


<div class="container">
<div class="row text-center">
<div class="col-sm-10 col-sm-offset-1">
<div class="row text-center">
<div class="text-center upload__drop-container" id="uploadButton" style="cursor: pointer">
<div class="row">
<div class="col-sm-12">
<h3 class="upload__drop-text"><span class="fa fa-cloud-upload fa-5x"></span></h3>
@if (!Model.Error)
{
<div class="container">
<div class="row text-center">
<div class="col-sm-10 col-sm-offset-1">
<div class="row text-center">
<div class="text-center upload__drop-container" id="uploadButton" style="cursor: pointer">
<div class="row">
<div class="col-sm-12">
<h3 class="upload__drop-text"><span class="fa fa-cloud-upload fa-5x"></span></h3>
</div>
</div> </div>
</div>
<div class="row">
<div class="col-sm-12">
<h2 class="upload__drop-text">DRAG &amp; DROP</h2>
<div class="row">
<div class="col-sm-12">
<h2 class="upload__drop-text">DRAG &amp; DROP</h2>
</div>
</div> </div>
</div>
<div class="row">
<div class="col-sm-12">
<span class="upload__drop-text">Or select files from your system</span>
<div class="row">
<div class="col-sm-12">
<span class="upload__drop-text">Or select files from your system</span>
</div>
</div> </div>
</div>
<br />
<div class="row">
<div class="col-sm-12">
<small class="upload__drop-text text-muted">(Individual file size limit @StringHelper.GetBytesReadable(Model.MaxUploadSize))</small>
<br />
<div class="row">
<div class="col-sm-12">
<small class="upload__drop-text text-muted">(Individual file size limit @StringHelper.GetBytesReadable(Model.MaxUploadSize))</small>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-sm-1 hidden-xs upload__help-icon">
<a href="@Url.SubRouteUrl("help", "Help.Upload")" target="_blank"><i class="fa fa-question-circle-o"></i></a>
</div>
</div> </div>
<div class="col-sm-1 hidden-xs upload__help-icon">
<a href="@Url.SubRouteUrl("help", "Help.Upload")" target="_blank"><i class="fa fa-question-circle-o"></i></a>
</div>
</div>


<div class="row form-horizontal">
<div class="col-sm-5 col-sm-offset-1">
<div class="form-group form-group-sm" data-toggle="tooltip" data-placement="top" title="When enabled, each file is encrypted before upload using an AES-256-CTR cipher. A key is generated and required to download the file later.">
<label for="encrypt" class="col-sm-6 col-xs-3 control-label">Encrypt in Browser</label>
<div class="col-sm-6 col-xs-9">
<input type="checkbox" class="center-block" name="encrypt" id="encrypt" @(Model.Encrypt ? "checked" : string.Empty) />
<div class="row form-horizontal">
<div class="col-sm-5 col-sm-offset-1">
<div class="form-group form-group-sm" data-toggle="tooltip" data-placement="top" title="When enabled, each file is encrypted before upload using an AES-256-CTR cipher. A key is generated and required to download the file later.">
<label for="encrypt" class="col-sm-6 col-xs-3 control-label">Encrypt in Browser</label>
<div class="col-sm-6 col-xs-9">
<input type="checkbox" class="center-block" name="encrypt" id="encrypt" @(Model.Encrypt ? "checked" : string.Empty) />
</div>
</div>
</div>
<div class="col-sm-5">
<div class="form-group form-group-sm">
@if (!User.Identity.IsAuthenticated)
{
@:<label class="col-xs-3 control-label" data-toggle="tooltip" data-placement="top" title="Uploads expire after 1 day for unregistered users">Expiration*</label>
}
else
{
@:<label class="col-xs-3 control-label">Expiration</label>
}
<fieldset @if (!User.Identity.IsAuthenticated) { @: disabled
}>
<div class="col-xs-4 hidden" id="length-div">
<input type="number" min="1" step="1" class="form-control" name="expirelength" id="expirelength" value="@Model.ExpirationLength">
</div>
<div class="col-xs-9" id="unit-div">
<select class="form-control" name="expireunit" id="expireunit">
@foreach (ExpirationUnit unit in Enum.GetValues(typeof(ExpirationUnit)))
{
<!option value="@unit" @(Model.ExpirationUnit == unit ? "selected" : string.Empty)>@unit.ToString()</!option>
}
</select>
</div>
</fieldset>
</div> </div>
</div> </div>
</div> </div>
<div class="col-sm-5">
<div class="form-group form-group-sm">
@if (!User.Identity.IsAuthenticated)
{
@:<label class="col-xs-3 control-label" data-toggle="tooltip" data-placement="top" title="Uploads expire after 1 day for unregistered users">Expiration*</label>
}
else
{
@:<label class="col-xs-3 control-label">Expiration</label>
}
<fieldset @if (!User.Identity.IsAuthenticated) { @: disabled
}>
<div class="col-xs-4 hidden" id="length-div">
<input type="number" min="1" step="1" class="form-control" name="expirelength" id="expirelength" value="@Model.ExpirationLength">
</div>
<div class="col-xs-9" id="unit-div">
<select class="form-control" name="expireunit" id="expireunit">
@foreach (ExpirationUnit unit in Enum.GetValues(typeof(ExpirationUnit)))

<div class="row upload__action-buttons" id="upload-action-buttons" style="display: none">
<div class="col-sm-12">
<div class="btn-group pull-right" role="group">
<button type="button" class="btn btn-default btn-sm" id="copy-all" data-toggle="popover" data-trigger="manual" data-placement="top" data-content="Copied to Clipboard" data-container="body"><i class="fa fa-clipboard"></i>&nbsp;Copy All Links</button>
<button type="button" class="btn btn-default btn-sm" id="create-vault"><i class="fa fa-folder"></i>&nbsp;Create Vault</button>
@if (User.Identity.IsAuthenticated && Model.Vaults != null && Model.Vaults.Any())
{
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown"><i class="fa fa-plus"></i>&nbsp;Add to Vault <span class="caret"></span></button>
<ul class="dropdown-menu" id="add-to-vault-menu">
@foreach (Vault item in Model.Vaults)
{ {
<!option value="@unit" @(Model.ExpirationUnit == unit ? "selected" : string.Empty)>@unit.ToString()</!option>
<li><a href="#" class="add-to-vault" data-add-to-vault-url="@Url.SubRouteUrl("vault", "Vault.EditVault", new { url = item.Url, type = "Upload" })">@item.Title</a></li>
} }
</select>
</div>
</fieldset>
</ul>
}
</div>
</div> </div>
</div> </div>
</div>

<div class="row upload__action-buttons" id="upload-action-buttons" style="display: none">
<div class="col-sm-12">
<div class="btn-group pull-right" role="group">
<button type="button" class="btn btn-default btn-sm" id="copy-all" data-toggle="popover" data-trigger="manual" data-placement="top" data-content="Copied to Clipboard" data-container="body"><i class="fa fa-clipboard"></i>&nbsp;Copy All Links</button>
<button type="button" class="btn btn-default btn-sm" id="create-vault"><i class="fa fa-folder"></i>&nbsp;Create Vault</button>
@if (User.Identity.IsAuthenticated && Model.Vaults != null && Model.Vaults.Any())
{
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown"><i class="fa fa-plus"></i>&nbsp;Add to Vault <span class="caret"></span></button>
<ul class="dropdown-menu" id="add-to-vault-menu">
@foreach (Vault item in Model.Vaults)
{
<li><a href="#" class="add-to-vault" data-add-to-vault-url="@Url.SubRouteUrl("vault", "Vault.EditVault", new { url = item.Url, type = "Upload" })">@item.Title</a></li>
}
</ul>
}
</div>
<div class="container" id="upload-links">
</div>
<br />
<div class="text-center">
Useful Tools: <a href="@Url.SubRouteUrl("help", "Help.Tools")">Upload Scripts and Utilities</a>
</div> </div>
</div> </div>
<div class="container" id="upload-links">
</div>
<br />
<div class="text-center">
Useful Tools: <a href="@Url.SubRouteUrl("help", "Help.Tools")">Upload Scripts and Utilities</a>
</div>
</div>


<div id="templates" style="display: none">
<div class="row" id="upload-template">
<div class="col-sm-12">
<div class="panel panel-default">
<div class="panel-heading text-center" id="upload-header">
<div class="row">
<div class="col-sm-10 col-sm-offset-1" id="upload-title"></div>
<div class="col-sm-1">
<button type="button" class="close pull-right" id="upload-close"><i class="fa fa-times-circle"></i></button>
<div id="templates" style="display: none">
<div class="row" id="upload-template">
<div class="col-sm-12">
<div class="panel panel-default">
<div class="panel-heading text-center" id="upload-header">
<div class="row">
<div class="col-sm-10 col-sm-offset-1" id="upload-title"></div>
<div class="col-sm-1">
<button type="button" class="close pull-right" id="upload-close"><i class="fa fa-times-circle"></i></button>
</div>
</div> </div>
</div> </div>
</div>
<div class="panel-body" id="upload-details">
<div class="row" id="upload-link-panel">
<input type="hidden" id="upload-url" />
<div class="col-sm-8">
<dl class="dl-horizontal" style="margin-bottom: 0;">
<dt>Url</dt>
<dd><a href="#" id="upload-link" target="_blank" class="alert-link"></a></dd>
<dt>Content-Type</dt>
<dd id="upload-contentType"></dd>
<dt>File Size</dt>
<dd id="upload-contentLength"></dd>
<dt>Expiration</dt>
<dd id="upload-expiration"></dd>
</dl>
</div>
<div class="col-sm-4">
<div class="btn-group pull-right" role="group">
<button type="button" class="btn btn-default btn-sm" id="shortenUrl"><i class="fa fa-link"></i>&nbsp;Shorten</button>
<button type="button" class="btn btn-default btn-sm" id="delete-link"><i class="fa fa-trash"></i>&nbsp;Deletion Link</button>
<div class="panel-body" id="upload-details">
<div class="row" id="upload-link-panel">
<input type="hidden" id="upload-url" />
<div class="col-sm-8">
<dl class="dl-horizontal" style="margin-bottom: 0;">
<dt>Url</dt>
<dd><a href="#" id="upload-link" target="_blank" class="alert-link"></a></dd>
<dt>Content-Type</dt>
<dd id="upload-contentType"></dd>
<dt>File Size</dt>
<dd id="upload-contentLength"></dd>
<dt>Expiration</dt>
<dd id="upload-expiration"></dd>
</dl>
</div>
<div class="col-sm-4">
<div class="btn-group pull-right" role="group">
<button type="button" class="btn btn-default btn-sm" id="shortenUrl"><i class="fa fa-link"></i>&nbsp;Shorten</button>
<button type="button" class="btn btn-default btn-sm" id="delete-link"><i class="fa fa-trash"></i>&nbsp;Deletion Link</button>
</div>
</div> </div>
</div> </div>
</div>
<div class="row" id="upload-progress-panel">
<div class="col-sm-12">
<div class="row">
<div class="col-sm-12 text-center">
<div class="progress" id="progress">
<div id="progress-bar" class="progress-bar" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div>
<div class="row" id="upload-progress-panel">
<div class="col-sm-12">
<div class="row">
<div class="col-sm-12 text-center">
<div class="progress" id="progress">
<div id="progress-bar" class="progress-bar" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div>
</div>
</div> </div>
</div> </div>
</div>
<div class="row">
<div class="col-sm-10 col-sm-offset-1 text-center">
<span id="progress-info"></span>
<div class="row">
<div class="col-sm-10 col-sm-offset-1 text-center">
<span id="progress-info"></span>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>


<bundle src="js/upload.min.js" append-version="true" />
<bundle src="js/upload.min.js" append-version="true" />
}
else
{
<div class="container">
<div class="row text-center">
<div class="col-sm-10 col-sm-offset-1">
<div class="row text-center">
<div class="text-center upload__drop-container-error" id="uploadButton" style="cursor: not-allowed">
<div class="row">
<div class="col-sm-12">
<h3 class="upload__drop-text"><span class="fa fa-cloud-upload fa-5x"></span></h3>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<h2 class="upload__drop-text">@Model.ErrorMessage</h2>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-1 hidden-xs upload__help-icon">
<a href="@Url.SubRouteUrl("help", "Help.Upload")" target="_blank"><i class="fa fa-question-circle-o"></i></a>
</div>
</div>
</div>
}

+ 4
- 0
Teknik/Areas/User/Models/UploadSettings.cs View File

[Column("ExpirationUnit")] [Column("ExpirationUnit")]
public ExpirationUnit ExpirationUnit { get; set; } public ExpirationUnit ExpirationUnit { get; set; }


[Column("MaxUploadStorage")]
public long? MaxUploadStorage { get; set; }

public UploadSettings() public UploadSettings()
{ {
Encrypt = false; Encrypt = false;
ExpirationLength = 1; ExpirationLength = 1;
ExpirationUnit = ExpirationUnit.Never; ExpirationUnit = ExpirationUnit.Never;
MaxUploadStorage = null;
} }
} }
} }

+ 5
- 0
Teknik/Areas/User/Views/User/ViewServiceData.cshtml View File

</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<div class="tab-pane active" id="uploads"> <div class="tab-pane active" id="uploads">
<br />
<div class="row">
<div class="col col-sm-12"><p>Total Upload Size: @StringHelper.GetBytesReadable(Model.Uploads.Sum(u => u.ContentLength))</p></div>
</div>
<br />
@foreach (Teknik.Areas.Upload.Models.Upload upload in Model.Uploads) @foreach (Teknik.Areas.Upload.Models.Upload upload in Model.Uploads)
{ {
<div class="panel panel-default" id="@upload.Url"> <div class="panel panel-default" id="@upload.Url">

+ 13
- 0
Teknik/Content/Upload/Upload.css View File

opacity: 1; opacity: 1;
} }


.upload__drop-container-error {
border: 2px dashed #dc3545;
background-color: #f5f5f5;
padding-top: 20px;
padding-bottom: 20px;
margin: 20px;
}

.upload__drop-container .upload__drop-text {
opacity: 1;
margin: 0;
}

.upload__help-icon { .upload__help-icon {
margin: 20px 0; margin: 20px 0;
} }

+ 772
- 0
Teknik/Data/Migrations/20200121080120_MaxUploadStorage.Designer.cs View File

// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Teknik.Data;

namespace Teknik.Data.Migrations
{
[DbContext(typeof(TeknikEntities))]
[Migration("20200121080120_MaxUploadStorage")]
partial class MaxUploadStorage
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.6-servicing-10079")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

modelBuilder.Entity("Teknik.Areas.Blog.Models.Blog", b =>
{
b.Property<int>("BlogId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<int>("UserId");

b.HasKey("BlogId");

b.HasIndex("UserId");

b.ToTable("Blogs");
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPost", b =>
{
b.Property<int>("BlogPostId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Article");

b.Property<int>("BlogId");

b.Property<DateTime>("DateEdited");

b.Property<DateTime>("DatePosted");

b.Property<DateTime>("DatePublished");

b.Property<bool>("Published");

b.Property<bool>("System");

b.Property<string>("Title");

b.HasKey("BlogPostId");

b.HasIndex("BlogId");

b.ToTable("BlogPosts");
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPostComment", b =>
{
b.Property<int>("BlogPostCommentId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Article");

b.Property<int>("BlogPostId");

b.Property<DateTime>("DateEdited");

b.Property<DateTime>("DatePosted");

b.Property<int?>("UserId");

b.HasKey("BlogPostCommentId");

b.HasIndex("BlogPostId");

b.HasIndex("UserId");

b.ToTable("BlogPostComments");
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPostTag", b =>
{
b.Property<int>("BlogPostTagId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<int>("BlogPostId");

b.Property<string>("Description");

b.Property<string>("Name");

b.HasKey("BlogPostTagId");

b.HasIndex("BlogPostId");

b.ToTable("BlogPostTags");
});

modelBuilder.Entity("Teknik.Areas.Contact.Models.Contact", b =>
{
b.Property<int>("ContactId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<DateTime>("DateAdded");

b.Property<string>("Email");

b.Property<string>("Message");

b.Property<string>("Name");

b.Property<string>("Subject");

b.HasKey("ContactId");

b.ToTable("Contact");
});

modelBuilder.Entity("Teknik.Areas.Paste.Models.Paste", b =>
{
b.Property<int>("PasteId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<int>("BlockSize");

b.Property<string>("Content");

b.Property<DateTime>("DateEdited");

b.Property<DateTime>("DatePosted");

b.Property<string>("DeleteKey")
.HasAnnotation("CaseSensitive", true);

b.Property<DateTime?>("ExpireDate");

b.Property<string>("FileName")
.HasAnnotation("CaseSensitive", true);

b.Property<string>("HashedPassword")
.HasAnnotation("CaseSensitive", true);

b.Property<string>("IV")
.HasAnnotation("CaseSensitive", true);

b.Property<string>("Key")
.HasAnnotation("CaseSensitive", true);

b.Property<int>("KeySize");

b.Property<int>("MaxViews");

b.Property<string>("Syntax");

b.Property<string>("Title");

b.Property<string>("Url")
.HasAnnotation("CaseSensitive", true);

b.Property<int?>("UserId");

b.Property<int>("Views");

b.HasKey("PasteId");

b.HasIndex("UserId");

b.ToTable("Pastes");
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.Podcast", b =>
{
b.Property<int>("PodcastId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<DateTime>("DateEdited");

b.Property<DateTime>("DatePosted");

b.Property<DateTime>("DatePublished");

b.Property<string>("Description");

b.Property<int>("Episode");

b.Property<bool>("Published");

b.Property<string>("Title");

b.HasKey("PodcastId");

b.ToTable("Podcasts");
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastComment", b =>
{
b.Property<int>("PodcastCommentId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Article");

b.Property<DateTime>("DateEdited");

b.Property<DateTime>("DatePosted");

b.Property<int>("PodcastId");

b.Property<int>("UserId");

b.HasKey("PodcastCommentId");

b.HasIndex("PodcastId");

b.HasIndex("UserId");

b.ToTable("PodcastComments");
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastFile", b =>
{
b.Property<int>("PodcastFileId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<long>("ContentLength");

b.Property<string>("ContentType");

b.Property<string>("FileName");

b.Property<string>("Path");

b.Property<int>("PodcastId");

b.Property<int>("Size");

b.HasKey("PodcastFileId");

b.HasIndex("PodcastId");

b.ToTable("PodcastFiles");
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastTag", b =>
{
b.Property<int>("PodcastTagId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Description");

b.Property<string>("Name");

b.Property<int>("PodcastId");

b.HasKey("PodcastTagId");

b.HasIndex("PodcastId");

b.ToTable("PodcastTags");
});

modelBuilder.Entity("Teknik.Areas.Shortener.Models.ShortenedUrl", b =>
{
b.Property<int>("ShortenedUrlId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<DateTime>("DateAdded");

b.Property<string>("OriginalUrl");

b.Property<string>("ShortUrl")
.HasAnnotation("CaseSensitive", true);

b.Property<int?>("UserId");

b.Property<int>("Views");

b.HasKey("ShortenedUrlId");

b.HasIndex("UserId");

b.ToTable("ShortenedUrls");
});

modelBuilder.Entity("Teknik.Areas.Stats.Models.Takedown", b =>
{
b.Property<int>("TakedownId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("ActionTaken");

b.Property<DateTime>("DateActionTaken");

b.Property<DateTime>("DateRequested");

b.Property<string>("Reason");

b.Property<string>("Requester");

b.Property<string>("RequesterContact");

b.HasKey("TakedownId");

b.ToTable("Takedowns");
});

modelBuilder.Entity("Teknik.Areas.Stats.Models.Transaction", b =>
{
b.Property<int>("TransactionId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<decimal>("Amount")
.HasColumnType("decimal(19, 5)");

b.Property<int>("Currency");

b.Property<DateTime>("DateSent");

b.Property<string>("Reason");

b.HasKey("TransactionId");

b.ToTable("Transactions");
});

modelBuilder.Entity("Teknik.Areas.Upload.Models.Upload", b =>
{
b.Property<int>("UploadId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<int>("BlockSize");

b.Property<long>("ContentLength");

b.Property<string>("ContentType");

b.Property<DateTime>("DateUploaded");

b.Property<string>("DeleteKey")
.HasAnnotation("CaseSensitive", true);

b.Property<int>("Downloads");

b.Property<DateTime?>("ExpireDate");

b.Property<string>("FileName")
.HasAnnotation("CaseSensitive", true);

b.Property<string>("IV")
.HasAnnotation("CaseSensitive", true);

b.Property<string>("Key")
.HasAnnotation("CaseSensitive", true);

b.Property<int>("KeySize");

b.Property<int>("MaxDownloads");

b.Property<int?>("Takedown_TakedownId");

b.Property<string>("Url")
.HasAnnotation("CaseSensitive", true);

b.Property<int?>("UserId");

b.HasKey("UploadId");

b.HasIndex("Takedown_TakedownId");

b.HasIndex("UserId");

b.ToTable("Uploads");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.Feature", b =>
{
b.Property<string>("FeatureId")
.ValueGeneratedOnAdd();

b.Property<string>("Description");

b.Property<bool>("Enabled");

b.Property<string>("Name");

b.HasKey("FeatureId");

b.ToTable("Features");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.InviteCode", b =>
{
b.Property<int>("InviteCodeId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<bool>("Active");

b.Property<int?>("ClaimedUserId");

b.Property<string>("Code")
.HasAnnotation("CaseSensitive", true);

b.Property<int?>("OwnerId");

b.HasKey("InviteCodeId");

b.HasIndex("ClaimedUserId")
.IsUnique()
.HasFilter("[ClaimedUserId] IS NOT NULL");

b.HasIndex("OwnerId");

b.ToTable("InviteCodes");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.User", b =>
{
b.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Username");

b.HasKey("UserId");

b.ToTable("Users");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.UserFeature", b =>
{
b.Property<int>("UserFeatureId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("FeatureId");

b.Property<int>("UserId");

b.HasKey("UserFeatureId");

b.HasIndex("FeatureId");

b.HasIndex("UserId");

b.ToTable("UserFeatures");
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.Vault", b =>
{
b.Property<int>("VaultId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<DateTime>("DateCreated");

b.Property<DateTime>("DateEdited");

b.Property<string>("Description");

b.Property<string>("Title");

b.Property<string>("Url");

b.Property<int?>("UserId");

b.Property<int>("Views");

b.HasKey("VaultId");

b.HasIndex("UserId");

b.ToTable("Vaults");
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.VaultItem", b =>
{
b.Property<int>("VaultItemId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<DateTime>("DateAdded");

b.Property<string>("Description");

b.Property<string>("Discriminator")
.IsRequired();

b.Property<int>("Index");

b.Property<string>("Title");

b.Property<int>("VaultId");

b.HasKey("VaultItemId");

b.HasIndex("VaultId");

b.ToTable("VaultItems");

b.HasDiscriminator<string>("Discriminator").HasValue("VaultItem");
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.PasteVaultItem", b =>
{
b.HasBaseType("Teknik.Areas.Vault.Models.VaultItem");

b.Property<int>("PasteId");

b.HasIndex("PasteId");

b.HasDiscriminator().HasValue("PasteVaultItem");
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.UploadVaultItem", b =>
{
b.HasBaseType("Teknik.Areas.Vault.Models.VaultItem");

b.Property<int>("UploadId");

b.HasIndex("UploadId");

b.HasDiscriminator().HasValue("UploadVaultItem");
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.Blog", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPost", b =>
{
b.HasOne("Teknik.Areas.Blog.Models.Blog", "Blog")
.WithMany("BlogPosts")
.HasForeignKey("BlogId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPostComment", b =>
{
b.HasOne("Teknik.Areas.Blog.Models.BlogPost", "BlogPost")
.WithMany("Comments")
.HasForeignKey("BlogPostId")
.OnDelete(DeleteBehavior.Cascade);

b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany()
.HasForeignKey("UserId");
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPostTag", b =>
{
b.HasOne("Teknik.Areas.Blog.Models.BlogPost", "BlogPost")
.WithMany("Tags")
.HasForeignKey("BlogPostId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Paste.Models.Paste", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany("Pastes")
.HasForeignKey("UserId");
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastComment", b =>
{
b.HasOne("Teknik.Areas.Podcast.Models.Podcast", "Podcast")
.WithMany("Comments")
.HasForeignKey("PodcastId")
.OnDelete(DeleteBehavior.Cascade);

b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastFile", b =>
{
b.HasOne("Teknik.Areas.Podcast.Models.Podcast", "Podcast")
.WithMany("Files")
.HasForeignKey("PodcastId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastTag", b =>
{
b.HasOne("Teknik.Areas.Podcast.Models.Podcast", "Podcast")
.WithMany("Tags")
.HasForeignKey("PodcastId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Shortener.Models.ShortenedUrl", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany("ShortenedUrls")
.HasForeignKey("UserId");
});

modelBuilder.Entity("Teknik.Areas.Upload.Models.Upload", b =>
{
b.HasOne("Teknik.Areas.Stats.Models.Takedown")
.WithMany("Attachments")
.HasForeignKey("Takedown_TakedownId");

b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany("Uploads")
.HasForeignKey("UserId");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.InviteCode", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "ClaimedUser")
.WithOne("ClaimedInviteCode")
.HasForeignKey("Teknik.Areas.Users.Models.InviteCode", "ClaimedUserId");

b.HasOne("Teknik.Areas.Users.Models.User", "Owner")
.WithMany("OwnedInviteCodes")
.HasForeignKey("OwnerId");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.User", b =>
{
b.OwnsOne("Teknik.Areas.Users.Models.BlogSettings", "BlogSettings", b1 =>
{
b1.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b1.Property<string>("Description")
.HasColumnName("Description");

b1.Property<string>("Title")
.HasColumnName("Title");

b1.HasKey("UserId");

b1.ToTable("Users");

b1.HasOne("Teknik.Areas.Users.Models.User")
.WithOne("BlogSettings")
.HasForeignKey("Teknik.Areas.Users.Models.BlogSettings", "UserId")
.OnDelete(DeleteBehavior.Cascade);
});

b.OwnsOne("Teknik.Areas.Users.Models.UploadSettings", "UploadSettings", b1 =>