Mobiletto je abstraktna plast za shranjevanje JavaScript z izbirnim preglednim šifriranjem na strani odjemalca.
- Zakaj Mobiletto?
- [Hitri začetek](#Hitri začetek)
- Mobiletto CLI
- Vir
- Namestitev
- Podpora in financiranje
- Osnovna uporaba
- Metapodatki
- Nadomestni slog uvoza
- Caching
- Zrcaljenje
- Prozorno šifriranje
- [Vrtenje tipk](#Vrtenje tipk)
- [Vmesnik gonilnika](#Vmesnik gonilnika)
- Beleženje
Ta dokument README.md je bil preveden prek hokeylization v vsak jezik, ki ga podpira Google Prevajalnik!
Prepričan sem, da ni popolno, a upam, da je bolje kot nič!
🇸🇦 arabščina 🇧🇩 bengalščina 🇩🇪 nemščina 🇺🇸 angleščina 🇪🇸 španščina 🇫🇷 francoščina 🇹🇩 Hausa 🇮🇳 Hindi 🇮🇩 indonezijščina 🇮🇹 italijanščina 🇯🇵 japonščina 🇰🇷 korejščina 🇮🇳 Marathi 🇵🇱 poljščina 🇧🇷 portugalščina 🇷🇺 rusko 🇰🇪 svahili 🇵🇭 Tagalog 🇹🇷 turščina 🇵🇰 urdu 🇻🇳 vietnamščina 🇨🇳 kitajščina
Ta poseben prevod izvirnika README je lahko pomanjkljivo -- popravki so zelo dobrodošli! Prosimo, pošljite zahtevo za vlečenje na GitHub, ali če vam to ni všeč, odprite težavo
Ko ustvarite novo težavo GitHub o prevodu, naredite naslednje:
- vključite URL strani (kopirajte/prilepite iz naslovne vrstice brskalnika)
- vključi točno besedilo, ki je napačno (kopiraj/prilepi iz brskalnika)
- Prosimo, opišite, kaj je narobe -- ali je prevod napačen? je formatiranje nekako pokvarjeno?
- vljudno prosim za predlog boljšega prevoda oziroma kako naj bo besedilo pravilno oblikovano
- Hvala vam!
Različni ponudniki shranjevanja v oblaku imajo nezdružljive API-je. Tudi tisti, ki si prizadevajo za "združljivost S3" imajo idiosinkratično vedenje.
Ko izberete določenega prodajalca prostora za shranjevanje za svojo aplikacijo, če kodirate neposredno v njihov API, vašo aplikacijo je zdaj odvisen od te storitve. Ko čas mineva in se koda kopiči, pride do menjave prodajalcev vse bolj nevzdržno. Dobrodošli v zabavnem svetu zaklenjenosti prodajalca!
Mobiletto je bil zasnovan za rešitev tega problema. Če svojo aplikacijo kodirate v API mobiletto, lahko enostavno spremenite ponudnike shranjevanja in vedite, da se bo plast shranjevanja vaše aplikacije obnašala enako.
Vsi vozniki so preizkušeni glede enakega obnašanja s 60+ testi za vsakega voznika. Testiramo vse voznike z vsako kombinacijo:
- Šifriranje: omogočeno in onemogočeno
- Predpomnilnik Redis: omogočen in onemogočen
S tem pristopom smo prepričani, da se bo mobiletto obnašal enako ne glede na to, kateri gonilnik uporabljate, in ne glede na to, ali omogočite predpomnjenje in/ali šifriranje.
Trenutni gonilniki za shranjevanje Mobiletto:
s3
: Amazon S3b2
: Backblaze B2local
: lokalni datotečni sistem
Prispevki za podporo več ponudnikov shranjevanja v oblaku so zelo dobrodošli!
Mobiletto naj bi ga kot knjižnico uporabljala druga koda JavaScript.
Za delo z mobiletto v ukazni vrstici uporabite mobiletto-cli
Poskušam biti profesionalni razvijalec odprtokodne programske opreme. Delal sem v v industriji programske opreme že vrsto let ustanavljam uspešna podjetja in jih prodajam javnim podjetjem. Pred kratkim sem izgubil službo in pravzaprav nimam nobenega drugega dela
Zato bom poskusil napisati uporabno programsko opremo in preveril, ali deluje
Če radi uporabljate to programsko opremo, vam bom zelo hvaležen celo za najmanjši mesečni prispevek prek Patreona
Hvala vam!
Namestite z uporabo npm
ali yarn
. Verjetno želite lite
različico, ki ne vključuje vseh
prevedene datoteke README:
npm install mobiletto-lite
yarn add mobiletto-lite
Če res želite datoteke README v vseh jezikih, namestite polno različico:
npm install mobiletto
yarn add mobiletto
Kratek primer uporabe gonilnika mobiletto s3
.
Ta koda bi delovala enako, če bi bil gonilnik b2
ali 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!
Veliko obširnejši primer, ki prikazuje večino ponujenih funkcij:
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
Ukaz metadata
vrne metapodatke o posameznem vnosu datotečnega sistema.
Podobno je vrnjena vrednost iz ukaza list
niz metapodatkovnih objektov.
Metapodatkovni objekt je videti takole:
{
"name": "fully/qualified/path/to/file",
"type": "entry-type",
"size": size-in-bytes,
"ctime": creation-time-epoch-millis,
"mtime": modification-time-epoch-millis
}
Lastnost type
typeje lahko
file file
, dir
, link
ali special
.
Odvisno od vrste gonilnika ukaz list
morda ne vrne vseh polj. Lastnosti name
in type
mora biti vedno prisoten. Naslednji ukaz metadata
bo vrnil vse razpoložljive lastnosti.
Uvozite modul s polnim obsegom in uporabite funkcijo connect
:
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 najbolje deluje s predpomnilnikom redis .
Mobiletto se bo poskušal povezati z instanco redis na 127.0.0.1:6379
Preglasite lahko eno od teh možnosti:
- Nastavite spremenljivko env
MOBILETTO_REDIS_HOST
MOBILETTO_REDIS_HOST`, mobile naj se poveže tukaj namesto lokalnega gostitelja - Nastavite spremenljivko env
MOBILETTO_REDIS_PORT
, ta vrata bodo uporabljena
Mobiletto bo vse svoje ključe redis shranil s predpono _mobiletto__
. To lahko spremenite
z nastavitvijo MOBILETTO_REDIS_PREFIX
env var.
Predpomnjenje za vsako povezavo lahko nastavite tudi s predmetom 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)
Če želite onemogočiti: enabled: false
v svoj objekt opts.redisConfig
, ko vzpostavite povezavo.
Kot je razloženo spodaj, bo onemogočanje predpomnjenja negativno vplivalo na zmogljivost in povzročilo več zahtev za shranjevanje, ki ga resnično potrebujete.
Šifrirano shranjevanje: branje/zapisovanje v šifrirano shranjevanje je le malo počasnejše od običajnega, vendar je navigacija po imenikih (kar nekatere stvari počnejo) precej draga. Uporaba predpomnilnika redis vam bo znatno povečal učinkovitost.
Privzeti predpomnilnik je varen, vendar ne deluje dobro, če imate veliko operacij pisanja/odstranjevanja. Vsaka operacija zapisovanja ali odstranjevanja razveljavi celoten predpomnilnik, kar zagotavlja, da bodo naslednja branja videla zadnje spremembe.
Če uporabljate orodje CLI, kot je mobiletto-cli,
zagotovo boste želeli omogočiti predpomnilnik mo
.
// 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')
Ukaz mirror
izvede enkratno kopijo vseh datotek z enega mobilnega telefona na drugega.
Ne izvaja nobenega postopka za vzdrževanje ogledala skozi čas. Znova zaženite ukaz mirror
za sinhronizacijo morebitnih manjkajočih datotek.
Vrednost, vrnjena iz mirror
, je preprost objekt s števci za število uspešno obdelanih datotek
zrcaljeno in koliko datotek je imelo napake:
{
success: count-of-files-mirrored,
errors: count-of-files-with-errors
}
OPOZORILO: Zrcaljenje velikih naborov podatkov je lahko zelo zamudno in zahteva pasovno širino
S semantiko klica mirror
je lahko včasih zmedeno razumeti, kdo je
bralec in kdo je pisec. Predstavljajte si to kot izjavo o dodelitvi: "levi mobiletto"
je stvar, ki je dodeljena (zapisani zrcaljeni podatki) in "desni mobiletto" (
argument metode mirror
) je vrednost, ki se dodeli (preberejo se zrcalni podatki).
Omogoči pregledno šifriranje na strani odjemalca:
// 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
Kaj se dogaja? Ločen "vnos imenika" (dirent) imenik (šifriran) spremlja, katere datoteke so v njem imenik (ali dirent imenik).
- Ukaz
list
prebere vnosne datoteke imenika in dešifrira vsako navedeno pot; nato vrne metapodatke za vsako datoteko - Ukazi
list
so bolj neučinkoviti, še posebej za imenike z velikim številom datotek - Ukaz
write
zapiše dirent datoteke v dirent direktorij vsakega starša, rekurzivno; nato zapiše datoteko - Ukazi
write
bodo povzročili O(N) zapisov, pri čemer je N = globina v hierarhiji imenika - Ukaz
remove
odstrani ustrezno dirent datoteko in njeno nadrejeno datoteko, če je prazna; nato odstrani datoteko - Nerekurzivni ukazi
remove
bodo povzročili O(N) branj in potencialno prav toliko brisov, pri čemer je N = globina v hierarhiji imenika - Rekurzivni ukazi
remove
v velikih in globokih datotečnih sistemih so lahko dragi
Upoštevajte, da tudi z omogočenim šifriranjem na strani odjemalca nasprotnik s popolnim vpogledom v vašo šifrirano stran strežnika shranjevanje lahko tudi brez ključa še vedno vidi skupno število imenikov in koliko datotek je v vsakem ter z malo truda, odkrijte del ali celotno celotno strukturo hierarhije imenika. Opomba: za večjo varnost uporabite razmeroma ravno strukturo. Nasprotnik ne bi poznal imen imenikov/datotek, razen če bi poznal tudi vaše šifriranje ključ ali kako drugače uspešno razbil šifriranje. Potem so vse stave izključene!
Operacije v šifriranem pomnilniku so lahko počasne. Rekurzivni seznami in odstranitve so lahko zelo počasni. Predpomnjenje prek redisa izjemno pomaga, vendar upoštevajte, da se predpomnilnik izprazni ob kakršnem koli zapisovanju ali odstranjevanju.
Ustvarite mobiletto s svojim novim ključem, nato pa vanj zrcaljite stare podatke:
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...
Gonilnik je katera koli datoteka JS, ki izvozi funkcijo 'storageClient' s tem podpisom:
function storageClient (key, secret, opts)
key
: niz, vaš API ključ (zalocal
gonilnik je to osnovni imenik)secret
: niz, vaša skrivnost API-ja (lahko jo izpustite zalocal
gonilnik)opts
: objekt, lastnosti so za vsak gonilnik:- Za
local
locallastnosti
fileModein
dirMode` določata, kako se ustvarjajo nove datoteke in imeniki - Za
s3
je zahtevana lastnostbucket
. Izbirne lastnosti so: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 '/'
Objekt, ki ga vrne funkcija storageClient, mora definirati te funkcije:
// 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 uporablja knjižnico za beleženje winston.
Dnevniki bodo vsebovali poti datotek in sporočila o napakah, vendar nikoli ne bodo vsebovali ključev, skrivnosti, ali katere koli druge informacije o konfiguraciji povezave.
Uporabite spremenljivko okolja MOBILETTO_LOG_LEVEL
, da nastavite raven dnevnika z eno
ravni npm
, opredeljenih v https://www.npmjs.com/package/winston#logging-levels
Privzeta raven je error
. Najbolj podrobna raven je silly
, čeprav trenutno mobiletto
se ne beleži na ravneh pod debug
MOBILETTO_LOG_LEVEL=silly # maximum logs!
Privzeto beležnik piše v konzolo. Za pošiljanje dnevnikov v datoteko nastavite MOBILETTO_LOG_FILE
spremenljivka okolja. Ko se prijavite v datoteko, se dnevniki ne bodo več zapisovali v konzolo.
MOBILETTO_LOG_FILE=/var/my_mobiletto_log
Za izklop beleženja:
MOBILETTO_LOG_FILE=/dev/null