@@ -1,7 +1,9 @@ | |||||
package bubble.app.analytics; | package bubble.app.analytics; | ||||
import bubble.model.account.Account; | import bubble.model.account.Account; | ||||
import bubble.model.app.*; | |||||
import bubble.model.app.AppData; | |||||
import bubble.model.app.AppSite; | |||||
import bubble.model.app.BubbleApp; | |||||
import bubble.model.app.config.AppDataDriverBase; | import bubble.model.app.config.AppDataDriverBase; | ||||
import bubble.model.app.config.AppDataView; | import bubble.model.app.config.AppDataView; | ||||
import bubble.model.device.Device; | import bubble.model.device.Device; | ||||
@@ -9,6 +11,7 @@ import bubble.rule.analytics.TrafficAnalyticsRuleDriver; | |||||
import bubble.rule.analytics.TrafficRecord; | import bubble.rule.analytics.TrafficRecord; | ||||
import lombok.Getter; | import lombok.Getter; | ||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
import org.cobbzilla.util.network.NetworkUtil; | |||||
import org.cobbzilla.wizard.cache.redis.RedisService; | import org.cobbzilla.wizard.cache.redis.RedisService; | ||||
import org.cobbzilla.wizard.dao.SearchResults; | import org.cobbzilla.wizard.dao.SearchResults; | ||||
import org.cobbzilla.wizard.model.search.SearchBoundComparison; | import org.cobbzilla.wizard.model.search.SearchBoundComparison; | ||||
@@ -18,15 +21,20 @@ import org.joda.time.DateTime; | |||||
import org.joda.time.DateTimeZone; | import org.joda.time.DateTimeZone; | ||||
import org.joda.time.format.DateTimeFormatter; | import org.joda.time.format.DateTimeFormatter; | ||||
import java.util.*; | |||||
import java.util.ArrayList; | |||||
import java.util.Collections; | |||||
import java.util.List; | |||||
import java.util.TreeSet; | |||||
import java.util.concurrent.TimeUnit; | import java.util.concurrent.TimeUnit; | ||||
import static bubble.rule.analytics.TrafficAnalyticsRuleDriver.FQDN_SEP; | |||||
import static bubble.rule.analytics.TrafficAnalyticsRuleDriver.RECENT_TRAFFIC_PREFIX; | |||||
import static bubble.rule.analytics.TrafficAnalyticsRuleDriver.*; | |||||
import static java.util.concurrent.TimeUnit.DAYS; | import static java.util.concurrent.TimeUnit.DAYS; | ||||
import static java.util.concurrent.TimeUnit.HOURS; | import static java.util.concurrent.TimeUnit.HOURS; | ||||
import static org.apache.commons.lang3.RandomUtils.nextInt; | |||||
import static org.cobbzilla.util.daemon.ZillaRuntime.now; | import static org.cobbzilla.util.daemon.ZillaRuntime.now; | ||||
import static org.cobbzilla.util.daemon.ZillaRuntime.shortError; | import static org.cobbzilla.util.daemon.ZillaRuntime.shortError; | ||||
import static org.cobbzilla.util.http.HttpContentTypes.TYPICAL_WEB_TYPES; | |||||
import static org.cobbzilla.util.http.HttpContentTypes.fileExt; | |||||
import static org.cobbzilla.util.json.JsonUtil.json; | import static org.cobbzilla.util.json.JsonUtil.json; | ||||
import static org.cobbzilla.util.string.StringUtil.PCT; | import static org.cobbzilla.util.string.StringUtil.PCT; | ||||
import static org.cobbzilla.util.time.TimeUtil.DATE_FORMAT_YYYY_MM_DD; | import static org.cobbzilla.util.time.TimeUtil.DATE_FORMAT_YYYY_MM_DD; | ||||
@@ -35,6 +43,7 @@ import static org.cobbzilla.wizard.model.search.SearchBoundComparison.Constants. | |||||
import static org.cobbzilla.wizard.model.search.SearchField.OP_SEP; | import static org.cobbzilla.wizard.model.search.SearchField.OP_SEP; | ||||
import static org.cobbzilla.wizard.model.search.SortOrder.ASC; | import static org.cobbzilla.wizard.model.search.SortOrder.ASC; | ||||
import static org.cobbzilla.wizard.model.search.SortOrder.DESC; | import static org.cobbzilla.wizard.model.search.SortOrder.DESC; | ||||
import static org.cobbzilla.wizard.util.TestNames.*; | |||||
@Slf4j | @Slf4j | ||||
public class TrafficAnalyticsAppDataDriver extends AppDataDriverBase { | public class TrafficAnalyticsAppDataDriver extends AppDataDriverBase { | ||||
@@ -47,16 +56,23 @@ public class TrafficAnalyticsAppDataDriver extends AppDataDriverBase { | |||||
public static final SearchSort SORT_TSTAMP_DESC = new SearchSort("meta1", DESC); | public static final SearchSort SORT_TSTAMP_DESC = new SearchSort("meta1", DESC); | ||||
public static final SearchSort SORT_FQDN_CASE_INSENSITIVE_ASC = new SearchSort("meta2", ASC, "lower"); | public static final SearchSort SORT_FQDN_CASE_INSENSITIVE_ASC = new SearchSort("meta2", ASC, "lower"); | ||||
public static final int MAX_RECENT_PAGE_SIZE = 50; | |||||
@Getter(lazy=true) private final RedisService recentTraffic = redis.prefixNamespace(RECENT_TRAFFIC_PREFIX); | @Getter(lazy=true) private final RedisService recentTraffic = redis.prefixNamespace(RECENT_TRAFFIC_PREFIX); | ||||
@Override public SearchResults query(Account caller, Device device, BubbleApp app, AppSite site, AppDataView view, SearchQuery query) { | @Override public SearchResults query(Account caller, Device device, BubbleApp app, AppSite site, AppDataView view, SearchQuery query) { | ||||
if (configuration.testMode()) recordTestTraffic(caller, device); | |||||
if (view.getName().equals(VIEW_recent)) { | if (view.getName().equals(VIEW_recent)) { | ||||
final RedisService traffic = getRecentTraffic(); | final RedisService traffic = getRecentTraffic(); | ||||
final TreeSet<String> keys = new TreeSet<>(Collections.reverseOrder()); | final TreeSet<String> keys = new TreeSet<>(Collections.reverseOrder()); | ||||
keys.addAll(traffic.keys("*")); | keys.addAll(traffic.keys("*")); | ||||
final List<TrafficRecord> records = new ArrayList<>(); | final List<TrafficRecord> records = new ArrayList<>(); | ||||
int i = 0; | int i = 0; | ||||
if (query.getPageSize() > MAX_RECENT_PAGE_SIZE) { | |||||
query.setPageSize(MAX_RECENT_PAGE_SIZE); | |||||
} | |||||
for (String key : keys) { | for (String key : keys) { | ||||
if (i < query.getPageOffset()) { | if (i < query.getPageOffset()) { | ||||
i++; | i++; | ||||
@@ -73,8 +89,12 @@ public class TrafficAnalyticsAppDataDriver extends AppDataDriverBase { | |||||
log.warn("query: error parsing TrafficRecord: "+shortError(e)); | log.warn("query: error parsing TrafficRecord: "+shortError(e)); | ||||
} | } | ||||
} | } | ||||
if (records.size() >= query.getPageSize()) { | |||||
log.info("query: max page size reached "+query.getPageSize()+", breaking"); | |||||
} | |||||
i++; | i++; | ||||
} | } | ||||
log.info("query: VIEW_recent: returning "+records.size()+" / "+keys.size()+" recent traffic records"); | |||||
return new SearchResults(records, keys.size()); | return new SearchResults(records, keys.size()); | ||||
} | } | ||||
@@ -92,6 +112,21 @@ public class TrafficAnalyticsAppDataDriver extends AppDataDriverBase { | |||||
return processResults(searchService.search(false, caller, dataDAO, query)); | return processResults(searchService.search(false, caller, dataDAO, query)); | ||||
} | } | ||||
private void recordTestTraffic(Account caller, Device device) { | |||||
recordRecentTraffic(new TrafficRecord() | |||||
.setRequestTime(now()) | |||||
.setIp(NetworkUtil.getFirstPublicIpv4()) | |||||
.setFqdn(safeNationality()+".example.com") | |||||
.setUri("/traffic/"+safeColor()+"/"+ safeAnimal() | |||||
+ fileExt(TYPICAL_WEB_TYPES[nextInt(0, TYPICAL_WEB_TYPES.length)])) | |||||
.setReferer("NONE") | |||||
.setAccountUuid(caller.getUuid()) | |||||
.setAccountName(caller.getName()) | |||||
.setDeviceUuid(device.getUuid()) | |||||
.setDeviceName(device.getName()), | |||||
getRecentTraffic()); | |||||
} | |||||
private SearchResults processResults(SearchResults searchResults) { | private SearchResults processResults(SearchResults searchResults) { | ||||
final List<TrafficAnalyticsData> data = new ArrayList<>(); | final List<TrafficAnalyticsData> data = new ArrayList<>(); | ||||
if (searchResults.hasResults()) { | if (searchResults.hasResults()) { | ||||
@@ -55,7 +55,7 @@ public class BubbleBlockAppConfigDriver implements AppConfigDriver { | |||||
@Autowired private BubbleConfiguration configuration; | @Autowired private BubbleConfiguration configuration; | ||||
@Override public Object getView(Account account, BubbleApp app, String view, Map<String, String> params) { | @Override public Object getView(Account account, BubbleApp app, String view, Map<String, String> params) { | ||||
final String id = params.get(PARAM_ID); | |||||
String id = params.get(PARAM_ID); | |||||
switch (view) { | switch (view) { | ||||
case VIEW_manageLists: | case VIEW_manageLists: | ||||
return loadAllLists(account, app); | return loadAllLists(account, app); | ||||
@@ -65,7 +65,11 @@ public class BubbleBlockAppConfigDriver implements AppConfigDriver { | |||||
return loadList(account, app, id); | return loadList(account, app, id); | ||||
case VIEW_manageRules: | case VIEW_manageRules: | ||||
if (empty(id)) throw notFoundEx(id); | |||||
if (empty(id)) { | |||||
final BubbleBlockList builtinList = getBuiltinList(account, app); | |||||
if (builtinList == null) throw notFoundEx(id); | |||||
id = builtinList.getId(); | |||||
} | |||||
return loadListEntries(account, app, id); | return loadListEntries(account, app, id); | ||||
} | } | ||||
throw notFoundEx(view); | throw notFoundEx(view); | ||||
@@ -3,11 +3,13 @@ package bubble.model.app; | |||||
import bubble.model.account.Account; | import bubble.model.account.Account; | ||||
import bubble.model.account.AccountTemplate; | import bubble.model.account.AccountTemplate; | ||||
import bubble.model.app.config.AppDataConfig; | import bubble.model.app.config.AppDataConfig; | ||||
import bubble.model.app.config.AppDataField; | |||||
import com.fasterxml.jackson.annotation.JsonIgnore; | import com.fasterxml.jackson.annotation.JsonIgnore; | ||||
import lombok.Getter; | import lombok.Getter; | ||||
import lombok.NoArgsConstructor; | import lombok.NoArgsConstructor; | ||||
import lombok.Setter; | import lombok.Setter; | ||||
import lombok.experimental.Accessors; | import lombok.experimental.Accessors; | ||||
import org.cobbzilla.util.collection.ArrayUtil; | |||||
import org.cobbzilla.wizard.model.Identifiable; | import org.cobbzilla.wizard.model.Identifiable; | ||||
import org.cobbzilla.wizard.model.entityconfig.IdentifiableBaseParentEntity; | import org.cobbzilla.wizard.model.entityconfig.IdentifiableBaseParentEntity; | ||||
import org.cobbzilla.wizard.model.entityconfig.annotations.*; | import org.cobbzilla.wizard.model.entityconfig.annotations.*; | ||||
@@ -78,10 +80,19 @@ public class BubbleApp extends IdentifiableBaseParentEntity implements AccountTe | |||||
@Column(length=100000, nullable=false) @ECField(index=50) | @Column(length=100000, nullable=false) @ECField(index=50) | ||||
@JsonIgnore @Getter @Setter private String dataConfigJson; | @JsonIgnore @Getter @Setter private String dataConfigJson; | ||||
@Transient public AppDataConfig getDataConfig () { return dataConfigJson == null ? null : json(dataConfigJson, AppDataConfig.class); } | |||||
@Transient public AppDataConfig getDataConfig () { return dataConfigJson == null ? null : ensureDefaults(json(dataConfigJson, AppDataConfig.class)); } | |||||
public BubbleApp setDataConfig (AppDataConfig adc) { return setDataConfigJson(adc == null ? null : json(adc, DB_JSON_MAPPER)); } | public BubbleApp setDataConfig (AppDataConfig adc) { return setDataConfigJson(adc == null ? null : json(adc, DB_JSON_MAPPER)); } | ||||
public boolean hasDataConfig () { return getDataConfig() != null; } | public boolean hasDataConfig () { return getDataConfig() != null; } | ||||
private AppDataConfig ensureDefaults(AppDataConfig adc) { | |||||
for (AppDataField field : adc.getFields()) { | |||||
if (!adc.hasConfigField(field)) { | |||||
adc.setConfigFields(ArrayUtil.append(adc.getConfigFields(), field)); | |||||
} | |||||
} | |||||
return adc; | |||||
} | |||||
@ECSearchable @ECField(index=60) | @ECSearchable @ECField(index=60) | ||||
@ECIndex @Column(nullable=false) | @ECIndex @Column(nullable=false) | ||||
@Getter @Setter private Boolean template = false; | @Getter @Setter private Boolean template = false; | ||||
@@ -11,6 +11,7 @@ public class AppConfigAction { | |||||
@Getter @Setter private AppConfigScope scope = AppConfigScope.item; | @Getter @Setter private AppConfigScope scope = AppConfigScope.item; | ||||
@Getter @Setter private String when; | @Getter @Setter private String when; | ||||
@Getter @Setter private String view; | @Getter @Setter private String view; | ||||
@Getter @Setter private String dataView; | |||||
@Getter @Setter private String successView; | @Getter @Setter private String successView; | ||||
@Getter @Setter private String successMessage; | @Getter @Setter private String successMessage; | ||||
@Getter @Setter private Integer index = 0; | @Getter @Setter private Integer index = 0; | ||||
@@ -7,5 +7,6 @@ public class AppDataAction { | |||||
@Getter @Setter private String name; | @Getter @Setter private String name; | ||||
@Getter @Setter private String when; | @Getter @Setter private String when; | ||||
@Getter @Setter private String route; | |||||
} | } |
@@ -4,6 +4,7 @@ import bubble.server.BubbleConfiguration; | |||||
import lombok.Getter; | import lombok.Getter; | ||||
import lombok.Setter; | import lombok.Setter; | ||||
import java.util.Arrays; | |||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||
@@ -48,6 +49,10 @@ public class AppDataConfig { | |||||
@Getter @Setter private AppDataField[] configFields; | @Getter @Setter private AppDataField[] configFields; | ||||
public boolean hasConfigFields () { return !empty(configFields); } | public boolean hasConfigFields () { return !empty(configFields); } | ||||
public boolean hasConfigField(AppDataField field) { | |||||
return hasConfigFields() && Arrays.stream(getConfigFields()).anyMatch(f -> f.getName().equals(field.getName())); | |||||
} | |||||
@Getter @Setter private AppConfigView[] configViews; | @Getter @Setter private AppConfigView[] configViews; | ||||
public boolean hasConfigViews () { return !empty(configViews); } | public boolean hasConfigViews () { return !empty(configViews); } | ||||
@@ -5,6 +5,7 @@ import bubble.model.account.Account; | |||||
import bubble.model.app.AppSite; | import bubble.model.app.AppSite; | ||||
import bubble.model.app.BubbleApp; | import bubble.model.app.BubbleApp; | ||||
import bubble.model.device.Device; | import bubble.model.device.Device; | ||||
import bubble.server.BubbleConfiguration; | |||||
import bubble.service.SearchService; | import bubble.service.SearchService; | ||||
import org.cobbzilla.wizard.cache.redis.RedisService; | import org.cobbzilla.wizard.cache.redis.RedisService; | ||||
import org.cobbzilla.wizard.dao.SearchResults; | import org.cobbzilla.wizard.dao.SearchResults; | ||||
@@ -24,6 +25,7 @@ public abstract class AppDataDriverBase implements AppDataDriver { | |||||
@Autowired protected AppDataDAO dataDAO; | @Autowired protected AppDataDAO dataDAO; | ||||
@Autowired protected SearchService searchService; | @Autowired protected SearchService searchService; | ||||
@Autowired protected RedisService redis; | @Autowired protected RedisService redis; | ||||
@Autowired protected BubbleConfiguration configuration; | |||||
@Override public SearchResults query(Account caller, Device device, BubbleApp app, AppSite site, AppDataView view, SearchQuery query) { | @Override public SearchResults query(Account caller, Device device, BubbleApp app, AppSite site, AppDataView view, SearchQuery query) { | ||||
query.setBound("app", app.getUuid()); | query.setBound("app", app.getUuid()); | ||||
@@ -7,6 +7,7 @@ import org.cobbzilla.util.collection.HasPriority; | |||||
public class AppDataView implements HasPriority { | public class AppDataView implements HasPriority { | ||||
@Getter @Setter private AppDataPresentation presentation = AppDataPresentation.app; | @Getter @Setter private AppDataPresentation presentation = AppDataPresentation.app; | ||||
@Getter @Setter private AppDataViewLayout layout = AppDataViewLayout.table; | |||||
@Getter @Setter private String name; | @Getter @Setter private String name; | ||||
@Getter @Setter private Integer priority = 0; | @Getter @Setter private Integer priority = 0; | ||||
@@ -0,0 +1,13 @@ | |||||
package bubble.model.app.config; | |||||
import com.fasterxml.jackson.annotation.JsonCreator; | |||||
import static bubble.ApiConstants.enumFromString; | |||||
public enum AppDataViewLayout { | |||||
table, tiles; | |||||
@JsonCreator public static AppDataViewLayout fromString (String v) { return enumFromString(AppDataViewLayout.class, v); } | |||||
} |
@@ -43,11 +43,18 @@ public class TrafficAnalyticsRuleDriver extends AbstractAppRuleDriver { | |||||
final String site = ruleHarness.getMatcher().getSite(); | final String site = ruleHarness.getMatcher().getSite(); | ||||
final String fqdn = filter.getFqdn(); | final String fqdn = filter.getFqdn(); | ||||
getRecentTraffic().set(now()+"_"+randomAlphanumeric(10), json(new TrafficRecord(filter, account, device, req)), EX, RECENT_TRAFFIC_EXPIRATION); | |||||
final TrafficRecord rec = new TrafficRecord(filter, account, device, req); | |||||
recordRecentTraffic(rec); | |||||
incrementCounters(account, device, app, site, fqdn); | incrementCounters(account, device, app, site, fqdn); | ||||
return FilterMatchResponse.NO_MATCH; // we are done, don't need to look at/modify stream | return FilterMatchResponse.NO_MATCH; // we are done, don't need to look at/modify stream | ||||
} | } | ||||
public void recordRecentTraffic(TrafficRecord rec) { recordRecentTraffic(rec, getRecentTraffic()); } | |||||
public static void recordRecentTraffic(TrafficRecord rec, RedisService recentTraffic) { | |||||
recentTraffic.set(now() + "_" + randomAlphanumeric(10), json(rec), EX, RECENT_TRAFFIC_EXPIRATION); | |||||
} | |||||
public void incrementCounters(Account account, Device device, String app, String site, String fqdn) { | public void incrementCounters(Account account, Device device, String app, String site, String fqdn) { | ||||
incr(account, device, app, site, fqdn, PREFIX_HOURLY, DATE_FORMAT_YYYY_MM_DD_HH.print(now())); | incr(account, device, app, site, fqdn, PREFIX_HOURLY, DATE_FORMAT_YYYY_MM_DD_HH.print(now())); | ||||
incr(account, null, app, site, fqdn, PREFIX_HOURLY, DATE_FORMAT_YYYY_MM_DD_HH.print(now())); | incr(account, null, app, site, fqdn, PREFIX_HOURLY, DATE_FORMAT_YYYY_MM_DD_HH.print(now())); | ||||
@@ -6,13 +6,14 @@ import bubble.resources.stream.FilterMatchersRequest; | |||||
import lombok.Getter; | import lombok.Getter; | ||||
import lombok.NoArgsConstructor; | import lombok.NoArgsConstructor; | ||||
import lombok.Setter; | import lombok.Setter; | ||||
import lombok.experimental.Accessors; | |||||
import org.glassfish.grizzly.http.server.Request; | import org.glassfish.grizzly.http.server.Request; | ||||
import static bubble.ApiConstants.getRemoteHost; | import static bubble.ApiConstants.getRemoteHost; | ||||
import static java.util.UUID.randomUUID; | import static java.util.UUID.randomUUID; | ||||
import static org.cobbzilla.util.daemon.ZillaRuntime.now; | import static org.cobbzilla.util.daemon.ZillaRuntime.now; | ||||
@NoArgsConstructor | |||||
@NoArgsConstructor @Accessors(chain=true) | |||||
public class TrafficRecord { | public class TrafficRecord { | ||||
@Getter @Setter private String uuid = randomUUID().toString(); | @Getter @Setter private String uuid = randomUUID().toString(); | ||||
@@ -100,8 +100,9 @@ public class DeviceIdService { | |||||
log.warn("findDeviceByIp("+ipAddr+") test mode and no admin devices, returning dummy device"); | log.warn("findDeviceByIp("+ipAddr+") test mode and no admin devices, returning dummy device"); | ||||
return new Device().setAccount(adminUuid).setName("dummy"); | return new Device().setAccount(adminUuid).setName("dummy"); | ||||
} else { | } else { | ||||
log.warn("findDeviceByIp("+ipAddr+") test mode, returning first admin device"); | |||||
return adminDevices.get(0); | |||||
log.warn("findDeviceByIp("+ipAddr+") test mode, returning and possibly initializing first admin device"); | |||||
final Device device = adminDevices.get(0); | |||||
return device.uninitialized() ? deviceDAO.update(device.initTotpKey()) : device; | |||||
} | } | ||||
} | } | ||||
@@ -92,7 +92,6 @@ public class AppMessageService { | |||||
ensureFieldNameAndDescription(props, cfgKeyPrefix, field.getName()); | ensureFieldNameAndDescription(props, cfgKeyPrefix, field.getName()); | ||||
} | } | ||||
} | } | ||||
if (cfg.hasConfigViews()) { | if (cfg.hasConfigViews()) { | ||||
for (AppConfigView configView : cfg.getConfigViews()) { | for (AppConfigView configView : cfg.getConfigViews()) { | ||||
final String viewKey = cfgKeyPrefix + MSG_SUFFIX_VIEW + configView.getName(); | final String viewKey = cfgKeyPrefix + MSG_SUFFIX_VIEW + configView.getName(); | ||||
@@ -120,6 +119,11 @@ public class AppMessageService { | |||||
} | } | ||||
} | } | ||||
} | } | ||||
// anything from data fields not yet defined, copy as config field name/desc | |||||
for (AppDataField field : cfg.getFields()) { | |||||
ensureFieldNameAndDescription(props, cfgKeyPrefix, field.getName()); | |||||
} | |||||
} | } | ||||
} | } | ||||
return props; | return props; | ||||
@@ -11,6 +11,8 @@ message_undefined=undefined | |||||
# Display of percent values has localized variations | # Display of percent values has localized variations | ||||
label_percent={{percent}}% | label_percent={{percent}}% | ||||
message_truncated_show_ellipsis={{msg}}... | |||||
# Date/Calendar names | # Date/Calendar names | ||||
label_date={{MMM}} {{d}}, {{YYYY}} | label_date={{MMM}} {{d}}, {{YYYY}} | ||||
label_date_short={{M}}/{{d}}/{{YYYY}} | label_date_short={{M}}/{{d}}/{{YYYY}} | ||||
@@ -24,8 +24,19 @@ | |||||
{"name": "device", "required": false, "index": 10, "when": "view !== \"recent\""}, | {"name": "device", "required": false, "index": 10, "when": "view !== \"recent\""}, | ||||
{"name": "meta2", "required": false, "operator": "like", "index": 20, "when": "view !== \"recent\""} | {"name": "meta2", "required": false, "operator": "like", "index": 20, "when": "view !== \"recent\""} | ||||
], | ], | ||||
"actions": [ | |||||
{ | |||||
"name": "filterHost", | |||||
"when": "view === \"recent\"", | |||||
"route": "/app/BubbleBlock/config/manageRules/local?action=createRule&rule={{fqdn}}" | |||||
}, { | |||||
"name": "filterUrl", | |||||
"when": "view === \"recent\"", | |||||
"route": "/app/BubbleBlock/config/manageRules/local?action=createRule&rule={{ encodeURIComponent( fqdn + (uri.startsWith('/') ? uri : '/'+uri) ) }}" | |||||
} | |||||
], | |||||
"views": [ | "views": [ | ||||
{"name": "recent"}, | |||||
{"name": "recent", "layout": "tiles"}, | |||||
{"name": "last_24_hours"}, | {"name": "last_24_hours"}, | ||||
{"name": "last_7_days"}, | {"name": "last_7_days"}, | ||||
{"name": "last_30_days"} | {"name": "last_30_days"} | ||||
@@ -47,12 +58,13 @@ | |||||
"AppMessage": [{ | "AppMessage": [{ | ||||
"locale": "en_US", | "locale": "en_US", | ||||
"messages": [ | "messages": [ | ||||
{"name": "name", "value": "Stoolpidgeon"}, | |||||
{"name": "description", "value": "Review recent internet traffic for your devices. Block stuff that looks off."}, | |||||
{"name": "name", "value": "Castigator"}, | |||||
{"name": "summary", "value": "Network Analytics and Filter Creator"}, | |||||
{"name": "description", "value": "Review recent internet traffic for your devices. Block stuff that you don't like."}, | |||||
{"name": "field.ctime", "value": "When"}, | {"name": "field.ctime", "value": "When"}, | ||||
{"name": "field.requestTime", "value": "When"}, | {"name": "field.requestTime", "value": "When"}, | ||||
{"name": "field.accountName", "value": "Account"}, | {"name": "field.accountName", "value": "Account"}, | ||||
{"name": "field.fqdn", "value": "URL"}, | |||||
{"name": "field.fqdn", "value": "Host"}, | |||||
{"name": "field.device", "value": "Device"}, | {"name": "field.device", "value": "Device"}, | ||||
{"name": "field.deviceName", "value": "Device"}, | {"name": "field.deviceName", "value": "Device"}, | ||||
{"name": "field.ip", "value": "From IP"}, | {"name": "field.ip", "value": "From IP"}, | ||||
@@ -62,6 +74,8 @@ | |||||
{"name": "field.data", "value": "Count"}, | {"name": "field.data", "value": "Count"}, | ||||
{"name": "param.meta2", "value": "Site"}, | {"name": "param.meta2", "value": "Site"}, | ||||
{"name": "param.device", "value": "Device"}, | {"name": "param.device", "value": "Device"}, | ||||
{"name": "action.filterHost", "value": "Block Host"}, | |||||
{"name": "action.filterUrl", "value": "Block URL"}, | |||||
{"name": "view.recent", "value": "Recent Traffic"}, | {"name": "view.recent", "value": "Recent Traffic"}, | ||||
{"name": "view.recent.requestTime.format", "value": "{{MM}} {{d}} @ {{h}}:{{m}}:{{s}} {{a}}"}, | {"name": "view.recent.requestTime.format", "value": "{{MM}} {{d}} @ {{h}}:{{m}}:{{s}} {{a}}"}, | ||||
{"name": "view.last_24_hours", "value": "Last 24 Hours"}, | {"name": "view.last_24_hours", "value": "Last 24 Hours"}, | ||||
@@ -46,7 +46,7 @@ | |||||
{"name": "disableList", "when": "item.enabled", "index": 20}, | {"name": "disableList", "when": "item.enabled", "index": 20}, | ||||
{"name": "manageList", "view": "manageList", "index": 30}, | {"name": "manageList", "view": "manageList", "index": 30}, | ||||
{"name": "manageRules", "view": "manageRules", "when": "item.url === ''", "index": 40}, | {"name": "manageRules", "view": "manageRules", "when": "item.url === ''", "index": 40}, | ||||
{"name": "removeList", "index": 50, "when": "item.url !==''"}, | |||||
{"name": "removeList", "index": 50, "when": "item.url !== ''"}, | |||||
{ | { | ||||
"name": "createList", "scope": "app", "view": "manageList", "index": 10, | "name": "createList", "scope": "app", "view": "manageList", "index": 10, | ||||
"params": ["url"], | "params": ["url"], | ||||
@@ -131,7 +131,8 @@ | |||||
"AppMessage": [{ | "AppMessage": [{ | ||||
"locale": "en_US", | "locale": "en_US", | ||||
"messages": [ | "messages": [ | ||||
{"name": "name", "value": "BlockParty!"}, | |||||
{"name": "name", "value": "Block Party!"}, | |||||
{"name": "summary", "value": "Network Filter and Content Blocker"}, | |||||
{"name": "description", "value": "Block adware, malware, phishing/scam sites, and much more"}, | {"name": "description", "value": "Block adware, malware, phishing/scam sites, and much more"}, | ||||
{"name": "field.ctime", "value": "When"}, | {"name": "field.ctime", "value": "When"}, | ||||
{"name": "field.fqdn", "value": "URL"}, | {"name": "field.fqdn", "value": "URL"}, | ||||
@@ -22,7 +22,8 @@ | |||||
"AppMessage": [{ | "AppMessage": [{ | ||||
"locale": "en_US", | "locale": "en_US", | ||||
"messages": [ | "messages": [ | ||||
{"name": "name", "value": "ShadowBan"}, | |||||
{"name": "name", "value": "Shadow Ban"}, | |||||
{"name": "summary", "value": "User Blocker"}, | |||||
{"name": "description", "value": "Throw the garbage to the curb!"}, | {"name": "description", "value": "Throw the garbage to the curb!"}, | ||||
{"name": "view.blocked_users", "value": "Manage Blocked Users"}, | {"name": "view.blocked_users", "value": "Manage Blocked Users"}, | ||||
{"name": "field.key", "value": "Username"}, | {"name": "field.key", "value": "Username"}, | ||||
@@ -1 +1 @@ | |||||
Subproject commit 47b7bc1d0500c5fc429acda124b0d16aa95d1f89 | |||||
Subproject commit c7a9253a2b7bfaf9af0cc78a9430acc5337416dd |
@@ -1 +1 @@ | |||||
Subproject commit b68eecfa79696b72b7242de27de530c4e0112c28 | |||||
Subproject commit e7c3727fc3e1405b3bba6b95de0271db23309e14 |
@@ -1 +1 @@ | |||||
Subproject commit 6a3824eee748ded81eb4caf065f444f565708b1c | |||||
Subproject commit 0c7796f86e508d38f48a94270f52d3444ecf720c |