diff --git a/i18n.yaml b/i18n.yaml index 8d556ac..5598560 100644 --- a/i18n.yaml +++ b/i18n.yaml @@ -8,6 +8,10 @@ reference: btn-new-secret: New Secret btn-reveal-secret: Show me the secret! btn-show-explanation: How does this work? + expire-default: Default Expiry + expire-n-days: '{n} day | {n} days' + expire-n-hours: '{n} hour | {n} hours' + expire-n-minutes: '{n} minute | {n} minutes' items-explanation: - You enter a secret into the field on this page - Your browser encrypts the secret using a generated password @@ -16,8 +20,10 @@ reference: - You pass the displayed URL containing the ID and the decryption password to the recipient - 'The recipient can view the secret exactly once: If they can''t, the secret might have been viewed by someone else!' - After the encrypted secret has been retrieved once, it is deleted from the server + label-expiry: 'Expire in:' label-secret-data: 'Secret data:' text-burn-hint: Please remember not to go to this URL yourself as that would destroy the secret. Just pass it to someone else! + text-burn-time: If not viewed before, this secret will automatically be burned on text-hint-burned: Attention: You're only seeing this once. As soon as you reload the page the secret will be gone so maybe copy it now… text-powered-by: Powered by text-pre-reveal-hint: To reveal the secret click this button but be aware doing so will destroy the secret. You can only view it once! diff --git a/src/app.vue b/src/app.vue index b0f6006..b9c3788 100644 --- a/src/app.vue +++ b/src/app.vue @@ -101,13 +101,41 @@ rows="5" /> - - {{ $t('btn-create-secret') }} - + + + + {{ $t('btn-create-secret') }} + + + + + + + + @@ -165,6 +193,10 @@

+

+ {{ $t('text-burn-time') }} + {{ secretExpiry.toLocaleString() }} +

passwordCharset[n % passwordCharset.length]) .join('') crypto.enc(this.secret, this.securePassword) - .then(secret => fetch('api/create', { - body: JSON.stringify({ secret }), - headers: { - 'content-type': 'application/json', - }, - method: 'POST', - }) - .then(resp => { - if (resp.status !== 201) { + .then(secret => { + let reqURL = 'api/create' + if (this.selectedExpiry !== null) { + reqURL = `api/create?expire=${this.selectedExpiry}` + } + + return fetch(reqURL, { + body: JSON.stringify({ secret }), + headers: { + 'content-type': 'application/json', + }, + method: 'POST', + }) + .then(resp => { + if (resp.status !== 201) { // Server says "no" + this.error = this.$t('alert-something-went-wrong') + this.showError = true + return + } + + resp.json() + .then(data => { + this.secretId = data.secret_id + this.secret = '' + + if (data.expires_at) { + this.secretExpiry = new Date(data.expires_at) + } + + // Give the interface a moment to transistion and focus + window.setTimeout(() => this.$refs.secretUrl.focus(), 100) + }) + }) + .catch(err => { + // Network error this.error = this.$t('alert-something-went-wrong') this.showError = true - return - } - - resp.json() - .then(data => { - this.secretId = data.secret_id - this.secret = '' - - // Give the interface a moment to transistion and focus - window.setTimeout(() => this.$refs.secretUrl.focus(), 100) - }) - }) - .catch(err => { - // Network error - this.error = this.$t('alert-something-went-wrong') - this.showError = true - })) + }) + }) return false }, + expiryChoices() { + const choices = [{ text: this.$t('expire-default'), value: null }] + for (const choice of [ + { text: this.$tc('expire-n-days', 90), value: 90 * 86400 }, // 90 days + { text: this.$tc('expire-n-days', 30), value: 30 * 86400 }, // 30 days + { text: this.$tc('expire-n-days', 7), value: 7 * 86400 }, // 7 days + { text: this.$tc('expire-n-days', 3), value: 3 * 86400 }, // 3 days + { text: this.$tc('expire-n-days', 1), value: 24 * 3600 }, // 1 day + { text: this.$tc('expire-n-hours', 12), value: 12 * 3600 }, // 12 hours + { text: this.$tc('expire-n-hours', 4), value: 4 * 3600 }, // 4 hours + { text: this.$tc('expire-n-hours', 1), value: 60 * 60 }, // 1 hour + { text: this.$tc('expire-n-minutes', 30), value: 30 * 60 }, // 30 minutes + { text: this.$tc('expire-n-minutes', 5), value: 5 * 60 }, // 5 minutes + ]) { + if (maxSecretExpire > 0 && choice.value > maxSecretExpire) { + continue + } + + choices.push(choice) + } + + return choices + }, + // hashLoad reacts on a changed window hash an starts the diplaying of the secret hashLoad() { const hash = decodeURIComponent(window.location.hash) diff --git a/src/langs/langs.js b/src/langs/langs.js index ba26984..1f710fc 100644 --- a/src/langs/langs.js +++ b/src/langs/langs.js @@ -3,7 +3,7 @@ export default { 'ca': JSON.parse('{"alert-secret-not-found":"Aquest no és el secret que busques\u0026hellip; - Si esperaves que el secret estiguera ací, és possible que s\'haja vist compromés, ja que una altra persona podria haver obert l\'enllaç en comptes de tu.","alert-something-went-wrong":"Alguna cosa ha eixit malament. Ens sap molt greu\u0026hellip;","btn-create-secret":"Crea el secret!","btn-new-secret":"Nou secret","btn-reveal-secret":"Mostra\'m el secret!","btn-show-explanation":"Com funciona?","items-explanation":["Introduïx un secret en el formulari que hi ha en aquesta pàgina","El teu navegador xifra el secret utilitzant una contrasenya generada","Únicament s\'envia al servidor el secret xifrat (mai s\'envien ni el secret sense xifrar ni la contrasenya!)","El servidor emmagatzema el secret xifrat durant un temps limitat","Envia al destinatari l\'enllaç mostrat, que conté l\'identificador del secret i la contrasenya de desxifrat","El destinatari pot veure el secret una sola vegada: si no pot, el secret podria haver sigut vist per una altra persona!","Quan s\'ha obtingut per primera i única vegada el secret xifrat, s\'elimina del servidor"],"label-secret-data":"Informació secreta:","text-burn-hint":"Per favor, recorda no accedir a aquest enllaç tu mateix, ja que això destruiria el secret. Només has de passar-li\'l a una altra persona!","text-hint-burned":"\u003cstrong\u003eAtenció:\u003c/strong\u003e Només veuràs això una vegada. Quan recarregues la pàgina, el secret desapareixerà, així que copia\'l ja\u0026hellip;","text-powered-by":"Funciona amb","text-pre-reveal-hint":"Per a mostrar el secret prem aquest botó, però tingues en compte que en fer-ho es destruirà. Només pots veure\'l una vegada!","text-pre-url":"El teu secret ha sigut creat i emmagatzemat en el següent enllaç:","title-explanation":"Així és com funciona\u0026hellip;","title-new-secret":"Crea un nou secret","title-reading-secret":"Obtenint el teu secret\u0026hellip;","title-secret-created":"Secret creat!"}'), 'de': JSON.parse('{"alert-secret-not-found":"Das ist nicht das Secret, was du suchst\u0026hellip; - Falls du diesen Link noch nicht selbst geöffnet hast, könnte das Secret kompromittiert sein, da jemand anderes den Link geöffnet haben könnte.","alert-something-went-wrong":"Irgendwas ging schief. Entschuldigung\u0026hellip;","btn-create-secret":"Secret erstellen!","btn-new-secret":"Neues Secret","btn-reveal-secret":"Zeig mir das Secret!","btn-show-explanation":"Wie funktioniert das?","items-explanation":["Du gibst ein Secret auf dieser Seite ein","Dein Browser verschlüsselt das Secret mit einem generierten Passwort","Nur das verschlüsselte Secret wird an den Server geschickt (das Passwort oder das Secret im Klartext werden niemals übertragen!)","Der Server speichert das verschlüsselte Secret für eine Weile","Du gibst die angezeigte URL, welche die ID und das Passwort des Secrets enthält, an den Empfänger","Der Empfänger kann das Secret einmalig abrufen: Funktioniert das nicht, könnte jemand anderes es abgerufen haben!","Wenn das verschlüsselte Secret das erste Mal abgerufen wurde, wird es automatisch vom Server gelöscht"],"label-secret-data":"Inhalt des Secrets:","text-burn-hint":"Bitte rufe die URL nicht selbst auf, da das Secret dadurch zerstört würde. Gib sie einfach weiter!","text-hint-burned":"\u003cstrong\u003eAchtung:\u003c/strong\u003e Du kannst das nur einmal ansehen! Sobald du die Seite neu lädst, ist das Secret verschwunden, also besser direkt kopieren und sicher abspeichern\u0026hellip;","text-powered-by":"Läuft mit","text-pre-reveal-hint":"Um das Secret anzuzeigen klicke diesen Button aber denk dran, dass das Secret nur einmal angezeigt und dabei gelöscht wird.","text-pre-url":"Dein Secret wurde angelegt und unter folgender URL gespeichert:","text-secret-create-disabled":"Auf dieser Instanz wurde das Erstellen neuer Secrets deaktiviert.","title-explanation":"So funktioniert es\u0026hellip;","title-new-secret":"Erstelle ein neues Secret","title-reading-secret":"Secret auslesen\u0026hellip;","title-secret-create-disabled":"Erstellen von Secrets deaktiviert…","title-secret-created":"Secret erstellt!"}'), - 'en': JSON.parse('{"alert-secret-not-found":"This is not the secret you are looking for\u0026hellip; - If you expected the secret to be here it might be compromised as someone else might have opened the link already.","alert-something-went-wrong":"Something went wrong. I\'m very sorry about this\u0026hellip;","btn-create-secret":"Create the secret!","btn-new-secret":"New Secret","btn-reveal-secret":"Show me the secret!","btn-show-explanation":"How does this work?","items-explanation":["You enter a secret into the field on this page","Your browser encrypts the secret using a generated password","Only the encrypted secret is sent to the server (neither the plain secret nor the password are ever sent!)","The server stores the encrypted secret for a certain time","You pass the displayed URL containing the ID and the decryption password to the recipient","The recipient can view the secret exactly once: If they can\'t, the secret might have been viewed by someone else!","After the encrypted secret has been retrieved once, it is deleted from the server"],"label-secret-data":"Secret data:","text-burn-hint":"Please remember not to go to this URL yourself as that would destroy the secret. Just pass it to someone else!","text-hint-burned":"\u003cstrong\u003eAttention:\u003c/strong\u003e You\'re only seeing this once. As soon as you reload the page the secret will be gone so maybe copy it now\u0026hellip;","text-powered-by":"Powered by","text-pre-reveal-hint":"To reveal the secret click this button but be aware doing so will destroy the secret. You can only view it once!","text-pre-url":"Your secret was created and stored using this URL:","text-secret-create-disabled":"The creation of new secrets is disabled in this instance.","title-explanation":"This is how it works\u0026hellip;","title-new-secret":"Create a new secret","title-reading-secret":"Reading your secret\u0026hellip;","title-secret-create-disabled":"Secret creation disabled…","title-secret-created":"Secret created!"}'), + 'en': JSON.parse('{"alert-secret-not-found":"This is not the secret you are looking for\u0026hellip; - If you expected the secret to be here it might be compromised as someone else might have opened the link already.","alert-something-went-wrong":"Something went wrong. I\'m very sorry about this\u0026hellip;","btn-create-secret":"Create the secret!","btn-new-secret":"New Secret","btn-reveal-secret":"Show me the secret!","btn-show-explanation":"How does this work?","expire-default":"Default Expiry","expire-n-days":"{n} day | {n} days","expire-n-hours":"{n} hour | {n} hours","expire-n-minutes":"{n} minute | {n} minutes","items-explanation":["You enter a secret into the field on this page","Your browser encrypts the secret using a generated password","Only the encrypted secret is sent to the server (neither the plain secret nor the password are ever sent!)","The server stores the encrypted secret for a certain time","You pass the displayed URL containing the ID and the decryption password to the recipient","The recipient can view the secret exactly once: If they can\'t, the secret might have been viewed by someone else!","After the encrypted secret has been retrieved once, it is deleted from the server"],"label-expiry":"Expire in:","label-secret-data":"Secret data:","text-burn-hint":"Please remember not to go to this URL yourself as that would destroy the secret. Just pass it to someone else!","text-burn-time":"If not viewed before, this secret will automatically burned on","text-hint-burned":"\u003cstrong\u003eAttention:\u003c/strong\u003e You\'re only seeing this once. As soon as you reload the page the secret will be gone so maybe copy it now\u0026hellip;","text-powered-by":"Powered by","text-pre-reveal-hint":"To reveal the secret click this button but be aware doing so will destroy the secret. You can only view it once!","text-pre-url":"Your secret was created and stored using this URL:","text-secret-create-disabled":"The creation of new secrets is disabled in this instance.","title-explanation":"This is how it works\u0026hellip;","title-new-secret":"Create a new secret","title-reading-secret":"Reading your secret\u0026hellip;","title-secret-create-disabled":"Secret creation disabled…","title-secret-created":"Secret created!"}'), 'es': JSON.parse('{"alert-secret-not-found":"Este no es el secreto que buscas\u0026hellip; - Si esperabas que el secreto estuviera aquí, es posible que se haya visto comprometido, ya que otra persona podría haber abierto el enlace en tu lugar.","alert-something-went-wrong":"Algo ha salido mal. Lo sentimos mucho\u0026hellip;","btn-create-secret":"¡Crea el secreto!","btn-new-secret":"Nuevo secreto","btn-reveal-secret":"¡Muéstrame el secreto!","btn-show-explanation":"¿Cómo funciona?","items-explanation":["Introduce un secreto en el formulario que hay en esta página","Tu navegador cifra el secreto utilizando una contraseña generada","Únicamente se envía al servidor el secreto cifrado (¡nunca se envían ni el secreto sin cifrar ni la contraseña!)","El servidor almacena el secreto cifrado durante un tiempo limitado","Envía al destinatario el enlace mostrado, que contiene el identificador del secreto y la contraseña de descifrado","El destinatario puede ver el secreto una sola vez: si no puede, ¡el secreto podría haber sido visto por otra persona!","Cuando se ha obtenido por primera y única vez el secreto cifrado, se elimina del servidor"],"label-secret-data":"Información secreta:","text-burn-hint":"Por favor, recuerda no acceder a este enlace tú mismo, ya que esto destruiría el secreto. ¡Solo tienes que pasárselo a otra persona!","text-hint-burned":"\u003cstrong\u003eAtención:\u003c/strong\u003e Solo verás esto una vez. En cuanto recargues la página, el secreto desaparecerá, así que cópialo ya\u0026hellip;","text-powered-by":"Funciona con","text-pre-reveal-hint":"Para mostrar el secreto pulsa este botón, pero ten en cuenta que al hacerlo se destruirá. ¡Solo puedes verlo una vez!","text-pre-url":"Tu secreto ha sido creado y almacenado en el siguiente enlace:","text-secret-create-disabled":"En este caso, la creación de nuevos secretos está desactivada.","title-explanation":"Así es como funciona\u0026hellip;","title-new-secret":"Crea un nuevo secreto","title-reading-secret":"Obteniendo tu secreto\u0026hellip;","title-secret-create-disabled":"Creación secreta desactivada...","title-secret-created":"¡Secreto creado!"}'), 'fr': JSON.parse('{"alert-secret-not-found":"Ce secret n\'est pas celui que vous cherchez\u0026hellip; - Si vous comptiez trouvez ce secret ici, il a pu être compromis car quelqu\'un a probablement déjà ouvert le lien.","alert-something-went-wrong":"Un problème est survenu. Nous en sommes désolés\u0026hellip;","btn-create-secret":"Créer le secret!","btn-new-secret":"Nouveau secret","btn-reveal-secret":"Voir le secret!","btn-show-explanation":"Comment ça fonctionne?","items-explanation":["Vous saisissez le secret dans un champ sur cette page","Votre navigateur chiffre le secret en utilisant un mot de passe généré","Seul le secret chiffré est envoyé au serveur (ni le secret en clair, ni le mot de passe ne sont envoyés!)","Le serveur stocke le secret chiffré pendant un certain temps","Vous fournissez l\'URL affichée contenant l\'identifiant et le mot de passe de déchiffrage au destinataire","Le destintaire ne peut voir le secret qu\'une fois: si cela ne fonctionne pas, c\'est que le secret a été consulté par quelqu\'un d\'autre!","Dès que le secret chiffré a été récupéré, il est supprimé du serveur"],"label-secret-data":"Données secrètes:","text-burn-hint":"Attention de ne pas ouvrir cette URL vous-même, cela détruirait le secret. Fournissez-la à quelqu\'un d\'autre!","text-hint-burned":"\u003cstrong\u003eAttention:\u003c/strong\u003e Vous ne pouvez consulter ce contenu qu\'une fois. Le secret sera détruit dès que vous rechargez la page, donc copiez le maintenant\u0026hellip;","text-powered-by":"Propulsé par","text-pre-reveal-hint":"Pour afficher le secret, cliquez sur ce bouton, mais soyez conscient que cela le détruira. Vous ne pouvez l\'afficher qu\'une fois!","text-pre-url":"Votre secret a été créé et stocké à cette URL:","text-secret-create-disabled":"La création de nouveaux secrets est désactivée dans ce cas.","title-explanation":"Voici comment ça fonctionne\u0026hellip;","title-new-secret":"Créer un nouveau secret","title-reading-secret":"Lecture du secret\u0026hellip;","title-secret-create-disabled":"Création secrète désactivée...","title-secret-created":"Secret créé!"}'), 'lv': JSON.parse('{"alert-secret-not-found":"\u003cstrong\u003eZiņa nav atrasta!\u003c/strong\u003e\u0026hellip; - Ja ievadītā saite ir pareiza, tad ir beidzies ziņas glabāšanas laiks, vai arī tā jau vienreiz ir atvērta.","alert-something-went-wrong":"Neparedzēta sistēmas kļūda. Atvainojiet par sagādātajām neērtībām\u0026hellip;","btn-create-secret":"Šifrēt ziņu!","btn-new-secret":"Jauna ziņa","btn-reveal-secret":"Atvērt ziņu!","btn-show-explanation":"Kā tas strādā?","items-explanation":["Tu ievadi ziņu ievades laukā","Pārlūks nošifrē ziņu ar uzģenerētu paroli","Tikai šifrētā ziņa tiek nosūtīta serverim (nešifrētā ziņa un parole sūtīta netiek!)","Serveris noteiktu laiku glabā šifrēto ziņu","Tu nodod URL ar ziņas ID un atšifrēšanas paroli saņēmējam","Saņēmējs var atvērt ziņu tikai vienreiz: ja tas neizdodas, iespējams, ziņu jau atvēris kāds cits!","Kad ziņa tiek atvērta pirmo reizi, tā no servera tiek dzēsta"],"label-secret-data":"Ziņa:","text-burn-hint":"Lūdzu atceries neatvērt saiti pats, jo tad ziņa tiks dzēsta. Nodod saiti ziņas saņēmējam!","text-hint-burned":"\u003cstrong\u003eUzmanību:\u003c/strong\u003e Ziņa tiek parādīta tikai vienu reizi. Līdzko lapa tiks pārlādēta, ziņa būs neatgriezeniski zaudēta, tāpēc nepieciešamības gadījumā nokopē to tagad\u0026hellip;","text-powered-by":"Darbina","text-pre-reveal-hint":"Lai parādītu ziņu nospied šo pogu, bet rēķinies ar to, ka pēc apskates ziņa vairs nebūs pieejama. To var atvērt tikai vienreiz!","text-pre-url":"Ziņa ir nošifrēta un ir atverama šajā adresē:","text-secret-create-disabled":"Šajā gadījumā jaunu noslēpumu izveide ir atspējota.","title-explanation":"Tā tas strādā\u0026hellip;","title-new-secret":"Šifrēt ziņu","title-reading-secret":"Atver ziņu\u0026hellip;","title-secret-create-disabled":"Slepena izveide atspējota...","title-secret-created":"Ziņa nošifrēta!"}'), diff --git a/src/style.scss b/src/style.scss index e7238a0..cff8f68 100644 --- a/src/style.scss +++ b/src/style.scss @@ -22,7 +22,7 @@ $web-font-path: ''; color: #d1d1d1; } - .form-control { + .form-control, .custom-select { /* Force overwrite bright textareas */ background-color: rgb(24, 26, 27); border-color: rgb(129, 120, 106);