diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..333d29757 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +dev_database diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..1d8bc6e4d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{go,sh}] +indent_style = tab + +[{Makefile,Caddyfile}] +indent_style = tab + +[LICENSE] +trim_trailing_whitespace = false +insert_final_newline = false diff --git a/.gitignore b/.gitignore index 18532909f..6190866a7 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ # Dependency directories (remove the comment below to include it) # vendor/ +dev_database/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..58279259a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,33 @@ +FROM node:22-alpine AS node +WORKDIR /app + +# Download the node dependencies first before adding the rest for caching +COPY ./net/web/package.json ./net/web/yarn.lock ./ +RUN yarn --frozen-lockfile + +COPY ./net/web/ ./ +RUN yarn run build + +FROM golang:alpine AS go +EXPOSE 7000 +WORKDIR /app/databag + +RUN apk add build-base imagemagick sqlite ffmpeg curl + +RUN mkdir -p /opt/databag +RUN mkdir -p /var/lib/databag +RUN mkdir -p /app/databag/net + +COPY ./net/server /app/databag/net/server +COPY ./net/transform /opt/databag/transform + +WORKDIR /app/databag/net/server +RUN go mod download +RUN CGO_ENABLED=1 go build -o databag . + +COPY --from=node /app/build /app/databag/net/web/build + +ENV DEV=0 +ENV ADMIN=password + +ENTRYPOINT /app/databag/net/server/entrypoint.sh diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..dccbceeb8 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +default: + @grep '^[^#[:space:].].*:' Makefile + +dev-start: + docker compose -f docker-compose.dev.yml up -d +dev-stop: + docker compose -f docker-compose.dev.yml down + +dev-restart-server: + docker compose -f docker-compose.dev.yml restart net-server +dev-restart-web: + docker compose -f docker-compose.dev.yml restart net-web +dev-restart-repeater: + docker compose -f docker-compose.dev.yml restart net-repeater + +prod-docker-start: + docker compose up -d + +prod-raw-build: + ./build.sh diff --git a/README.md b/README.md index 3cd0e8278..350ebe48b 100644 --- a/README.md +++ b/README.md @@ -56,10 +56,24 @@ The app is available on fdroid as well as the google and apple stores. You can t To use databag, you will need a DNS name pointing to your node with a certificate. You can deploy a node manually, but you will have a much easier time using a container service. Containers for arm64 and amd64 are available [here](https://hub.docker.com/r/balzack/databag/tags). -### Docker Compose Command +### Docker Compose -From the net/container sub directory: - - sudo docker-compose -f compose.yaml -p databag up +Launch with dockerhub container using docker compose: + +#### Standard launch +```shell +# From the net/container sub directory: +docker-compose -f compose.yaml -p databag up +``` + +#### Launch with certbot https certificate +```shell +# FIRST: create a DNS entry in your DNS to point your desired subdomain to your host +# SECOND: edit the net/container/docker-compose-swag.yml to include your domain name +# THIRD: From the root of the project directory: +mkdir -p ~/appdata +docker-compose -f net/container/docker-compose-swag.yml -p databag up +``` ### Example with Portainer and Nginx Proxy Manager @@ -154,4 +168,4 @@ If you want to enable audio and video calls, you should setup your own relay ser ### Roadmap -Please let me know any missing features; [here](/doc/backlog.md) is the current backlog. Features are prioritized based on interest from the community. +Please add any missing features; [here](/doc/backlog.md) is the current backlog. Features are prioritized based on interest from the community. diff --git a/app/mobile/.gitignore b/app/mobile/.gitignore new file mode 100644 index 000000000..fd4f2b066 --- /dev/null +++ b/app/mobile/.gitignore @@ -0,0 +1,2 @@ +node_modules +.DS_Store diff --git a/app/mobile/src/api/setCardStatus.js b/app/mobile/src/api/setCardStatus.js index 975b8a4df..0b4238cb3 100644 --- a/app/mobile/src/api/setCardStatus.js +++ b/app/mobile/src/api/setCardStatus.js @@ -10,12 +10,18 @@ export async function setCardConnecting(server, token, cardId) { } export async function setCardConnected(server, token, cardId, access, view, article, channel, profile) { + const insecure = /^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|:\d+$|$)){4}$/.test(server); + const protocol = insecure ? 'http' : 'https'; + let card = await fetchWithTimeout(`${protocol}://${server}/contact/cards/${cardId}/status?agent=${token}&token=${access}&viewRevision=${view}&articleRevision=${article}&channelRevision=${channel}&profileRevision=${profile}`, { method: 'PUT', body: JSON.stringify('connected') } ); checkResponse(card); return await card.json(); } export async function setCardConfirmed(server, token, cardId) { + const insecure = /^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|:\d+$|$)){4}$/.test(server); + const protocol = insecure ? 'http' : 'https'; + let card = await fetchWithTimeout(`${protocol}://${server}/contact/cards/${cardId}/status?agent=${token}`, { method: 'PUT', body: JSON.stringify('confirmed') } ); checkResponse(card); return await card.json(); diff --git a/app/mobile/src/constants/Strings.js b/app/mobile/src/constants/Strings.js index 3a8b25aac..07209328c 100644 --- a/app/mobile/src/constants/Strings.js +++ b/app/mobile/src/constants/Strings.js @@ -206,7 +206,7 @@ const Strings = [ selectTopic: 'Select Topic for Sharing', mfaTitle: 'Multi-Factor Authentication', - mfaSteps: 'Store the SHA256 secret and confirm the verification code', + mfaSteps: 'Store the secret and confirm the verification code', mfaError: 'verification code error', mfaDisabled: 'verification temporarily disabled', mfaConfirm: 'Confirm', @@ -417,7 +417,7 @@ const Strings = [ selectTopic: 'Choisissez le sujet à partager', mfaTitle: 'Authentification Multi-Factor', - mfaSteps: 'Enregistrez le secret SHA256 et confirmez le code de vérification', + mfaSteps: 'Enregistrez le secret et confirmez le code de vérification', mfaEnter: 'Entrez votre code de vérification', mfaError: 'erreur de code de vérification', mfaDisabled: 'vérification temporairement désactivée', @@ -629,7 +629,7 @@ const Strings = [ selectTopic: 'Elija un tema para compartir', mfaTitle: 'Autenticación de Dos Factores', - mfaSteps: 'Guarde el secreto SHA256 y confirme el código de verificación', + mfaSteps: 'Guarde el secreto y confirme el código de verificación', mfaEnter: 'Ingresa tu código de verificación', mfaError: 'error de código de verificación', mfaDisabled: 'verificación temporalmente deshabilitada', @@ -641,18 +641,18 @@ const Strings = [ }, { languageCode: 'de', - visibleRegistry: 'Sichtbar in der Registrierung', + visibleRegistry: 'Im Verzeichnis sichtbar', edit: 'Bearbeiten', enableNotifications: 'Mitteilungen', - allowUnsealed: 'Unsichere Themen', - sealedTopics: 'Gesicherte Themen', - colorMode: 'Farmodus', + allowUnsealed: 'Unversiegelte Themen ermöglichen', + sealedTopics: 'Versiegelte Themen', + colorMode: 'Farbmodus', hourMode: 'Stunde', dateMode: 'Datum', language: 'Sprache', logout: 'Ausloggen', - changeLogin: 'Kennwort Aktualisieren', - deleteAccount: 'Konto Löschen', + changeLogin: 'Kennwort aktualisieren', + deleteAccount: 'Konto löschen', contacts: 'Kontakte', topics: 'Themen', messages: 'Mitteilungen', @@ -663,8 +663,8 @@ const Strings = [ messages: 'Nachrichtenübermittlung', timeFull: '24h', timeHalf: '12h', - monthStart: 'mm/dd', - monthEnd: 'dd/mm', + monthStart: 'mm/tt', + monthEnd: 'tt/mm', error: 'Fehler', tryAgain: 'Bitte versuche es erneut.', @@ -681,33 +681,33 @@ const Strings = [ removeSeal: 'Sicherheitsschlüssel entfernen', disableSeal: 'Sicherheitsschlüssel deaktivieren', unlockSeal: 'Sicherheitsschlüssel entsperren', - typeDelete: 'Geben Sie [löschen]', + typeDelete: 'Bitte geben Sie [löschen] ein', deleteKey: 'löschen', - enableTopics: 'Aktivieren Sie gesicherte Themen', - manageTopics: 'Sicherheitsschlüssel verwalten', + enableTopics: 'Aktivieren Sie versiegelte Themen', + manageTopics: 'Versiegelungsschlüssel verwalten', changePassword: 'Ändern Sie das Passwort des Sicherheitsschlüssels', update: 'Aktualisieren', changeKey: 'Schlüsselpasswort ändern', delayMessage: 'Die Schlüsselgenerierung kann mehrere Minuten dauern.', changeMessage: 'Hier können Sie den Benutzernamen und/oder das Passwort für Ihr Konto ändern.', - cancel: 'Stornieren', + cancel: 'Abbrechen', confirmlogout: 'Ausloggen', loggingOut: 'Abmelden bestätigen', username: 'Nutzername', save: 'Speichern', - notAvailable: 'Benutzername Nicht Verfügbar', + notAvailable: 'Benutzername nicht verfügbar', blockedContacts: 'Blockierte Kontakte', - restoreContact: 'Kontakt Wiederherstellen?', + restoreContact: 'Kontakt wiederherstellen?', blockedTopics: 'Blockierte Themen', - restoreTopic: 'Thema Wiederherstellen?', + restoreTopic: 'Thema wiederherstellen?', blockedMessages: 'Blockierte Nachrichten', - restoreMessage: 'Nachricht Wiederherstellen?', + restoreMessage: 'Nachricht wiederherstellen?', close: 'Schließen', ok: 'OK', - noBlockedContacts: 'Keine Blockierten Kontakte', - noBlockedTopics: 'Keine Blockierten Themen', - noBlockedMessages: 'Keine Blockierten Nachrichten', + noBlockedContacts: 'Keine blockierten Kontakte', + noBlockedTopics: 'Keine blockierten Themen', + noBlockedMessages: 'Keine blockierten Nachrichten', restore: 'Wiederherstellen', //profile page @@ -715,20 +715,20 @@ const Strings = [ name: 'Name', location: 'Standort', description: 'Beschreibung', - registryVisible: 'Sichtbar in der Registrierung', + registryVisible: 'Im Verzeichnis sichtbar', editImage: 'Bild Bearbeiten', editDetails: 'Details Bearbeiten', //contacts page - back: 'Rückwärts', - deleteContact: 'Kontakt Löschen', + back: 'Zurück', + deleteContact: 'Kontakt löschen', confirmDelete: 'Löschen', - disconnectContact: 'Kontakt Trennen', + disconnectContact: 'Kontakt trennen', confirmDisconnect: 'Trennen', - blockContact: 'Kontakt Ausblenden', - confirmBlock: 'Verstecken', - reportContact: 'Kontakt Melden', - confirmReport: 'Bericht', + blockContact: 'Kontakt blockieren', + confirmBlock: 'Blockieren', + reportContact: 'Kontakt melden', + confirmReport: 'Melden', confirmed: 'Gerettet', pending: 'Unbekannt', connecting: 'Verbinden', @@ -740,20 +740,20 @@ const Strings = [ actionConnect: 'Verbinden', actionAccept: 'Akzeptieren', actionSave: 'Speichern', - actionCancel: 'Stornieren', + actionCancel: 'Abbrechen', actionDisconnect: 'Trennen', actionIgnore: 'Ignorieren', actionDelete: 'Löschen', - actionBlock: 'Verstecken', - actionReport: 'Bericht', + actionBlock: 'Blockieren', + actionReport: 'Melden', // contact list page add: 'Hinzufügen', contactFilter: 'Kontakte', serverFilter: 'Server', usernameFilter: 'Benutzername', - viewProfile: 'Profil Anzeigen', - messageContact: 'Nachricht Senden', + viewProfile: 'Profil anzeigen', + messageContact: 'Nachricht senden', callContact: 'Kontakt Anrufen', noContacts: 'Keine Kontakte Gefunden', @@ -762,8 +762,8 @@ const Strings = [ contacts: 'Kontakte', topics: 'Themen', subject: 'Titel (optional)', - create: 'Erstellen', - sealed: 'Gesichert', + create: 'Starten', + sealed: 'versiegeln', newTopic: 'Neues Thema', new: 'Neu', @@ -776,9 +776,9 @@ const Strings = [ editSubject: 'Titel bearbeiten', topicMembers: 'Themenmitglieder', leaveTopic: 'Verlasse das Thema', - deleteTopic: 'Das Thema Löschen', - blockTopic: 'Blockiere das Thema', - reportTopic: 'Das Thema Melden', + deleteTopic: 'Das Thema löschen', + blockTopic: 'Das Thema blockieren', + reportTopic: 'Das Thema melden', unknown: 'Unbekannt', accounts: 'Konten', @@ -789,8 +789,8 @@ const Strings = [ federatedHost: 'Verbundname', storageLimit: 'Raum (GB) / Konto', keyType: 'Schlüsselart', - enableImage: 'Bilddateien Aktivieren', - enableAudio: 'Audiodateien Aktivieren', + enableImage: 'Bilddateien aktivieren', + enableAudio: 'Audiodateien aktivieren', enableVideo: 'Videodateien aktivieren', enableBinary: 'Binärdateien aktivieren', enableCalls: 'Anrufe Ermöglichen', @@ -823,14 +823,14 @@ const Strings = [ editMessage: 'Nachrichtentext Bearbeiten', emptyTopic: 'Keine Nachrichten', - notes: 'Anmerkungen', + notes: 'Notizen', noTopics: 'Keine Themen', welcome: 'Willkommen bei Databag', communication: 'Kommunikation für das dezentrale Internet', setup: 'Richten Sie Ihr Profil ein', connect: 'Verbinde dich mit Menschen', - start: 'Eine Konversation Beginnen', + start: 'Eine Konversation beginnen', started: 'Loslegen', deleteMessage: 'Nachricht Löschen', @@ -841,7 +841,7 @@ const Strings = [ selectTopic: 'Wählen Sie ein Thema zum Teilen aus', mfaTitle: 'Zwei-Faktor-Authentifizierung', - mfaSteps: 'Speichern Sie das SHA256-Geheimnis und bestätigen Sie den Bestätigungscode', + mfaSteps: 'Speichern Sie das Geheimnis und bestätigen Sie den Bestätigungscode', mfaEnter: 'Geben Sie Ihren Bestätigungs-Code ein', mfaError: 'Verifizierungscodefehler', mfaDisabled: 'Verifizierung vorübergehend deaktiviert', @@ -1038,7 +1038,7 @@ const Strings = [ selectTopic: 'Escolha o tópico para compartilhar', mfaTitle: 'Autenticação de Dois Fatores', - mfaSteps: 'Salve o segredo SHA256 e confirme o código de verificação', + mfaSteps: 'Salve o segredo e confirme o código de verificação', mfaEnter: 'Digite seu código de verificação', mfaError: 'erro de código de verificação', mfaDisabled: 'verificação temporariamente desativada', @@ -1233,7 +1233,7 @@ const Strings = [ selectTopic: 'Выберите тему для обмена', mfaTitle: 'Двухфакторная аутентификация', - mfaSteps: 'Сохраните секрет SHA256 и подтвердите код подтверждения', + mfaSteps: 'Сохраните секрет и подтвердите код подтверждения', mfaEnter: 'Введите Ваш верификационный код', mfaError: 'ошибка проверочного кода', mfaDisabled: 'проверка временно отключена', @@ -1242,7 +1242,222 @@ const Strings = [ disable: 'Отключить', confirmDisable: 'Отключение двухфакторной аутентификации', disablePrompt: 'Вы уверены, что хотите отключить двухфакторную аутентификацию?', - } + }, + { + // settings screen + languageCode: 'el', + visibleRegistry: 'Ορατός Λογαριασμός στο Μητρώο του Διακομιστή', + edit: 'Επεξεργασία', + enableNotifications: 'Ειδοποιήσεις', + allowUnsealed: 'Επιτρέψτε μη σφραγισμένες συζητήσεις', + sealedTopics: 'Σφραγισμένες Συζητήσεις', + colorMode: 'Λειτουργία Χρώματος', + hourMode: 'Ώρα', + dateMode: 'Ημερομηνία', + language: 'Γλώσσα', + logout: 'Αποσύνδεση', + changeLogin: 'Αλλαγή Στοιχείων Σύνδεσης', + deleteAccount: 'Διαγραφή Λογαριασμού', + contacts: 'Επαφές', + topics: 'Συζητήσεις', + messages: 'Μηνύματα', + support: 'Υποστήριξη', + blocked: 'Μπλοκαρισμένα', + account: 'Λογαριασμός', + display: 'Μορφοποίηση', + messaging: 'Μηνύματα', + timeFull: '24ωρο', + timeHalf: '12ωρο', + monthStart: 'μμ/ηη', + monthEnd: 'ηη/μμ', + error: 'Σφάλμα', + tryAgain: 'Προσπαθήστε ξανά.', + + // seal wizard + sealUnset: 'Δημιουργήστε ένα κλειδί για να ενεργοποιήσετε την κρυπτογράφηση από άκρη σε άκρη (E2EE).', + sealUnlocked: 'Απενεργοποιώντας το κλειδί σφράγισης, δεν θα έχετε πρόσβαση σε κρυπτογραφημένες συζητήσεις σε αυτήν τη συσκευή μέχρι να το ενεργοποιήσετε ξανά.', + sealLocked: 'Ξεκλειδώστε το κλειδί σφράγισης για να υποστηρίξετε κρυπτογραφημένες συζητήσεις σε αυτήν τη συσκευή.', + sealDelete: 'Η διαγραφή του κλειδιού θα αφαιρέσει οριστικά την πρόσβαση σε κρυπτογραφημένες συζητήσεις για ΟΛΕΣ τις συσκευές σας.', + password: 'Κωδικός Πρόσβασης', + confirmPassword: 'Επιβεβαίωση Κωδικού', + generate: 'Δημιουργία', + disable: 'Απενεργοποίηση', + delete: 'Διαγραφή', + unlock: 'Ξεκλείδωμα', + removeSeal: 'Αφαίρεση Κλειδιού Σφράγισης', + disableSeal: 'Απενεργοποίηση Κλειδιού Σφράγισης', + unlockSeal: 'Ξεκλείδωμα Κλειδιού Σφράγισης', + typeDelete: 'Πληκτρολογήστε [διαγραφή]', + deleteKey: 'διαγραφή', + enableTopics: 'Ενεργοποίηση Σφραγισμένων Συζητήσεων', + manageTopics: 'Διαχείριση Κλειδιού Σφράγισης', + changePassword: 'Αλλαγή κωδικού κλειδιού σφράγισης.', + update: 'Αλλαγή', + changeKey: 'Αλλαγή Κωδικού Κλειδιού', + delayMessage: 'Η δημιουργία κλειδιού μπορεί να διαρκέσει αρκετά λεπτά.', + changeMessage: 'Εδώ μπορείτε να αλλάξετε το όνομα χρήστη και/ή τον κωδικό πρόσβασης για το λογαριασμό σας.', + + // settings modals + cancel: 'Ακύρωση', + confirmLogout: 'Αποσύνδεση', + loggingOut: 'Αποσύνδεση', + username: 'Όνομα Χρήστη', + save: 'Αποθήκευση', + notAvailable: 'Το όνομα χρήστη δεν είναι διαθέσιμο', + blockedContacts: 'Μπλοκαρισμένες Επαφές', + restoreContact: 'Επαναφορά Επαφής;', + blockedTopics: 'Μπλοκαρισμένες Συζητήσεις', + restoreTopic: 'Επαναφορά Συζήτησης;', + blockedMessages: 'Μπλοκαρισμένα Μηνύματα', + restoreMessage: 'Επαναφορά Μηνύματος;', + close: 'Κλείσιμο', + ok: 'Εντάξει', + noBlockedContacts: 'Δεν υπάρχουν μπλοκαρισμένες επαφές', + noBlockedTopics: 'Δεν υπάρχουν μπλοκαρισμένες συζητήσεις', + noBlockedMessages: 'Δεν υπάρχουν μπλοκαρισμένα μηνύματα', + restore: 'Επαναφορά', + + // profile page + edit: 'Επεξεργασία', + name: 'Όνομα', + location: 'Τοποθεσία', + description: 'Περιγραφή', + registryVisible: 'Ορατό στο Μητρώο', + editImage: 'Επεξεργασία Εικόνας', + editDetails: 'Επεξεργασία Λεπτομερειών', + + // contacts page + back: 'Πίσω', + deleteContact: 'Διαγραφή Επαφής', + confirmDelete: 'Διαγραφή', + disconnectContact: 'Αποσύνδεση Επαφής', + confirmDisconnect: 'Αποσύνδεση', + blockContact: 'Μπλοκάρισμα Επαφής', + confirmBlock: 'Μπλοκάρισμα', + reportContact: 'Αναφορά Επαφής', + confirmReport: 'Αναφορά', + confirmed: 'Αποθηκεύτηκε', + pending: 'Άγνωστο', + connecting: 'Αίτημα Στάλθηκε', + connected: 'Συνδέθηκε', + requested: 'Λήψη Αιτήματος', + unsaved: 'Μη Αποθηκευμένο', + offsync: 'Εκτός Συγχρονισμού', + actionResync: 'Επανασυγχρονισμός', + actionConnect: 'Σύνδεση', + actionAccept: 'Αποδοχή', + actionSave: 'Αποθήκευση', + actionCancel: 'Ακύρωση', + actionDisconnect: 'Αποσύνδεση', + actionIgnore: 'Παράβλεψη', + actionDelete: 'Διαγραφή', + actionBlock: 'Μπλοκάρισμα', + actionReport: 'Αναφορά', + actionLeave: 'Αποχώρηση', + + // contact list page + add: 'Προσθήκη', + contactFilter: 'Επαφές', + serverFilter: 'Διακομιστής', + usernameFilter: 'Όνομα Χρήστη', + viewProfile: 'Προβολή Προφίλ', + messageContact: 'Αποστολή Μηνύματος', + callContact: 'Κλήση Επαφής', + noContacts: 'Δεν βρέθηκαν Επαφές', + + // channels list + profile: 'Προφίλ', + contacts: 'Επαφές', + topics: 'Συζητήσεις', + subject: 'Θέμα (προαιρετικό)', + create: 'Δημιουργία', + sealed: 'Σφραγισμένο', + newTopic: 'Νέο Θέμα', + new: 'Νέο', + + // details + topic: 'Συζήτηση', + host: 'οικοδεσπότης', + guest: 'επισκέπτης', + leave: 'Αποχώρηση', + members: 'Μέλη', + editSubject: 'Επεξεργασία Θέματος', + topicMembers: 'Μέλη Συζήτησης', + leaveTopic: 'Αποχώρηση από τη Συζήτηση', + deleteTopic: 'Διαγραφή Συζήτησης', + blockTopic: 'Μπλοκάρισμα Συζήτησης', + reportTopic: 'Αναφορά Συζήτησης', + unknown: 'άγνωστο', + + accounts: 'Λογαριασμοί', + createAccount: 'Δημιουργία Λογαριασμού', + accessAccount: 'Πρόσβαση Λογαριασμού', + token: 'Token', + settings: 'Ρυθμίσεις', + federatedHost: 'Ομοσπονδιακός Οικοδεσπότης', + storageLimit: 'Όριο Αποθήκευσης (GB) / Λογαριασμό', + keyType: 'Τύπος Κλειδιού Λογαριασμού', + enableImage: 'Ενεργοποίηση Ουράς Εικόνας', + enableAudio: 'Ενεργοποίηση Ουράς Ήχου', + enableVideo: 'Ενεργοποίηση Ουράς Βίντεο', + enableBinary: 'Ενεργοποίηση υπόλοιπων Ψηφιακών Αρχείων', + enableCalls: 'Ενεργοποίηση Κλήσεων WebRTC', + iceService: 'Υπηρεσία Cloudflare', + relayUrl: 'URL Αναμετάδοσης', + relayUsername: 'Όνομα Χρήστη Αναμετάδοσης', + relayPassword: 'Κωδικός Αναμετάδοσης', + + newMessage: 'Νέο Μήνυμα', + fontSize: 'Μέγεθος Γραμματοσειράς', + small: 'Μικρό', + medium: 'Μεσαίο', + large: 'Μεγάλο', + fontColor: 'Χρώμα Γραμματοσειράς', + selectedColor: 'Επιλεγμένο Χρώμα', + + login: 'Σύνδεση', + createAccount: 'Δημιουργία Λογαριασμού', + forgotPassword: 'Ξεχάσατε τον Κωδικό;', + adminAccess: 'Πρόσβαση Διαχειριστή', + server: 'Διακομιστής', + access: 'Πρόσβαση', + defaultPublic: 'Ο προεπιλεγμένος δημόσιος διακομιστής είναι μόνο για δοκιμές. Χρησιμοποιήστε ιδιωτικό διακομιστή σε διαφορετική περίπτωση.', + confirmPassword: 'Επιβεβαίωση Κωδικού', + accountLogin: 'Σύνδεση Λογαριασμού', + accessAccount: 'Πρόσβαση Λογαριασμού', + agree: 'Συμφωνώ με τους Όρους Χρήσης', + terms: 'Προβολή Όρων Χρήσης', + policy: 'Όροι Χρήσης και Πολιτική Χρήστη', + + editMessage: 'Επεξεργασία Κειμένου Μηνύματος', + emptyTopic: 'Κενό Θέμα', + noTopics: 'Δεν υπάρχουν Συζητήσεις', + notes: 'Σημειώσεις', + + welcome: 'Καλώς Ήρθατε στο Databag', + communication: 'Επικοινωνία για τον Αποκεντρωμένο Ιστό', + setup: 'Ρυθμίστε το Προφίλ σας', + connect: 'Συνδεθείτε με Άτομα', + start: 'Ξεκινήστε μια Συνομιλία', + started: 'Ξεκινήστε', + + deleteMessage: 'Διαγραφή Μηνύματος', + blockMessage: 'Μπλοκάρισμα Μηνύματος', + reportMessage: 'Αναφορά Μηνύματος', + select: 'Επιλογή', + selectTopic: 'Επιλέξτε Συζήτηση για Κοινή Χρήση', + + mfaTitle: 'Επαλήθευση Πολλαπλών Παραγόντων (MFA)', + mfaSteps: 'Αποθηκεύστε το μυστικό κωδικό και επιβεβαιώστε τον κωδικό επαλήθευσης', + mfaError: 'σφάλμα κωδικού επαλήθευσης', + mfaDisabled: 'η επαλήθευση είναι προσωρινά απενεργοποιημένη', + mfaConfirm: 'Επιβεβαίωση', + mfaEnter: 'Εισαγάγετε τον κωδικό επαλήθευσης σας', + + disable: 'Απενεργοποίηση', + confirmDisable: 'Απενεργοποίηση Επαλήθευσης Πολλαπλών Παραγόντων ', + disablePrompt: 'Είστε σίγουροι ότι θέλετε να απενεργοποιήσετε την επαλήθευση πολλαπλών παραγόντων;', + } ]; export function getLanguageStrings() { @@ -1268,5 +1483,8 @@ export function getLanguageStrings() { if (lang === 'ru') { return Strings[5]; } + if (lang === 'el') { + return Strings[6]; + } return Strings[0]; }; diff --git a/app/mobile/src/session/conversation/topicItem/useTopicItem.hook.js b/app/mobile/src/session/conversation/topicItem/useTopicItem.hook.js index 41b75334c..fea13e652 100644 --- a/app/mobile/src/session/conversation/topicItem/useTopicItem.hook.js +++ b/app/mobile/src/session/conversation/topicItem/useTopicItem.hook.js @@ -279,20 +279,14 @@ export function useTopicItem(item, hosting, remove, contentKey) { }; const clickableText = (text) => { - const urlPatternn = new RegExp('^(https?:\\/\\/)?'+ // protocol - '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name - '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address - '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path - '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string - '(\\#[-a-z\\d_]*)?$','i'); // fragment locator - + const urlPattern = new RegExp('(https?:\\/\\/)?(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,4}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)'); const hostPattern = new RegExp('^https?:\\/\\/', 'i'); let clickable = []; let group = ''; const words = text == null ? [''] : text.split(' '); words.forEach((word, index) => { - if (!!urlPatternn.test(word)) { + if (!!urlPattern.test(word)) { clickable.push({ group }); group = ''; const url = !!hostPattern.test(word) ? word : `https://${word}`; diff --git a/doc/backlog.md b/doc/backlog.md index eb05aa0aa..e9f36b8a6 100644 --- a/doc/backlog.md +++ b/doc/backlog.md @@ -2,6 +2,16 @@ # Backlog +**Call UI:** pop-up window does not appear outside the app, nor does it activate the lock screen for interaction. + +**Call UI:** The notification for an incoming call sends two alerts using the message notification sound and then stops ringing. + +**Call UI:** Notifications from the notification bar either open on the first tap, the second, or, at most, the third consecutive tap. + +**Call UI:** There is no "waiting to answer" sound until the recipient accepts the call and the proximity sensor engages after the call is been answered. + +**Call UI:** There is no timer to track the call duration. + **SDK:** refactor mobile and browser code into an SDK and app **Typescript:** refactor mobile and browser apps to use typescript @@ -14,8 +24,6 @@ **Embed STUN/TURN:** directly support nat translation for webrtc as part of the server -**Mobile HTTP:** support plaintext communcation with server from mobile app - **More Languages:** add any additional language requests **CLI:** create a CLI client for automation @@ -24,8 +32,6 @@ **Preferred Nodes:** add an admin managed list of other nodes and present the nodes in a select box when searching for contacts -**2-Factor Auth:** add 2-factor auth capabilities - **SSO:** support single sign on **Audio & Video Message:** support recording and sending audio and video messages @@ -56,6 +62,8 @@ **APN:** avoid firebase for iOS and use APN directly +**Password Recover:** add a mechanism for password recovery based on shared secret + **Auto Archive:** add ability to auto delete old data **1-1 Sealed:** admin config for defaulting to sealed topic when starting a 1-1 topic from the contact list diff --git a/doc/pizero.md b/doc/pizero.md index a17296171..cefd7de68 100644 --- a/doc/pizero.md +++ b/doc/pizero.md @@ -23,7 +23,7 @@ These instructions assume you have the following setup: apt-get -y install curl
apt-get -y install net-tools
apt-get -y install jq
- apt-get -y install netcat
+ apt-get -y install netcat-openbsd
apt-get -y install unzip
apt-get -y install wget
apt-get -y install git
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 000000000..466ce1e6b --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,37 @@ +name: databag-dev + +services: + net-web: + build: + context: ./net/web + dockerfile: Dockerfile.dev + working_dir: /app + volumes: + - ./net/web:/app + command: sh -c "yarn && chokidar '**/*.js' '**/*.ts' -c 'yarn run build' --debounce 18000 --initial --ignore node_modules --ignore build" + net-server: + build: + context: ./net/server + dockerfile: Dockerfile.dev + ports: + - 127.0.0.1:7000:7000 + volumes: + - ./net/server:/app/databag/net/server + - ./dev_database:/var/lib/databag + - ./net/transform:/opt/databag/transform + - ./net/web/build:/app/databag/net/web/build + working_dir: /app + environment: + - ADMIN=password + - DEV=1 + command: /app/databag/net/server/entrypoint.sh + net-repeater: + build: + context: ./net/repeater + dockerfile: Dockerfile.dev + working_dir: /app + volumes: + - ./net/repeater:/app + ports: + - 127.0.0.1:7878:7878 + command: go run main.go diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..0184bc942 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,14 @@ +name: databag + +services: + app: + build: . + ports: + - 127.0.0.1:7000:7000 + volumes: + - database:/var/lib/databag + environment: + - ADMIN=password + +volumes: + database: diff --git a/examples/docker-basic/docker-compose.yml b/examples/docker-basic/docker-compose.yml new file mode 100644 index 000000000..92dec0cae --- /dev/null +++ b/examples/docker-basic/docker-compose.yml @@ -0,0 +1,14 @@ +name: databag-example-http + +services: + app: + image: balzack/databag:latest + restart: unless-stopped + ports: + - 127.0.0.1:7000:7000 + volumes: + - database:/var/lib/databag + environment: + - ADMIN=password +volumes: + database: diff --git a/examples/docker-ssl/Caddyfile b/examples/docker-ssl/Caddyfile new file mode 100644 index 000000000..0eafd0b13 --- /dev/null +++ b/examples/docker-ssl/Caddyfile @@ -0,0 +1,3 @@ +example.com { + reverse_proxy http://app:443 +} diff --git a/examples/docker-ssl/docker-compose.yml b/examples/docker-ssl/docker-compose.yml new file mode 100644 index 000000000..b13856f15 --- /dev/null +++ b/examples/docker-ssl/docker-compose.yml @@ -0,0 +1,29 @@ +name: databag-example-ssl + +services: + caddy: + image: caddy + restart: unless-stopped + ports: + - 80:80 + - 443:443 + volumes: + # Edit the Caddyfile and replace "example.com" with your own domain + - ./Caddyfile:/etc/caddy/Caddyfile:ro + # Recommended by Caddy + - caddy_data:/data + - caddy_config:/config + depends_on: + - app + app: + image: balzack/databag:latest + restart: unless-stopped + volumes: + - database:/var/lib/databag + environment: + - ADMIN=password + - DATABAG_PORT=443 +volumes: + database: + caddy_data: + caddy_config: diff --git a/examples/linux/databag.service b/examples/linux/databag.service new file mode 100644 index 000000000..ca43cf8d1 --- /dev/null +++ b/examples/linux/databag.service @@ -0,0 +1,17 @@ +[Unit] +Description=databag server +After=network.target +StartLimitIntervalSec=0 + +[Service] +Type=simple +Restart=always +RestartSec=1 +User=databag +ExecStart=/app/databag/net/server/entrypoint.sh + +# [Service] +# Environment="ADMIN=password" + +[Install] +WantedBy=multi-user.target diff --git a/examples/linux/install.sh b/examples/linux/install.sh new file mode 100755 index 000000000..db7d7145b --- /dev/null +++ b/examples/linux/install.sh @@ -0,0 +1,85 @@ +#!/bin/bash +set -e + +function confirm() { + read -p "Are you sure you want to continue? [Y/n] " reply + if [ "$reply" != "Y" ] && [ "$reply" != "y" ]; then + echo "Aborting" + exit 1 + fi +} + +if ! id "databag" >/dev/null 2>&1; then + echo "User databag not found, creating..." + confirm + sudo useradd databag +fi + +if [[ ! -d "/app/databag" ]]; then + echo "Creating app directory for databag, this requires sudo permissions" + sudo rm -rf /app/databag || true + sudo mkdir -p /app/databag + + echo "Downloading databag repository into /app/databag" + git clone --depth 1 https://github.com/balzack/databag.git /app/databag + sudo chown -R databag:databag /app/databag +fi +cd /app/databag + +# You might be running this script from the root of this repository +if [[ "/app/databag" != $(pwd) ]]; then + cd ../.. +fi + +if [[ "/app/databag" != $(pwd) ]]; then + echo "Install databag must be done from /app/databag" + echo "Please re-clone the github repository into /app/databag, like so:" + echo "mkdir -p /app; https://github.com/balzack/databag /app/databag" + exit 1 +fi + +if [[ -z $(which yarn) ]]; then + echo "Yarn is not installed, installing..." + npm install --global yarn +fi + +echo "Building frontend files..." +cd net/web +yarn --frozen-lockfile +echo "Removing old frontend files, requires sudo permissions..." +sudo rm -rf build +yarn run build +sudo chown -R databag:databag build +cd ../.. + +echo "Building backend files..." +cd net/server +CGO_ENABLED=1 go build -o databag . +cd ../.. + +echo "Creating databag locations..." +sudo mkdir -p /opt/databag +sudo mkdir -p /var/lib/databag +sudo chown -R databag:databag /var/lib/databag + +echo "Copying transform scripts..." +sudo mkdir -p /opt/databag/transform +sudo cp net/transform/*.sh /opt/databag/transform/ +sudo chmod +x /opt/databag/transform/*.sh + +if [[ ! -f /etc/systemd/system/databag.service ]]; then + function createService() { + echo "Creating databag service, requires sudo permissions..." + sudo cp examples/linux/databag.service /etc/systemd/system/databag.service + sudo chmod 664 /etc/systemd/system/databag.service + sudo systemctl daemon-reload + } + function startService() { + echo "Starting databag service..." + sudo systemctl start databag.service + } + createService $? "Failed to install databag service" + startService $? "Failed to start databag service" +fi +echo "" +echo "Done" diff --git a/examples/linux/uninstall.sh b/examples/linux/uninstall.sh new file mode 100755 index 000000000..b31075f8f --- /dev/null +++ b/examples/linux/uninstall.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +function confirm() { + read -p "Are you sure you want to continue? [Y/n] " reply + if [ "$reply" != "Y" ] && [ "$reply" != "y" ]; then + echo "Aborting" + exit 1 + fi +} + +echo "Stopping, disabling and removing databag service..." +confirm +sudo systemctl stop databag.service +sudo systemctl disable databag.service +sudo rm /etc/systemd/system/databag.service +sudo systemctl reload + +echo "Removing databag data..." +confirm +sudo rm -rf /app/databag /opt/databag /var/lib/databag +if [ -z "$(ls -A /app)" ]; then + sudo rmdir /app +fi + +echo "Removing databag user..." +confirm +sudo userdel databag + +echo "Done" diff --git a/net/container/Dockerfile b/net/container/Dockerfile deleted file mode 100644 index 83e391c0a..000000000 --- a/net/container/Dockerfile +++ /dev/null @@ -1,67 +0,0 @@ -FROM ubuntu:20.04 as build -ARG TARGETPLATFORM -LABEL maintainer="roland.osborne@gmail.com" - -EXPOSE 7000 - -ENV TZ=America/Los_Angeles -RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone - -RUN apt-get update - -RUN apt-get -y install curl net-tools jq netcat unzip wget git vim fail2ban imagemagick-6.q16 ffmpeg build-essential sqlite3 npm - -RUN apt-get -y upgrade - -RUN npm install --global yarn -RUN npm install -g n -RUN n stable - -RUN mkdir /app - -RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then ARCHITECTURE=amd64; elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then ARCHITECTURE=arm64; elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then ARCHITECTURE=aarch64; else ARCHITECTURE=unsupported; fi \ - && wget -P /app https://go.dev/dl/go1.22.2.linux-${ARCHITECTURE}.tar.gz \ - && tar -C /usr/local -xzf /app/go1.22.2.linux-${ARCHITECTURE}.tar.gz - -RUN git clone https://github.com/balzack/databag.git /app/databag - -RUN yarn config set network-timeout 300000 -RUN yarn --cwd /app/databag/net/web install -RUN yarn --cwd /app/databag/net/web build -RUN cd /app/databag/net/server; /usr/local/go/bin/go build databag - -RUN mkdir /opt/databag - -ADD transform /opt/databag/transform - -RUN mkdir -p /var/lib/databag - -RUN echo 'export PATH=$PATH:/usr/local/go/bin' >> /root/.bashrc -RUN echo "set expandtab\nset tabstop=2\nset softtabstop=2\nset shiftwidth=2\nset encoding=utf-8\nset fileencoding=utf-8\n" > /root/.vimrc -RUN echo "bind 'set mark-symlinked-directories on'" >> /root/.bashrc - -ADD entrypoint.sh /app -ADD dev_setup.sh /app - -RUN rm -rf /usr/local/go -RUN rm -rf /root/go -RUN rm -rf /app/go* -RUN rm -rf /root/.cache/go* - -RUN yarn cache clean -RUN rm -rf /app/databag/app -RUN rm -rf /app/databag/net/web/node_modules - -RUN n prune -RUN npm uninstall -g n -RUN rm -rf /usr/local/n -RUN rm -rf /usr/local/bin/node - -RUN apt-get -y remove git build-essential npm vim nodejs linux-libc-dev -RUN rm -rf /var/lib/apt/lists - -FROM scratch -COPY --from=build / / - -ENTRYPOINT ["/app/entrypoint.sh"] - diff --git a/net/container/Dockerfile.dev b/net/container/Dockerfile.dev deleted file mode 100644 index 5030c6ba8..000000000 --- a/net/container/Dockerfile.dev +++ /dev/null @@ -1,50 +0,0 @@ -FROM ubuntu:20.04 as build -ARG TARGETPLATFORM -LABEL maintainer="roland.osborne@gmail.com" - -EXPOSE 7000 - -ENV TZ=America/Los_Angeles -RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone - -RUN apt-get update - -RUN apt-get -y install curl net-tools jq netcat unzip wget git vim fail2ban imagemagick-6.q16 ffmpeg build-essential sqlite3 npm - -RUN apt-get -y upgrade - -RUN npm install --global yarn -RUN npm install -g n -RUN n stable - -RUN mkdir /app - -RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then ARCHITECTURE=amd64; elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then ARCHITECTURE=arm64; elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then ARCHITECTURE=aarch64; else ARCHITECTURE=unsupported; fi \ - && wget -P /app https://go.dev/dl/go1.22.2.linux-${ARCHITECTURE}.tar.gz \ - && tar -C /usr/local -xzf /app/go1.22.2.linux-${ARCHITECTURE}.tar.gz - -RUN git clone https://github.com/balzack/databag.git /app/databag - -RUN yarn config set network-timeout 300000 -RUN yarn --cwd /app/databag/net/web install -RUN yarn --cwd /app/databag/net/web build -RUN cd /app/databag/net/server; /usr/local/go/bin/go build databag - -RUN mkdir /opt/databag - -ADD transform /opt/databag/transform - -RUN mkdir -p /var/lib/databag - -RUN echo 'export PATH=$PATH:/usr/local/go/bin' >> /root/.bashrc -RUN echo "set expandtab\nset tabstop=2\nset softtabstop=2\nset shiftwidth=2\nset encoding=utf-8\nset fileencoding=utf-8\n" > /root/.vimrc -RUN echo "bind 'set mark-symlinked-directories on'" >> /root/.bashrc - -ADD dev_setup.sh /app -ADD entrypoint.sh /app - -FROM scratch -COPY --from=build / / - -ENTRYPOINT ["/app/entrypoint.sh"] - diff --git a/net/container/dev_setup.sh b/net/container/dev_setup.sh deleted file mode 100755 index 8a026811f..000000000 --- a/net/container/dev_setup.sh +++ /dev/null @@ -1,20 +0,0 @@ -cd /root -wget -P /app https://go.dev/dl/go1.22.2.linux-amd64.tar.gz -tar -C /usr/local -xzf /app/go1.22.2.linux-amd64.tar.gz - -apt-get update -apt-get -y install git build-essential npm vim - -curl -fsSL https://deb.nodesource.com/setup_18.x | bash - -apt install -y nodejs - -npm install --global yarn -npm install -g n -n stable - -cd /app/databag -git checkout . - -yarn --cwd /app/databag/net/web install -yarn --cwd /app/databag/net/web build -cd /app/databag/net/server; /usr/local/go/bin/go build databag diff --git a/net/container/docker-compose.dev.yml b/net/container/docker-compose.dev.yml deleted file mode 100644 index ef69a0340..000000000 --- a/net/container/docker-compose.dev.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: "3.9" -services: - databag: - environment: - - DEV=1 - container_name: databag - image: balzack/databag:latest - ports: - - "7000:7000" - volumes: - - ./databag-data:/var/lib/databag - diff --git a/net/container/docker-compose.yml b/net/container/docker-compose.yml deleted file mode 100644 index b0878d245..000000000 --- a/net/container/docker-compose.yml +++ /dev/null @@ -1,10 +0,0 @@ -version: "3.9" -services: - databag: - container_name: databag - image: balzack/databag:latest - ports: - - "7000:7000" - volumes: - - ./databag-data:/var/lib/databag - diff --git a/net/container/entrypoint.sh b/net/container/entrypoint.sh deleted file mode 100755 index 4b3ec4e57..000000000 --- a/net/container/entrypoint.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -set -e - -sqlite3 /var/lib/databag/databag.db "VACUUM;" -sqlite3 /var/lib/databag/databag.db "CREATE TABLE IF NOT EXISTS 'configs' ('id' integer NOT NULL UNIQUE,'config_id' text NOT NULL,'str_value' text,'num_value' integer,'bool_value' numeric,'bin_value' blob,PRIMARY KEY ('id'));" -sqlite3 /var/lib/databag/databag.db "CREATE UNIQUE INDEX IF NOT EXISTS 'idx_configs_config_id' ON 'configs'('config_id');" - -if [[ -v ADMIN ]]; then - sqlite3 /var/lib/databag/databag.db "delete from configs where config_id='configured';" - sqlite3 /var/lib/databag/databag.db "delete from configs where config_id='token';" - sqlite3 /var/lib/databag/databag.db "insert into configs (config_id, str_value) values ('token', '$ADMIN');" - sqlite3 /var/lib/databag/databag.db "insert into configs (config_id, bool_value) values ('configured', true);" -fi - -if [ "$DEV" == "1" ]; then - cd /app/databag/net/server - ./databag -p 7000 -w /app/databag/net/web/build -s /var/lib/databag -t /opt/databag/transform & - /app/dev_setup.sh || true - while true; do - sleep 1; - done -else - cd /app/databag/net/server - ./databag -p 7000 -w /app/databag/net/web/build -s /var/lib/databag -t /opt/databag/transform -fi diff --git a/net/repeater/Dockerfile b/net/repeater/Dockerfile new file mode 100644 index 000000000..29f8c0b7b --- /dev/null +++ b/net/repeater/Dockerfile @@ -0,0 +1,9 @@ +FROM golang:alpine + +WORKDIR /app +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +RUN go build -o repeater . +EXPOSE 7878 +ENTRYPOINT ./repeater diff --git a/net/repeater/Dockerfile.dev b/net/repeater/Dockerfile.dev new file mode 100644 index 000000000..3865d5480 --- /dev/null +++ b/net/repeater/Dockerfile.dev @@ -0,0 +1,6 @@ +FROM golang:alpine + +WORKDIR /app +COPY go.mod go.sum ./ +RUN go mod download +RUN rm go.mod go.sum diff --git a/net/repeater/go.mod b/net/repeater/go.mod new file mode 100644 index 000000000..dc1a8e5fd --- /dev/null +++ b/net/repeater/go.mod @@ -0,0 +1,70 @@ +module repeater + +go 1.17 + +require ( + github.com/google/uuid v1.6.0 + github.com/gorilla/handlers v1.5.2 + github.com/gorilla/mux v1.8.1 + github.com/gorilla/websocket v1.5.1 + github.com/kr/pretty v0.3.1 + github.com/stretchr/testify v1.8.4 + github.com/theckman/go-securerandom v0.1.1 + github.com/valyala/fastjson v1.6.4 + golang.org/x/crypto v0.24.0 + gorm.io/driver/sqlite v1.5.5 + gorm.io/gorm v1.25.9 +) + +require ( + cloud.google.com/go v0.112.1 // indirect + cloud.google.com/go/compute v1.24.0 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/firestore v1.15.0 // indirect + cloud.google.com/go/iam v1.1.7 // indirect + cloud.google.com/go/longrunning v0.5.5 // indirect + cloud.google.com/go/storage v1.40.0 // indirect + firebase.google.com/go/v4 v4.14.1 // indirect + github.com/MicahParks/keyfunc v1.9.0 // indirect + github.com/SherClockHolmes/webpush-go v1.3.0 // indirect + github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.3 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pquerna/otp v1.4.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/oauth2 v0.18.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/time v0.5.0 // indirect + google.golang.org/api v0.170.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/appengine/v2 v2.0.2 // indirect + google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2 // indirect + google.golang.org/grpc v1.62.1 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/net/repeater/go.sum b/net/repeater/go.sum new file mode 100644 index 000000000..0277da3a8 --- /dev/null +++ b/net/repeater/go.sum @@ -0,0 +1,273 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= +cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= +cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= +cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/firestore v1.15.0 h1:/k8ppuWOtNuDHt2tsRV42yI21uaGnKDEQnRFeBpbFF8= +cloud.google.com/go/firestore v1.15.0/go.mod h1:GWOxFXcv8GZUtYpWHw/w6IuYNux/BtmeVTMmjrm4yhk= +cloud.google.com/go/iam v1.1.7 h1:z4VHOhwKLF/+UYXAJDFwGtNF0b6gjsW1Pk9Ml0U/IoM= +cloud.google.com/go/iam v1.1.7/go.mod h1:J4PMPg8TtyurAUvSmPj8FF3EDgY1SPRZxcUGrn7WXGA= +cloud.google.com/go/longrunning v0.5.5 h1:GOE6pZFdSrTb4KAiKnXsJBtlE6mEyaW44oKyMILWnOg= +cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= +cloud.google.com/go/storage v1.40.0 h1:VEpDQV5CJxFmJ6ueWNsKxcr1QAYOXEgxDa+sBbJahPw= +cloud.google.com/go/storage v1.40.0/go.mod h1:Rrj7/hKlG87BLqDJYtwR0fbPld8uJPbQ2ucUMY7Ir0g= +firebase.google.com/go/v4 v4.14.1 h1:4qiUETaFRWoFGE1XP5VbcEdtPX93Qs+8B/7KvP2825g= +firebase.google.com/go/v4 v4.14.1/go.mod h1:fgk2XshgNDEKaioKco+AouiegSI9oTWVqRaBdTTGBoM= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID3+o= +github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw= +github.com/SherClockHolmes/webpush-go v1.3.0 h1:CAu3FvEE9QS4drc3iKNgpBWFfGqNthKlZhp5QpYnu6k= +github.com/SherClockHolmes/webpush-go v1.3.0/go.mod h1:AxRHmJuYwKGG1PVgYzToik1lphQvDnqFYDqimHvwhIw= +github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= +github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= +github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= +github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= +github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg= +github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/theckman/go-securerandom v0.1.1 h1:5KctSyM0D5KKFK+bsypIyLq7yik0CEaI5i2fGcUGcsQ= +github.com/theckman/go-securerandom v0.1.1/go.mod h1:bmkysLfBH6i891sBpcP4xRM3XIB7jMeiKJB31jlResI= +github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= +github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.170.0 h1:zMaruDePM88zxZBG+NG8+reALO2rfLhe/JShitLyT48= +google.golang.org/api v0.170.0/go.mod h1:/xql9M2btF85xac/VAm4PsLMTLVGUOpq4BE9R8jyNy8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/appengine/v2 v2.0.2 h1:MSqyWy2shDLwG7chbwBJ5uMyw6SNqJzhJHNDwYB0Akk= +google.golang.org/appengine/v2 v2.0.2/go.mod h1:PkgRUWz4o1XOvbqtWTkBtCitEJ5Tp4HoVEdMMYQR/8E= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= +google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c h1:kaI7oewGK5YnVwj+Y+EJBO/YN1ht8iTL9XkFHtVZLsc= +google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c/go.mod h1:VQW3tUculP/D4B+xVCo+VgSq8As6wA9ZjHl//pmk+6s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2 h1:9IZDv+/GcI6u+a4jRFRLxQs0RUCfavGfoOgEW6jpkI0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E= +gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE= +gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8= +gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/net/repeater/internal/api_notify.go b/net/repeater/internal/api_notify.go new file mode 100644 index 000000000..ff1f0f671 --- /dev/null +++ b/net/repeater/internal/api_notify.go @@ -0,0 +1,61 @@ +package repeater + +import ( + "encoding/json" + "log" + "net/http" + "os" + "runtime" + "strings" + "fmt" + "firebase.google.com/go/v4/messaging" +) + +func ParseRequest(r *http.Request, w http.ResponseWriter, obj interface{}) error { + r.Body = http.MaxBytesReader(w, r.Body, 1024) + dec := json.NewDecoder(r.Body) + return dec.Decode(&obj) +} + +func WriteResponse(w http.ResponseWriter, v interface{}) { + body, err := json.Marshal(v) + if err != nil { + _, file, line, _ := runtime.Caller(1) + p, _ := os.Getwd() + log.Printf("%s:%d %s", strings.TrimPrefix(file, p), line, err.Error()) + w.WriteHeader(http.StatusInternalServerError) + } else { + w.Header().Set("Content-Type", "application/json") + w.Write(body) + } +} + +//Notify proxies push notification to device +func Notify(w http.ResponseWriter, r *http.Request) { + + var msg PushMessage + if err := ParseRequest(r, w, &msg); err != nil { + ErrResponse(w, http.StatusBadRequest, err) + return + } + + // See documentation on defining a message payload. + notification := &messaging.Notification{ Title: msg.Title, Body: msg.Body } + message := &messaging.Message{ + Notification : notification, + Token: msg.Token, + } + + // Send a message to the device corresponding to the provided + // registration token. + response, err := FCMClient.Send(FCMContext, message) + if err != nil { + ErrResponse(w, http.StatusBadRequest, err) + return + } + // Response is a message ID string. + fmt.Println("Successfully sent message:", response) + + res := &PushResponse{ Message: response } + WriteResponse(w, res); +} diff --git a/net/repeater/internal/logger.go b/net/repeater/internal/logger.go new file mode 100644 index 000000000..4d59dded1 --- /dev/null +++ b/net/repeater/internal/logger.go @@ -0,0 +1,60 @@ +package repeater + +import ( + "github.com/kr/pretty" + "log" + "net/http" + "os" + "runtime" + "strings" + "time" +) + +//Logger prints endpoint details +func Logger(inner http.Handler, name string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + + inner.ServeHTTP(w, r) + + log.Printf( + "%s %s %s %s", + r.Method, + r.RequestURI, + name, + time.Since(start), + ) + }) +} + +//ErrResponse prints detailed error event and sets response +func ErrResponse(w http.ResponseWriter, code int, err error) { + if err != nil { + _, file, line, _ := runtime.Caller(1) + p, _ := os.Getwd() + log.Printf("%s:%d %s", strings.TrimPrefix(file, p), line, err.Error()) + } + w.WriteHeader(code) +} + +//ErrMsg prints detailed error event +func ErrMsg(err error) { + if err != nil { + _, file, line, _ := runtime.Caller(1) + p, _ := os.Getwd() + log.Printf("%s:%d %s", strings.TrimPrefix(file, p), line, err.Error()) + } +} + +//LogMsg prints detailed error string +func LogMsg(msg string) { + _, file, line, _ := runtime.Caller(1) + p, _ := os.Getwd() + log.Printf("%s:%d %s", strings.TrimPrefix(file, p), line, msg) +} + +//PrintMsg prints debug message +func PrintMsg(obj interface{}) { + pretty.Println(obj) +} + diff --git a/net/repeater/internal/models.go b/net/repeater/internal/models.go new file mode 100644 index 000000000..0dc613be3 --- /dev/null +++ b/net/repeater/internal/models.go @@ -0,0 +1,14 @@ +package repeater + +//PushMessage notification +type PushMessage struct { + Title string `json:"title"` + Body string `json:"body"` + Token string `json:"token"` +} + +//PushResponse notification response +type PushResponse struct { + Message string `json:"message"` +} + diff --git a/net/repeater/internal/routers.go b/net/repeater/internal/routers.go new file mode 100644 index 000000000..50b422f4d --- /dev/null +++ b/net/repeater/internal/routers.go @@ -0,0 +1,66 @@ +package repeater + +import ( + "firebase.google.com/go/v4/messaging" + firebase "firebase.google.com/go/v4" + "github.com/gorilla/mux" + "net/http" + "strings" + "context" + "log" +) + +type route struct { + Name string + Method string + Pattern string + HandlerFunc http.HandlerFunc +} + +type routes []route + +var FCMClient *messaging.Client +var FCMContext context.Context + +//NewRouter allocate router for databag API +func NewRouter() *mux.Router { + + app, err := firebase.NewApp(context.Background(), nil) + if err != nil { + log.Fatalf("error initializing app: %v\n", err) + } + + ctx := context.Background() + client, err := app.Messaging(ctx) + if err != nil { + log.Fatalf("error getting Messaging client: %v\n", err) + } + FCMClient = client + FCMContext = ctx + + router := mux.NewRouter().StrictSlash(true) + for _, route := range endpoints { + var handler http.Handler + handler = route.HandlerFunc + handler = Logger(handler, route.Name) + + router. + Methods(route.Method). + Path(route.Pattern). + Name(route.Name). + Handler(handler) + } + + return router +} + +var endpoints = routes{ + + route{ + "Notify", + strings.ToUpper("Post"), + "/notify", + Notify, + }, +} + diff --git a/net/repeater/main.go b/net/repeater/main.go new file mode 100644 index 000000000..bb6ac74e6 --- /dev/null +++ b/net/repeater/main.go @@ -0,0 +1,40 @@ +package main + +import ( + app "repeater/internal" + "github.com/gorilla/handlers" + "log" + "net/http" + "os" +) + +func main() { + var cert string + var key string + + port := ":7878" + + args := os.Args[1:]; + for i := 0; i + 1 < len(args); i += 2 { + if args[i] == "-p" { + port = ":" + args[i + 1] + } else if args[i] == "-c" { + cert = args[i + 1] + } else if args[i] == "-k" { + key = args[i + 1] + } + } + + router := app.NewRouter() + origins := handlers.AllowedOrigins([]string{"*"}) + headers := handlers.AllowedHeaders([]string{"content-type", "authorization", "credentials"}) + methods := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"}) + + if cert != "" && key != "" { + log.Printf("using args:" + " -p " + port[1:] + " -c " + cert + " -k " + key) + log.Fatal(http.ListenAndServeTLS(port, cert, key, handlers.CORS(origins, headers, methods)(router))) + } else { + log.Printf("using args:" + " -p " + port[1:]) + log.Fatal(http.ListenAndServe(port, handlers.CORS(origins, headers, methods)(router))) + } +} diff --git a/net/repeater/repeater.service b/net/repeater/repeater.service new file mode 100644 index 000000000..84c389420 --- /dev/null +++ b/net/repeater/repeater.service @@ -0,0 +1,17 @@ +[Unit] +Description=push notification repeater for databag network +After=network.target +StartLimitIntervalSec=0 + +[Service] +Type=simple +Restart=always +RestartSec=1 +User=root +ExecStart=/usr/bin/repeater -p 443 -c /etc/letsencrypt/live/repeater.coredb.org/fullchain.pem -k /etc/letsencrypt/live/repeater.coredb.org/privkey.pem + +[Service] +Environment="GOOGLE_APPLICATION_CREDENTIALS=/opt/databag/databag.json" + +[Install] +WantedBy=multi-user.target diff --git a/net/server/.dockerignore b/net/server/.dockerignore new file mode 100644 index 000000000..99b97449c --- /dev/null +++ b/net/server/.dockerignore @@ -0,0 +1 @@ +databag diff --git a/net/server/.gitignore b/net/server/.gitignore new file mode 100644 index 000000000..e6105547f --- /dev/null +++ b/net/server/.gitignore @@ -0,0 +1 @@ +/databag \ No newline at end of file diff --git a/net/server/Dockerfile.dev b/net/server/Dockerfile.dev new file mode 100644 index 000000000..4cc17e124 --- /dev/null +++ b/net/server/Dockerfile.dev @@ -0,0 +1,15 @@ +FROM golang:alpine + +RUN apk add build-base imagemagick sqlite ffmpeg curl + +RUN mkdir -p /opt/databag +RUN mkdir -p /var/lib/databag +RUN mkdir -p /app/databag/net + +RUN mkdir -p /tmp/databag-go-cache +WORKDIR /tmp/databag-go-cache +COPY go.mod go.sum ./ +RUN go mod download + +WORKDIR / +RUN rm -rf /tmp/databag-go-cache diff --git a/net/server/entrypoint.sh b/net/server/entrypoint.sh new file mode 100755 index 000000000..63c173f56 --- /dev/null +++ b/net/server/entrypoint.sh @@ -0,0 +1,24 @@ +#!/bin/sh +set -e + +sqlite3 /var/lib/databag/databag.db "VACUUM;" +sqlite3 /var/lib/databag/databag.db "CREATE TABLE IF NOT EXISTS 'configs' ('id' integer NOT NULL UNIQUE,'config_id' text NOT NULL,'str_value' text,'num_value' integer,'bool_value' numeric,'bin_value' blob,PRIMARY KEY ('id'));" +sqlite3 /var/lib/databag/databag.db "CREATE UNIQUE INDEX IF NOT EXISTS 'idx_configs_config_id' ON 'configs'('config_id');" + +if [[ -n "$ADMIN" ]]; then + sqlite3 /var/lib/databag/databag.db "delete from configs where config_id='configured';" + sqlite3 /var/lib/databag/databag.db "delete from configs where config_id='token';" + sqlite3 /var/lib/databag/databag.db "insert into configs (config_id, str_value) values ('token', '$ADMIN');" + sqlite3 /var/lib/databag/databag.db "insert into configs (config_id, bool_value) values ('configured', true);" +fi + +if [[ -z "$DATABAG_PORT" ]]; then + DATABAG_PORT=7000 +fi + +cd /app/databag/net/server +if [[ "$DEV" == "1" ]]; then + CGO_ENABLED=1 go run main.go -p $DATABAG_PORT -w /app/databag/net/web/build -s /var/lib/databag -t /opt/databag/transform +else + ./databag -p $DATABAG_PORT -w /app/databag/net/web/build -s /var/lib/databag -t /opt/databag/transform +fi diff --git a/net/server/internal/api_addAccountApp.go b/net/server/internal/api_addAccountApp.go index e2b020937..43c5fbc32 100644 --- a/net/server/internal/api_addAccountApp.go +++ b/net/server/internal/api_addAccountApp.go @@ -34,7 +34,11 @@ func AddAccountApp(w http.ResponseWriter, r *http.Request) { return; } - opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256} + algorithm := otp.AlgorithmSHA256; + if account.MFAAlgorithm == APPMFASHA1 { + algorithm = otp.AlgorithmSHA1 + } + opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: algorithm} if valid, _ := totp.ValidateCustom(code, account.MFASecret, time.Now(), opts); !valid { err := store.DB.Transaction(func(tx *gorm.DB) error { if account.MFAFailedTime + APPMFAFailPeriod > curTime { diff --git a/net/server/internal/api_addAdminMFAuth.go b/net/server/internal/api_addAdminMFAuth.go index c7bd97c69..aef5d98c5 100644 --- a/net/server/internal/api_addAdminMFAuth.go +++ b/net/server/internal/api_addAdminMFAuth.go @@ -25,7 +25,7 @@ func AddAdminMFAuth(w http.ResponseWriter, r *http.Request) { Issuer: APPMFAIssuer, AccountName: "admin", Digits: otp.DigitsSix, - Algorithm: otp.AlgorithmSHA256, + Algorithm: otp.AlgorithmSHA1, }) err = store.DB.Transaction(func(tx *gorm.DB) error { diff --git a/net/server/internal/api_addMultiFactorAuth.go b/net/server/internal/api_addMultiFactorAuth.go index 323ccb9ef..ba135c3f3 100644 --- a/net/server/internal/api_addMultiFactorAuth.go +++ b/net/server/internal/api_addMultiFactorAuth.go @@ -24,7 +24,7 @@ func AddMultiFactorAuth(w http.ResponseWriter, r *http.Request) { Issuer: APPMFAIssuer, AccountName: account.Handle, Digits: otp.DigitsSix, - Algorithm: otp.AlgorithmSHA256, + Algorithm: otp.AlgorithmSHA1, }) err = store.DB.Transaction(func(tx *gorm.DB) error { diff --git a/net/server/internal/api_setAdminAccess.go b/net/server/internal/api_setAdminAccess.go index 30426a4f8..f25db66a3 100644 --- a/net/server/internal/api_setAdminAccess.go +++ b/net/server/internal/api_setAdminAccess.go @@ -40,8 +40,13 @@ func SetAdminAccess(w http.ResponseWriter, r *http.Request) { return; } - secret := getStrConfigValue(CNFMFASecret, ""); - opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256} + secret := getStrConfigValue(CNFMFASecret, "") + algorithm := getStrConfigValue(CNFMFAAlgorithm, APPMFASHA256) + mfaAlgorithm := otp.AlgorithmSHA256 + if algorithm == APPMFASHA1 { + mfaAlgorithm = otp.AlgorithmSHA1 + } + opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: mfaAlgorithm} if valid, _ := totp.ValidateCustom(code, secret, time.Now(), opts); !valid { err := store.DB.Transaction(func(tx *gorm.DB) error { if failedTime + APPMFAFailPeriod > curTime { diff --git a/net/server/internal/api_setAdminMFAuth.go b/net/server/internal/api_setAdminMFAuth.go index 66ebd516a..016758e25 100644 --- a/net/server/internal/api_setAdminMFAuth.go +++ b/net/server/internal/api_setAdminMFAuth.go @@ -38,39 +38,44 @@ func SetAdminMFAuth(w http.ResponseWriter, r *http.Request) { return; } + mfaAlgorithm := APPMFASHA1 secret := getStrConfigValue(CNFMFASecret, ""); - opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256} + opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA1} if valid, _ := totp.ValidateCustom(code, secret, time.Now(), opts); !valid { - err := store.DB.Transaction(func(tx *gorm.DB) error { - if failedTime + APPMFAFailPeriod > curTime { - if res := tx.Clauses(clause.OnConflict{ - Columns: []clause.Column{{Name: "config_id"}}, - DoUpdates: clause.AssignmentColumns([]string{"num_value"}), - }).Create(&store.Config{ConfigID: CNFMFAFailedCount, NumValue: failedCount + 1}).Error; res != nil { - return res - } - } else { - if res := tx.Clauses(clause.OnConflict{ - Columns: []clause.Column{{Name: "config_id"}}, - DoUpdates: clause.AssignmentColumns([]string{"num_value"}), - }).Create(&store.Config{ConfigID: CNFMFAFailedTime, NumValue: curTime}).Error; res != nil { - return res - } - if res := tx.Clauses(clause.OnConflict{ - Columns: []clause.Column{{Name: "config_id"}}, - DoUpdates: clause.AssignmentColumns([]string{"num_value"}), - }).Create(&store.Config{ConfigID: CNFMFAFailedCount, NumValue: 1}).Error; res != nil { - return res + mfaAlgorithm = APPMFASHA256 + opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256} + if valid, _ := totp.ValidateCustom(code, secret, time.Now(), opts); !valid { + err := store.DB.Transaction(func(tx *gorm.DB) error { + if failedTime + APPMFAFailPeriod > curTime { + if res := tx.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "config_id"}}, + DoUpdates: clause.AssignmentColumns([]string{"num_value"}), + }).Create(&store.Config{ConfigID: CNFMFAFailedCount, NumValue: failedCount + 1}).Error; res != nil { + return res + } + } else { + if res := tx.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "config_id"}}, + DoUpdates: clause.AssignmentColumns([]string{"num_value"}), + }).Create(&store.Config{ConfigID: CNFMFAFailedTime, NumValue: curTime}).Error; res != nil { + return res + } + if res := tx.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "config_id"}}, + DoUpdates: clause.AssignmentColumns([]string{"num_value"}), + }).Create(&store.Config{ConfigID: CNFMFAFailedCount, NumValue: 1}).Error; res != nil { + return res + } } + return nil + }) + if err != nil { + LogMsg("failed to increment fail count"); } - return nil - }) - if err != nil { - LogMsg("failed to increment fail count"); - } - ErrResponse(w, http.StatusUnauthorized, errors.New("invalid code")) - return + ErrResponse(w, http.StatusUnauthorized, errors.New("invalid code")) + return + } } err := store.DB.Transaction(func(tx *gorm.DB) error { @@ -81,6 +86,13 @@ func SetAdminMFAuth(w http.ResponseWriter, r *http.Request) { }).Create(&store.Config{ConfigID: CNFMFAConfirmed, BoolValue: true}).Error; res != nil { return res } + // upsert mfa algorithm + if res := tx.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "config_id"}}, + DoUpdates: clause.AssignmentColumns([]string{"num_value"}), + }).Create(&store.Config{ConfigID: CNFMFAAlgorithm, StrValue: mfaAlgorithm}).Error; res != nil { + return res + } return nil }) if err != nil { diff --git a/net/server/internal/api_setMultiFactorAuth.go b/net/server/internal/api_setMultiFactorAuth.go index 2c8cf68f7..6c656f1aa 100644 --- a/net/server/internal/api_setMultiFactorAuth.go +++ b/net/server/internal/api_setMultiFactorAuth.go @@ -35,32 +35,37 @@ func SetMultiFactorAuth(w http.ResponseWriter, r *http.Request) { return; } - opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256} + mfaAlgorithm := APPMFASHA1 + opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA1} if valid, _ := totp.ValidateCustom(code, account.MFASecret, time.Now(), opts); !valid { - err := store.DB.Transaction(func(tx *gorm.DB) error { - if account.MFAFailedTime + APPMFAFailPeriod > curTime { - account.MFAFailedCount += 1 - if res := tx.Model(account).Update("mfa_failed_count", account.MFAFailedCount).Error; res != nil { - return res - } - } else { - account.MFAFailedTime = curTime - if res := tx.Model(account).Update("mfa_failed_time", account.MFAFailedTime).Error; res != nil { - return res - } - account.MFAFailedCount = 1 - if res := tx.Model(account).Update("mfa_failed_count", account.MFAFailedCount).Error; res != nil { - return res + mfaAlgorithm = APPMFASHA256 + opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256} + if valid, _ := totp.ValidateCustom(code, account.MFASecret, time.Now(), opts); !valid { + err := store.DB.Transaction(func(tx *gorm.DB) error { + if account.MFAFailedTime + APPMFAFailPeriod > curTime { + account.MFAFailedCount += 1 + if res := tx.Model(account).Update("mfa_failed_count", account.MFAFailedCount).Error; res != nil { + return res + } + } else { + account.MFAFailedTime = curTime + if res := tx.Model(account).Update("mfa_failed_time", account.MFAFailedTime).Error; res != nil { + return res + } + account.MFAFailedCount = 1 + if res := tx.Model(account).Update("mfa_failed_count", account.MFAFailedCount).Error; res != nil { + return res + } } + return nil + }) + if err != nil { + LogMsg("failed to increment fail count"); } - return nil - }) - if err != nil { - LogMsg("failed to increment fail count"); - } - ErrResponse(w, http.StatusUnauthorized, errors.New("invalid code")) - return + ErrResponse(w, http.StatusUnauthorized, errors.New("invalid code")) + return + } } err = store.DB.Transaction(func(tx *gorm.DB) error { @@ -69,6 +74,11 @@ func SetMultiFactorAuth(w http.ResponseWriter, r *http.Request) { ErrResponse(w, http.StatusInternalServerError, res) return res } + account.MFAAlgorithm = mfaAlgorithm; + if res := tx.Model(account).Update("mfa_algorithm", account.MFAAlgorithm).Error; res != nil { + ErrResponse(w, http.StatusInternalServerError, res) + return res + } account.AccountRevision += 1; if res := tx.Model(&account).Update("account_revision", account.AccountRevision).Error; res != nil { return res diff --git a/net/server/internal/api_setPushEvent.go b/net/server/internal/api_setPushEvent.go index f03e4723b..cdbac04c4 100644 --- a/net/server/internal/api_setPushEvent.go +++ b/net/server/internal/api_setPushEvent.go @@ -121,9 +121,8 @@ func SendPushEvent(account store.Account, event string) { if pushToken == "" || pushToken == "null" { continue; } - url := "https://fcm.googleapis.com/fcm/send" - payload := Payload{ Title: messageTitle, Body: messageBody, Sound: "default" }; - message := Message{ Notification: payload, To: pushToken }; + url := "https://repeater.coredb.org/notify" + message := PushMessage{ Title: messageTitle, Body: messageBody, Token: pushToken }; body, err := json.Marshal(message) if err != nil { @@ -136,7 +135,6 @@ func SendPushEvent(account store.Account, event string) { continue } req.Header.Set("Content-Type", "application/json; charset=utf-8") - req.Header.Set("Authorization", "key=AAAAkgDXt8c:APA91bEjH67QpUWU6uAfCIXLqm0kf6AdPNVICZPCcWbmgW9NGYIErAxMDTy4LEbe4ik93Ho4Z-AJNIhr6nXXKC9qKmyKkkYHJWAEVH47_FXBQV6rsoi9ZB_oiuV66XKKAy1V40GmvfaX") client := &http.Client{} resp, err := client.Do(req) if err != nil { diff --git a/net/server/internal/appValues.go b/net/server/internal/appValues.go index e16eae202..2701ae8ea 100644 --- a/net/server/internal/appValues.go +++ b/net/server/internal/appValues.go @@ -156,6 +156,12 @@ const APPMFAFailPeriod = 300 //APPMFAFailCount limit of login failures in period const APPMFAFailCount = 4 +//APPMFASHA256 internal mfa algorithm sha256 +const APPMFASHA256 = "sha256" + +//APPMFASHA1 internal mfa alogirthm sha1 +const APPMFASHA1 = "sha1" + //AppCardStatus compares cards status with string func AppCardStatus(status string) bool { if status == APPCardPending { diff --git a/net/server/internal/configUtil.go b/net/server/internal/configUtil.go index 3502e7056..7a0f2ed61 100644 --- a/net/server/internal/configUtil.go +++ b/net/server/internal/configUtil.go @@ -78,6 +78,9 @@ const CNFMFAEnabled = "mfa_enabled" //CNFMFAConfirmed specified if mfa has been confirmed for admin const CNFMFAConfirmed = "mfa_confirmed" +//CNFMFAAlgorirthm specifies internal mfa alogirhtm to use +const CNFMFAAlgorithm = "mfa_algorithm" + //CNFMFASecret specified the mfa secret const CNFMFASecret = "mfa_secret" diff --git a/net/server/internal/models.go b/net/server/internal/models.go index 56a2ebf5f..130716e16 100644 --- a/net/server/internal/models.go +++ b/net/server/internal/models.go @@ -619,3 +619,13 @@ type Ring struct { IcePassword string `json:"icePassword"` } + +type PushMessage struct { + Title string `json:"title"` + Body string `json:"body"` + Token string `json:"token"` +} + +type PushResponse struct { + Message string `json:"message"` +} diff --git a/net/server/internal/store/schema.go b/net/server/internal/store/schema.go index 3e095913b..334fc41b0 100644 --- a/net/server/internal/store/schema.go +++ b/net/server/internal/store/schema.go @@ -84,6 +84,7 @@ type Account struct { MFAEnabled bool `gorm:"not null;default:false"` MFAConfirmed bool `gorm:"not null;default:false"` MFASecret string + MFAAlgorithm string MFAFailedTime int64 MFAFailedCount uint Forward string diff --git a/net/container/transform/transform_acopy.sh b/net/transform/transform_acopy.sh similarity index 100% rename from net/container/transform/transform_acopy.sh rename to net/transform/transform_acopy.sh diff --git a/net/container/transform/transform_icopy.sh b/net/transform/transform_icopy.sh similarity index 100% rename from net/container/transform/transform_icopy.sh rename to net/transform/transform_icopy.sh diff --git a/net/container/transform/transform_ilg.sh b/net/transform/transform_ilg.sh similarity index 100% rename from net/container/transform/transform_ilg.sh rename to net/transform/transform_ilg.sh diff --git a/net/container/transform/transform_ithumb.sh b/net/transform/transform_ithumb.sh similarity index 100% rename from net/container/transform/transform_ithumb.sh rename to net/transform/transform_ithumb.sh diff --git a/net/container/transform/transform_vcopy.sh b/net/transform/transform_vcopy.sh similarity index 100% rename from net/container/transform/transform_vcopy.sh rename to net/transform/transform_vcopy.sh diff --git a/net/container/transform/transform_vhd.sh b/net/transform/transform_vhd.sh similarity index 100% rename from net/container/transform/transform_vhd.sh rename to net/transform/transform_vhd.sh diff --git a/net/container/transform/transform_vlq.sh b/net/transform/transform_vlq.sh similarity index 100% rename from net/container/transform/transform_vlq.sh rename to net/transform/transform_vlq.sh diff --git a/net/container/transform/transform_vsd.sh b/net/transform/transform_vsd.sh similarity index 100% rename from net/container/transform/transform_vsd.sh rename to net/transform/transform_vsd.sh diff --git a/net/container/transform/transform_vthumb.sh b/net/transform/transform_vthumb.sh similarity index 100% rename from net/container/transform/transform_vthumb.sh rename to net/transform/transform_vthumb.sh diff --git a/net/web/.dockerignore b/net/web/.dockerignore new file mode 100644 index 000000000..b512c09d4 --- /dev/null +++ b/net/web/.dockerignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/net/web/.gitignore b/net/web/.gitignore new file mode 100644 index 000000000..b7dab5e9c --- /dev/null +++ b/net/web/.gitignore @@ -0,0 +1,2 @@ +node_modules +build \ No newline at end of file diff --git a/net/web/Dockerfile.dev b/net/web/Dockerfile.dev new file mode 100644 index 000000000..4f5164703 --- /dev/null +++ b/net/web/Dockerfile.dev @@ -0,0 +1,7 @@ +FROM node:22-alpine + +WORKDIR /app + +RUN npm install --global chokidar-cli + +ENV SHELL=/bin/sh \ No newline at end of file diff --git a/net/web/src/constants/Strings.js b/net/web/src/constants/Strings.js index 93d7b7e8b..ae657ee3d 100644 --- a/net/web/src/constants/Strings.js +++ b/net/web/src/constants/Strings.js @@ -193,7 +193,7 @@ export const en = { securedMessage: 'Sealed Message', mfaTitle: 'Multi-Factor Authentication', - mfaSteps: 'Store the SHA256 secret and confirm the verification code', + mfaSteps: 'Store the secret and confirm the verification code', mfaError: 'verification code error', mfaDisabled: 'verification temporarily disabled', mfaConfirm: 'Confirm', @@ -403,7 +403,7 @@ export const fr = { sealedMessage: 'Message Sécurisé', mfaTitle: 'Authentification Multi-Factor', - mfaSteps: 'Enregistrez le secret SHA256 et confirmez le code de vérification', + mfaSteps: 'Enregistrez le secret et confirmez le code de vérification', mfaEnter: 'Entrez votre code de vérification', mfaError: 'erreur de code de vérification', mfaDisabled: 'vérification temporairement désactivée', @@ -612,7 +612,7 @@ export const sp = { sealedMessage: 'Mensaje Seguro', mfaTitle: 'Autenticación de Dos Factores', - mfaSteps: 'Guarde el secreto SHA256 y confirme el código de verificación', + mfaSteps: 'Guarde el secreto y confirme el código de verificación', mfaEnter: 'Ingresa tu código de verificación', mfaError: 'error de código de verificación', mfaDisabled: 'verificación temporalmente deshabilitada', @@ -821,7 +821,7 @@ export const pt = { sealedMessage: 'Mensagem Segura', mfaTitle: 'Autenticação de Dois Fatores', - mfaSteps: 'Salve o segredo SHA256 e confirme o código de verificação', + mfaSteps: 'Salve o segredo e confirme o código de verificação', mfaEnter: 'Digite seu código de verificação', mfaError: 'erro de código de verificação', mfaDisabled: 'verificação temporariamente desativada', @@ -1030,7 +1030,7 @@ export const de = { sealedMessage: 'Gesicherte Nachricht', mfaTitle: 'Zwei-Faktor-Authentifizierung', - mfaSteps: 'Speichern Sie das SHA256-Geheimnis und bestätigen Sie den Bestätigungscode', + mfaSteps: 'Speichern Sie das Geheimnis und bestätigen Sie den Bestätigungscode', mfaEnter: 'Geben Sie Ihren Bestätigungs-Code ein', mfaError: 'Verifizierungscodefehler', mfaDisabled: 'Verifizierung vorübergehend deaktiviert', @@ -1239,7 +1239,7 @@ export const ru = { sealedMessage: 'Защищенное Cообщение', mfaTitle: 'Двухфакторная аутентификация', - mfaSteps: 'Сохраните секрет SHA256 и подтвердите код подтверждения', + mfaSteps: 'Сохраните секрет и подтвердите код подтверждения', mfaEnter: 'Введите Ваш верификационный код', mfaError: 'ошибка проверочного кода', mfaDisabled: 'проверка временно отключена', @@ -1252,3 +1252,212 @@ export const ru = { confirmDisable: 'Отключение двухфакторной аутентификации', disablePrompt: 'Вы уверены, что хотите отключить двухфакторную аутентификацию?', }; + +export const el = { + code: 'el', + settings: 'Ρυθμίσεις', + contacts: 'Επαφές', + logout: 'Αποσύνδεση', + confirmLogout: 'Είστε σίγουροι ότι θέλετε να αποσυνδεθείτε;', + contactsUpdated: 'Η κατάσταση των επαφών ενημερώθηκε', + disconnected: 'Αποσυνδεθήκατε από το διακομιστή', + allDevices: 'Αποσύνδεση από όλες τις συσκευές', + ok: 'Εντάξει', + cancel: 'Ακύρωση', + enableNotifications: 'Ειδοποιήσεις', + + new: 'Νέο', + newMessage: 'Νέο Μήνυμα', + topics: 'Συζητήσεις', + unsetSealing: 'Κατάργηση Κλειδιού Σφράγισης', + newTopic: 'Νέα Συζήτηση', + + noContacts: 'Δεν υπάρχουν Επαφές', + noTopics: 'Δεν υπάρχουν Συζητήσεις', + noConnected: 'Δεν υπάρχουν Συνδεδεμένες Επαφές', + subjectOptional: 'Θέμα (προαιρετικό)', + members: 'Μέλη', + sealedTopic: 'Σφραγισμένο Θέμα', + start: 'Έναρξη', + + communication: 'Επικοινωνία για τον Αποκεντρωμένο Ιστό', + setupProfile: 'Ρυθμίστε το προφίλ σας', + connectPeople: 'Συνδεθείτε με άτομα', + startConversation: 'Ξεκινήστε μια συνομιλία', + + default: 'Προεπιλογή', + dark: 'Σκούρο', + light: 'Φωτεινό', + + operationFailed: 'Η λειτουργία απέτυχε', + tryAgain: 'Παρακαλώ προσπαθήστε ξανά.', + + add: 'Προσθήκη', + save: 'Αποθήκευση', + forget: 'Ξέχασε το', + unlock: 'Ξεκλείδωμα', + profile: 'Προφίλ', + application: 'Εφαρμογή', + account: 'Λογαριασμός', + name: 'Όνομα', + node: 'Κόμβος', + location: 'Τοποθεσία', + description: 'Περιγραφή', + timeFormat: 'Μορφή Ώρας', + dateFormat: 'Μορφή Ημερομηνίας', + theme: 'Θέμα', + language: 'Γλώσσα', + timeUs: '12ωρο', + timeEu: '24ωρο', + dateUs: 'μμ/ηη', + dateEu: 'ηη/μμ', + registry: 'Ορατός λογαριασμός στο Μητρώο Διακομιστή', + sealedTopics: 'Σφραγισμένα Θέματα', + changeLogin: 'Αλλαγή Σύνδεσης', + selectImage: 'Επιλογή', + profileImage: 'Εικόνα Προφίλ', + profileDetails: 'Λεπτομέρειες Προφίλ', + enableSealed: 'Ενεργοποίηση Σφραγισμένων Συζητήσεων', + password: 'Κωδικός Πρόσβασης', + newPassword: 'Νέος Κωδικός Πρόσβασης', + confirmPassword: 'Επιβεβαίωση Κωδικού Πρόσβασης', + deleteKey: "Πληκτρολογήστε 'delete' για να αφαιρέσετε το κλειδί", + delete: 'διαγραφή', + remove: 'Διαγραφή', + username: 'Όνομα Χρήστη', + updateProfile: 'Ενημέρωση Προφίλ', + + syncError: 'Σφάλμα Συγχρονισμού', + callTip: 'Κλήση Επαφής', + messageTip: 'Μήνυμα Επαφής', + connectedTip: 'Συνδεδεμένη Επαφή', + requestedTip: 'Αίτημα Σύνδεσης από Επαφή', + connectingTip: 'Αίτημα Σύνδεσης', + pendingTip: 'Αίτημα Σύνδεσης από Άγνωστη Επαφή', + confirmedTip: 'Αποσυνδεδεμένη Επαφή', + unsavedTip: 'Άγνωστη Επαφή', + + actions: 'Ενέργειες', + resync: 'Εκ νέου συγχρονισμός', + connect: 'Σύνδεση', + disconnect: 'Αποσύνδεση', + disconnectContact: 'Αποσύνδεση Επαφής', + deleteContact: 'Διαγραφή Επαφής', + saveContact: 'Αποθήκευση Επαφής', + saveAccept: 'Αποθήκευση και Αποδοχή Σύνδεσης', + saveRequest: 'Αποθήκευση και Αίτημα Σύνδεσης', + ignoreRequest: 'Αγνόηση Αιτήματος', + acceptConnection: 'Αποδοχή Σύνδεσης', + requestConnection: 'Αίτημα Σύνδεσης', + cancelRequest: 'Ακύρωση Αιτήματος', + resyncContact: 'Εκ νέου συγχρονισμός Επαφής', + + login: 'Σύνδεση', + create: 'Δημιουργία', + createAccount: 'Δημιουργία Λογαριασμού', + accountLogin: 'Σύνδεση Λογαριασμού', + toCreate: 'Οι λογαριασμοί δημιουργούνται μέσω συνδέσμου από τον πίνακα διαχειριστή.', + admin: 'Διαχειριστής', + loginError: 'Σφάλμα Σύνδεσης', + loginMessage: 'Παρακαλώ επιβεβαιώστε το όνομα χρήστη και τον κωδικό πρόσβασης.', + createError: 'Σφάλμα Δημιουργίας Λογαριασμού', + createMessage: 'Ελέγξτε το με τον διαχειριστή σας.', + adminError: 'Σφάλμα Πρόσβασης Διαχειριστή', + adminMessage: 'Παρακαλώ επιβεβαιώστε τον κωδικό πρόσβασης.', + + confirmDelete: 'Διαγραφή Λογαριασμού', + areSure: 'Είστε σίγουροι ότι θέλετε να διαγράψετε το λογαριασμό;', + + mb: 'MB', + gb: 'GB', + copied: 'Αντιγράφηκε', + accounts: 'Λογαριασμοί', + accessAccount: 'Πρόσβαση Λογαριασμού', + browserLink: 'Σύνδεσμος Περιηγητή', + mobileToken: 'Token Κινητού', + createLink: 'Δημιουργία Συνδέσμου Λογαριασμού', + configureServer: 'Διαμόρφωση Διακομιστή', + reloadAccounts: 'Επαναφόρτωση Λογαριασμών', + disableAccount: 'Απενεργοποίηση Λογαριασμού', + enableAccount: 'Ενεργοποίηση Λογαριασμού', + deleteAccount: 'Διαγραφή Λογαριασμού', + hostHint: 'domain:port/app', + federatedHost: 'Διεύθυνση Διακομιστή για Ομοσπονδιακή λειτουργία', + storageLimit: 'Όριο Αποθήκευσης (GB) / Λογαριασμό', + storageHint: '0 για Απεριόριστο', + keyType: 'Τύπος Κλειδιού Λογαριασμού', + accountCreation: 'Δυνατότητα δημιουργίας Δημόσιου Λογαριασμού', + enablePush: 'Ενεργοποίηση Ειδοποιήσεων', + allowUnsealed: 'Επιτρέψτε μη Σφραγισμένες Συζητήσεις', + topicContent: 'Περιεχόμενο Συζήτησης:', + enableImage: 'Ενεργοποίηση Ουράς Εικόνων', + imageHint: 'Επιτρέψτε την ανάρτηση εικόνων σε συζητήσεις', + enableAudio: 'Ενεργοποίηση Ουράς Ήχου', + audioHint: 'Επιτρέψτε την ανάρτηση ήχου σε συζητήσεις', + enableVideo: 'Ενεργοποίηση Ουράς Βίντεο', + videoHint: 'Επιτρέψτε την ανάρτηση βίντεο σε συζητήσεις', + enableBinary: 'Ενεργοποίηση Ψηφιακών Αρχείων', + binaryHint: 'Επιτρέψτε την ανάρτηση υπολοίπων ψηφιακών αρχείων σε συζητήσεις', + enableWeb: 'Ενεργοποίηση Κλήσεων WebRTC', + webHint: 'Ενεργοποίηση κλήσεων ήχου και βίντεο για τις επαφές', + enableService: 'Υπηρεσία Cloudflare', + serviceHint: 'Ενεργοποίηση Υπηρεσίας Cloudflare', + serverUrl: 'URL Διακομιστή WebRTC', + urlHint: 'turn:ip:port?transport=udp', + webUsername: 'Όνομα Χρήστη WebRTC', + webPassword: 'Κωδικός Πρόσβασης WebRTC', + failedLoad: 'Αποτυχία Φόρτωσης', + limit: 'Όριο', + + deleteMessage: 'Διαγραφή Μηνύματος', + messageHint: 'Είστε σίγουροι ότι θέλετε να διαγράψετε το μήνυμα;', + attachImage: 'Επισύναψη Εικόνας', + attachVideo: 'Επισύναψη Βίντεο', + attachAudio: 'Επισύναψη Ήχου', + attachFile: 'Επισύναψη Αρχείου', + fontColor: 'Αλλαγή Χρώματος Γραμματοσειράς', + fontSize: 'Αλλαγή Μεγέθους Γραμματοσειράς', + postMessage: 'Ανάρτηση Μηνύματος', + + close: 'Κλείσιμο', + leave: 'Αποχώρηση', + confirmTopic: 'Διαγραφή συζήτησης', + sureTopic: 'Είστε σίγουροι ότι θέλετε να διαγράψετε αυτήν τη συζήτηση;', + confirmLeave: 'Αποχώρηση από τη συζήτηση', + sureLeave: 'Είστε σίγουροι ότι θέλετε να αποχωρήσετε από αυτήν τη συζήτηση;', + + details: 'Λεπτομέρειες', + host: 'Διακομιστής', + guest: 'Επισκέπτης', + editSubject: 'Επεξεργασία Θέματος', + editMembership: 'Επεξεργασία Μελών', + deleteTopic: 'Διαγραφή Συζήτησης', + leaveTopic: 'Αποχώρηση από τη συζήτηση', + + integrated: 'Ενσωματωμένο', + microphone: 'Μικρόφωνο', + camera: 'Κάμερα', + + notes: 'Σημειώσεις', + + disconnecting: 'Αποσύνδεση Επαφής', + confirmDisconnect: 'Είστε σίγουροι ότι θέλετε να αποσυνδέσετε την επαφή;', + removing: 'Διαγραφή Επαφής', + confirmRemove: 'Είστε σίγουροι ότι θέλετε να διαγράψετε την επαφή;', + message: 'Μήνυμα', + securedMessage: 'Κρυπτογραφημένο Μήνυμα', + + mfaTitle: 'Επαλήθευση Πολλαπλών Παραγόντων (MFA)', + mfaSteps: 'Αποθηκεύστε το μυστικό και επιβεβαιώστε τον κωδικό επαλήθευσης', + mfaError: 'σφάλμα κωδικού επαλήθευσης', + mfaDisabled: 'η επαλήθευση είναι προσωρινά απενεργοποιημένη', + mfaConfirm: 'Επιβεβαίωση', + mfaEnter: 'Εισάγετε τον κωδικό επαλήθευσης σας', + + enableMultifactor: 'Ενεργοποίηση επαλήθευσης πολλαπλών παραγόντων (MFA)', + disableMultifactor: 'Απενεργοποίηση επαλήθευσης πολλαπλών παραγόντων (MFA)', + + disable: 'Απενεργοποίηση', + confirmDisable: 'Απενεργοποίηση Επαλήθευσης Πολλαπλών Παραγόντων (MFA)', + disablePrompt: 'Είστε σίγουροι πως θέλετε να απενεργοποιήσετε την επαλήθευση πολλαπλών παραγόντων (MFA)', +}; diff --git a/net/web/src/session/conversation/topicItem/TopicItem.jsx b/net/web/src/session/conversation/topicItem/TopicItem.jsx index d2d9a5812..8bd8208ab 100644 --- a/net/web/src/session/conversation/topicItem/TopicItem.jsx +++ b/net/web/src/session/conversation/topicItem/TopicItem.jsx @@ -126,7 +126,7 @@ export function TopicItem({ host, contentKey, sealed, topic, update, remove, str )} { !sealed && !state.editing && (
-
{ topic.clickable }
+
{ topic.clickable }
)} { state.editing && ( diff --git a/net/web/src/session/conversation/useConversation.hook.js b/net/web/src/session/conversation/useConversation.hook.js index c4c3a69cc..9cbe996cb 100644 --- a/net/web/src/session/conversation/useConversation.hook.js +++ b/net/web/src/session/conversation/useConversation.hook.js @@ -138,18 +138,15 @@ export function useConversation(cardId, channelId) { }, [state.contentKey]); const clickableText = (text) => { - const urlPattern = new RegExp('^(https?:\\/\\/)?'+ // protocol - '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name - '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address - '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path - '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string - '(\\#[-a-z\\d_]*)?$','i'); // fragment locator + const urlPattern = new RegExp('(https?:\\/\\/)?(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,4}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)'); const hostPattern = new RegExp('^https?:\\/\\/', 'i'); let group = ''; let clickable = []; + const words = text === [] ? '' : DOMPurify.sanitize(text).split(' '); + words.forEach((word, index) => { if (!!urlPattern.test(word)) { clickable.push({ group }); @@ -161,6 +158,7 @@ export function useConversation(cardId, channelId) { group += `${word} `; } }) + clickable.push({ group }); return

{ clickable }

; };