Mobiletto - гэта ўзровень абстракцыі сховішча JavaScript з дадатковым празрыстым шыфраваннем на баку кліента.
- [Чаму Mobiletto?](#Чаму Mobiletto?)
- [Хуткі старт](#Хуткі старт)
- Mobiletto CLI
- Крыніца
- Устаноўка
- Падтрымка і фінансаванне
- Асноўнае выкарыстанне
- Метададзеныя
- Альтэрнатыўны стыль імпарту
- Кэшаванне
- Адлюстраванне
- Празрыстае шыфраванне
- [Паварот клавіш](#Паварот клавіш)
- [Інтэрфейс драйвера](#Інтэрфейс драйвера)
- [Вядзенне журнала](#Вядзенне журнала)
Гэты дакумент README.md быў перакладзены праз hokeylization на усе мовы, якія падтрымліваюцца Google Translate!
Я ўпэўнены, што гэта не ідэальна, але я спадзяюся, што гэта лепш, чым нічога!
🇸🇦 арабская 🇧🇩 бенгальская 🇩🇪 нямецкая 🇺🇸 англійская 🇪🇸 Іспанская 🇫🇷 французская 🇹🇩 Хаўса 🇮🇳 хіндзі 🇮🇩 Інданезійская 🇮🇹 Італьянская 🇯🇵 японская 🇰🇷 карэйская 🇮🇳 маранты 🇵🇱 польская 🇧🇷 партугальская 🇷🇺 руская 🇰🇪 суахілі 🇵🇭 Тагалог 🇹🇷 турэцкая 🇵🇰 урду 🇻🇳 в'етнамская 🇨🇳 кітайская
Гэты канкрэтны пераклад арыгінала README можа мець недахопы -- выпраўленні вельмі вітаюцца! Адпраўце запыт на выцягванне на GitHub, або, калі вам гэта не зручна, адкрыйце пытанне
Калі вы ствараеце новую праблему GitHub аб перакладзе, зрабіце:
- уключыце URL старонкі (скапіруйце/устаўце з адраснага радка браўзера)
- уключыце дакладны няправільны тэкст (скапіруйце/устаўце з браўзера)
- калі ласка, апішыце, што не так -- пераклад няправільны? фарматаванне неяк парушана?
- калі ласка, прапануйце лепшы пераклад ці тое, як правільна афармляць тэкст
- Дзякуй!
Розныя пастаўшчыкі воблачных сховішчаў маюць несумяшчальныя API. Нават тыя, якія імкнуцца да "сумяшчальнасці S3" маюць ідыясінкратычныя паводзіны.
Калі вы выбіраеце пэўнага пастаўшчыка сховішча для вашага прыкладання, калі вы кодуеце непасрэдна яго API, ваша прыкладанне зараз залежыць ад гэтай службы. Час ідзе і код назапашваецца, пастаўшчыкі мяняюцца усё больш невыносным. Сардэчна запрашаем у вясёлы свет блакіроўкі пастаўшчыкоў!
Mobiletto быў распрацаваны, каб вырашыць гэтую праблему. Закадзіраваўшы сваю праграму ў API mobiletto, вы можаце лёгка зменіце пастаўшчыка сховішча і ведайце, што ўзровень сховішча вашага прыкладання будзе паводзіць сябе аднолькава.
Усе драйверы правяраюцца на аднолькавыя паводзіны з больш чым 60 тэстамі для кожнага драйвера. Мы тэстуем усе драйверы з кожнай камбінацыяй:
- Шыфраванне: як уключана, так і адключана
- Кэш Redis: уключаны і адключаны
Такі падыход дае нам спакой, што mobiletto будзе паводзіць сябе аднолькава незалежна ад таго, які драйвер вы выкарыстоўваеце, і незалежна ад таго, уключылі вы кэшаванне і/або шыфраванне.
Актуальныя драйверы захоўвання Mobiletto:
s3
: Amazon S3b2
: Backblaze B2local
: лакальная файлавая сістэма
Уклад у падтрымку большай колькасці пастаўшчыкоў воблачных сховішчаў вельмі вітаецца!
Mobiletto прызначаны для выкарыстання іншым кодам JavaScript у якасці бібліятэкі.
Каб працаваць з mobiletto ў камандным радку, выкарыстоўвайце mobiletto-cli
Я спрабую быць прафесійным распрацоўшчыкам праграмнага забеспячэння з адкрытым зыходным кодам. Я працаваў у У індустрыі праграмнага забеспячэння на працягу многіх гадоў я ствараў паспяховыя кампаніі і прадаваў іх публічным кампаніям. Нядаўна я страціў працу, і ў мяне няма іншай працы
Таму я паспрабую напісаць карыснае праграмнае забеспячэнне і пагляджу, ці спрацуе гэта
Калі вам падабаецца карыстацца гэтай праграмай, я быў бы вельмі ўдзячны нават за найменшы штомесячны ўзнос праз Patreon
Дзякуй!
Усталюйце з дапамогай npm
або yarn
. Верагодна, вам патрэбна lite
версія, якая не ўключае ўсе
перакладзеныя файлы README:
npm install mobiletto-lite
yarn add mobiletto-lite
Калі вы сапраўды хочаце файлы README на ўсіх мовах, усталюйце поўную версію:
npm install mobiletto
yarn add mobiletto
Кароткі прыклад выкарыстання драйвера mobiletto s3
.
Гэты код працаваў бы аднолькава, калі б драйвер быў b2
або 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!
Значна больш шырокі прыклад, які паказвае большасць прапанаваных функцый:
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
Каманда metadata
вяртае метаданыя аб адным запісе файлавай сістэмы.
Падобным чынам значэнне, якое вяртае каманда list
, з'яўляецца масівам аб'ектаў метададзеных.
Аб'ект метададзеных выглядае так:
{
"name": "fully/qualified/path/to/file",
"type": "entry-type",
"size": size-in-bytes,
"ctime": creation-time-epoch-millis,
"mtime": modification-time-epoch-millis
}
type
можа быць file
, dir
, link
або special
.
У залежнасці ад тыпу драйвера, каманда list
можа не вяртаць усе палі. name
і type
заўсёды павінна прысутнічаць. Наступная каманда metadata
верне ўсе даступныя ўласцівасці.
Імпартуйце поўны модуль і выкарыстоўвайце функцыю 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 лепш за ўсё працуе з кэшам Redis.
Mobiletto паспрабуе падключыцца да асобніка Redis на 127.0.0.1:6379
Вы можаце перавызначыць любы з наступных варыянтаў:
- Усталюйце
MOBILETTO_REDIS_HOST
, мабільнае падключэнне тут замест лакальнага - Усталюйце
MOBILETTO_REDIS_PORT
, гэты порт будзе выкарыстоўвацца
Mobiletto будзе захоўваць усе свае ключы Redis з прэфіксам _mobiletto__
. Вы можаце змяніць гэта
шляхам усталявання MOBILETTO_REDIS_PREFIX
var.
Вы таксама можаце ўсталяваць кэшаванне кожнага злучэння з дапамогай аб'екта 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)
Каб адключыць: перадайце enabled: false
у вашым аб'екце opts.redisConfig
пры ўсталяванні злучэння.
Як сказана ніжэй, адключэнне кэшавання негатыўна паўплывае на прадукцыйнасць і пацягне за сабой больш запытаў у сховішча, якое вам сапраўды трэба.
Зашыфраванае сховішча: чытанне/запіс зашыфраванага сховішча толькі крыху павольней звычайнага, але навігацыя па каталогах (што некаторыя рэчы робяць) даволі дарагая. Выкарыстанне кэша Redis дасць вам значны прырост прадукцыйнасці.
Кэш па змаўчанні бяспечны, але не працуе добра, калі ў вас шмат аперацый запісу/выдалення. Любая аперацыя запісу або выдалення робіць увесь кэш несапраўдным, гарантуючы, што наступныя чытанні будуць бачыць апошнія змены.
Калі вы выкарыстоўваеце інструмент CLI, напрыклад mobiletto-cli,
вы абавязкова захочаце ўключыць кэш Redis, бо ён захоўваецца пры выкананні каманды 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')
Каманда mirror
выконвае аднаразовае капіраванне ўсіх файлаў з аднаго mobiletto на іншы.
Ён не запускае ніякага працэсу для падтрымання люстэрка з цягам часу. Запусціце каманду mirror
яшчэ раз
для сінхранізацыі адсутных файлаў.
Значэнне, якое вяртае mirror
, - гэта просты аб'ект са лічыльнікамі колькасці файлаў, якія былі паспяхова перададзены
адлюстравана і колькі файлаў было з памылкамі:
{
success: count-of-files-mirrored,
errors: count-of-files-with-errors
}
ПАПЯРЭДЖАННЕ: адлюстраванне вялікіх набораў даных можа заняць вельмі шмат часу і паласы прапускання
З mirror
часам бывае складана зразумець, хто з'яўляецца
чытач і хто пісьменнік. Уявіце сабе гэта як заяву аб прысваенні: "левае mobiletto"
гэта рэч, якой прызначаецца (запісаныя люстраныя даныя), і "правы mobiletto" (the
аргумент метаду mirror
) — гэта значэнне, якое прысвойваецца (люстраваныя даныя чытаюцца).
Уключыць празрыстае шыфраванне на баку кліента:
// 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
Што адбываецца? Асобны "запіс каталога" (дырэнт) каталог (зашыфраваны) адсочвае, якія файлы знаходзяцца ў ім каталог (ён жа каталог dirent).
- Каманда
list
чытае файлы запісу каталога, расшыфроўвае кожны пералічаны шлях; затым вяртае метададзеныя для кожнага файла - Каманды
list
больш неэфектыўныя, асабліва для каталогаў з вялікай колькасцю файлаў - Каманда
write
запісвае dirent-файлы ў dirent-каталог кожнага з бацькоў рэкурсіўна; затым запісвае файл - Каманды
write
прывядуць да O(N) запісаў, прычым N = глыбіня ў іерархіі каталогаў - Каманда
remove
выдаляе адпаведны файл dirent і яго бацькоўскі файл, калі ён пусты; затым выдаляе файл - Нерэкурсіўныя каманды
remove
прывядуць да O(N) чытанняў і патэнцыйна столькі ж выдаленняў, з N = глыбінёй у іерархіі каталогаў - Рэкурсіўныя каманды
remove
на вялікіх і глыбокіх файлавых сістэмах могуць быць дарагімі
Звярніце ўвагу, што нават пры ўключаным шыфраванні на баку кліента, праціўнік з поўнай бачнасцю вашага зашыфраванага боку сервера сховішча, нават без ключа, можа па-ранейшаму бачыць агульную колькасць каталогаў і колькасць файлаў у кожным, а таксама з некаторыя намаганні, выявіць частку або ўсю агульную структуру іерархіі каталогаў. Заўвага: выкарыстоўвайце адносна плоскую структуру для лепшай бяспекі. Праціўнік не будзе ведаць імёны каталогаў/файлаў, калі ён таксама не будзе ведаць ваша шыфраванне ключ або іншым чынам паспяхова ўзламаў шыфраванне. Тады ўсе стаўкі адключаны!
Аперацыі ў зашыфраваным сховішчы могуць быць павольнымі. Рэкурсіўныя спісы і выдаленні могуць быць вельмі павольнымі. Кэшаванне праз redis надзвычай дапамагае, але звярніце ўвагу, што кэш ачышчаецца пры любой запісе або выдаленні.
Стварыце mobiletto з вашым новым ключом, затым адлюстравайце ў ім старыя дадзеныя:
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...
Драйвер - гэта любы файл JS, які экспартуе функцыю "storageClient" з такой сігнатурай:
function storageClient (key, secret, opts)
key
: радок, ваш ключ API (дляlocal
драйвера гэта базавы каталог)secret
: радок, ваш сакрэт API (можна апусціць дляlocal
драйвера)opts
: аб'ект, уласцівасці кожнага драйвера:- Для
local
localўласцівасці
fileModeі
dirMode` вызначаюць, як ствараюцца новыя файлы і каталогі - Для
s3
патрабуецца ўласцівасцьbucket
. Дадатковыя ўласцівасці: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 '/'
Аб'ект, які вяртае функцыя storageClient, павінен вызначаць наступныя функцыі:
// 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 выкарыстоўвае бібліятэку запісу winston.
Журналы будуць утрымліваць шляхі да файлаў і паведамленні пра памылкі, але ніколі не будуць утрымліваць ключы, сакрэты, або любую іншую інфармацыю аб канфігурацыі злучэння.
Выкарыстоўвайце зменную асяроддзя MOBILETTO_LOG_LEVEL
, каб усталяваць узровень часопіса, выкарыстоўваючы адзін
узроўняў npm
, вызначаных у https://www.npmjs.com/package/winston#logging-levels
Узровень па змаўчанні - error
. Найбольш шматслоўным узроўнем з'яўляецца silly
, хоць у цяперашні час mobiletto
не рэгіструецца на ўзроўнях, ніжэйшых за debug
MOBILETTO_LOG_LEVEL=silly # maximum logs!
Па змаўчанні рэгістратар запісвае ў кансоль. Каб адправіць журналы ў файл, усталюйце MOBILETTO_LOG_FILE
зменная асяроддзя. Пры ўваходзе ў файл журналы больш не будуць запісвацца ў кансоль.
MOBILETTO_LOG_FILE=/var/my_mobiletto_log
Каб адключыць запіс:
MOBILETTO_LOG_FILE=/dev/null