@@ -0,0 +1,145 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.cloud.dns.godaddy; | |||
import com.fasterxml.jackson.databind.JsonNode; | |||
import com.fasterxml.jackson.databind.node.ArrayNode; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.util.dns.DnsType; | |||
import org.cobbzilla.util.http.HttpRequestBean; | |||
import org.cobbzilla.util.http.HttpResponseBean; | |||
import org.cobbzilla.util.http.HttpUtil; | |||
import org.cobbzilla.util.http.URIUtil; | |||
import org.cobbzilla.util.io.FileUtil; | |||
import org.cobbzilla.util.main.BaseMain; | |||
import org.cobbzilla.util.network.NetworkUtil; | |||
import java.util.*; | |||
import java.util.stream.Collectors; | |||
import static bubble.cloud.dns.godaddy.GoDaddyDnsConfig.GODADDY_BASE_URI; | |||
import static org.apache.http.HttpHeaders.CONTENT_TYPE; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.empty; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.shortError; | |||
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | |||
import static org.cobbzilla.util.http.HttpMethods.PUT; | |||
import static org.cobbzilla.util.http.HttpSchemes.SCHEME_HTTP; | |||
import static org.cobbzilla.util.http.HttpUtil.url2string; | |||
import static org.cobbzilla.util.io.FileUtil.abs; | |||
import static org.cobbzilla.util.json.JsonUtil.json; | |||
import static org.cobbzilla.util.network.NetworkUtil.normalizeHost; | |||
@Slf4j | |||
public class GoDaddyDnsCleanerMain extends BaseMain<GoDaddyDnsCleanerOptions> { | |||
private static final DnsType[] DNS_TYPES = new DnsType[] { DnsType.A, DnsType.AAAA, DnsType.CNAME }; | |||
public static void main(String[] args) { main(GoDaddyDnsCleanerMain.class, args); } | |||
@Override protected void run() throws Exception { | |||
final GoDaddyDnsCleanerOptions opts = getOptions(); | |||
Set<String> bubbleHosts; | |||
try { | |||
bubbleHosts = new HashSet<>(FileUtil.toStringList(opts.getBubbleHostsFile())); | |||
} catch (Exception e) { | |||
err("Error reading bubbleHosts file: "+shortError(e)); | |||
return; | |||
} | |||
if (empty(bubbleHosts)) { | |||
err("No bubble hosts found in file: "+abs(opts.getBubbleHostsFile())); | |||
return; | |||
} | |||
if (bubbleHosts.iterator().next().contains(",")) { | |||
bubbleHosts = bubbleHosts.stream() | |||
.map(h -> h.split(",")[2]) | |||
.map(NetworkUtil::normalizeHost) | |||
.collect(Collectors.toSet()); | |||
} | |||
final String bootUrl = opts.getBootUrl(); | |||
final HttpResponseBean bootJsonResponse = HttpUtil.getResponse(bootUrl); | |||
if (!bootJsonResponse.isOk()) { | |||
err("Error loading boot.json from "+bootUrl+": "+bootJsonResponse); | |||
} | |||
Set<String> sageSet = new TreeSet<>(); | |||
try { | |||
final JsonNode bootJson = json(bootJsonResponse.getEntityString(), JsonNode.class); | |||
final ArrayNode sages = (ArrayNode) bootJson.get("sages"); | |||
for (int i=0; i<sages.size(); i++) { | |||
sageSet.add(normalizeHost(sages.get(i).textValue())); | |||
} | |||
} catch (Exception e) { | |||
err("Error parsing boot.json ("+bootUrl+"): "+shortError(e)); | |||
return; | |||
} | |||
final SortedSet<String> retain = new TreeSet<>(); | |||
retain.addAll(bubbleHosts); | |||
retain.addAll(bubbleHosts.stream() | |||
.map(h -> { | |||
final int firstDot = h.indexOf('.'); | |||
if (firstDot == -1 || firstDot == h.length()-1) throw new IllegalStateException("invalid bubble host: "+h); | |||
return h.substring(firstDot+1); | |||
}) | |||
.collect(Collectors.toSet())); | |||
retain.addAll(sageSet); | |||
retain.addAll(opts.getAdditionalSages()); | |||
retain.addAll(opts.getRetainHosts()); | |||
final GoDaddyDnsDriver dns = new GoDaddyDnsDriver(); | |||
dns.setCredentials(opts.getCredentials()); | |||
final Set<String> bubbleDomains = bubbleHosts.stream().map(URIUtil::hostToDomain).collect(Collectors.toSet()); | |||
for (String domain : bubbleDomains) { | |||
final String url = GODADDY_BASE_URI + domain + "/records"; | |||
try { | |||
for (DnsType type : DNS_TYPES) { | |||
final Set<GoDaddyDnsRecord> removed = new HashSet<>(); | |||
final Set<GoDaddyDnsRecord> retainRecords = new HashSet<>(); | |||
final GoDaddyDnsRecord[] dnsRecords = dns._listGoDaddyDnsRecords(url+"/"+type); | |||
for (GoDaddyDnsRecord rec : dnsRecords) { | |||
final String host = rec.getName() + "." + domain; | |||
if (retain.contains(host)) { | |||
retainRecords.add(rec); | |||
} else { | |||
if (opts.hasHttpCheckTimeout()) { | |||
try { | |||
url2string(SCHEME_HTTP + host + "/", opts.getHttpCheckTimeoutMillis()); | |||
final String msg = "Host seems alive, not removing: " + host; | |||
log.warn(msg); | |||
out("WARN: " + msg); | |||
retainRecords.add(rec); | |||
} catch (Exception e) { | |||
final String message = "Error connecting to " + host + ": " + shortError(e); | |||
log.debug(message); | |||
out(message); | |||
removed.add(rec); | |||
} | |||
} else { | |||
removed.add(rec); | |||
} | |||
} | |||
} | |||
log.info("Removing "+type+" records:\n" + json(removed)); | |||
final HttpRequestBean domainUpdate = dns.auth(url+"/"+type) | |||
.setMethod(PUT) | |||
.setHeader(CONTENT_TYPE, APPLICATION_JSON) | |||
.setEntity(json(retainRecords)); | |||
final HttpResponseBean response = HttpUtil.getResponse(domainUpdate); | |||
if (!response.isOk()) { | |||
log.error("Error updating "+type+" records for domain: " + domain + ": " + response); | |||
} | |||
} | |||
} catch (Exception e) { | |||
log.error("Error reading DNS records for domain: "+domain+": "+shortError(e)); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,94 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.cloud.dns.godaddy; | |||
import bubble.model.cloud.CloudCredentials; | |||
import lombok.Getter; | |||
import lombok.Setter; | |||
import org.cobbzilla.util.collection.NameAndValue; | |||
import org.cobbzilla.util.main.BaseMainOptions; | |||
import org.kohsuke.args4j.Option; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.util.Collections; | |||
import java.util.Set; | |||
import static java.util.concurrent.TimeUnit.SECONDS; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.empty; | |||
import static org.cobbzilla.util.network.NetworkUtil.toHostSet; | |||
public class GoDaddyDnsCleanerOptions extends BaseMainOptions { | |||
public static final String VAR_GODADDY_API_KEY = "GODADDY_API_KEY"; | |||
public static final String VAR_GODADDY_API_SECRET = "GODADDY_API_SECRET"; | |||
public static final String DEFAULT_BOOT_URL = "https://raw.githubusercontent.com/getbubblenow/bubble-config/master/boot.json"; | |||
public static final String USAGE_API_KEY_ENV_VAR = "Name of environment variable containing GoDaddy API Key. Default is "+VAR_GODADDY_API_KEY; | |||
public static final String OPT_API_KEY_ENV_VAR = "-k"; | |||
public static final String LONGOPT_API_KEY_ENV_VAR = "--api-key"; | |||
public static final int DEFAULT_HTTP_CHECK_TIMEOUT_SECONDS = 10; | |||
@Option(name=OPT_API_KEY_ENV_VAR, aliases=LONGOPT_API_KEY_ENV_VAR, usage=USAGE_API_KEY_ENV_VAR) | |||
@Getter @Setter private String apiKeyVar = VAR_GODADDY_API_KEY; | |||
public static final String USAGE_SECRET_KEY_ENV_VAR = "Name of environment variable containing GoDaddy Secret Key. Default is "+VAR_GODADDY_API_SECRET; | |||
public static final String OPT_SECRET_KEY_ENV_VAR = "-s"; | |||
public static final String LONGOPT_SECRET_KEY_ENV_VAR = "--secret-key"; | |||
@Option(name=OPT_SECRET_KEY_ENV_VAR, aliases=LONGOPT_SECRET_KEY_ENV_VAR, usage=USAGE_SECRET_KEY_ENV_VAR) | |||
@Getter @Setter private String secretKeyVar = VAR_GODADDY_API_SECRET; | |||
public static final String USAGE_LIVE_BUBBLES = "File containing live/running Bubbles, one hostname per line. DNS records for these will be retained"; | |||
public static final String OPT_LIVE_BUBBLES = "-b"; | |||
public static final String LONGOPT_LIVE_BUBBLES = "--bubbles-file"; | |||
@Option(name=OPT_LIVE_BUBBLES, aliases=LONGOPT_LIVE_BUBBLES, usage=USAGE_LIVE_BUBBLES, required=true) | |||
@Getter @Setter private File bubbleHostsFile; | |||
public static final String USAGE_BOOT_CONFIG_URL = "Url or File to boot.json containing initial sages. Default is "+DEFAULT_BOOT_URL; | |||
public static final String OPT_BOOT_CONFIG_URL = "-B"; | |||
public static final String LONGOPT_BOOT_CONFIG_URL = "--boot"; | |||
@Option(name=OPT_BOOT_CONFIG_URL, aliases=LONGOPT_BOOT_CONFIG_URL, usage=USAGE_BOOT_CONFIG_URL) | |||
@Getter @Setter private String bootUrl = DEFAULT_BOOT_URL; | |||
public static final String USAGE_ADDITIONAL_SAGES = "File containing additional sage hosts, one per line"; | |||
public static final String OPT_ADDITIONAL_SAGES = "-A"; | |||
public static final String LONGOPT_ADDITIONAL_SAGES = "--additional-sages"; | |||
@Option(name=OPT_ADDITIONAL_SAGES, aliases=LONGOPT_ADDITIONAL_SAGES, usage=USAGE_ADDITIONAL_SAGES) | |||
@Getter @Setter private File additionalSagesFile; | |||
public boolean hasAdditionalSagesFile () { return !empty(additionalSagesFile); } | |||
public Set<String> getAdditionalSages () throws IOException { | |||
return hasAdditionalSagesFile() ? toHostSet(additionalSagesFile) : Collections.emptySet(); | |||
} | |||
public static final String USAGE_RETAIN_HOSTS = "File containing additional hosts to retain, one per line"; | |||
public static final String OPT_RETAIN_HOSTS = "-R"; | |||
public static final String LONGOPT_RETAIN_HOSTS = "--retain-hosts"; | |||
@Option(name=OPT_RETAIN_HOSTS, aliases=LONGOPT_RETAIN_HOSTS, usage=USAGE_RETAIN_HOSTS) | |||
@Getter @Setter private File retainHostsFile; | |||
public boolean hasRetainHostsFile () { return !empty(retainHostsFile); } | |||
public Set<String> getRetainHosts () throws IOException { | |||
return hasRetainHostsFile() ? toHostSet(retainHostsFile) : Collections.emptySet(); | |||
} | |||
public static final String USAGE_HTTP_CHECK = "Timeout for HTTP check in seconds. Use zero to disable check. Default is "+DEFAULT_HTTP_CHECK_TIMEOUT_SECONDS+" seconds"; | |||
public static final String OPT_HTTP_CHECK = "-H"; | |||
public static final String LONGOPT_HTTP_CHECK = "--http-check-timeout"; | |||
@Option(name=OPT_HTTP_CHECK, aliases=LONGOPT_HTTP_CHECK, usage=USAGE_HTTP_CHECK) | |||
@Getter @Setter private long httpCheckTimeout = DEFAULT_HTTP_CHECK_TIMEOUT_SECONDS; | |||
public boolean hasHttpCheckTimeout () { return getHttpCheckTimeout() > 0; } | |||
public long getHttpCheckTimeoutMillis () { return SECONDS.toMillis(getHttpCheckTimeout()); } | |||
public CloudCredentials getCredentials() { | |||
final String apiKey = System.getenv(getApiKeyVar()); | |||
if (empty(apiKey)) throw new IllegalArgumentException("Env var not found or empty: "+getApiKeyVar()); | |||
final String secretKey = System.getenv(getSecretKeyVar()); | |||
if (empty(secretKey)) throw new IllegalArgumentException("Env var not found or empty: "+getApiKeyVar()); | |||
return new CloudCredentials(new NameAndValue[] { | |||
new NameAndValue(VAR_GODADDY_API_KEY, apiKey), | |||
new NameAndValue(VAR_GODADDY_API_SECRET, secretKey), | |||
}); | |||
} | |||
} |
@@ -195,18 +195,20 @@ public class GoDaddyDnsDriver extends DnsDriverBase<GoDaddyDnsConfig> { | |||
private final Map<String, GoDaddyDnsRecord[]> listCache = new ExpirationMap<>(SECONDS.toMillis(10)); | |||
public GoDaddyDnsRecord[] listGoDaddyDnsRecords(final String goDaddyApiUrl) { | |||
return listCache.computeIfAbsent(goDaddyApiUrl, url -> { | |||
final var request = auth(url); | |||
final HttpResponseBean response; | |||
try { | |||
response = HttpUtil.getResponse(request); | |||
} catch (Exception e) { | |||
log.error("listGoDaddyDnsRecords(" + url + "): " + e, e); | |||
return GoDaddyDnsRecord.EMPTY_ARRAY; | |||
} | |||
if (!response.isOk()) throw new IllegalStateException("listGoDaddyDnsRecords: " + response); | |||
return json(response.getEntityString(), GoDaddyDnsRecord[].class); | |||
}); | |||
return listCache.computeIfAbsent(goDaddyApiUrl, this::_listGoDaddyDnsRecords); | |||
} | |||
public GoDaddyDnsRecord[] _listGoDaddyDnsRecords(String url) { | |||
final var request = auth(url); | |||
final HttpResponseBean response; | |||
try { | |||
response = HttpUtil.getResponse(request); | |||
} catch (Exception e) { | |||
log.error("listGoDaddyDnsRecords(" + url + "): " + e, e); | |||
return GoDaddyDnsRecord.EMPTY_ARRAY; | |||
} | |||
if (!response.isOk()) throw new IllegalStateException("listGoDaddyDnsRecords: " + response); | |||
return json(response.getEntityString(), GoDaddyDnsRecord[].class); | |||
} | |||
public HttpRequestBean auth(String url) { return new HttpRequestBean(url).setHeader(AUTHORIZATION, getAuthValue()); } | |||
@@ -5,6 +5,7 @@ | |||
package bubble.cloud.dns.godaddy; | |||
import bubble.model.cloud.BubbleDomain; | |||
import lombok.EqualsAndHashCode; | |||
import lombok.Getter; | |||
import lombok.NoArgsConstructor; | |||
import lombok.Setter; | |||
@@ -14,7 +15,7 @@ import org.cobbzilla.util.dns.DnsType; | |||
import static org.cobbzilla.util.dns.DnsRecord.OPT_NS_NAME; | |||
@NoArgsConstructor @Accessors(chain=true) | |||
@NoArgsConstructor @Accessors(chain=true) @EqualsAndHashCode(of={"name", "type", "data"}) | |||
public class GoDaddyDnsRecord { | |||
public static final GoDaddyDnsRecord[] EMPTY_ARRAY = new GoDaddyDnsRecord[0]; | |||
@@ -1,3 +1,7 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.dao.account; | |||
import bubble.model.account.TrustedClient; | |||
@@ -4,6 +4,7 @@ | |||
*/ | |||
package bubble.main; | |||
import bubble.cloud.dns.godaddy.GoDaddyDnsCleanerMain; | |||
import bubble.main.http.BubbleHttpDeleteMain; | |||
import bubble.main.http.BubbleHttpGetMain; | |||
import bubble.main.http.BubbleHttpPostMain; | |||
@@ -36,7 +37,8 @@ public class BubbleMain { | |||
{"rekey", RekeyDatabaseMain.class}, | |||
{"generate-algo-conf", GenerateAlgoConfMain.class}, | |||
{"const", ConstMain.class}, | |||
{"file-header", FileHeaderMain.class} | |||
{"file-header", FileHeaderMain.class}, | |||
{"gd-cleanup", GoDaddyDnsCleanerMain.class} | |||
}); | |||
public static void main(String[] args) throws Exception { | |||
@@ -1,3 +1,7 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.model.account; | |||
@@ -1,3 +1,7 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.model.account; | |||
import com.fasterxml.jackson.annotation.JsonIgnore; | |||
@@ -1,3 +1,7 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.model.account; | |||
import lombok.AllArgsConstructor; | |||
@@ -1,3 +1,7 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.model.cloud; | |||
import lombok.Getter; | |||
@@ -1,3 +1,7 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.notify.upgrade; | |||
import bubble.model.app.AppTemplateEntity; | |||
@@ -1,3 +1,7 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.resources.account; | |||
import bubble.dao.SessionDAO; | |||
@@ -1,3 +1,7 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.rule; | |||
import lombok.Getter; | |||
@@ -0,0 +1,42 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.service.bill; | |||
import bubble.dao.bill.AccountPlanDAO; | |||
import bubble.dao.bill.BillDAO; | |||
import bubble.dao.bill.BubblePlanDAO; | |||
import bubble.model.bill.AccountPlan; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.util.daemon.SimpleDaemon; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
import java.util.List; | |||
import static java.util.concurrent.TimeUnit.DAYS; | |||
import static java.util.concurrent.TimeUnit.HOURS; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.now; | |||
@Service @Slf4j | |||
public class FirstBillNoticeService extends SimpleDaemon { | |||
private static final long BILLING_CHECK_INTERVAL = HOURS.toMillis(6); | |||
@Override protected long getSleepTime() { return BILLING_CHECK_INTERVAL; } | |||
@Autowired private AccountPlanDAO accountPlanDAO; | |||
@Autowired private BubblePlanDAO planDAO; | |||
@Autowired private BillDAO billDAO; | |||
@Override protected void process() { | |||
// sort plans by Account ctime, newer Accounts are billed before older Accounts | |||
final List<AccountPlan> plansToBillSoon = accountPlanDAO.findBillableAccountPlans(now()+DAYS.toMillis(3)); | |||
// iterate plans, find plans that have no promotions to apply and will thus actually be charged | |||
// for each such plan, send an email to the account owner telling them: | |||
// when they will be billed, what the amount will be, how it will appear on their statement, and how to cancel | |||
} | |||
} |
@@ -1,3 +1,7 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.service.stream; | |||
import lombok.extern.slf4j.Slf4j; | |||
@@ -1,3 +1,7 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.service.upgrade; | |||
import bubble.dao.app.AppTemplateEntityDAO; | |||
@@ -1,3 +1,7 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.service.upgrade; | |||
import bubble.dao.account.AccountDAO; | |||
@@ -1,3 +1,7 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package db; | |||
import lombok.NonNull; | |||
@@ -2561,7 +2561,6 @@ dearn | |||
dears | |||
deary | |||
deash | |||
death | |||
deave | |||
deaws | |||
deawy | |||
@@ -2570,10 +2569,6 @@ debar | |||
debby | |||
debel | |||
debes | |||
debit | |||
debts | |||
debud | |||
debug | |||
debur | |||
debus | |||
debut | |||
@@ -2600,7 +2595,6 @@ deers | |||
deets | |||
deeve | |||
deevs | |||
defat | |||
defer | |||
deffo | |||
defis | |||
@@ -2636,9 +2630,6 @@ deman | |||
demes | |||
demic | |||
demit | |||
demob | |||
demon | |||
demos | |||
dempt | |||
demur | |||
denar | |||
@@ -2664,13 +2655,11 @@ derns | |||
deros | |||
derro | |||
derry | |||
derth | |||
dervs | |||
deshi | |||
desks | |||
desse | |||
deter | |||
detox | |||
deuce | |||
devas | |||
devel | |||
@@ -2679,7 +2668,6 @@ devon | |||
devot | |||
dewan | |||
dewar | |||
dewax | |||
dewed | |||
dexes | |||
dexie | |||
@@ -2730,19 +2718,16 @@ dined | |||
diner | |||
dines | |||
dinge | |||
dingo | |||
dings | |||
dingy | |||
dinic | |||
dinks | |||
dinky | |||
dinna | |||
dinos | |||
dints | |||
diode | |||
diols | |||
diota | |||
dippy | |||
dipso | |||
diram | |||
direr | |||
@@ -2751,7 +2736,6 @@ dirke | |||
dirks | |||
dirls | |||
dirts | |||
dirty | |||
disas | |||
disci | |||
disco | |||
@@ -2768,7 +2752,6 @@ ditsy | |||
ditto | |||
ditts | |||
ditty | |||
ditzy | |||
divan | |||
divas | |||
dived | |||
@@ -2838,7 +2821,6 @@ donas | |||
donee | |||
doner | |||
donga | |||
dongs | |||
donko | |||
donna | |||
donne | |||
@@ -2939,7 +2921,6 @@ drays | |||
dread | |||
dream | |||
drear | |||
dreck | |||
dreed | |||
drees | |||
dregs | |||
@@ -2982,7 +2963,6 @@ drove | |||
drown | |||
drows | |||
drubs | |||
drugs | |||
druid | |||
drums | |||
drunk | |||
@@ -3073,7 +3053,6 @@ dwams | |||
dwang | |||
dwarf | |||
dwaum | |||
dweeb | |||
dwell | |||
dwelt | |||
dwile | |||
@@ -3129,7 +3108,6 @@ edile | |||
edits | |||
educe | |||
educt | |||
eejit | |||
eerie | |||
eeven | |||
eevns | |||
@@ -3221,8 +3199,6 @@ ender | |||
endew | |||
endow | |||
endue | |||
enema | |||
enemy | |||
enews | |||
enfix | |||
eniac | |||
@@ -3389,9 +3365,7 @@ fados | |||
faena | |||
faery | |||
faffs | |||
faggy | |||
fagin | |||
fagot | |||
faiks | |||
fails | |||
faine | |||
@@ -3400,11 +3374,6 @@ faint | |||
fairs | |||
fairy | |||
faith | |||
faked | |||
faker | |||
fakes | |||
fakey | |||
fakie | |||
fakir | |||
falaj | |||
falls | |||
@@ -3436,18 +3405,9 @@ farle | |||
farls | |||
farms | |||
faros | |||
farse | |||
farts | |||
fasci | |||
fasti | |||
fasts | |||
fatal | |||
fated | |||
fates | |||
fatly | |||
fatso | |||
fatty | |||
fatwa | |||
faugh | |||
fauld | |||
fault | |||
@@ -3500,13 +3460,9 @@ felid | |||
fella | |||
fells | |||
felly | |||
felon | |||
felts | |||
felty | |||
femal | |||
femes | |||
femme | |||
femmy | |||
femur | |||
fence | |||
fends | |||
@@ -3539,9 +3495,7 @@ fetid | |||
fetor | |||
fetta | |||
fetts | |||
fetwa | |||
feuar | |||
feuds | |||
feued | |||
fever | |||
fewer | |||
@@ -3646,7 +3600,6 @@ flail | |||
flair | |||
flake | |||
flaks | |||
flaky | |||
flame | |||
flamm | |||
flams | |||
@@ -3673,7 +3626,6 @@ flees | |||
fleet | |||
flegs | |||
fleme | |||
flesh | |||
flews | |||
flexo | |||
fleys | |||
@@ -3698,7 +3650,6 @@ flobs | |||
flock | |||
flocs | |||
floes | |||
flogs | |||
flong | |||
flood | |||
floor | |||
@@ -3720,8 +3671,6 @@ flues | |||
fluey | |||
fluff | |||
fluid | |||
fluke | |||
fluky | |||
flume | |||
flump | |||
flung | |||
@@ -3831,7 +3780,6 @@ frats | |||
fraud | |||
fraus | |||
frays | |||
freak | |||
freed | |||
freer | |||
frees | |||
@@ -11742,7 +11690,6 @@ warty | |||
wases | |||
washy | |||
wasps | |||
waspy | |||
waste | |||
wasts | |||
watap | |||
@@ -11815,7 +11762,6 @@ welly | |||
welsh | |||
welts | |||
wembs | |||
wench | |||
wends | |||
wenge | |||
wenny | |||
@@ -11862,9 +11808,6 @@ whigs | |||
while | |||
whilk | |||
whims | |||
whine | |||
whins | |||
whiny | |||
whios | |||
whips | |||
whipt | |||
@@ -11936,7 +11879,6 @@ wingy | |||
winks | |||
winna | |||
winns | |||
winos | |||
winze | |||
wiped | |||
wiper | |||
@@ -11961,9 +11903,6 @@ withe | |||
withs | |||
withy | |||
witty | |||
wived | |||
wiver | |||
wives | |||
wizen | |||
wizes | |||
woads | |||
@@ -11981,16 +11920,12 @@ wonks | |||
wonky | |||
wonts | |||
woods | |||
woody | |||
wooed | |||
wooer | |||
woofs | |||
woofy | |||
woold | |||
wools | |||
wooly | |||
woons | |||
woops | |||
woose | |||
woosh | |||
wootz | |||
@@ -12054,7 +11989,6 @@ xenia | |||
xenic | |||
xenon | |||
xeric | |||
xerox | |||
xerus | |||
xoana | |||
xrays | |||
@@ -12183,16 +12117,12 @@ yogis | |||
yoick | |||
yojan | |||
yoked | |||
yokel | |||
yoker | |||
yokes | |||
yokul | |||
yolks | |||
yolky | |||
yomim | |||
yomps | |||
yonic | |||
yonis | |||
yonks | |||
yoofs | |||
yoops | |||
@@ -12232,7 +12162,6 @@ yummo | |||
yummy | |||
yumps | |||
yupon | |||
yuppy | |||
yurta | |||
yurts | |||
yuzus | |||
@@ -12272,7 +12201,6 @@ zibet | |||
ziffs | |||
zigan | |||
zilas | |||
zilch | |||
zilla | |||
zills | |||
zimbi | |||
@@ -1,5 +1,7 @@ | |||
#!/bin/bash | |||
# | |||
# Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
# | |||
LOG=/tmp/dhparams.log | |||
DH_PARAMS=/etc/nginx/dhparams.pem | |||
@@ -1 +1 @@ | |||
Subproject commit 55cc17c6c4241b8eb6c1ada2ce29ce08307bbfee | |||
Subproject commit 7974c29cd2cf5eb312ae98de6f7a884258812268 |
@@ -1 +1 @@ | |||
Subproject commit fc87156268ff3380321b83d9b6e2408441f58047 | |||
Subproject commit 622fcee16099536b72aefc92475463a1e9b28756 |