···902902 "reauth": {
903903 "title": "Re-authentication Required",
904904 "subtitle": "Please verify your identity to continue.",
905905+ "password": "Password",
906906+ "totp": "TOTP",
907907+ "passkey": "Passkey",
908908+ "authenticatorCode": "Authenticator Code",
905909 "usePassword": "Use Password",
906910 "usePasskey": "Use Passkey",
907911 "useTotp": "Use Authenticator",
···909913 "totpPlaceholder": "Enter 6-digit code",
910914 "verify": "Verify",
911915 "verifying": "Verifying...",
916916+ "authenticating": "Authenticating...",
917917+ "passkeyPrompt": "Click the button below to authenticate with your passkey.",
912918 "cancel": "Cancel"
913919 },
914920 "delegation": {
···10711077 "beforeMigrate4": "Your old PDS will be notified to deactivate your account",
10721078 "importantWarning": "Account migration is a significant action. Make sure you trust the destination PDS and understand that your data will be moved. If something goes wrong, recovery may require manual intervention.",
10731079 "learnMore": "Learn more about migration risks",
10801080+ "comingSoon": "Coming soon",
10811081+ "oauthCompleting": "Completing authentication...",
10821082+ "oauthFailed": "Authentication Failed",
10831083+ "tryAgain": "Try Again",
10741084 "resume": {
10751085 "title": "Resume Migration?",
10761086 "incomplete": "You have an incomplete migration in progress:",
···10901100 "desc": "Move your existing AT Protocol account to this server.",
10911101 "understand": "I understand the risks and want to proceed"
10921102 },
10931093- "sourceLogin": {
10941094- "title": "Sign In to Your Current PDS",
10951095- "desc": "Enter your credentials for the account you want to migrate.",
11031103+ "sourceAuth": {
11041104+ "title": "Enter Your Current Handle",
11051105+ "titleResume": "Resume Migration",
11061106+ "desc": "Enter the handle of the account you want to migrate.",
11071107+ "descResume": "Re-authenticate to your source PDS to continue the migration.",
10961108 "handle": "Handle",
10971097- "handlePlaceholder": "you.bsky.social",
10981098- "password": "Password",
10991099- "twoFactorCode": "Two-Factor Code",
11001100- "twoFactorRequired": "Two-factor authentication required",
11011101- "signIn": "Sign In & Continue"
11091109+ "handlePlaceholder": "alice.bsky.social",
11101110+ "handleHint": "Your current handle on your existing PDS",
11111111+ "continue": "Continue",
11121112+ "connecting": "Connecting...",
11131113+ "reauthenticate": "Re-authenticate",
11141114+ "resumeTitle": "Migration in Progress",
11151115+ "resumeFrom": "From",
11161116+ "resumeTo": "To",
11171117+ "resumeProgress": "Progress",
11181118+ "resumeOAuthNote": "You need to re-authenticate via OAuth to continue."
11021119 },
11031120 "chooseHandle": {
11041121 "title": "Choose Your New Handle",
11051122 "desc": "Select a handle for your account on this PDS.",
11061106- "handleHint": "Your full handle will be: @{handle}"
11231123+ "migratingFrom": "Migrating from",
11241124+ "newHandle": "New Handle",
11251125+ "checkingAvailability": "Checking availability...",
11261126+ "handleAvailable": "Handle is available!",
11271127+ "handleTaken": "Handle is already taken",
11281128+ "handleHint": "You can also use your own domain by entering the full handle (e.g., alice.mydomain.com)",
11291129+ "email": "Email Address",
11301130+ "authMethod": "Authentication Method",
11311131+ "authPassword": "Password",
11321132+ "authPasswordDesc": "Traditional password-based login",
11331133+ "authPasskey": "Passkey",
11341134+ "authPasskeyDesc": "Passwordless login using biometrics or security key",
11351135+ "password": "Password",
11361136+ "passwordHint": "At least 8 characters",
11371137+ "passkeyInfo": "You'll set up a passkey after your account is created. Your device will prompt you to use biometrics (fingerprint, Face ID) or a security key.",
11381138+ "inviteCode": "Invite Code"
11071139 },
11081140 "review": {
11091141 "title": "Review Migration",
11101110- "desc": "Please review and confirm your migration details.",
11421142+ "desc": "Please confirm the details of your migration.",
11111143 "currentHandle": "Current Handle",
11121144 "newHandle": "New Handle",
11131113- "sourcePds": "Source PDS",
11141114- "targetPds": "This PDS",
11451145+ "did": "DID",
11461146+ "sourcePds": "From PDS",
11471147+ "targetPds": "To PDS",
11151148 "email": "Email",
11491149+ "authentication": "Authentication",
11501150+ "authPasskey": "Passkey (passwordless)",
11511151+ "authPassword": "Password",
11161152 "inviteCode": "Invite Code",
11171117- "confirm": "I confirm I want to migrate my account",
11181118- "startMigration": "Start Migration"
11531153+ "warning": "After you click \"Start Migration\", your repository and data will begin transferring. This process cannot be easily undone.",
11541154+ "startMigration": "Start Migration",
11551155+ "starting": "Starting..."
11191156 },
11201157 "migrating": {
11211121- "title": "Migrating Your Account",
11221122- "desc": "Please wait while we transfer your data...",
11231123- "gettingServiceAuth": "Getting service authorization...",
11241124- "creatingAccount": "Creating account on new PDS...",
11251125- "exportingRepo": "Exporting repository...",
11261126- "importingRepo": "Importing repository...",
11271127- "countingBlobs": "Counting blobs...",
11281128- "migratingBlobs": "Migrating blobs ({current}/{total})...",
11291129- "migratingPrefs": "Migrating preferences...",
11301130- "requestingPlc": "Requesting PLC operation..."
11581158+ "title": "Migration in Progress",
11591159+ "desc": "Please wait while your account is being transferred...",
11601160+ "exportRepo": "Export repository",
11611161+ "importRepo": "Import repository",
11621162+ "migrateBlobs": "Migrate blobs",
11631163+ "migratePrefs": "Migrate preferences"
11641164+ },
11651165+ "passkeySetup": {
11661166+ "title": "Set Up Your Passkey",
11671167+ "desc": "Your email has been verified. Now set up your passkey for secure, passwordless login.",
11681168+ "nameLabel": "Passkey Name (optional)",
11691169+ "namePlaceholder": "e.g., MacBook Pro, iPhone",
11701170+ "nameHint": "A friendly name to identify this passkey",
11711171+ "instructions": "Click the button below to register your passkey. Your device will prompt you to use biometrics (fingerprint, Face ID) or a security key.",
11721172+ "register": "Register Passkey",
11731173+ "registering": "Registering..."
11741174+ },
11751175+ "appPassword": {
11761176+ "title": "Save Your App Password",
11771177+ "desc": "Your passkey has been created. An app password has been generated for you to use with apps that don't support passkeys yet.",
11781178+ "warning": "This app password is required to sign into apps that don't support passkeys yet (like bsky.app). You will only see this password once.",
11791179+ "label": "App Password for",
11801180+ "saved": "I have saved my app password in a secure location",
11811181+ "continue": "Continue"
11311182 },
11321183 "emailVerify": {
11331184 "title": "Verify Your Email",
···11401191 "verifying": "Verifying..."
11411192 },
11421193 "plcToken": {
11431143- "title": "Verify Your Identity",
11441144- "desc": "A verification code has been sent to your email on your current PDS.",
11451145- "tokenLabel": "Verification Token",
11461146- "tokenPlaceholder": "Enter the token from your email",
11471147- "resend": "Resend Token",
11481148- "resending": "Resending..."
11941194+ "title": "Verify Migration",
11951195+ "desc": "A verification code has been sent to the email registered with your old account.",
11961196+ "info": "This code confirms you have access to the account and authorizes updating your identity to point to this PDS.",
11971197+ "tokenLabel": "Verification Code",
11981198+ "tokenPlaceholder": "Enter code from email",
11991199+ "resend": "Resend Code",
12001200+ "complete": "Complete Migration",
12011201+ "completing": "Verifying..."
11491202 },
11501203 "didWebUpdate": {
11511204 "title": "Update Your DID Document",
···11681221 "success": {
11691222 "title": "Migration Complete!",
11701223 "desc": "Your account has been successfully migrated to this PDS.",
11711171- "newHandle": "New Handle",
12241224+ "yourNewHandle": "Your new handle",
11721225 "did": "DID",
11731173- "goToDashboard": "Go to Dashboard"
12261226+ "blobsWarning": "{count} blobs could not be migrated. These may be images or other media that are no longer available.",
12271227+ "redirecting": "Redirecting to dashboard..."
12281228+ },
12291229+ "error": {
12301230+ "title": "Migration Error",
12311231+ "desc": "An error occurred during migration.",
12321232+ "startOver": "Start Over"
12331233+ },
12341234+ "common": {
12351235+ "back": "Back",
12361236+ "cancel": "Cancel",
12371237+ "continue": "Continue",
12381238+ "whatWillHappen": "What will happen:",
12391239+ "step1": "Log in to your current PDS",
12401240+ "step2": "Choose your new handle on this server",
12411241+ "step3": "Your repository and blobs will be transferred",
12421242+ "step4": "Verify the migration via email",
12431243+ "step5": "Your identity will be updated to point here",
12441244+ "beforeProceed": "Before you proceed:",
12451245+ "warning1": "You need access to the email registered with your current account",
12461246+ "warning2": "Large accounts may take several minutes to transfer",
12471247+ "warning3": "Your old account will be deactivated after migration"
11741248 }
11751249 },
11761250 "outbound": {
+103-29
frontend/src/locales/fi.json
···902902 "reauth": {
903903 "title": "Uudelleentodennus vaaditaan",
904904 "subtitle": "Vahvista henkilöllisyytesi jatkaaksesi.",
905905+ "password": "Salasana",
906906+ "totp": "TOTP",
907907+ "passkey": "Pääsyavain",
908908+ "authenticatorCode": "Todentajan koodi",
905909 "usePassword": "Käytä salasanaa",
906910 "usePasskey": "Käytä pääsyavainta",
907911 "useTotp": "Käytä todentajaa",
···909913 "totpPlaceholder": "Syötä 6-numeroinen koodi",
910914 "verify": "Vahvista",
911915 "verifying": "Vahvistetaan...",
916916+ "authenticating": "Todennetaan...",
917917+ "passkeyPrompt": "Klikkaa alla olevaa painiketta todentaaksesi pääsyavaimellasi.",
912918 "cancel": "Peruuta"
913919 },
914920 "verifyChannel": {
···10711077 "beforeMigrate4": "Vanhalle PDS:llesi ilmoitetaan tilisi deaktivoinnista",
10721078 "importantWarning": "Tilin siirto on merkittävä toimenpide. Varmista, että luotat kohde-PDS:ään ja ymmärrät, että tietosi siirretään. Jos jokin menee pieleen, palautus voi vaatia manuaalista toimenpidettä.",
10731079 "learnMore": "Lue lisää siirron riskeistä",
10801080+ "comingSoon": "Tulossa pian",
10811081+ "oauthCompleting": "Viimeistellään todennusta...",
10821082+ "oauthFailed": "Todennus epäonnistui",
10831083+ "tryAgain": "Yritä uudelleen",
10741084 "resume": {
10751085 "title": "Jatka siirtoa?",
10761086 "incomplete": "Sinulla on keskeneräinen siirto:",
···10901100 "desc": "Siirrä olemassa oleva AT Protocol -tilisi tälle palvelimelle.",
10911101 "understand": "Ymmärrän riskit ja haluan jatkaa"
10921102 },
10931093- "sourceLogin": {
10941094- "title": "Kirjaudu nykyiseen PDS:ääsi",
10951095- "desc": "Syötä siirrettävän tilin tunnukset.",
11031103+ "sourceAuth": {
11041104+ "title": "Syötä nykyinen käyttäjätunnuksesi",
11051105+ "titleResume": "Jatka siirtoa",
11061106+ "desc": "Syötä siirrettävän tilin käyttäjätunnus.",
11071107+ "descResume": "Tunnistaudu uudelleen lähde-PDS:ään jatkaaksesi siirtoa.",
10961108 "handle": "Käyttäjätunnus",
10971097- "handlePlaceholder": "sinä.bsky.social",
10981098- "password": "Salasana",
10991099- "twoFactorCode": "Kaksivaiheinen koodi",
11001100- "twoFactorRequired": "Kaksivaiheinen tunnistautuminen vaaditaan",
11011101- "signIn": "Kirjaudu ja jatka"
11091109+ "handlePlaceholder": "maija.bsky.social",
11101110+ "handleHint": "Nykyinen käyttäjätunnuksesi nykyisessä PDS:ssäsi",
11111111+ "continue": "Jatka",
11121112+ "connecting": "Yhdistetään...",
11131113+ "reauthenticate": "Tunnistaudu uudelleen",
11141114+ "resumeTitle": "Siirto käynnissä",
11151115+ "resumeFrom": "Mistä",
11161116+ "resumeTo": "Minne",
11171117+ "resumeProgress": "Edistyminen",
11181118+ "resumeOAuthNote": "Sinun täytyy tunnistautua uudelleen OAuth:n kautta jatkaaksesi."
11021119 },
11031120 "chooseHandle": {
11041121 "title": "Valitse uusi käyttäjätunnuksesi",
11051122 "desc": "Valitse käyttäjätunnus tilillesi tässä PDS:ssä.",
11061106- "handleHint": "Täydellinen käyttäjätunnuksesi on: @{handle}"
11231123+ "migratingFrom": "Siirretään tililtä",
11241124+ "newHandle": "Uusi käyttäjätunnus",
11251125+ "checkingAvailability": "Tarkistetaan saatavuutta...",
11261126+ "handleAvailable": "Käyttäjätunnus on saatavilla!",
11271127+ "handleTaken": "Käyttäjätunnus on jo varattu",
11281128+ "handleHint": "Voit myös käyttää omaa verkkotunnustasi syöttämällä täydellisen käyttäjätunnuksen (esim. maija.omadomain.fi)",
11291129+ "email": "Sähköpostiosoite",
11301130+ "authMethod": "Tunnistautumistapa",
11311131+ "authPassword": "Salasana",
11321132+ "authPasswordDesc": "Perinteinen salasanapohjainen kirjautuminen",
11331133+ "authPasskey": "Pääsyavain",
11341134+ "authPasskeyDesc": "Salasanaton kirjautuminen biometriikalla tai suojausavaimella",
11351135+ "password": "Salasana",
11361136+ "passwordHint": "Vähintään 8 merkkiä",
11371137+ "passkeyInfo": "Määrität pääsyavaimen tilisi luomisen jälkeen. Laitteesi pyytää käyttämään biometriikkaa (sormenjälki, Face ID) tai suojausavainta.",
11381138+ "inviteCode": "Kutsukoodi"
11071139 },
11081140 "review": {
11091141 "title": "Tarkista siirto",
11101110- "desc": "Tarkista ja vahvista siirtotietosi.",
11421142+ "desc": "Vahvista siirtosi tiedot.",
11111143 "currentHandle": "Nykyinen käyttäjätunnus",
11121144 "newHandle": "Uusi käyttäjätunnus",
11451145+ "did": "DID",
11131146 "sourcePds": "Lähde-PDS",
11141114- "targetPds": "Tämä PDS",
11471147+ "targetPds": "Kohde-PDS",
11151148 "email": "Sähköposti",
11491149+ "authentication": "Tunnistautuminen",
11501150+ "authPasskey": "Pääsyavain (salasanaton)",
11511151+ "authPassword": "Salasana",
11161152 "inviteCode": "Kutsukoodi",
11171117- "confirm": "Vahvistan haluavani siirtää tilini",
11181118- "startMigration": "Aloita siirto"
11531153+ "warning": "Kun klikkaat \"Aloita siirto\", tietovarastosi ja datasi alkavat siirtyä. Tätä prosessia ei voi helposti peruuttaa.",
11541154+ "startMigration": "Aloita siirto",
11551155+ "starting": "Aloitetaan..."
11191156 },
11201157 "migrating": {
11211121- "title": "Siirretään tiliäsi",
11221122- "desc": "Odota, kun siirrämme tietojasi...",
11231123- "gettingServiceAuth": "Haetaan palveluvaltuutusta...",
11241124- "creatingAccount": "Luodaan tiliä uuteen PDS:ään...",
11251125- "exportingRepo": "Viedään tietovarastoa...",
11261126- "importingRepo": "Tuodaan tietovarastoa...",
11271127- "countingBlobs": "Lasketaan blob-tiedostoja...",
11281128- "migratingBlobs": "Siirretään blob-tiedostoja ({current}/{total})...",
11291129- "migratingPrefs": "Siirretään asetuksia...",
11301130- "requestingPlc": "Pyydetään PLC-toimintoa..."
11581158+ "title": "Siirto käynnissä",
11591159+ "desc": "Odota, kun tiliäsi siirretään...",
11601160+ "exportRepo": "Vie tietovarasto",
11611161+ "importRepo": "Tuo tietovarasto",
11621162+ "migrateBlobs": "Siirrä blob-tiedostot",
11631163+ "migratePrefs": "Siirrä asetukset"
11641164+ },
11651165+ "passkeySetup": {
11661166+ "title": "Määritä pääsyavaimesi",
11671167+ "desc": "Sähköpostisi on vahvistettu. Määritä nyt pääsyavaimesi turvallista, salasanatonta kirjautumista varten.",
11681168+ "nameLabel": "Pääsyavaimen nimi (valinnainen)",
11691169+ "namePlaceholder": "esim. MacBook Pro, iPhone",
11701170+ "nameHint": "Kutsumanimi tämän pääsyavaimen tunnistamiseen",
11711171+ "instructions": "Klikkaa alla olevaa painiketta rekisteröidäksesi pääsyavaimesi. Laitteesi pyytää käyttämään biometriikkaa (sormenjälki, Face ID) tai suojausavainta.",
11721172+ "register": "Rekisteröi pääsyavain",
11731173+ "registering": "Rekisteröidään..."
11741174+ },
11751175+ "appPassword": {
11761176+ "title": "Tallenna sovellussalasanasi",
11771177+ "desc": "Pääsyavaimesi on luotu. Sovellussalasana on luotu sinulle käytettäväksi sovellusten kanssa, jotka eivät vielä tue pääsyavaimia.",
11781178+ "warning": "Tämä sovellussalasana vaaditaan kirjautumiseen sovelluksissa, jotka eivät vielä tue pääsyavaimia (kuten bsky.app). Näet tämän salasanan vain kerran.",
11791179+ "label": "Sovellussalasana kohteelle",
11801180+ "saved": "Olen tallentanut sovellussalasanani turvalliseen paikkaan",
11811181+ "continue": "Jatka"
11311182 },
11321183 "emailVerify": {
11331184 "title": "Vahvista sähköpostisi",
···11401191 "verifying": "Vahvistetaan..."
11411192 },
11421193 "plcToken": {
11431143- "title": "Vahvista henkilöllisyytesi",
11441144- "desc": "Vahvistuskoodi on lähetetty sähköpostiisi nykyisessä PDS:ssäsi.",
11941194+ "title": "Vahvista siirto",
11951195+ "desc": "Vahvistuskoodi on lähetetty vanhaan tiliisi rekisteröityyn sähköpostiin.",
11961196+ "info": "Tämä koodi vahvistaa, että sinulla on pääsy tiliin ja valtuuttaa identiteettisi päivityksen osoittamaan tähän PDS:ään.",
11451197 "tokenLabel": "Vahvistuskoodi",
11461198 "tokenPlaceholder": "Syötä sähköpostista saatu koodi",
11471147- "resend": "Lähetä uudelleen",
11481148- "resending": "Lähetetään..."
11991199+ "resend": "Lähetä koodi uudelleen",
12001200+ "complete": "Viimeistele siirto",
12011201+ "completing": "Vahvistetaan..."
11491202 },
11501203 "didWebUpdate": {
11511204 "title": "Päivitä DID-dokumenttisi",
···11681221 "success": {
11691222 "title": "Siirto valmis!",
11701223 "desc": "Tilisi on siirretty onnistuneesti tähän PDS:ään.",
11711171- "newHandle": "Uusi käyttäjätunnus",
12241224+ "yourNewHandle": "Uusi käyttäjätunnuksesi",
11721225 "did": "DID",
11731173- "goToDashboard": "Siirry hallintapaneeliin"
12261226+ "blobsWarning": "{count} blob-tiedostoa ei voitu siirtää. Nämä voivat olla kuvia tai muuta mediaa, jotka eivät ole enää saatavilla.",
12271227+ "redirecting": "Uudelleenohjataan hallintapaneeliin..."
12281228+ },
12291229+ "error": {
12301230+ "title": "Siirtovirhe",
12311231+ "desc": "Siirron aikana tapahtui virhe.",
12321232+ "startOver": "Aloita alusta"
12331233+ },
12341234+ "common": {
12351235+ "back": "Takaisin",
12361236+ "cancel": "Peruuta",
12371237+ "continue": "Jatka",
12381238+ "whatWillHappen": "Mitä tapahtuu:",
12391239+ "step1": "Kirjaudu nykyiseen PDS:ääsi",
12401240+ "step2": "Valitse uusi käyttäjätunnus tällä palvelimella",
12411241+ "step3": "Tietovarastosi ja blob-tiedostosi siirretään",
12421242+ "step4": "Vahvista siirto sähköpostilla",
12431243+ "step5": "Identiteettisi päivitetään osoittamaan tänne",
12441244+ "beforeProceed": "Ennen kuin jatkat:",
12451245+ "warning1": "Tarvitset pääsyn nykyiseen tiliisi rekisteröityyn sähköpostiin",
12461246+ "warning2": "Suurten tilien siirto voi kestää useita minuutteja",
12471247+ "warning3": "Vanha tilisi deaktivoidaan siirron jälkeen"
11741248 }
11751249 },
11761250 "outbound": {
···189189 "title": "DID 문서 편집기",
190190 "preview": "현재 DID 문서",
191191 "verificationMethods": "검증 방법 (서명 키)",
192192+ "verificationMethodsDesc": "DID를 대신하여 동작할 수 있는 서명 키입니다. 새 PDS로 마이그레이션할 때 해당 서명 키를 여기에 추가하세요.",
192193 "addKey": "키 추가",
193194 "removeKey": "삭제",
194195 "keyId": "키 ID",
195196 "keyIdPlaceholder": "#atproto",
196197 "publicKey": "공개 키 (Multibase)",
197198 "publicKeyPlaceholder": "zQ3sh...",
199199+ "noKeys": "구성된 검증 방법이 없습니다. 로컬 PDS 키를 사용 중입니다.",
198200 "alsoKnownAs": "다른 이름 (핸들)",
201201+ "alsoKnownAsDesc": "DID를 가리키는 핸들입니다. 새 PDS에서 핸들이 변경되면 업데이트하세요.",
199202 "addHandle": "핸들 추가",
203203+ "removeHandle": "삭제",
204204+ "handle": "핸들",
200205 "handlePlaceholder": "at://handle.pds.com",
201201- "serviceEndpoint": "서비스 엔드포인트 (현재 PDS)",
206206+ "noHandles": "구성된 핸들이 없습니다. 로컬 핸들을 사용 중입니다.",
207207+ "serviceEndpoint": "서비스 엔드포인트",
208208+ "serviceEndpointDesc": "현재 계정 데이터를 호스팅하는 PDS입니다. 마이그레이션할 때 업데이트하세요.",
209209+ "currentPds": "현재 PDS URL",
202210 "save": "변경사항 저장",
203211 "saving": "저장 중...",
204212 "success": "DID 문서가 업데이트되었습니다",
213213+ "saveFailed": "DID 문서 저장에 실패했습니다",
214214+ "loadFailed": "DID 문서 로드에 실패했습니다",
215215+ "invalidMultibase": "공개 키는 'z'로 시작하는 유효한 multibase 문자열이어야 합니다",
216216+ "invalidHandle": "핸들은 at:// URI여야 합니다 (예: at://handle.example.com)",
205217 "helpTitle": "이것은 무엇인가요?",
206218 "helpText": "다른 PDS로 마이그레이션하면 해당 PDS가 새 서명 키를 생성합니다. 여기에서 DID 문서를 업데이트하여 새 키와 위치를 가리키도록 하세요."
207219 },
···890902 "reauth": {
891903 "title": "재인증 필요",
892904 "subtitle": "계속하려면 본인 확인을 해주세요.",
905905+ "password": "비밀번호",
906906+ "totp": "TOTP",
907907+ "passkey": "패스키",
908908+ "authenticatorCode": "인증 코드",
893909 "usePassword": "비밀번호 사용",
894910 "usePasskey": "패스키 사용",
895911 "useTotp": "인증 앱 사용",
···897913 "totpPlaceholder": "6자리 코드 입력",
898914 "verify": "확인",
899915 "verifying": "확인 중...",
916916+ "authenticating": "인증 중...",
917917+ "passkeyPrompt": "아래 버튼을 클릭하여 패스키로 인증하세요.",
900918 "cancel": "취소"
901919 },
902920 "verifyChannel": {
···10591077 "beforeMigrate4": "이전 PDS에 계정 비활성화가 통보됩니다",
10601078 "importantWarning": "계정 마이그레이션은 중요한 작업입니다. 대상 PDS를 신뢰하고 데이터가 이동된다는 것을 이해하세요. 문제가 발생하면 수동 복구가 필요할 수 있습니다.",
10611079 "learnMore": "마이그레이션 위험에 대해 자세히 알아보기",
10801080+ "comingSoon": "곧 출시 예정",
10811081+ "oauthCompleting": "인증 완료 중...",
10821082+ "oauthFailed": "인증 실패",
10831083+ "tryAgain": "다시 시도",
10621084 "resume": {
10631085 "title": "마이그레이션을 재개하시겠습니까?",
10641086 "incomplete": "완료되지 않은 마이그레이션이 있습니다:",
···10781100 "desc": "기존 AT Protocol 계정을 이 서버로 이동합니다.",
10791101 "understand": "위험을 이해하고 계속 진행합니다"
10801102 },
10811081- "sourceLogin": {
10821082- "title": "현재 PDS에 로그인",
10831083- "desc": "마이그레이션할 계정의 인증 정보를 입력하세요.",
11031103+ "sourceAuth": {
11041104+ "title": "현재 핸들 입력",
11051105+ "titleResume": "마이그레이션 재개",
11061106+ "desc": "마이그레이션할 계정의 핸들을 입력하세요.",
11071107+ "descResume": "마이그레이션을 계속하려면 소스 PDS에 재인증하세요.",
10841108 "handle": "핸들",
10851085- "handlePlaceholder": "you.bsky.social",
10861086- "password": "비밀번호",
10871087- "twoFactorCode": "2단계 인증 코드",
10881088- "twoFactorRequired": "2단계 인증이 필요합니다",
10891089- "signIn": "로그인 및 계속"
11091109+ "handlePlaceholder": "alice.bsky.social",
11101110+ "handleHint": "현재 PDS에서의 핸들",
11111111+ "continue": "계속",
11121112+ "connecting": "연결 중...",
11131113+ "reauthenticate": "재인증",
11141114+ "resumeTitle": "마이그레이션 진행 중",
11151115+ "resumeFrom": "출발지",
11161116+ "resumeTo": "목적지",
11171117+ "resumeProgress": "진행 상황",
11181118+ "resumeOAuthNote": "계속하려면 OAuth로 재인증이 필요합니다."
10901119 },
10911120 "chooseHandle": {
10921121 "title": "새 핸들 선택",
10931122 "desc": "이 PDS에서 사용할 계정 핸들을 선택하세요.",
10941094- "handleHint": "전체 핸들: @{handle}"
11231123+ "migratingFrom": "마이그레이션 원본",
11241124+ "newHandle": "새 핸들",
11251125+ "checkingAvailability": "사용 가능 여부 확인 중...",
11261126+ "handleAvailable": "핸들을 사용할 수 있습니다!",
11271127+ "handleTaken": "핸들이 이미 사용 중입니다",
11281128+ "handleHint": "전체 핸들(예: alice.mydomain.com)을 입력하여 자체 도메인을 사용할 수도 있습니다",
11291129+ "email": "이메일 주소",
11301130+ "authMethod": "인증 방법",
11311131+ "authPassword": "비밀번호",
11321132+ "authPasswordDesc": "기존 비밀번호 기반 로그인",
11331133+ "authPasskey": "패스키",
11341134+ "authPasskeyDesc": "생체 인식 또는 보안 키를 사용한 비밀번호 없는 로그인",
11351135+ "password": "비밀번호",
11361136+ "passwordHint": "최소 8자",
11371137+ "passkeyInfo": "계정 생성 후 패스키를 설정합니다. 기기에서 생체 인식(지문, Face ID) 또는 보안 키 사용을 요청합니다.",
11381138+ "inviteCode": "초대 코드"
10951139 },
10961140 "review": {
10971141 "title": "마이그레이션 검토",
10981098- "desc": "마이그레이션 세부 정보를 검토하고 확인하세요.",
11421142+ "desc": "마이그레이션 세부 정보를 확인하세요.",
10991143 "currentHandle": "현재 핸들",
11001144 "newHandle": "새 핸들",
11451145+ "did": "DID",
11011146 "sourcePds": "소스 PDS",
11021102- "targetPds": "이 PDS",
11471147+ "targetPds": "대상 PDS",
11031148 "email": "이메일",
11491149+ "authentication": "인증",
11501150+ "authPasskey": "패스키 (비밀번호 없음)",
11511151+ "authPassword": "비밀번호",
11041152 "inviteCode": "초대 코드",
11051105- "confirm": "계정 마이그레이션을 확인합니다",
11061106- "startMigration": "마이그레이션 시작"
11531153+ "warning": "\"마이그레이션 시작\"을 클릭하면 저장소와 데이터 전송이 시작됩니다. 이 과정은 쉽게 되돌릴 수 없습니다.",
11541154+ "startMigration": "마이그레이션 시작",
11551155+ "starting": "시작 중..."
11071156 },
11081157 "migrating": {
11091109- "title": "계정 마이그레이션 중",
11101110- "desc": "데이터를 전송하는 중입니다...",
11111111- "gettingServiceAuth": "서비스 인증 획득 중...",
11121112- "creatingAccount": "새 PDS에 계정 생성 중...",
11131113- "exportingRepo": "저장소 내보내기 중...",
11141114- "importingRepo": "저장소 가져오기 중...",
11151115- "countingBlobs": "blob 개수 세는 중...",
11161116- "migratingBlobs": "blob 마이그레이션 중 ({current}/{total})...",
11171117- "migratingPrefs": "환경설정 마이그레이션 중...",
11181118- "requestingPlc": "PLC 작업 요청 중..."
11581158+ "title": "마이그레이션 진행 중",
11591159+ "desc": "계정을 전송하는 중입니다...",
11601160+ "exportRepo": "저장소 내보내기",
11611161+ "importRepo": "저장소 가져오기",
11621162+ "migrateBlobs": "blob 마이그레이션",
11631163+ "migratePrefs": "환경설정 마이그레이션"
11641164+ },
11651165+ "passkeySetup": {
11661166+ "title": "패스키 설정",
11671167+ "desc": "이메일이 인증되었습니다. 안전한 비밀번호 없는 로그인을 위해 패스키를 설정하세요.",
11681168+ "nameLabel": "패스키 이름 (선택사항)",
11691169+ "namePlaceholder": "예: MacBook Pro, iPhone",
11701170+ "nameHint": "이 패스키를 식별하기 위한 이름",
11711171+ "instructions": "아래 버튼을 클릭하여 패스키를 등록하세요. 기기에서 생체 인식(지문, Face ID) 또는 보안 키 사용을 요청합니다.",
11721172+ "register": "패스키 등록",
11731173+ "registering": "등록 중..."
11741174+ },
11751175+ "appPassword": {
11761176+ "title": "앱 비밀번호 저장",
11771177+ "desc": "패스키가 생성되었습니다. 아직 패스키를 지원하지 않는 앱에서 사용할 앱 비밀번호가 생성되었습니다.",
11781178+ "warning": "이 앱 비밀번호는 아직 패스키를 지원하지 않는 앱(예: bsky.app)에 로그인할 때 필요합니다. 이 비밀번호는 한 번만 표시됩니다.",
11791179+ "label": "앱 비밀번호:",
11801180+ "saved": "앱 비밀번호를 안전한 곳에 저장했습니다",
11811181+ "continue": "계속"
11191182 },
11201183 "emailVerify": {
11211184 "title": "이메일 인증",
···11281191 "verifying": "인증 중..."
11291192 },
11301193 "plcToken": {
11311131- "title": "신원 확인",
11321132- "desc": "현재 PDS에 등록된 이메일로 인증 코드가 전송되었습니다.",
11331133- "tokenLabel": "인증 토큰",
11341134- "tokenPlaceholder": "이메일에서 받은 토큰 입력",
11351135- "resend": "재전송",
11361136- "resending": "전송 중..."
11941194+ "title": "마이그레이션 확인",
11951195+ "desc": "이전 계정에 등록된 이메일로 인증 코드가 전송되었습니다.",
11961196+ "info": "이 코드는 계정 접근 권한을 확인하고 이 PDS를 가리키도록 아이덴티티 업데이트를 승인합니다.",
11971197+ "tokenLabel": "인증 코드",
11981198+ "tokenPlaceholder": "이메일에서 받은 코드 입력",
11991199+ "resend": "코드 재전송",
12001200+ "complete": "마이그레이션 완료",
12011201+ "completing": "확인 중..."
11371202 },
11381203 "didWebUpdate": {
11391204 "title": "DID 문서 업데이트",
···11561221 "success": {
11571222 "title": "마이그레이션 완료!",
11581223 "desc": "계정이 이 PDS로 성공적으로 마이그레이션되었습니다.",
11591159- "newHandle": "새 핸들",
12241224+ "yourNewHandle": "새 핸들",
11601225 "did": "DID",
11611161- "goToDashboard": "대시보드로 이동"
12261226+ "blobsWarning": "{count}개의 blob을 마이그레이션할 수 없습니다. 더 이상 사용할 수 없는 이미지나 기타 미디어일 수 있습니다.",
12271227+ "redirecting": "대시보드로 리디렉션 중..."
12281228+ },
12291229+ "error": {
12301230+ "title": "마이그레이션 오류",
12311231+ "desc": "마이그레이션 중 오류가 발생했습니다.",
12321232+ "startOver": "처음부터 다시 시작"
12331233+ },
12341234+ "common": {
12351235+ "back": "뒤로",
12361236+ "cancel": "취소",
12371237+ "continue": "계속",
12381238+ "whatWillHappen": "진행 과정:",
12391239+ "step1": "현재 PDS에 로그인",
12401240+ "step2": "이 서버에서 새 핸들 선택",
12411241+ "step3": "저장소와 blob이 전송됩니다",
12421242+ "step4": "이메일로 마이그레이션 확인",
12431243+ "step5": "아이덴티티가 여기를 가리키도록 업데이트됩니다",
12441244+ "beforeProceed": "진행하기 전에:",
12451245+ "warning1": "현재 계정에 등록된 이메일에 접근할 수 있어야 합니다",
12461246+ "warning2": "대용량 계정 전송에는 몇 분이 걸릴 수 있습니다",
12471247+ "warning3": "마이그레이션 후 이전 계정은 비활성화됩니다"
11621248 }
11631249 },
11641250 "outbound": {
+119-33
frontend/src/locales/sv.json
···189189 "title": "DID-dokumentredigerare",
190190 "preview": "Nuvarande DID-dokument",
191191 "verificationMethods": "Verifieringsmetoder (signeringsnycklar)",
192192+ "verificationMethodsDesc": "Signeringsnycklar som kan agera å din DIDs vägnar. När du migrerar till en ny PDS, lägg till deras signeringsnyckel här.",
192193 "addKey": "Lägg till nyckel",
193194 "removeKey": "Ta bort",
194195 "keyId": "Nyckel-ID",
195196 "keyIdPlaceholder": "#atproto",
196197 "publicKey": "Publik nyckel (Multibase)",
197198 "publicKeyPlaceholder": "zQ3sh...",
199199+ "noKeys": "Inga verifieringsmetoder konfigurerade. Använder lokal PDS-nyckel.",
198200 "alsoKnownAs": "Även känd som (användarnamn)",
201201+ "alsoKnownAsDesc": "Användarnamn som pekar på din DID. Uppdatera detta när ditt användarnamn ändras på en ny PDS.",
199202 "addHandle": "Lägg till användarnamn",
203203+ "removeHandle": "Ta bort",
204204+ "handle": "Användarnamn",
200205 "handlePlaceholder": "at://handle.pds.com",
201201- "serviceEndpoint": "Tjänstslutpunkt (nuvarande PDS)",
206206+ "noHandles": "Inga användarnamn konfigurerade. Använder lokalt användarnamn.",
207207+ "serviceEndpoint": "Tjänstslutpunkt",
208208+ "serviceEndpointDesc": "PDS som för närvarande lagrar din kontodata. Uppdatera detta vid migrering.",
209209+ "currentPds": "Nuvarande PDS-URL",
202210 "save": "Spara ändringar",
203211 "saving": "Sparar...",
204212 "success": "DID-dokumentet har uppdaterats",
213213+ "saveFailed": "Kunde inte spara DID-dokument",
214214+ "loadFailed": "Kunde inte ladda DID-dokument",
215215+ "invalidMultibase": "Publik nyckel måste vara en giltig multibase-sträng som börjar med 'z'",
216216+ "invalidHandle": "Användarnamn måste vara en at:// URI (t.ex. at://handle.example.com)",
205217 "helpTitle": "Vad är detta?",
206218 "helpText": "När du flyttar till en annan PDS genererar den PDS nya signeringsnycklar. Uppdatera ditt DID-dokument här så att det pekar på dina nya nycklar och plats."
207219 },
···890902 "reauth": {
891903 "title": "Återautentisering krävs",
892904 "subtitle": "Verifiera din identitet för att fortsätta.",
905905+ "password": "Lösenord",
906906+ "totp": "TOTP",
907907+ "passkey": "Passkey",
908908+ "authenticatorCode": "Autentiseringskod",
893909 "usePassword": "Använd lösenord",
894910 "usePasskey": "Använd nyckel",
895911 "useTotp": "Använd autentiserare",
···897913 "totpPlaceholder": "Ange 6-siffrig kod",
898914 "verify": "Verifiera",
899915 "verifying": "Verifierar...",
916916+ "authenticating": "Autentiserar...",
917917+ "passkeyPrompt": "Klicka på knappen nedan för att autentisera med din passkey.",
900918 "cancel": "Avbryt"
901919 },
902920 "verifyChannel": {
···10591077 "beforeMigrate4": "Din gamla PDS kommer att meddelas om kontoinaktivering",
10601078 "importantWarning": "Kontoflyttning är en betydande åtgärd. Se till att du litar på mål-PDS och förstår att din data kommer att flyttas. Om något går fel kan manuell återställning krävas.",
10611079 "learnMore": "Läs mer om flyttningsrisker",
10801080+ "comingSoon": "Kommer snart",
10811081+ "oauthCompleting": "Slutför autentisering...",
10821082+ "oauthFailed": "Autentisering misslyckades",
10831083+ "tryAgain": "Försök igen",
10621084 "resume": {
10631085 "title": "Återuppta flytt?",
10641086 "incomplete": "Du har en ofullständig flytt pågående:",
···10781100 "desc": "Flytta ditt befintliga AT Protocol-konto till denna server.",
10791101 "understand": "Jag förstår riskerna och vill fortsätta"
10801102 },
10811081- "sourceLogin": {
10821082- "title": "Logga in på din nuvarande PDS",
10831083- "desc": "Ange uppgifterna för kontot du vill flytta.",
11031103+ "sourceAuth": {
11041104+ "title": "Ange ditt nuvarande användarnamn",
11051105+ "titleResume": "Återuppta flytt",
11061106+ "desc": "Ange användarnamnet för kontot du vill flytta.",
11071107+ "descResume": "Autentisera dig igen till din käll-PDS för att fortsätta flytten.",
10841108 "handle": "Användarnamn",
10851085- "handlePlaceholder": "du.bsky.social",
10861086- "password": "Lösenord",
10871087- "twoFactorCode": "Tvåfaktorkod",
10881088- "twoFactorRequired": "Tvåfaktorautentisering krävs",
10891089- "signIn": "Logga in och fortsätt"
11091109+ "handlePlaceholder": "alice.bsky.social",
11101110+ "handleHint": "Ditt nuvarande användarnamn på din befintliga PDS",
11111111+ "continue": "Fortsätt",
11121112+ "connecting": "Ansluter...",
11131113+ "reauthenticate": "Autentisera igen",
11141114+ "resumeTitle": "Flytt pågår",
11151115+ "resumeFrom": "Från",
11161116+ "resumeTo": "Till",
11171117+ "resumeProgress": "Framsteg",
11181118+ "resumeOAuthNote": "Du måste autentisera dig igen via OAuth för att fortsätta."
10901119 },
10911120 "chooseHandle": {
10921121 "title": "Välj ditt nya användarnamn",
10931122 "desc": "Välj ett användarnamn för ditt konto på denna PDS.",
10941094- "handleHint": "Ditt fullständiga användarnamn blir: @{handle}"
11231123+ "migratingFrom": "Flyttar från",
11241124+ "newHandle": "Nytt användarnamn",
11251125+ "checkingAvailability": "Kontrollerar tillgänglighet...",
11261126+ "handleAvailable": "Användarnamnet är tillgängligt!",
11271127+ "handleTaken": "Användarnamnet är redan taget",
11281128+ "handleHint": "Du kan också använda din egen domän genom att ange det fullständiga användarnamnet (t.ex. alice.mindomän.se)",
11291129+ "email": "E-postadress",
11301130+ "authMethod": "Autentiseringsmetod",
11311131+ "authPassword": "Lösenord",
11321132+ "authPasswordDesc": "Traditionell lösenordsbaserad inloggning",
11331133+ "authPasskey": "Passkey",
11341134+ "authPasskeyDesc": "Lösenordslös inloggning med biometri eller säkerhetsnyckel",
11351135+ "password": "Lösenord",
11361136+ "passwordHint": "Minst 8 tecken",
11371137+ "passkeyInfo": "Du kommer att konfigurera en passkey efter att ditt konto skapats. Din enhet kommer att uppmana dig att använda biometri (fingeravtryck, Face ID) eller en säkerhetsnyckel.",
11381138+ "inviteCode": "Inbjudningskod"
10951139 },
10961140 "review": {
10971141 "title": "Granska flytt",
10981098- "desc": "Granska och bekräfta dina flyttdetaljer.",
11421142+ "desc": "Bekräfta detaljerna för din flytt.",
10991143 "currentHandle": "Nuvarande användarnamn",
11001144 "newHandle": "Nytt användarnamn",
11011101- "sourcePds": "Käll-PDS",
11021102- "targetPds": "Denna PDS",
11451145+ "did": "DID",
11461146+ "sourcePds": "Från PDS",
11471147+ "targetPds": "Till PDS",
11031148 "email": "E-post",
11491149+ "authentication": "Autentisering",
11501150+ "authPasskey": "Passkey (lösenordslös)",
11511151+ "authPassword": "Lösenord",
11041152 "inviteCode": "Inbjudningskod",
11051105- "confirm": "Jag bekräftar att jag vill flytta mitt konto",
11061106- "startMigration": "Starta flytt"
11531153+ "warning": "När du klickar på \"Starta flytt\" börjar ditt arkiv och data överföras. Denna process kan inte enkelt ångras.",
11541154+ "startMigration": "Starta flytt",
11551155+ "starting": "Startar..."
11071156 },
11081157 "migrating": {
11091109- "title": "Flyttar ditt konto",
11101110- "desc": "Vänta medan vi överför din data...",
11111111- "gettingServiceAuth": "Hämtar tjänstauktorisering...",
11121112- "creatingAccount": "Skapar konto på ny PDS...",
11131113- "exportingRepo": "Exporterar arkiv...",
11141114- "importingRepo": "Importerar arkiv...",
11151115- "countingBlobs": "Räknar blobbar...",
11161116- "migratingBlobs": "Flyttar blobbar ({current}/{total})...",
11171117- "migratingPrefs": "Flyttar inställningar...",
11181118- "requestingPlc": "Begär PLC-operation..."
11581158+ "title": "Flytt pågår",
11591159+ "desc": "Vänta medan ditt konto överförs...",
11601160+ "exportRepo": "Exportera arkiv",
11611161+ "importRepo": "Importera arkiv",
11621162+ "migrateBlobs": "Flytta blobbar",
11631163+ "migratePrefs": "Flytta inställningar"
11641164+ },
11651165+ "passkeySetup": {
11661166+ "title": "Konfigurera din passkey",
11671167+ "desc": "Din e-post har verifierats. Konfigurera nu din passkey för säker, lösenordslös inloggning.",
11681168+ "nameLabel": "Passkey-namn (valfritt)",
11691169+ "namePlaceholder": "t.ex. MacBook Pro, iPhone",
11701170+ "nameHint": "Ett vänligt namn för att identifiera denna passkey",
11711171+ "instructions": "Klicka på knappen nedan för att registrera din passkey. Din enhet kommer att uppmana dig att använda biometri (fingeravtryck, Face ID) eller en säkerhetsnyckel.",
11721172+ "register": "Registrera passkey",
11731173+ "registering": "Registrerar..."
11741174+ },
11751175+ "appPassword": {
11761176+ "title": "Spara ditt applösenord",
11771177+ "desc": "Din passkey har skapats. Ett applösenord har genererats för dig att använda med appar som inte stödjer passkeys ännu.",
11781178+ "warning": "Detta applösenord krävs för att logga in i appar som inte stödjer passkeys ännu (som bsky.app). Du kommer bara att se detta lösenord en gång.",
11791179+ "label": "Applösenord för",
11801180+ "saved": "Jag har sparat mitt applösenord på en säker plats",
11811181+ "continue": "Fortsätt"
11191182 },
11201183 "emailVerify": {
11211184 "title": "Verifiera din e-post",
···11281191 "verifying": "Verifierar..."
11291192 },
11301193 "plcToken": {
11311131- "title": "Verifiera din identitet",
11321132- "desc": "En verifieringskod har skickats till din e-post på din nuvarande PDS.",
11331133- "tokenLabel": "Verifieringstoken",
11341134- "tokenPlaceholder": "Ange token från din e-post",
11351135- "resend": "Skicka igen",
11361136- "resending": "Skickar..."
11941194+ "title": "Verifiera flytt",
11951195+ "desc": "En verifieringskod har skickats till e-posten registrerad på ditt gamla konto.",
11961196+ "info": "Denna kod bekräftar att du har tillgång till kontot och auktoriserar uppdatering av din identitet för att peka på denna PDS.",
11971197+ "tokenLabel": "Verifieringskod",
11981198+ "tokenPlaceholder": "Ange kod från e-post",
11991199+ "resend": "Skicka kod igen",
12001200+ "complete": "Slutför flytt",
12011201+ "completing": "Verifierar..."
11371202 },
11381203 "didWebUpdate": {
11391204 "title": "Uppdatera ditt DID-dokument",
···11561221 "success": {
11571222 "title": "Flytt klar!",
11581223 "desc": "Ditt konto har framgångsrikt flyttats till denna PDS.",
11591159- "newHandle": "Nytt användarnamn",
12241224+ "yourNewHandle": "Ditt nya användarnamn",
11601225 "did": "DID",
11611161- "goToDashboard": "Gå till instrumentpanel"
12261226+ "blobsWarning": "{count} blobbar kunde inte flyttas. Dessa kan vara bilder eller annan media som inte längre är tillgängliga.",
12271227+ "redirecting": "Omdirigerar till instrumentpanel..."
12281228+ },
12291229+ "error": {
12301230+ "title": "Flyttfel",
12311231+ "desc": "Ett fel uppstod under flytten.",
12321232+ "startOver": "Börja om"
12331233+ },
12341234+ "common": {
12351235+ "back": "Tillbaka",
12361236+ "cancel": "Avbryt",
12371237+ "continue": "Fortsätt",
12381238+ "whatWillHappen": "Vad som kommer att hända:",
12391239+ "step1": "Logga in på din nuvarande PDS",
12401240+ "step2": "Välj ditt nya användarnamn på denna server",
12411241+ "step3": "Ditt arkiv och blobbar kommer att överföras",
12421242+ "step4": "Verifiera flytten via e-post",
12431243+ "step5": "Din identitet kommer att uppdateras för att peka hit",
12441244+ "beforeProceed": "Innan du fortsätter:",
12451245+ "warning1": "Du behöver tillgång till e-posten registrerad på ditt nuvarande konto",
12461246+ "warning2": "Stora konton kan ta flera minuter att överföra",
12471247+ "warning3": "Ditt gamla konto kommer att inaktiveras efter flytten"
11621248 }
11631249 },
11641250 "outbound": {
···11use cid::Cid;
22+use ipld_core::ipld::Ipld;
23use jacquard_repo::commit::Commit;
34use jacquard_repo::storage::BlockStore;
44-use ipld_core::ipld::Ipld;
55use sqlx::PgPool;
66use std::str::FromStr;
77use std::sync::Arc;
···107107 }
108108 }
109109110110- info!(success, failed, "Completed genesis commit blocks_cids backfill");
110110+ info!(
111111+ success,
112112+ failed, "Completed genesis commit blocks_cids backfill"
113113+ );
111114}
112115113116pub async fn backfill_repo_rev(db: &PgPool, block_store: PostgresBlockStore) {
114114- let repos_missing_rev = match sqlx::query!(
115115- "SELECT user_id, repo_root_cid FROM repos WHERE repo_rev IS NULL"
116116- )
117117- .fetch_all(db)
118118- .await
119119- {
120120- Ok(rows) => rows,
121121- Err(e) => {
122122- error!("Failed to query repos for backfill: {}", e);
123123- return;
124124- }
125125- };
117117+ let repos_missing_rev =
118118+ match sqlx::query!("SELECT user_id, repo_root_cid FROM repos WHERE repo_rev IS NULL")
119119+ .fetch_all(db)
120120+ .await
121121+ {
122122+ Ok(rows) => rows,
123123+ Err(e) => {
124124+ error!("Failed to query repos for backfill: {}", e);
125125+ return;
126126+ }
127127+ };
126128127129 if repos_missing_rev.is_empty() {
128130 debug!("No repos need repo_rev backfill");
···244246 if let Some(prev) = commit.prev {
245247 to_visit.push(prev);
246248 }
247247- } else if let Ok(ipld) = serde_ipld_dagcbor::from_slice::<Ipld>(&block) {
248248- if let Ipld::Map(ref obj) = ipld {
249249- if let Some(Ipld::Link(left_cid)) = obj.get("l") {
250250- to_visit.push(*left_cid);
251251- }
252252- if let Some(Ipld::List(entries)) = obj.get("e") {
253253- for entry in entries {
254254- if let Ipld::Map(entry_obj) = entry {
255255- if let Some(Ipld::Link(tree_cid)) = entry_obj.get("t") {
256256- to_visit.push(*tree_cid);
257257- }
258258- if let Some(Ipld::Link(val_cid)) = entry_obj.get("v") {
259259- to_visit.push(*val_cid);
260260- }
249249+ } else if let Ok(Ipld::Map(ref obj)) = serde_ipld_dagcbor::from_slice::<Ipld>(&block) {
250250+ if let Some(Ipld::Link(left_cid)) = obj.get("l") {
251251+ to_visit.push(*left_cid);
252252+ }
253253+ if let Some(Ipld::List(entries)) = obj.get("e") {
254254+ for entry in entries {
255255+ if let Ipld::Map(entry_obj) = entry {
256256+ if let Some(Ipld::Link(tree_cid)) = entry_obj.get("t") {
257257+ to_visit.push(*tree_cid);
258258+ }
259259+ if let Some(Ipld::Link(val_cid)) = entry_obj.get("v") {
260260+ to_visit.push(*val_cid);
261261 }
262262 }
263263 }
···361361362362 let blob_refs = crate::sync::import::find_blob_refs_ipld(&record_ipld, 0);
363363 for blob_ref in blob_refs {
364364- let record_uri = format!(
365365- "at://{}/{}/{}",
366366- user.did, record.collection, record.rkey
367367- );
364364+ let record_uri = format!("at://{}/{}/{}", user.did, record.collection, record.rkey);
368365 if let Err(e) = sqlx::query!(
369366 r#"
370367 INSERT INTO record_blobs (repo_id, record_uri, blob_cid)
···490487 did: &str,
491488 _handle: &str,
492489) -> Result<(), String> {
493493- let user_id: uuid::Uuid = sqlx::query_scalar!(
494494- "SELECT id FROM users WHERE did = $1",
495495- did
496496- )
497497- .fetch_one(db)
498498- .await
499499- .map_err(|e| format!("DB error fetching user: {}", e))?;
490490+ let user_id: uuid::Uuid = sqlx::query_scalar!("SELECT id FROM users WHERE did = $1", did)
491491+ .fetch_one(db)
492492+ .await
493493+ .map_err(|e| format!("DB error fetching user: {}", e))?;
500494501495 let blob_storage_keys: Vec<String> = sqlx::query_scalar!(
502496 r#"SELECT storage_key as "storage_key!" FROM blobs WHERE created_by_user = $1"#,
+101-28
tests/account_lifecycle.rs
···1111 let (access_jwt, did) = create_account_and_login(&client).await;
12121313 let status1 = client
1414- .get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
1414+ .get(format!(
1515+ "{}/xrpc/com.atproto.server.checkAccountStatus",
1616+ base
1717+ ))
1518 .bearer_auth(&access_jwt)
1619 .send()
1720 .await
···1922 assert_eq!(status1.status(), StatusCode::OK);
2023 let body1: Value = status1.json().await.unwrap();
2124 let initial_blocks = body1["repoBlocks"].as_i64().unwrap();
2222- assert!(initial_blocks >= 2, "New account should have at least 2 blocks (commit + empty MST)");
2525+ assert!(
2626+ initial_blocks >= 2,
2727+ "New account should have at least 2 blocks (commit + empty MST)"
2828+ );
23292430 let create_res = client
2531 .post(format!("{}/xrpc/com.atproto.repo.createRecord", base))
···3844 .unwrap();
3945 assert_eq!(create_res.status(), StatusCode::OK);
4046 let create_body: Value = create_res.json().await.unwrap();
4141- let rkey = create_body["uri"].as_str().unwrap().split('/').last().unwrap().to_string();
4747+ let rkey = create_body["uri"]
4848+ .as_str()
4949+ .unwrap()
5050+ .split('/')
5151+ .last()
5252+ .unwrap()
5353+ .to_string();
42544355 let status2 = client
4444- .get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
5656+ .get(format!(
5757+ "{}/xrpc/com.atproto.server.checkAccountStatus",
5858+ base
5959+ ))
4560 .bearer_auth(&access_jwt)
4661 .send()
4762 .await
4863 .unwrap();
4964 let body2: Value = status2.json().await.unwrap();
5065 let after_create_blocks = body2["repoBlocks"].as_i64().unwrap();
5151- assert!(after_create_blocks > initial_blocks, "Block count should increase after creating a record");
6666+ assert!(
6767+ after_create_blocks > initial_blocks,
6868+ "Block count should increase after creating a record"
6969+ );
52705371 let delete_res = client
5472 .post(format!("{}/xrpc/com.atproto.repo.deleteRecord", base))
···6482 assert_eq!(delete_res.status(), StatusCode::OK);
65836684 let status3 = client
6767- .get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
8585+ .get(format!(
8686+ "{}/xrpc/com.atproto.server.checkAccountStatus",
8787+ base
8888+ ))
6889 .bearer_auth(&access_jwt)
6990 .send()
7091 .await
···86107 let (access_jwt, _) = create_account_and_login(&client).await;
8710888109 let status = client
8989- .get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
110110+ .get(format!(
111111+ "{}/xrpc/com.atproto.server.checkAccountStatus",
112112+ base
113113+ ))
90114 .bearer_auth(&access_jwt)
91115 .send()
92116 .await
···9612097121 let repo_rev = body["repoRev"].as_str().unwrap();
98122 assert!(!repo_rev.is_empty(), "repoRev should not be empty");
9999- assert!(repo_rev.chars().all(|c| c.is_alphanumeric()), "repoRev should be alphanumeric TID");
123123+ assert!(
124124+ repo_rev.chars().all(|c| c.is_alphanumeric()),
125125+ "repoRev should be alphanumeric TID"
126126+ );
100127}
101128102129#[tokio::test]
···106133 let (access_jwt, _) = create_account_and_login(&client).await;
107134108135 let status = client
109109- .get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
136136+ .get(format!(
137137+ "{}/xrpc/com.atproto.server.checkAccountStatus",
138138+ base
139139+ ))
110140 .bearer_auth(&access_jwt)
111141 .send()
112142 .await
···114144 assert_eq!(status.status(), StatusCode::OK);
115145 let body: Value = status.json().await.unwrap();
116146117117- assert_eq!(body["validDid"], true, "validDid should be true for active account with correct DID document");
118118- assert_eq!(body["activated"], true, "activated should be true for active account");
147147+ assert_eq!(
148148+ body["validDid"], true,
149149+ "validDid should be true for active account with correct DID document"
150150+ );
151151+ assert_eq!(
152152+ body["activated"], true,
153153+ "activated should be true for active account"
154154+ );
119155}
120156121157#[tokio::test]
···128164 let delete_after = future_time.to_rfc3339();
129165130166 let deactivate = client
131131- .post(format!("{}/xrpc/com.atproto.server.deactivateAccount", base))
167167+ .post(format!(
168168+ "{}/xrpc/com.atproto.server.deactivateAccount",
169169+ base
170170+ ))
132171 .bearer_auth(&access_jwt)
133172 .json(&json!({
134173 "deleteAfter": delete_after
···139178 assert_eq!(deactivate.status(), StatusCode::OK);
140179141180 let status = client
142142- .get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
181181+ .get(format!(
182182+ "{}/xrpc/com.atproto.server.checkAccountStatus",
183183+ base
184184+ ))
143185 .bearer_auth(&access_jwt)
144186 .send()
145187 .await
···170212 assert_eq!(create_res.status(), StatusCode::OK);
171213 let body: Value = create_res.json().await.unwrap();
172214173173- assert!(body["accessJwt"].is_string(), "accessJwt should always be returned");
174174- assert!(body["refreshJwt"].is_string(), "refreshJwt should always be returned");
215215+ assert!(
216216+ body["accessJwt"].is_string(),
217217+ "accessJwt should always be returned"
218218+ );
219219+ assert!(
220220+ body["refreshJwt"].is_string(),
221221+ "refreshJwt should always be returned"
222222+ );
175223 assert!(body["did"].is_string(), "did should be returned");
176224177225 if body["didDoc"].is_object() {
···201249 assert_eq!(create_res.status(), StatusCode::OK);
202250 let body: Value = create_res.json().await.unwrap();
203251204204- let access_jwt = body["accessJwt"].as_str().expect("accessJwt should be present");
205205- let refresh_jwt = body["refreshJwt"].as_str().expect("refreshJwt should be present");
252252+ let access_jwt = body["accessJwt"]
253253+ .as_str()
254254+ .expect("accessJwt should be present");
255255+ let refresh_jwt = body["refreshJwt"]
256256+ .as_str()
257257+ .expect("refreshJwt should be present");
206258207259 assert!(!access_jwt.is_empty(), "accessJwt should not be empty");
208260 assert!(!refresh_jwt.is_empty(), "refreshJwt should not be empty");
209261210262 let parts: Vec<&str> = access_jwt.split('.').collect();
211211- assert_eq!(parts.len(), 3, "accessJwt should be a valid JWT with 3 parts");
263263+ assert_eq!(
264264+ parts.len(),
265265+ 3,
266266+ "accessJwt should be a valid JWT with 3 parts"
267267+ );
212268}
213269214270#[tokio::test]
···224280 assert_eq!(describe.status(), StatusCode::OK);
225281 let body: Value = describe.json().await.unwrap();
226282227227- assert!(body.get("links").is_some(), "describeServer should include links object");
228228- assert!(body.get("contact").is_some(), "describeServer should include contact object");
283283+ assert!(
284284+ body.get("links").is_some(),
285285+ "describeServer should include links object"
286286+ );
287287+ assert!(
288288+ body.get("contact").is_some(),
289289+ "describeServer should include contact object"
290290+ );
229291230292 let links = &body["links"];
231231- assert!(links.get("privacyPolicy").is_some() || links["privacyPolicy"].is_null(),
232232- "links should have privacyPolicy field (can be null)");
233233- assert!(links.get("termsOfService").is_some() || links["termsOfService"].is_null(),
234234- "links should have termsOfService field (can be null)");
293293+ assert!(
294294+ links.get("privacyPolicy").is_some() || links["privacyPolicy"].is_null(),
295295+ "links should have privacyPolicy field (can be null)"
296296+ );
297297+ assert!(
298298+ links.get("termsOfService").is_some() || links["termsOfService"].is_null(),
299299+ "links should have termsOfService field (can be null)"
300300+ );
235301236302 let contact = &body["contact"];
237237- assert!(contact.get("email").is_some() || contact["email"].is_null(),
238238- "contact should have email field (can be null)");
303303+ assert!(
304304+ contact.get("email").is_some() || contact["email"].is_null(),
305305+ "contact should have email field (can be null)"
306306+ );
239307}
240308241309#[tokio::test]
···274342275343 assert_eq!(delete_res.status(), StatusCode::BAD_REQUEST);
276344 let error_body: Value = delete_res.json().await.unwrap();
277277- assert!(error_body["message"].as_str().unwrap().contains("password length")
278278- || error_body["error"].as_str().unwrap() == "InvalidRequest");
345345+ assert!(
346346+ error_body["message"]
347347+ .as_str()
348348+ .unwrap()
349349+ .contains("password length")
350350+ || error_body["error"].as_str().unwrap() == "InvalidRequest"
351351+ );
279352}
···174174 let body: Value = get_resp.json().await.unwrap();
175175 let prefs_arr = body["preferences"].as_array().unwrap();
176176 assert_eq!(prefs_arr.len(), 1);
177177- assert_eq!(prefs_arr[0]["$type"], "app.bsky.actor.defs#adultContentPref");
177177+ assert_eq!(
178178+ prefs_arr[0]["$type"],
179179+ "app.bsky.actor.defs#adultContentPref"
180180+ );
178181}
179182180183#[tokio::test]
···393396 let client = client();
394397 let base = base_url().await;
395398 let (token, _did) = create_account_and_login(&client).await;
396396- let current_year = chrono::Utc::now().format("%Y").to_string().parse::<i32>().unwrap();
399399+ let current_year = chrono::Utc::now()
400400+ .format("%Y")
401401+ .to_string()
402402+ .parse::<i32>()
403403+ .unwrap();
397404 let birth_year = current_year - 15;
398405 let prefs = json!({
399406 "preferences": [
+4-1
tests/admin_invite.rs
···217217 .expect("Failed to get invite codes");
218218 let list_body: Value = list_res.json().await.unwrap();
219219 let codes = list_body["codes"].as_array().unwrap();
220220- let admin_codes: Vec<_> = codes.iter().filter(|c| c["forAccount"].as_str() == Some(&did)).collect();
220220+ let admin_codes: Vec<_> = codes
221221+ .iter()
222222+ .filter(|c| c["forAccount"].as_str() == Some(&did))
223223+ .collect();
221224 for code in admin_codes {
222225 assert_eq!(code["disabled"], true);
223226 }
+20-5
tests/did_web.rs
···569569 let jwt = verify_new_account(&client, &did).await;
570570 let target_pds = "https://pds2.example.com";
571571 let res = client
572572- .post(format!("{}/xrpc/com.atproto.server.deactivateAccount", base))
572572+ .post(format!(
573573+ "{}/xrpc/com.atproto.server.deactivateAccount",
574574+ base
575575+ ))
573576 .bearer_auth(&jwt)
574577 .json(&json!({ "migratingTo": target_pds }))
575578 .send()
···633636 .expect("Failed to send request");
634637 assert_eq!(res.status(), StatusCode::OK);
635638 let res = client
636636- .post(format!("{}/xrpc/com.atproto.server.deactivateAccount", base))
639639+ .post(format!(
640640+ "{}/xrpc/com.atproto.server.deactivateAccount",
641641+ base
642642+ ))
637643 .bearer_auth(&jwt)
638644 .json(&json!({ "migratingTo": "https://pds2.example.com" }))
639645 .send()
···770776 );
771777 let target_pds = "https://pds3.example.com";
772778 let res = client
773773- .post(format!("{}/xrpc/com.atproto.server.deactivateAccount", base))
779779+ .post(format!(
780780+ "{}/xrpc/com.atproto.server.deactivateAccount",
781781+ base
782782+ ))
774783 .bearer_auth(&jwt)
775784 .json(&json!({ "migratingTo": target_pds }))
776785 .send()
···785794 .expect("Failed to send request");
786795 assert_eq!(res.status(), StatusCode::OK);
787796 let body: Value = res.json().await.expect("Response was not JSON");
788788- assert_eq!(body["active"], false, "Migrated account should not be active");
797797+ assert_eq!(
798798+ body["active"], false,
799799+ "Migrated account should not be active"
800800+ );
789801 assert_eq!(
790802 body["status"], "migrated",
791803 "Status should be 'migrated' after migration"
···819831 assert!(did.starts_with("did:plc:"), "Should be did:plc account");
820832 let jwt = verify_new_account(&client, &did).await;
821833 let res = client
822822- .post(format!("{}/xrpc/com.atproto.server.deactivateAccount", base))
834834+ .post(format!(
835835+ "{}/xrpc/com.atproto.server.deactivateAccount",
836836+ base
837837+ ))
823838 .bearer_auth(&jwt)
824839 .json(&json!({ "migratingTo": "https://pds2.example.com" }))
825840 .send()
+32-19
tests/email_update.rs
···112112 .expect("Failed to update email");
113113 assert_eq!(res.status(), StatusCode::OK);
114114115115- let user_email: Option<String> = sqlx::query_scalar!("SELECT email FROM users WHERE did = $1", did)
116116- .fetch_one(pool)
117117- .await
118118- .expect("User not found");
115115+ let user_email: Option<String> =
116116+ sqlx::query_scalar!("SELECT email FROM users WHERE did = $1", did)
117117+ .fetch_one(pool)
118118+ .await
119119+ .expect("User not found");
119120 assert_eq!(user_email, Some(new_email));
120121}
121122···255256 assert_eq!(res.status(), StatusCode::OK);
256257 let body: Value = res.json().await.expect("Invalid JSON");
257258 let did = body["did"].as_str().expect("No did").to_string();
258258- let access_jwt = body["accessJwt"].as_str().expect("No accessJwt").to_string();
259259+ let access_jwt = body["accessJwt"]
260260+ .as_str()
261261+ .expect("No accessJwt")
262262+ .to_string();
259263260264 let body_text: String = sqlx::query_scalar!(
261265 "SELECT body FROM comms_queue WHERE user_id = (SELECT id FROM users WHERE did = $1) AND comms_type = 'email_verification' ORDER BY created_at DESC LIMIT 1",
···283287 .expect("Failed to confirm email");
284288 assert_eq!(res.status(), StatusCode::OK);
285289286286- let verified: bool = sqlx::query_scalar!(
287287- "SELECT email_verified FROM users WHERE did = $1",
288288- did
289289- )
290290- .fetch_one(pool)
291291- .await
292292- .expect("User not found");
290290+ let verified: bool =
291291+ sqlx::query_scalar!("SELECT email_verified FROM users WHERE did = $1", did)
292292+ .fetch_one(pool)
293293+ .await
294294+ .expect("User not found");
293295 assert!(verified);
294296}
295297···317319 assert_eq!(res.status(), StatusCode::OK);
318320 let body: Value = res.json().await.expect("Invalid JSON");
319321 let did = body["did"].as_str().expect("No did").to_string();
320320- let access_jwt = body["accessJwt"].as_str().expect("No accessJwt").to_string();
322322+ let access_jwt = body["accessJwt"]
323323+ .as_str()
324324+ .expect("No accessJwt")
325325+ .to_string();
321326322327 let body_text: String = sqlx::query_scalar!(
323328 "SELECT body FROM comms_queue WHERE user_id = (SELECT id FROM users WHERE did = $1) AND comms_type = 'email_verification' ORDER BY created_at DESC LIMIT 1",
···370375 .expect("Failed to create account");
371376 assert_eq!(res.status(), StatusCode::OK);
372377 let body: Value = res.json().await.expect("Invalid JSON");
373373- let access_jwt = body["accessJwt"].as_str().expect("No accessJwt").to_string();
378378+ let access_jwt = body["accessJwt"]
379379+ .as_str()
380380+ .expect("No accessJwt")
381381+ .to_string();
374382375383 let res = client
376384 .post(format!("{}/xrpc/com.atproto.server.confirmEmail", base_url))
···411419 assert_eq!(res.status(), StatusCode::OK);
412420 let body: Value = res.json().await.expect("Invalid JSON");
413421 let did = body["did"].as_str().expect("No did").to_string();
414414- let access_jwt = body["accessJwt"].as_str().expect("No accessJwt").to_string();
422422+ let access_jwt = body["accessJwt"]
423423+ .as_str()
424424+ .expect("No accessJwt")
425425+ .to_string();
415426416427 let res = client
417428 .post(format!(
···491502 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
492503 let body: Value = res.json().await.expect("Invalid JSON");
493504 assert_eq!(body["error"], "InvalidRequest");
494494- assert!(body["message"]
495495- .as_str()
496496- .unwrap_or("")
497497- .contains("already in use"));
505505+ assert!(
506506+ body["message"]
507507+ .as_str()
508508+ .unwrap_or("")
509509+ .contains("already in use")
510510+ );
498511}
+4-1
tests/identity.rs
···393393 .await
394394 .expect("Failed to get session");
395395 let session_body: Value = session.json().await.expect("Invalid JSON");
396396- let current_handle = session_body["handle"].as_str().expect("No handle").to_string();
396396+ let current_handle = session_body["handle"]
397397+ .as_str()
398398+ .expect("No handle")
399399+ .to_string();
397400 let short_handle = current_handle.split('.').next().unwrap_or(¤t_handle);
398401 let res = client
399402 .post(format!(
+13-3
tests/invite.rs
···2525 assert!(body["code"].is_string());
2626 let code = body["code"].as_str().unwrap();
2727 assert!(!code.is_empty());
2828- assert!(code.contains('-'), "Code should be in hostname-xxxxx-xxxxx format");
2828+ assert!(
2929+ code.contains('-'),
3030+ "Code should be in hostname-xxxxx-xxxxx format"
3131+ );
2932 let parts: Vec<&str> = code.split('-').collect();
3030- assert!(parts.len() >= 3, "Code should have at least 3 parts (hostname + 2 random parts)");
3333+ assert!(
3434+ parts.len() >= 3,
3535+ "Code should have at least 3 parts (hostname + 2 random parts)"
3636+ );
3137}
32383339#[tokio::test]
···363369 let body: Value = res.json().await.expect("Response was not valid JSON");
364370 let codes = body["codes"].as_array().unwrap();
365371 for c in codes {
366366- assert_ne!(c["code"].as_str().unwrap(), code, "Disabled code should be filtered out");
372372+ assert_ne!(
373373+ c["code"].as_str().unwrap(),
374374+ code,
375375+ "Disabled code should be filtered out"
376376+ );
367377 }
368378}
+20-5
tests/lifecycle_session.rs
···291291 let base = base_url().await;
292292 let (jwt, _did) = create_account_and_login(&client).await;
293293 let create_res = client
294294- .post(format!("{}/xrpc/com.atproto.server.createAppPassword", base))
294294+ .post(format!(
295295+ "{}/xrpc/com.atproto.server.createAppPassword",
296296+ base
297297+ ))
295298 .bearer_auth(&jwt)
296299 .json(&json!({ "name": "My App" }))
297300 .send()
···299302 .expect("Failed to create app password");
300303 assert_eq!(create_res.status(), StatusCode::OK);
301304 let duplicate_res = client
302302- .post(format!("{}/xrpc/com.atproto.server.createAppPassword", base))
305305+ .post(format!(
306306+ "{}/xrpc/com.atproto.server.createAppPassword",
307307+ base
308308+ ))
303309 .bearer_auth(&jwt)
304310 .json(&json!({ "name": "My App" }))
305311 .send()
···320326 let base = base_url().await;
321327 let (jwt, _did) = create_account_and_login(&client).await;
322328 let revoke_res = client
323323- .post(format!("{}/xrpc/com.atproto.server.revokeAppPassword", base))
329329+ .post(format!(
330330+ "{}/xrpc/com.atproto.server.revokeAppPassword",
331331+ base
332332+ ))
324333 .bearer_auth(&jwt)
325334 .json(&json!({ "name": "Does Not Exist" }))
326335 .send()
···356365 let did = account["did"].as_str().unwrap();
357366 let main_jwt = verify_new_account(&client, did).await;
358367 let create_app_res = client
359359- .post(format!("{}/xrpc/com.atproto.server.createAppPassword", base))
368368+ .post(format!(
369369+ "{}/xrpc/com.atproto.server.createAppPassword",
370370+ base
371371+ ))
360372 .bearer_auth(&main_jwt)
361373 .json(&json!({ "name": "Session Test App" }))
362374 .send()
···389401 "App password session should be valid before revocation"
390402 );
391403 let revoke_res = client
392392- .post(format!("{}/xrpc/com.atproto.server.revokeAppPassword", base))
404404+ .post(format!(
405405+ "{}/xrpc/com.atproto.server.revokeAppPassword",
406406+ base
407407+ ))
393408 .bearer_auth(&main_jwt)
394409 .json(&json!({ "name": "Session Test App" }))
395410 .send()