Mobiletto er et JavaScript-lagringsabstraksjonslag, med valgfri transparent kryptering på klientsiden.
- Hvorfor Mobiletto?
- Hurtigstart
- Mobiletto CLI
- Kilde
- Installasjon
- Support and Funding
- [Grunnleggende bruk](#Grunnleggende bruk)
- Metadata
- Alternativ importstil
- Caching
- Speiling
- Transparent kryptering
- Nøkkelrotasjon
- Drivergrensesnitt
- Logging
Dette README.md-dokumentet har blitt oversatt, via hokeylization, til alle språk som støttes av Google Translate!
Jeg er sikker på at det ikke er perfekt, men jeg håper det er bedre enn ingenting!
🇸🇦 arabisk 🇧🇩 Bengali 🇩🇪 tysk 🇺🇸 engelsk 🇪🇸 spansk 🇫🇷 fransk 🇹🇩 Hausa 🇮🇳 hindi 🇮🇩 indonesisk 🇮🇹 italiensk 🇯🇵 japansk 🇰🇷 koreansk 🇮🇳 Marathi 🇵🇱 polsk 🇧🇷 portugisisk 🇷🇺 russisk 🇰🇪 Swahili 🇵🇭 Tagalog 🇹🇷 tyrkisk 🇵🇰 Urdu 🇻🇳 vietnamesisk 🇨🇳 kinesisk
Denne spesielle oversettelsen av originalen README kan være feil -- rettelser er svært velkomne! Send en pull request på GitHub, eller hvis du ikke er komfortabel med å gjøre det, åpne et problem
Når du oppretter et nytt GitHub-problem om en oversettelse, vennligst gjør:
- Ta med sidens URL (kopier/lim inn fra nettleserens adresselinje)
- Ta med den eksakte teksten som er feil (kopier/lim inn fra nettleseren)
- beskriv hva som er galt -- er oversettelsen feil? er formateringen ødelagt på en eller annen måte?
- kom gjerne med et forslag til en bedre oversettelse, eller hvordan teksten bør formateres riktig
- Takk skal du ha!
De ulike skylagringsleverandørene har inkompatible APIer. Selv de som streber etter "S3-kompatibilitet" har idiosynkratisk oppførsel.
Når du velger en bestemt lagringsleverandør for appen din, hvis du koder direkte til deres API, appen din er nå avhengig av den tjenesten. Etter hvert som tiden går og koden akkumuleres, blir det skiftende leverandører stadig mer uholdbar. Velkommen til den morsomme verdenen med leverandørlåsing!
Mobiletto ble designet for å løse dette problemet. Ved å kode appen din til mobilettos API kan du enkelt endre lagringsleverandør og vite at appens lagringslag vil oppføre seg identisk.
Alle sjåfører testes for identisk oppførsel med 60+ tester for hver sjåfør. Vi tester alle sjåfører med hver kombinasjon av:
- Kryptering: både aktivert og deaktivert
- Redis cache: både aktivert og deaktivert
Denne tilnærmingen gir oss trygghet om at mobiletto vil oppføre seg likt uavhengig av hvilken driver du bruker, og uavhengig av om du aktiverer caching og/eller kryptering.
Gjeldende Mobiletto-lagringsdrivere:
s3
: Amazon S3b2
: Backblaze B2local
: lokalt filsystem
Bidrag for å støtte flere leverandører av nettskylagring er svært velkomne!
Mobiletto er ment å brukes som et bibliotek av annen JavaScript-kode.
For å jobbe med mobiletto på kommandolinjen, bruk mobiletto-cli
Jeg prøver å være en profesjonell programvareutvikler med åpen kildekode. Jeg har jobbet i programvareindustrien i mange år, har jeg startet suksessfulle selskaper og solgt dem til offentlige selskaper. Nylig mistet jeg jobben min, og jeg har egentlig ikke noe annet arbeid på rad
Så jeg skal prøve å skrive nyttig programvare og se om det fungerer
Hvis du liker å bruke denne programvaren, vil jeg være veldig takknemlig for selv minste månedlig bidrag via Patreon
Takk skal du ha!
Installer med npm
eller yarn
. Du vil sannsynligvis ha lite
versjonen som ikke inkluderer alle
oversatte README-filer:
npm install mobiletto-lite
yarn add mobiletto-lite
Hvis du virkelig vil ha README-filene på alle språk, installer fullversjonen:
npm install mobiletto
yarn add mobiletto
Et kort eksempel ved bruk av mobiletto s3
driveren.
Denne koden ville kjøre det samme hvis driveren var b2
eller local
.
const storage = require('mobiletto')
const bucket = await storage.connect('s3', aws_key, aws_secret, {bucket: 'bk'})
// list objects: returns array of metadata objects
const listing = await bucket.list()
const dirList = await bucket.list('some/dir/')
const everything = await bucket.list('', {recursive: true})
// write an entire file
let bytesWritten = await bucket.writeFile('some/path', someBufferOfData)
// write a file from a stream/generator
bytesWritten = await bucket.write('some/path', streamOrGenerator)
// read an entire file
// returns null if an exception would otherwise be thrown
const bufferOrNull = await bucket.safeReadFile('some/path')
// stream-read a file, passing data to callback
const bytesRead = await bucket.read('some/path', (chunk) => { ...do something with chunk... } )
// remove a file, returns the path removed
let removed = await bucket.remove('some/path') // removed is a string
// remove a directory, returns array of paths removed
removed = await bucket.remove('some/directory', {recursive: true}) // removed is now an array!
Et mye mer omfattende eksempel som viser de fleste funksjonene som tilbys:
const { mobiletto } = require('mobiletto')
// General usage
const api = await mobiletto(driverName, key, secret, opts)
// To use 'local' driver:
// * key: base directory
// * secret: ignored, can be null
// * opts object:
// * readOnly: optional, never change anything on the filesystem; default is false
// * fileMode: optional, permissions used when creating new files, default is 0600. can be string or integer
// * dirMode: optional, permissions used when creating new directories, default is 0700. can be string or integer
const local = await mobiletto('local', '/home/ubuntu/tmp', null, {fileMode: 0o0600, dirMode: '0700'})
// To use 's3' driver:
// * key: AWS Access Key ID
// * secret: AWS Secret Key
// * opts object:
// * readOnly: optional, never change anything on the bucket; default is false
// * bucket: required, name of the S3 bucket
// * region: optional, the AWS region to communicate with, default is us-east-1
// * prefix: optional, all read/writes within the S3 bucket will be under this prefix
// * delimiter: optional, directory delimiter, default is '/' (note: always '/' when encryption is enabled)
const s3 = await mobiletto('s3', aws_key, aws_secret, {bucket: 'bk', region: 'us-east-1'})
// To use 'b2' driver:
// * key: Backblaze Key ID
// * secret: Backblaze Application Key
// * opts object:
// * readOnly: optional, never change anything on the bucket; default is false
// * bucket: required, the ID (**not the name**) of the B2 bucket
// * prefix: optional, all read/writes within the B2 bucket will be under this prefix
// * delimiter: optional, directory delimiter, default is '/' (note: always '/' when encryption is enabled)
// * partSize: optional, large files will be split into chunks of this size when uploading
const b3 = await mobiletto('b2', b2_key_id, b2_app_key, {bucket: 'bk', partSize: 10000000})
// List files
api.list() // --> returns an array of metadata objects
// List files recursively
api.list({ recursive: true })
// List files in a directory
const path = 'some/path'
api.list(path)
api.list(path, { recursive: true }) // also supports recursive flag
// Visit files in a directory -- visitor function must be async
api.list(path, { visitor: myAsyncFunc })
api.list(path, { visitor: myAsyncFunc, recursive: true })
// The `list` method throws MobilettoNotFoundError if the path does not exist
// When you call `safeList` on a non-existent path, it returns an empty array
api.safeList('/path/that/does/not/exist') // returns []
// Read metadata for a file
api.metadata(path) // returns metadata object
// The `metadata` method throws MobilettoNotFoundError if the path does not exist
// When you call `safeMetadata` on a non-existent path, it returns null
api.safeMetadata('/tmp/does_not_exist') // returns null
// Read a file
// Provide a callback that writes the data someplace
const callback = (chunk) => { ... write chunk somewhere ... }
api.read(path, callback) // returns count of bytes read
// Read an entire file at once
const data = await api.readFile(path) // returns a byte Buffer of the file contents
// Read an entire file at once
// returns null if an exception would otherwise be thrown
const bufferOrNull = await bucket.safeReadFile('some/path')
// Write a file
// Provide a generator function that yields chunks of data
const generator = function* () {
while ( ... more-data-to-return ... ) {
data = ... load-data ...
yield data
}
}
local.api(path, generator) // returns count of bytes written
// Write an entire file at once (convenience method)
await api.writeFile(path, bufferOrString) // returns count of bytes written
// Delete a file
// Quiet param is optional (default false), when set errors will not be thrown if the path does not exist
// Always returns a value or throws an error.
// Return value may be a single string of the file removed, or an array of all files removed (driver-dependent)
const quiet = true
api.remove(path, {quiet}) // returns single path removed
// Recursively delete a directory and do it quietly (do not report errors)
const recursive = true
const quiet = true
api.remove(path, {recursive, quiet}) // returns array of paths removed
Kommandoen metadata
returnerer metadata om en enkelt filsystemoppføring.
På samme måte er returverdien fra kommandoen list
en rekke metadataobjekter.
Et metadataobjekt ser slik ut:
{
"name": "fully/qualified/path/to/file",
"type": "entry-type",
"size": size-in-bytes,
"ctime": creation-time-epoch-millis,
"mtime": modification-time-epoch-millis
}
Egenskapen type
kan være file
' , dir
, link
' eller special
.
Avhengig av typen driver kan det hende at en list
kommando ikke returnerer alle feltene. Egenskapene name
og type
.
skal alltid være tilstede. En påfølgende metadata
kommando vil returnere alle tilgjengelige egenskaper.
Importer den fullstendige modulen og bruk connect
-funksjonen:
const storage = require('mobiletto')
const opts = {bucket: 'bk', region: 'us-east-1'}
const s3 = await storage.connect('s3', aws_key, aws_secret, opts)
const objectData = await s3.readFile('some/path')
Mobiletto fungerer best med en redis- cache.
Mobiletto vil forsøke å koble til en redis-forekomst på 127.0.0.1:6379
Du kan overstyre en av disse:
- Angi
MOBILETTO_REDIS_HOST
env var, mobiletfor å koble til her i stedet for localhost - Sett
MOBILETTO_REDIS_PORT
env var, denne porten vil bli brukt
Mobiletto vil lagre alle redis-nøkler med prefikset _mobiletto__
. Du kan endre dette
ved å sette MOBILETTO_REDIS_PREFIX
env var.
Du kan også angi caching per tilkobling med opts.redisConfig
:
const redisConfig = {
enabled: true, // optional, default is true. if false other props are ignored
host: '127.0.0.1',
port: 6379,
prefix: '_mobiletto__'
}
const opts = { redisConfig, bucket: 'bk', region: 'us-east-1' }
const s3 = await storage.connect('s3', aws_key, aws_secret, opts)
For å deaktivere: pass enabled: false
i opts.redisConfig
objektet når du oppretter forbindelsen.
Som diskutert nedenfor, vil deaktivering av caching ha en negativ effekt på ytelsen og medføre flere forespørsler til lagring som du virkelig trenger.
Kryptert lagring: lesing/skriving av kryptert lagring er bare litt tregere enn normalt, men å navigere rundt i kataloger (som noen ting gjør) er ganske dyrt. Bruke en redis cache vil gi deg et betydelig ytelsesløft.
Standardbufferen er trygg, men fungerer ikke bra hvis du har mange skrive-/fjernoperasjoner. Enhver skrive- eller fjernoperasjon ugyldiggjør hele hurtigbufferen, og sikrer at påfølgende lesninger vil se siste endringer.
Hvis du bruker et CLI-verktøy som mobiletto-cli,
du vil definitivt ha redis-cachen aktivert, siden den varer på tvers av påkallinger av mo
kommandoen.
// Copy a local filesystem mobiletto to S3
s3.mirror(local)
// Mirror a local subdirectory from one mobiletto to an S3 mobiletto, with it's own subdirectory
local.mirror(s3, 'some/local-folder', 'some/s3-folder')
mirror
-kommandoen utfører en engangskopi av alle filer fra en mobiletto til en annen.
Den kjører ingen prosess for å vedlikeholde speilet over tid. Kjør mirror
-kommandoen på nytt
for å synkronisere eventuelle manglende filer.
Returverdien fra mirror
er et enkelt objekt med tellere for hvor mange filer som ble vellykket
speilet og hvor mange filer som hadde feil:
{
success: count-of-files-mirrored,
errors: count-of-files-with-errors
}
ADVARSEL: Speiling av store datasett kan være svært tidkrevende og båndbreddekrevende
Med mirror
kan det noen ganger være forvirrende å forstå hvem som er
leser og hvem som er forfatter. Se for deg det som en oppgaveerklæring: "venstre mobiltto"
er tingen som er tilordnet (speilet data skrevet), og "høyre mobiltto" (den
argumentet til mirror
-metoden) er verdien som tildeles (speilet data leses).
Aktiver gjennomsiktig kryptering på klientsiden:
// Pass encryption parameters
const encryption = {
// key is required, must be >= 16 chars
key: randomstring.generate(128),
// optional, the default is to derive IV from key
// when set, IV must be >= 16 chars
iv: randomstring.generate(128),
// optional, the default is aes-256-cbc
algo: 'aes-256-cbc'
}
const api = await mobiletto(driverName, key, secret, opts, encryption)
// Subsequent write operations will encrypt data (client side) when writing
// Subsequent read operations will decrypt data (client side) when reading
Hva skjer? En separat "katalogoppføring" (dirent) katalog (kryptert) sporer hvilke filer som er i den katalog (alias den direkte katalogen).
- Kommandoen
list
leser katalogoppføringsfilene, dekrypterer hver bane som er oppført; returnerer deretter metadata for hver fil list
kommandoer er mer ineffektive, spesielt for kataloger med et stort antall filerwrite
kommandoen skriver dirent-filer i hver forelders dirent-katalog, rekursivt; skriver deretter filenwrite
kommandoer vil medføre O(N)-skrivinger, med N = dybde i kataloghierarkiet- Kommandoen
remove
fjerner den korresponderende dirent-filen, og dens overordnede hvis tom, rekursivt; fjerner deretter filen - Ikke-rekursive
remove
-kommandoer vil medføre O(N)-lesninger og potensielt like mange slettinger, med N = dybde i kataloghierarkiet - Rekursive
remove
kommandoer på store og dype filsystemer kan være dyrt
Vær oppmerksom på at selv med kryptering på klientsiden aktivert, er en motstander med full synlighet til den krypterte serversiden din lagring, selv uten nøkkel, kan fortsatt se det totale antallet kataloger og hvor mange filer som er i hver, og med litt innsats, oppdage noe eller hele den generelle strukturen til kataloghierarkiet. Merk: Bruk en relativt flat struktur for bedre sikkerhet. Motstanderen ville ikke vite navnene på katalogene/filene med mindre de også kjente krypteringen din nøkkel eller på annen måte hadde knekket krypteringen. Da er alle spill av!
Operasjoner på kryptert lagring kan være treg. Rekursive oppføringer og fjerninger kan være veldig sakte. Bufring via redis hjelper enormt, men merk at hurtigbufferen tømmes ved skriving eller fjerning.
Lag en mobiletto med den nye nøkkelen, og spei den gamle dataen inn i den:
const storage = require('mobiletto')
const oldEncryption = { key: .... }
const oldStorage = await storage.connect('s3', aws_key, aws_secret, {bucket: 'bk', region: 'us-east-1'}, oldEncryption)
const newEncryption = { key: .... }
const newStorage = await storage.connect('s3', aws_key, aws_secret, {bucket: 'zz', region: 'us-east-1'}, newEncryption)
newStorage.mirror(oldStorage) // if oldStorage is very large, this may take a looooooong time...
En driver er en hvilken som helst JS-fil som eksporterer en 'storageClient'-funksjon med denne signaturen:
function storageClient (key, secret, opts)
key
: en streng, API-nøkkelen din (for denlocal
driveren er dette basiskatalogen)secret
: en streng, API-hemmeligheten din (kan utelates for denlocal
driveren)opts
: et objekt, egenskapene er per driver:- For
local
bestemmer egenskapenefileMode
ogdirMode
hvordan nye opprettende filer og kataloger opprettes - For
s3
bucket
egenskapen "bøtte". Valgfrie egenskaper er:region
: the S3 region, default is us-east-1prefix
: a prefix to prepend to all S3 paths, default is the empty stringdelimiter
: the directory delimiter, default is '/'
Objektet som storageClient-funksjonen returnerer må definere disse funksjonene:
// Test the driver before using, ensure proper configuration
async testConfig ()
// List files in path (or from base-directory)
// If recursive is true, list recursively
// If visitor is defined, it will be an async function. await the visitor function on each file found
// Otherwise, perform the listing and return an array of objects
async list (path, recursive = false, visitor = null) // path may be omitted
// Read metadata for a path
async metadata (path)
// Read a file
// callback receives a chunk of data. endCallback is called at end-of-stream
async read (path, callback, endCallback = null)
// Write a file
// driver must be able to handle a generator or a stream
async write (path, generatorOrReadableStream)
// Remove a file, or recursively delete a directory
// returns a string of a single path removed, or an array of multiple paths removed
async remove (path, recursive = false, quiet = false)
Mobiletto bruker loggingsbiblioteket winston.
Logger vil inneholde filstier og feilmeldinger, men vil aldri inneholde nøkler, hemmeligheter, eller annen tilkoblingskonfigurasjonsinformasjon.
Bruk MOBILETTO_LOG_LEVEL
for å angi loggnivået ved å bruke en
av npm
definert i https://www.npmjs.com/package/winston#logging-levels
Standardnivået er error
. Det mest omfattende nivået er silly
, men for øyeblikket mobiletto
logger ikke på nivåer under debug
MOBILETTO_LOG_LEVEL=silly # maximum logs!
Som standard skriver loggeren til konsollen. For å sende logger til en fil, sett MOBILETTO_LOG_FILE
miljøvariabel. Når du logger til en fil, vil logger ikke lenger skrives til konsollen.
MOBILETTO_LOG_FILE=/var/my_mobiletto_log
Slik slår du av logging:
MOBILETTO_LOG_FILE=/dev/null