mirror of
https://github.com/sismics/docs.git
synced 2024-11-22 14:07:55 +01:00
Closes #166: global quota
This commit is contained in:
parent
66f781b716
commit
fb75bafe96
@ -46,23 +46,28 @@ public class Constants {
|
|||||||
public static final List<String> SUPPORTED_LANGUAGES = Lists.newArrayList("eng", "fra", "ita", "deu", "spa", "por", "pol", "rus", "ukr", "ara", "hin", "chi_sim", "chi_tra", "jpn", "tha", "kor");
|
public static final List<String> SUPPORTED_LANGUAGES = Lists.newArrayList("eng", "fra", "ita", "deu", "spa", "por", "pol", "rus", "ukr", "ara", "hin", "chi_sim", "chi_tra", "jpn", "tha", "kor");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base URL environnement variable.
|
* Base URL environment variable.
|
||||||
*/
|
*/
|
||||||
public static final String BASE_URL_ENV = "DOCS_BASE_URL";
|
public static final String BASE_URL_ENV = "DOCS_BASE_URL";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default language environnement variable.
|
* Default language environment variable.
|
||||||
*/
|
*/
|
||||||
public static final String DEFAULT_LANGUAGE_ENV = "DOCS_DEFAULT_LANGUAGE";
|
public static final String DEFAULT_LANGUAGE_ENV = "DOCS_DEFAULT_LANGUAGE";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SMTP configuration environnement variables.
|
* SMTP configuration environment variables.
|
||||||
*/
|
*/
|
||||||
public static final String SMTP_HOSTNAME_ENV = "DOCS_SMTP_HOSTNAME";
|
public static final String SMTP_HOSTNAME_ENV = "DOCS_SMTP_HOSTNAME";
|
||||||
public static final String SMTP_PORT_ENV = "DOCS_SMTP_PORT";
|
public static final String SMTP_PORT_ENV = "DOCS_SMTP_PORT";
|
||||||
public static final String SMTP_USERNAME_ENV = "DOCS_SMTP_USERNAME";
|
public static final String SMTP_USERNAME_ENV = "DOCS_SMTP_USERNAME";
|
||||||
public static final String SMTP_PASSWORD_ENV = "DOCS_SMTP_PASSWORD";
|
public static final String SMTP_PASSWORD_ENV = "DOCS_SMTP_PASSWORD";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global quota environment variable.
|
||||||
|
*/
|
||||||
|
public static final String GLOBAL_QUOTA_ENV = "DOCS_GLOBAL_QUOTA";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expiration time of the password recovery in hours.
|
* Expiration time of the password recovery in hours.
|
||||||
*/
|
*/
|
||||||
|
@ -1,19 +1,5 @@
|
|||||||
package com.sismics.docs.core.dao.jpa;
|
package com.sismics.docs.core.dao.jpa;
|
||||||
|
|
||||||
import java.sql.Timestamp;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
|
||||||
import javax.persistence.NoResultException;
|
|
||||||
import javax.persistence.Query;
|
|
||||||
|
|
||||||
import org.mindrot.jbcrypt.BCrypt;
|
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.sismics.docs.core.constant.AuditLogType;
|
import com.sismics.docs.core.constant.AuditLogType;
|
||||||
import com.sismics.docs.core.dao.jpa.criteria.UserCriteria;
|
import com.sismics.docs.core.dao.jpa.criteria.UserCriteria;
|
||||||
@ -24,6 +10,13 @@ import com.sismics.docs.core.util.jpa.QueryParam;
|
|||||||
import com.sismics.docs.core.util.jpa.QueryUtil;
|
import com.sismics.docs.core.util.jpa.QueryUtil;
|
||||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||||
import com.sismics.util.context.ThreadLocalContext;
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
|
import org.mindrot.jbcrypt.BCrypt;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.NoResultException;
|
||||||
|
import javax.persistence.Query;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User DAO.
|
* User DAO.
|
||||||
@ -59,7 +52,7 @@ public class UserDao {
|
|||||||
* @param user User to create
|
* @param user User to create
|
||||||
* @param userId User ID
|
* @param userId User ID
|
||||||
* @return User ID
|
* @return User ID
|
||||||
* @throws Exception
|
* @throws Exception e
|
||||||
*/
|
*/
|
||||||
public String create(User user, String userId) throws Exception {
|
public String create(User user, String userId) throws Exception {
|
||||||
// Create the user UUID
|
// Create the user UUID
|
||||||
@ -116,9 +109,8 @@ public class UserDao {
|
|||||||
* Updates a user's quota.
|
* Updates a user's quota.
|
||||||
*
|
*
|
||||||
* @param user User to update
|
* @param user User to update
|
||||||
* @return Updated user
|
|
||||||
*/
|
*/
|
||||||
public User updateQuota(User user) {
|
public void updateQuota(User user) {
|
||||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||||
|
|
||||||
// Get the user
|
// Get the user
|
||||||
@ -128,8 +120,6 @@ public class UserDao {
|
|||||||
|
|
||||||
// Update the user
|
// Update the user
|
||||||
userFromDb.setStorageQuota(user.getStorageQuota());
|
userFromDb.setStorageQuota(user.getStorageQuota());
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -298,4 +288,15 @@ public class UserDao {
|
|||||||
}
|
}
|
||||||
return userDtoList;
|
return userDtoList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the global storage used by all users.
|
||||||
|
*
|
||||||
|
* @return Current global storage
|
||||||
|
*/
|
||||||
|
public long getGlobalStorageCurrent() {
|
||||||
|
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||||
|
Query query = em.createNativeQuery("select sum(u.USE_STORAGECURRENT_N) from T_USER u where u.USE_DELETEDATE_D is null");
|
||||||
|
return ((Number) query.getSingleResult()).longValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,8 @@ public class AppResource extends BaseResource {
|
|||||||
* @apiSuccess {Boolean} guest_login True if guest login is enabled
|
* @apiSuccess {Boolean} guest_login True if guest login is enabled
|
||||||
* @apiSuccess {String} total_memory Allocated JVM memory (in bytes)
|
* @apiSuccess {String} total_memory Allocated JVM memory (in bytes)
|
||||||
* @apiSuccess {String} free_memory Free JVM memory (in bytes)
|
* @apiSuccess {String} free_memory Free JVM memory (in bytes)
|
||||||
|
* @apiSuccess {String} global_storage_current Global storage currently used (in bytes)
|
||||||
|
* @apiSuccess {String} global_storage_quota Maximum global storage (in bytes)
|
||||||
* @apiPermission none
|
* @apiPermission none
|
||||||
* @apiVersion 1.5.0
|
* @apiVersion 1.5.0
|
||||||
*
|
*
|
||||||
@ -74,13 +76,23 @@ public class AppResource extends BaseResource {
|
|||||||
String currentVersion = configBundle.getString("api.current_version");
|
String currentVersion = configBundle.getString("api.current_version");
|
||||||
String minVersion = configBundle.getString("api.min_version");
|
String minVersion = configBundle.getString("api.min_version");
|
||||||
Boolean guestLogin = ConfigUtil.getConfigBooleanValue(ConfigType.GUEST_LOGIN);
|
Boolean guestLogin = ConfigUtil.getConfigBooleanValue(ConfigType.GUEST_LOGIN);
|
||||||
|
UserDao userDao = new UserDao();
|
||||||
|
String globalQuotaStr = System.getenv(Constants.GLOBAL_QUOTA_ENV);
|
||||||
|
long globalQuota = 0;
|
||||||
|
if (!Strings.isNullOrEmpty(globalQuotaStr)) {
|
||||||
|
globalQuota = Long.valueOf(globalQuotaStr);
|
||||||
|
}
|
||||||
|
|
||||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||||
.add("current_version", currentVersion.replace("-SNAPSHOT", ""))
|
.add("current_version", currentVersion.replace("-SNAPSHOT", ""))
|
||||||
.add("min_version", minVersion)
|
.add("min_version", minVersion)
|
||||||
.add("guest_login", guestLogin)
|
.add("guest_login", guestLogin)
|
||||||
.add("total_memory", Runtime.getRuntime().totalMemory())
|
.add("total_memory", Runtime.getRuntime().totalMemory())
|
||||||
.add("free_memory", Runtime.getRuntime().freeMemory());
|
.add("free_memory", Runtime.getRuntime().freeMemory())
|
||||||
|
.add("global_storage_current", userDao.getGlobalStorageCurrent());
|
||||||
|
if (globalQuota > 0) {
|
||||||
|
response.add("global_storage_quota", globalQuota);
|
||||||
|
}
|
||||||
|
|
||||||
return Response.ok().entity(response.build()).build();
|
return Response.ok().entity(response.build()).build();
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package com.sismics.docs.rest.resource;
|
|||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
|
import com.sismics.docs.core.constant.Constants;
|
||||||
import com.sismics.docs.core.constant.PermType;
|
import com.sismics.docs.core.constant.PermType;
|
||||||
import com.sismics.docs.core.dao.jpa.AclDao;
|
import com.sismics.docs.core.dao.jpa.AclDao;
|
||||||
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
||||||
@ -14,7 +15,10 @@ import com.sismics.docs.core.event.FileCreatedAsyncEvent;
|
|||||||
import com.sismics.docs.core.event.FileDeletedAsyncEvent;
|
import com.sismics.docs.core.event.FileDeletedAsyncEvent;
|
||||||
import com.sismics.docs.core.model.jpa.File;
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
import com.sismics.docs.core.model.jpa.User;
|
import com.sismics.docs.core.model.jpa.User;
|
||||||
import com.sismics.docs.core.util.*;
|
import com.sismics.docs.core.util.DirectoryUtil;
|
||||||
|
import com.sismics.docs.core.util.EncryptionUtil;
|
||||||
|
import com.sismics.docs.core.util.FileUtil;
|
||||||
|
import com.sismics.docs.core.util.PdfUtil;
|
||||||
import com.sismics.rest.exception.ClientException;
|
import com.sismics.rest.exception.ClientException;
|
||||||
import com.sismics.rest.exception.ForbiddenClientException;
|
import com.sismics.rest.exception.ForbiddenClientException;
|
||||||
import com.sismics.rest.exception.ServerException;
|
import com.sismics.rest.exception.ServerException;
|
||||||
@ -34,7 +38,9 @@ import javax.ws.rs.core.MediaType;
|
|||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
import javax.ws.rs.core.StreamingOutput;
|
import javax.ws.rs.core.StreamingOutput;
|
||||||
import java.io.*;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@ -130,11 +136,21 @@ public class FileResource extends BaseResource {
|
|||||||
throw new ServerException("ErrorGuessMime", "Error guessing mime type", e);
|
throw new ServerException("ErrorGuessMime", "Error guessing mime type", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate quota
|
// Validate user quota
|
||||||
if (user.getStorageCurrent() + fileSize > user.getStorageQuota()) {
|
if (user.getStorageCurrent() + fileSize > user.getStorageQuota()) {
|
||||||
throw new ClientException("QuotaReached", "Quota limit reached");
|
throw new ClientException("QuotaReached", "Quota limit reached");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate global quota
|
||||||
|
String globalStorageQuotaStr = System.getenv(Constants.GLOBAL_QUOTA_ENV);
|
||||||
|
if (!Strings.isNullOrEmpty(globalStorageQuotaStr)) {
|
||||||
|
long globalStorageQuota = Long.valueOf(globalStorageQuotaStr);
|
||||||
|
long globalStorageCurrent = userDao.getGlobalStorageCurrent();
|
||||||
|
if (globalStorageCurrent + fileSize > globalStorageQuota) {
|
||||||
|
throw new ClientException("QuotaReached", "Global quota limit reached");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get files of this document
|
// Get files of this document
|
||||||
FileDao fileDao = new FileDao();
|
FileDao fileDao = new FileDao();
|
||||||
|
@ -77,7 +77,7 @@ angular.module('docs').controller('DocumentDefault', function($scope, $rootScope
|
|||||||
})
|
})
|
||||||
.error(function (data) {
|
.error(function (data) {
|
||||||
newfile.status = $translate.instant('document.default.upload_error');
|
newfile.status = $translate.instant('document.default.upload_error');
|
||||||
if (data.type == 'QuotaReached') {
|
if (data.type === 'QuotaReached') {
|
||||||
newfile.status += ' - ' + $translate.instant('document.default.upload_error_quota');
|
newfile.status += ' - ' + $translate.instant('document.default.upload_error_quota');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -157,6 +157,10 @@
|
|||||||
|
|
||||||
<div class="row" ng-controller="Footer">
|
<div class="row" ng-controller="Footer">
|
||||||
<div class="col-md-12 footer text-center text-muted">
|
<div class="col-md-12 footer text-center text-muted">
|
||||||
|
<div class="alert alert-danger" ng-show="app.global_storage_quota && app.global_storage_quota * 0.8 < app.global_storage_current"
|
||||||
|
translate="index.global_quota_warning"
|
||||||
|
translate-values="{ current: app.global_storage_current / 1000000, percent: app.global_storage_current / app.global_storage_quota * 100, total: app.global_storage_quota / 1000000 }">
|
||||||
|
</div>
|
||||||
<ul class="list-inline">
|
<ul class="list-inline">
|
||||||
<li uib-dropdown class="dropdown">
|
<li uib-dropdown class="dropdown">
|
||||||
<a href uib-dropdown-toggle>
|
<a href uib-dropdown-toggle>
|
||||||
|
@ -35,7 +35,8 @@
|
|||||||
"error_info": "{{ count }} new error{{ count > 1 ? 's' : '' }}",
|
"error_info": "{{ count }} new error{{ count > 1 ? 's' : '' }}",
|
||||||
"logged_as": "Logged in as {{ username }}",
|
"logged_as": "Logged in as {{ username }}",
|
||||||
"nav_settings": "Settings",
|
"nav_settings": "Settings",
|
||||||
"logout": "Logout"
|
"logout": "Logout",
|
||||||
|
"global_quota_warning": "<strong>Warning!</strong> Global quota almost reached at {{ current | number: 0 }}MB ({{ percent | number: 1 }}%) used on {{ total | number: 0 }}MB"
|
||||||
},
|
},
|
||||||
"document": {
|
"document": {
|
||||||
"search_simple": "Simple search",
|
"search_simple": "Simple search",
|
||||||
|
@ -36,6 +36,7 @@ public class TestAppResource extends BaseJerseyTest {
|
|||||||
Long totalMemory = json.getJsonNumber("total_memory").longValue();
|
Long totalMemory = json.getJsonNumber("total_memory").longValue();
|
||||||
Assert.assertTrue(totalMemory > 0 && totalMemory > freeMemory);
|
Assert.assertTrue(totalMemory > 0 && totalMemory > freeMemory);
|
||||||
Assert.assertFalse(json.getBoolean("guest_login"));
|
Assert.assertFalse(json.getBoolean("guest_login"));
|
||||||
|
Assert.assertTrue(json.containsKey("global_storage_current"));
|
||||||
|
|
||||||
// Rebuild Lucene index
|
// Rebuild Lucene index
|
||||||
Response response = target().path("/app/batch/reindex").request()
|
Response response = target().path("/app/batch/reindex").request()
|
||||||
|
@ -9,3 +9,4 @@ log4j.logger.com.sismics=INFO
|
|||||||
log4j.logger.com.sismics.util.jpa=ERROR
|
log4j.logger.com.sismics.util.jpa=ERROR
|
||||||
log4j.logger.org.hibernate=ERROR
|
log4j.logger.org.hibernate=ERROR
|
||||||
log4j.logger.org.apache.pdfbox=INFO
|
log4j.logger.org.apache.pdfbox=INFO
|
||||||
|
log4j.logger.com.mchange=ERROR
|
||||||
|
Loading…
Reference in New Issue
Block a user