Improvement to under attack mode

This commit is contained in:
Piero Toffanin 2025-04-18 15:52:27 -04:00
parent fd0119bf70
commit 411b50178e
5 changed files with 30 additions and 11 deletions

View File

@ -365,7 +365,11 @@ def create_app(args):
need_key = True need_key = True
if args.under_attack and key_missing: if args.under_attack and key_missing:
need_key = True abort(make_response(jsonify({
'translatedText': secret.get_emoji(),
'alternatives': [],
'detectedLanguage': { 'confidence': 100, 'language': 'en' }
}), 200))
if need_key: if need_key:
description = _("Please contact the server operator to get an API key") description = _("Please contact the server operator to get an API key")
@ -473,7 +477,8 @@ def create_app(args):
url_prefix=args.url_prefix, url_prefix=args.url_prefix,
get_api_key_link=args.get_api_key_link, get_api_key_link=args.get_api_key_link,
api_secret=api_secret, api_secret=api_secret,
bogus_api_secret=bogus_api_secret), content_type='application/javascript; charset=utf-8') bogus_api_secret=bogus_api_secret,
under_attack=args.under_attack), content_type='application/javascript; charset=utf-8')
if args.require_api_key_secret: if args.require_api_key_secret:
response.headers['Last-Modified'] = http_date(datetime.now()) response.headers['Last-Modified'] = http_date(datetime.now())

View File

@ -457,6 +457,10 @@ code[class*="language-"], pre[class*="language-"] {
background-color: var(--sec-bg-color) background-color: var(--sec-bg-color)
} }
textarea:disabled{
opacity: 0.5;
}
@media (min-width: 280px) { @media (min-width: 280px) {
.btn-text { .btn-text {
display: inline; display: inline;

View File

@ -60,7 +60,7 @@ class MemoryStorage(Storage):
def set_str(self, key, value, ex=None): def set_str(self, key, value, ex=None):
self.store[key] = { self.store[key] = {
'value': value, 'value': value,
'ex': time.time() + ex 'ex': None if ex is None else time.time() + ex
} }
def get_str(self, key): def get_str(self, key):

View File

@ -17,6 +17,7 @@ document.addEventListener('DOMContentLoaded', function(){
settings: {}, settings: {},
sourceLang: "", sourceLang: "",
targetLang: "", targetLang: "",
apiKey: localStorage.getItem("api_key") || "",
loadingTranslation: false, loadingTranslation: false,
inputText: "", inputText: "",
@ -45,6 +46,7 @@ document.addEventListener('DOMContentLoaded', function(){
}, },
mounted: function() { mounted: function() {
const self = this; const self = this;
window._vueApp = self;
self.$el.classList.add("loaded"); self.$el.classList.add("loaded");
const settingsRequest = new XMLHttpRequest(); const settingsRequest = new XMLHttpRequest();
@ -137,7 +139,7 @@ document.addEventListener('DOMContentLoaded', function(){
' target: ' + this.$options.filters.escape(this.targetLang) + ',', ' target: ' + this.$options.filters.escape(this.targetLang) + ',',
' format: "' + (this.isHtml ? "html" : "text") + '",', ' format: "' + (this.isHtml ? "html" : "text") + '",',
' alternatives: 3,', ' alternatives: 3,',
' api_key: "' + (localStorage.getItem("api_key") || "") + '"', ' api_key: "' + this.apiKey + '"',
' }),', ' }),',
' headers: { "Content-Type": "application/json" }', ' headers: { "Content-Type": "application/json" }',
'});', '});',
@ -164,6 +166,9 @@ document.addEventListener('DOMContentLoaded', function(){
}); });
return tgtLangs; return tgtLangs;
} }
},
disableInput: function(){
return {% if under_attack %}true{% else %}false{% endif %} && this.apiKey === "";
} }
}, },
filters: { filters: {
@ -211,6 +216,7 @@ document.addEventListener('DOMContentLoaded', function(){
history.pushState(null, '', newRelativePathQuery); history.pushState(null, '', newRelativePathQuery);
}, },
handleInput: function(e){ handleInput: function(e){
if (this.disableInput) return;
this.closeSuggestTranslation(e) this.closeSuggestTranslation(e)
this.updateQueryParam('source', this.sourceLang) this.updateQueryParam('source', this.sourceLang)
@ -245,7 +251,7 @@ document.addEventListener('DOMContentLoaded', function(){
data.append("target", self.targetLang); data.append("target", self.targetLang);
data.append("format", self.isHtml ? "html" : "text"); data.append("format", self.isHtml ? "html" : "text");
data.append("alternatives", 3); data.append("alternatives", 3);
data.append("api_key", localStorage.getItem("api_key") || ""); data.append("api_key", self.apiKey);
if (self.apiSecret) data.append("secret", atob(self.apiSecret)); if (self.apiSecret) data.append("secret", atob(self.apiSecret));
request.open('POST', BaseUrl + '/translate', true); request.open('POST', BaseUrl + '/translate', true);
@ -329,7 +335,7 @@ document.addEventListener('DOMContentLoaded', function(){
data.append("s", self.translatedText); data.append("s", self.translatedText);
data.append("source", self.sourceLang); data.append("source", self.sourceLang);
data.append("target", self.targetLang); data.append("target", self.targetLang);
data.append("api_key", localStorage.getItem("api_key") || ""); data.append("api_key", self.apiKey);
request.open('POST', BaseUrl + '/suggest', true); request.open('POST', BaseUrl + '/suggest', true);
request.onload = function() { request.onload = function() {
@ -382,6 +388,7 @@ document.addEventListener('DOMContentLoaded', function(){
}, },
translateFile: function(e) { translateFile: function(e) {
e.preventDefault(); e.preventDefault();
if (this.disableInput) return;
let self = this; let self = this;
let translateFileRequest = new XMLHttpRequest(); let translateFileRequest = new XMLHttpRequest();
@ -392,7 +399,7 @@ document.addEventListener('DOMContentLoaded', function(){
data.append("file", this.inputFile); data.append("file", this.inputFile);
data.append("source", this.sourceLang); data.append("source", this.sourceLang);
data.append("target", this.targetLang); data.append("target", this.targetLang);
data.append("api_key", localStorage.getItem("api_key") || ""); data.append("api_key", this.apiKey);
if (self.apiSecret) data.append("secret", self.apiSecret); if (self.apiSecret) data.append("secret", self.apiSecret);
this.loadingFileTranslation = true this.loadingFileTranslation = true
@ -537,6 +544,9 @@ function setApiKey(){
if (newKey === null) newKey = ""; if (newKey === null) newKey = "";
localStorage.setItem("api_key", newKey); localStorage.setItem("api_key", newKey);
if (window._vueApp){
window._vueApp.apiKey = newKey;
}
} }
// Color scheme handling // Color scheme handling

View File

@ -181,11 +181,11 @@
{% endif %} {% endif %}
<h3 class="header center">{{ _h("Translation API") }}</h3> <h3 class="header center">{{ _h("Translation API") }}</h3>
<div id="translation-type-btns" class="s12 center" v-if="filesTranslation === true"> <div id="translation-type-btns" class="s12 center" v-if="filesTranslation === true">
<button type="button" class="btn btn-switch-type" @click="switchType('text')" :class="{'active': translationType === 'text'}"> <button type="button" class="btn btn-switch-type" @click="switchType('text')" :class="{'active': translationType === 'text'}" :disabled="disableInput">
<i aria-hidden="true" class="material-icons">title</i> <i aria-hidden="true" class="material-icons">title</i>
<span class="btn-text">{{ _h("Translate Text") }}</span> <span class="btn-text">{{ _h("Translate Text") }}</span>
</button> </button>
<button type="button" class="btn btn-switch-type" @click="switchType('files')" :class="{'active': translationType === 'files'}"> <button type="button" class="btn btn-switch-type" @click="switchType('files')" :class="{'active': translationType === 'files'}" :disabled="disableInput">
<i aria-hidden="true" class="material-icons">description</i> <i aria-hidden="true" class="material-icons">description</i>
<span class="btn-text">{{ _h("Translate Files") }}</span> <span class="btn-text">{{ _h("Translate Files") }}</span>
</button> </button>
@ -220,7 +220,7 @@
<label for="textarea1" class="sr-only"> <label for="textarea1" class="sr-only">
{{ _h("Text to translate") }} {{ _h("Text to translate") }}
</label> </label>
<textarea id="textarea1" :maxLength="charactersLimit" v-model="inputText" @input="handleInput" ref="inputTextarea" dir="auto"></textarea> <textarea id="textarea1" :maxLength="charactersLimit" v-model="inputText" @input="handleInput" ref="inputTextarea" dir="auto" :disabled="disableInput"></textarea>
<button class="btn-delete-text" title="{{ _h('Delete text') }}" aria-label="{{ _h('Delete text') }}" @click="deleteText"> <button class="btn-delete-text" title="{{ _h('Delete text') }}" aria-label="{{ _h('Delete text') }}" @click="deleteText">
<i class="material-icons">close</i> <i class="material-icons">close</i>
</button> </button>
@ -232,7 +232,7 @@
<label for="textarea2" class="sr-only"> <label for="textarea2" class="sr-only">
{{ _h("Translated text") }} {{ _h("Translated text") }}
</label> </label>
<textarea id="textarea2" v-model="translatedText" ref="translatedTextarea" dir="auto" v-bind:readonly="suggestions && !isSuggesting"></textarea> <textarea id="textarea2" v-model="translatedText" ref="translatedTextarea" dir="auto" v-bind:readonly="suggestions && !isSuggesting" :disabled="disableInput"></textarea>
<div class="actions"> <div class="actions">
<button v-if="suggestions && !loadingTranslation && inputText.length && !isSuggesting" class="btn-action" @click="suggestTranslation" aria-label="{{ _h('Suggest translation') }}"> <button v-if="suggestions && !loadingTranslation && inputText.length && !isSuggesting" class="btn-action" @click="suggestTranslation" aria-label="{{ _h('Suggest translation') }}">
<i class="material-icons">edit</i> <i class="material-icons">edit</i>