[{"data":1,"prerenderedAt":30781},["ShallowReactive",2],{"navigation":3,"posts-undefined-技术-0-999":20},[4,8,12,16],{"title":5,"path":6,"stem":7},"首页","\u002F","00.index",{"title":9,"path":10,"stem":11},"文章","\u002Fposts","01.posts",{"title":13,"path":14,"stem":15},"动态","\u002Fmoments","02.moments",{"title":17,"path":18,"stem":19},"关于","\u002Fabout","09.about",[21,545,1226,1462,4979,5340,5474,7156,7306,7532,8430,8472,10002,10059,10653,11080,11284,13225,13519,13761,14793,15811,16016,16176,18133,18279,18551,18708,18747,19483,19871,21236,21270,21725,22307,23118,23240,23369,23442,23507,23806,23962,24490,24803,24972,24993,25072,25130,25390,25696,25756,26572,26746,26819,27002,27036,27679,27717,27841,27967,28073,28113,28149,28176,28335,28367,28471,28620,29190,29447,30126,30244,30335,30439,30720,30743],{"id":22,"title":23,"body":24,"class":528,"cover":528,"coverSize":528,"date":529,"description":30,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":532,"navigation":415,"path":533,"readingTime":534,"seo":539,"sitemap":540,"stem":541,"tags":542,"time":528,"weather":528,"__hash__":544},"posts\u002Fposts\u002F2026\u002F20260124.use-vscode-for-kotlin.md","使用 VSCode 开发 Kotlin",{"type":25,"value":26,"toc":526},"minimark",[27,31,34,45,54,57,133,140,323,339,353,359,508,511,519,522],[28,29,30],"p",{},"之前有些后端项目用的 Kotlin + Spring Boot，IDE 用的 IDEA 开发的。但是 IDEA 中的 GitHub Copilot 插件实在太弱智，打算换成 VSCode 来开发 Kotlin。",[28,32,33],{},"网上几乎没有教程（可能确实没人这么干），踩了不少坑，试了很多插件，花了快一天时间才终于搞好。",[28,35,36,37,44],{},"首先，需要安装 VSCode 的 ",[38,39,43],"a",{"href":40,"rel":41},"https:\u002F\u002Fmarketplace.visualstudio.com\u002Fitems?itemName=vscjava.vscode-java-pack",[42],"nofollow","Java Extension Pack"," 插件包，这个插件包包含了 Java 开发所需的各种插件。",[28,46,47,48,53],{},"然后，还需要安装 ",[38,49,52],{"href":50,"rel":51},"https:\u002F\u002Fgithub.com\u002FKotlin\u002Fkotlin-lsp\u002Freleases",[42],"kotlin-lsp"," 插件，这个是 Kotlin 官方的插件，提供了 Kotlin 的语法高亮、格式化、跳转等功能。需要注意的是，这个插件尚未发布到 VSCode 市场，需要手动下载并安装。另外，该插件当前只支持 Gradle 项目，不支持 Maven 项目。如果是 Maven 项目，得转换成 Gradle 项目才能使用该插件。",[28,55,56],{},"VSCode 配置：",[58,59,64],"pre",{"className":60,"code":61,"language":62,"meta":63,"style":63},"language-json shiki shiki-themes material-theme-lighter github-light github-dark","{\n  \"[kotlin]\": {\n    \"editor.defaultFormatter\": \"JetBrains.kotlin\"\n  }\n}\n","json","",[65,66,67,76,96,121,127],"code",{"__ignoreMap":63},[68,69,72],"span",{"class":70,"line":71},"line",1,[68,73,75],{"class":74},"sP7_E","{\n",[68,77,79,83,87,90,93],{"class":70,"line":78},2,[68,80,82],{"class":81},"s39Yj","  \"",[68,84,86],{"class":85},"sseR_","[kotlin]",[68,88,89],{"class":81},"\"",[68,91,92],{"class":74},":",[68,94,95],{"class":74}," {\n",[68,97,99,102,106,108,110,114,118],{"class":70,"line":98},3,[68,100,101],{"class":81},"    \"",[68,103,105],{"class":104},"sZMiF","editor.defaultFormatter",[68,107,89],{"class":81},[68,109,92],{"class":74},[68,111,113],{"class":112},"sjJ54"," \"",[68,115,117],{"class":116},"s_sjI","JetBrains.kotlin",[68,119,120],{"class":112},"\"\n",[68,122,124],{"class":70,"line":123},4,[68,125,126],{"class":74},"  }\n",[68,128,130],{"class":70,"line":129},5,[68,131,132],{"class":74},"}\n",[28,134,135,136,139],{},"F5 调试用的 ",[65,137,138],{},"launch.json"," 配置：",[58,141,143],{"className":60,"code":142,"language":62,"meta":63,"style":63},"{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"type\": \"java\",\n      \"name\": \"SpringBoot\",\n      \"classPaths\": [\"$Auto\", \"${workspaceFolder}\u002Fbuild\u002Flibs\u002F*\"],\n      \"request\": \"launch\",\n      \"mainClass\": \"net.yuanfen.op.xams.ApplicationKt\"\n    }\n  ]\n}\n",[65,144,145,149,170,184,189,210,231,266,287,306,312,318],{"__ignoreMap":63},[68,146,147],{"class":70,"line":71},[68,148,75],{"class":74},[68,150,151,153,156,158,160,162,165,167],{"class":70,"line":78},[68,152,82],{"class":81},[68,154,155],{"class":85},"version",[68,157,89],{"class":81},[68,159,92],{"class":74},[68,161,113],{"class":112},[68,163,164],{"class":116},"0.2.0",[68,166,89],{"class":112},[68,168,169],{"class":74},",\n",[68,171,172,174,177,179,181],{"class":70,"line":98},[68,173,82],{"class":81},[68,175,176],{"class":85},"configurations",[68,178,89],{"class":81},[68,180,92],{"class":74},[68,182,183],{"class":74}," [\n",[68,185,186],{"class":70,"line":123},[68,187,188],{"class":74},"    {\n",[68,190,191,194,197,199,201,203,206,208],{"class":70,"line":129},[68,192,193],{"class":81},"      \"",[68,195,196],{"class":104},"type",[68,198,89],{"class":81},[68,200,92],{"class":74},[68,202,113],{"class":112},[68,204,205],{"class":116},"java",[68,207,89],{"class":112},[68,209,169],{"class":74},[68,211,213,215,218,220,222,224,227,229],{"class":70,"line":212},6,[68,214,193],{"class":81},[68,216,217],{"class":104},"name",[68,219,89],{"class":81},[68,221,92],{"class":74},[68,223,113],{"class":112},[68,225,226],{"class":116},"SpringBoot",[68,228,89],{"class":112},[68,230,169],{"class":74},[68,232,234,236,239,241,243,246,248,251,253,256,258,261,263],{"class":70,"line":233},7,[68,235,193],{"class":81},[68,237,238],{"class":104},"classPaths",[68,240,89],{"class":81},[68,242,92],{"class":74},[68,244,245],{"class":74}," [",[68,247,89],{"class":112},[68,249,250],{"class":116},"$Auto",[68,252,89],{"class":112},[68,254,255],{"class":74},",",[68,257,113],{"class":112},[68,259,260],{"class":116},"${workspaceFolder}\u002Fbuild\u002Flibs\u002F*",[68,262,89],{"class":112},[68,264,265],{"class":74},"],\n",[68,267,269,271,274,276,278,280,283,285],{"class":70,"line":268},8,[68,270,193],{"class":81},[68,272,273],{"class":104},"request",[68,275,89],{"class":81},[68,277,92],{"class":74},[68,279,113],{"class":112},[68,281,282],{"class":116},"launch",[68,284,89],{"class":112},[68,286,169],{"class":74},[68,288,290,292,295,297,299,301,304],{"class":70,"line":289},9,[68,291,193],{"class":81},[68,293,294],{"class":104},"mainClass",[68,296,89],{"class":81},[68,298,92],{"class":74},[68,300,113],{"class":112},[68,302,303],{"class":116},"net.yuanfen.op.xams.ApplicationKt",[68,305,120],{"class":112},[68,307,309],{"class":70,"line":308},10,[68,310,311],{"class":74},"    }\n",[68,313,315],{"class":70,"line":314},11,[68,316,317],{"class":74},"  ]\n",[68,319,321],{"class":70,"line":320},12,[68,322,132],{"class":74},[28,324,325,326,328,329,332,333,338],{},"另外，",[65,327,52],{}," 插件暂不支持 ",[65,330,331],{},"Gradle Kotlin DSL"," （",[38,334,337],{"href":335,"rel":336},"https:\u002F\u002Fgithub.com\u002FKotlin\u002Fkotlin-lsp\u002Fissues\u002F55",[42],"Issue#55","）。",[28,340,341,342,345,346,345,349,352],{},"尝试了 ",[65,343,344],{},"ktfmt","、",[65,347,348],{},"ktfmt-gradle",[65,350,351],{},"Spotless Gradle"," 等插件，最终都因为各种原因放弃了。",[28,354,355,356,358],{},"另外还遇到一个 ",[65,357,52],{}," 的奇怪 bug，就是插件一直报错和各种警告：",[58,360,364],{"className":361,"code":362,"language":363,"meta":63,"style":63},"language-log shiki shiki-themes material-theme-lighter github-light github-dark","Error while resolving org.jetbrains.kotlin.fir.declarations.impl.FirValueParameterImpl from SEALED_CLASS_INHERITORS to ANNOTATION_ARGUMENTS current declaration phase SEALED_CLASS_INHERITORS origin: Source session: class org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSourcesSession module data: class org.jetbrains.kotlin.analysis.low.level.api.fir.projectStructure.LLFirModuleData KaModule: class org.jetbrains.kotlin.idea.base.fir.projectStructure.modules.source.KaSourceModuleImpl platform: JVM (1.8)\n\n\nWARNING: package sun.awt.windows not in java.desktop\nWARNING: package sun.awt.X11 not in java.desktop\nWARNING: package com.sun.java.swing.plaf.gtk not in java.desktop\nWARNING - #c.i.i.p.PluginManager - Plugin descriptor for plugin 'intellij.kotlin.searching.xml' has declared element 'visibility' which has no effect there\nWARNING - #c.j.l.a.f.i.c.d.i.LSInspectionDiagnosticProviderImpl - org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments: Unable to get element context\n","log",[65,365,366,411,417,421,439,452,465,490],{"__ignoreMap":63},[68,367,368,371,375,378,381,384,387,390,393,396,399,402,405,408],{"class":70,"line":71},[68,369,370],{"class":116},"Error",[68,372,374],{"class":373},"su5hD"," while resolving ",[68,376,377],{"class":81},"org.jetbrains.kotlin.fir.declarations.impl.FirValueParameterImpl",[68,379,380],{"class":373}," from SEALED_CLASS_INHERITORS to ANNOTATION_ARGUMENTS current declaration phase SEALED_CLASS_INHERITORS origin: Source session: class ",[68,382,383],{"class":81},"org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSourcesSession",[68,385,386],{"class":373}," module data: class ",[68,388,389],{"class":81},"org.jetbrains.kotlin.analysis.low.level.api.fir.projectStructure.LLFirModuleData",[68,391,392],{"class":373}," KaModule: class ",[68,394,395],{"class":81},"org.jetbrains.kotlin.idea.base.fir.projectStructure.modules.source.KaSourceModuleImpl",[68,397,398],{"class":373}," platform: JVM (",[68,400,401],{"class":81},"1",[68,403,404],{"class":373},".",[68,406,407],{"class":81},"8",[68,409,410],{"class":373},")\n",[68,412,413],{"class":70,"line":78},[68,414,416],{"emptyLinePlaceholder":415},true,"\n",[68,418,419],{"class":70,"line":98},[68,420,416],{"emptyLinePlaceholder":415},[68,422,423,427,430,433,436],{"class":70,"line":123},[68,424,426],{"class":425},"s2YIT","WARNING",[68,428,429],{"class":373},": package ",[68,431,432],{"class":81},"sun.awt.windows",[68,434,435],{"class":373}," not in ",[68,437,438],{"class":81},"java.desktop\n",[68,440,441,443,445,448,450],{"class":70,"line":129},[68,442,426],{"class":425},[68,444,429],{"class":373},[68,446,447],{"class":81},"sun.awt.X11",[68,449,435],{"class":373},[68,451,438],{"class":81},[68,453,454,456,458,461,463],{"class":70,"line":212},[68,455,426],{"class":425},[68,457,429],{"class":373},[68,459,460],{"class":81},"com.sun.java.swing.plaf.gtk",[68,462,435],{"class":373},[68,464,438],{"class":81},[68,466,467,469,472,475,478,481,484,487],{"class":70,"line":233},[68,468,426],{"class":425},[68,470,471],{"class":373}," - #",[68,473,474],{"class":81},"c.i.i.p.PluginManager",[68,476,477],{"class":373}," - Plugin descriptor for plugin ",[68,479,480],{"class":116},"'intellij.kotlin.searching.xml'",[68,482,483],{"class":373}," has declared element ",[68,485,486],{"class":116},"'visibility'",[68,488,489],{"class":373}," which has no effect there\n",[68,491,492,494,496,499,502,505],{"class":70,"line":268},[68,493,426],{"class":425},[68,495,471],{"class":373},[68,497,498],{"class":81},"c.j.l.a.f.i.c.d.i.LSInspectionDiagnosticProviderImpl",[68,500,501],{"class":373}," - ",[68,503,504],{"class":81},"org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments",[68,506,507],{"class":373},": Unable to get element context\n",[28,509,510],{},"比较了两个文件夹，同样的代码，一个报错，一个不报错，报错的那个文件夹换个名字就好了。怀疑是缓存的问题，困扰了很久，最终找到了缓存位置：",[58,512,517],{"className":513,"code":515,"language":516},[514],"language-text","\u002FUsers\u002Fbean\u002FLibrary\u002FApplication Support\u002FJetBrains\u002Fanalyzer\u002Fworkspaces\u002F\n","text",[65,518,515],{"__ignoreMap":63},[28,520,521],{},"删除该目录下的缓存文件夹，重启 VSCode 后问题解决。",[523,524,525],"style",{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s2YIT, html code.shiki .s2YIT{--shiki-light:#E53935;--shiki-default:#B31D28;--shiki-dark:#FDAEB7}",{"title":63,"searchDepth":78,"depth":78,"links":527},[],null,"2026-01-24",false,"md",{},"\u002Fposts\u002F2026\u002Fuse-vscode-for-kotlin",{"text":535,"minutes":536,"time":537,"words":538},"3 min read",2.28,136800,456,{"title":23,"description":30},{"loc":533,"lastmod":529},"posts\u002F2026\u002F20260124.use-vscode-for-kotlin",[543],"技术","GXxZCrPdasr3Za_z0MuP3TOPnJjje6WtlA4J1g6XgRo",{"id":546,"title":547,"body":548,"class":528,"cover":1212,"coverSize":528,"date":1213,"description":552,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":1214,"navigation":415,"path":1215,"readingTime":1216,"seo":1221,"sitemap":1222,"stem":1223,"tags":1224,"time":528,"weather":528,"__hash__":1225},"posts\u002Fposts\u002F2026\u002F20260104.reduce-clickhouse-resource-usage-for-self-hosted-langfuse.md","优化自托管 Langfuse 的 ClickHouse 资源占用",{"type":25,"value":549,"toc":1210},[550,553,557,564,706,709,716,1194,1201,1204,1207],[28,551,552],{},"最近在研究 Langfuse，用 docker 部署了一套，但是发现空载的情况下 ClickHouse CPU 占用也不低，并且磁盘持续在写入，一不注意，7 天已经写入了 200 多 GB。",[554,555],"post-image",{"filename":556},"01.png",[28,558,559,560,563],{},"通过如下命令发现，大部分磁盘占用都是 ",[65,561,562],{},"trace_log"," 表造成的：",[58,565,569],{"className":566,"code":567,"language":568,"meta":63,"style":63},"language-sql shiki shiki-themes material-theme-lighter github-light github-dark","SELECT table, formatReadableSize(size) as size, rows FROM (\n    SELECT\n        table,\n        database,\n        sum(bytes) AS size,\n        sum(rows) AS rows\n    FROM system.parts\n    WHERE active\n    GROUP BY table, database\n    ORDER BY size DESC\n)\n","sql",[65,570,571,607,612,619,626,642,658,672,680,692,702],{"__ignoreMap":63},[68,572,573,577,580,583,586,589,592,595,598,601,604],{"class":70,"line":71},[68,574,576],{"class":575},"sw1J6","SELECT",[68,578,579],{"class":575}," table",[68,581,582],{"class":373},", formatReadableSize(",[68,584,585],{"class":575},"size",[68,587,588],{"class":373},") ",[68,590,591],{"class":575},"as",[68,593,594],{"class":575}," size",[68,596,597],{"class":373},", ",[68,599,600],{"class":575},"rows",[68,602,603],{"class":575}," FROM",[68,605,606],{"class":373}," (\n",[68,608,609],{"class":70,"line":78},[68,610,611],{"class":575},"    SELECT\n",[68,613,614,617],{"class":70,"line":98},[68,615,616],{"class":575},"        table",[68,618,169],{"class":373},[68,620,621,624],{"class":70,"line":123},[68,622,623],{"class":575},"        database",[68,625,169],{"class":373},[68,627,628,632,635,638,640],{"class":70,"line":129},[68,629,631],{"class":630},"sptTA","        sum",[68,633,634],{"class":373},"(bytes) ",[68,636,637],{"class":575},"AS",[68,639,594],{"class":575},[68,641,169],{"class":373},[68,643,644,646,649,651,653,655],{"class":70,"line":212},[68,645,631],{"class":630},[68,647,648],{"class":373},"(",[68,650,600],{"class":575},[68,652,588],{"class":373},[68,654,637],{"class":575},[68,656,657],{"class":575}," rows\n",[68,659,660,663,667,669],{"class":70,"line":233},[68,661,662],{"class":575},"    FROM",[68,664,666],{"class":665},"s_hVV"," system",[68,668,404],{"class":373},[68,670,671],{"class":665},"parts\n",[68,673,674,677],{"class":70,"line":268},[68,675,676],{"class":575},"    WHERE",[68,678,679],{"class":373}," active\n",[68,681,682,685,687,689],{"class":70,"line":289},[68,683,684],{"class":575},"    GROUP BY",[68,686,579],{"class":575},[68,688,597],{"class":373},[68,690,691],{"class":575},"database\n",[68,693,694,697,699],{"class":70,"line":308},[68,695,696],{"class":575},"    ORDER BY",[68,698,594],{"class":575},[68,700,701],{"class":575}," DESC\n",[68,703,704],{"class":70,"line":314},[68,705,410],{"class":373},[28,707,708],{},"于是便联想到之前的一次 ClickHouse 优化经历，决定关闭各种 trace 日志，来减少 ClickHouse 的资源占用。",[28,710,711,712,715],{},"网上大部分教程没有提到 ",[65,713,714],{},"background_schedule_pool_log","，但我发现它也会不断产生，也可以关闭。",[58,717,721],{"className":718,"code":719,"language":720,"meta":63,"style":63},"language-xml shiki shiki-themes material-theme-lighter github-light github-dark","\u003Cclickhouse>\n    \u003Cprofiles>\n        \u003Cdefault>\n            \u003Clog_queries>0\u003C\u002Flog_queries>\n            \u003Clog_query_threads>0\u003C\u002Flog_query_threads>\n        \u003C\u002Fdefault>\n    \u003C\u002Fprofiles>\n    \u003Clogger>\n        \u003Clevel>warning\u003C\u002Flevel>\n        \u003Cconsole>true\u003C\u002Fconsole>\n    \u003C\u002Flogger>\n    \u003Casynchronous_metric_log remove=\"1\" \u002F>\n    \u003Cbackup_log remove=\"1\" \u002F>\n    \u003Cerror_log remove=\"1\" \u002F>\n    \u003Cmetric_log remove=\"1\" \u002F>\n    \u003Cquery_thread_log remove=\"1\" \u002F>\n    \u003Cquery_log remove=\"1\" \u002F>\n    \u003Cquery_views_log remove=\"1\" \u002F>\n    \u003Cpart_log remove=\"1\" \u002F>\n    \u003Csession_log remove=\"1\" \u002F>\n    \u003Ctext_log remove=\"1\" \u002F>\n    \u003Ctrace_log remove=\"1\" \u002F>\n    \u003Ccrash_log remove=\"1\" \u002F>\n    \u003Copentelemetry_span_log remove=\"1\" \u002F>\n    \u003Czookeeper_log remove=\"1\" \u002F>\n    \u003Cprocessors_profile_log remove=\"1\" \u002F>\n    \u003Cbackground_schedule_pool_log remove=\"1\" \u002F>\n\u003C\u002Fclickhouse>\n","xml",[65,722,723,735,745,755,776,793,802,811,820,838,856,864,887,907,927,947,967,987,1007,1027,1047,1067,1086,1106,1126,1146,1166,1185],{"__ignoreMap":63},[68,724,725,728,732],{"class":70,"line":71},[68,726,727],{"class":74},"\u003C",[68,729,731],{"class":730},"sQzsp","clickhouse",[68,733,734],{"class":74},">\n",[68,736,737,740,743],{"class":70,"line":78},[68,738,739],{"class":74},"    \u003C",[68,741,742],{"class":730},"profiles",[68,744,734],{"class":74},[68,746,747,750,753],{"class":70,"line":98},[68,748,749],{"class":74},"        \u003C",[68,751,752],{"class":730},"default",[68,754,734],{"class":74},[68,756,757,760,763,766,769,772,774],{"class":70,"line":123},[68,758,759],{"class":74},"            \u003C",[68,761,762],{"class":730},"log_queries",[68,764,765],{"class":74},">",[68,767,768],{"class":373},"0",[68,770,771],{"class":74},"\u003C\u002F",[68,773,762],{"class":730},[68,775,734],{"class":74},[68,777,778,780,783,785,787,789,791],{"class":70,"line":129},[68,779,759],{"class":74},[68,781,782],{"class":730},"log_query_threads",[68,784,765],{"class":74},[68,786,768],{"class":373},[68,788,771],{"class":74},[68,790,782],{"class":730},[68,792,734],{"class":74},[68,794,795,798,800],{"class":70,"line":212},[68,796,797],{"class":74},"        \u003C\u002F",[68,799,752],{"class":730},[68,801,734],{"class":74},[68,803,804,807,809],{"class":70,"line":233},[68,805,806],{"class":74},"    \u003C\u002F",[68,808,742],{"class":730},[68,810,734],{"class":74},[68,812,813,815,818],{"class":70,"line":268},[68,814,739],{"class":74},[68,816,817],{"class":730},"logger",[68,819,734],{"class":74},[68,821,822,824,827,829,832,834,836],{"class":70,"line":289},[68,823,749],{"class":74},[68,825,826],{"class":730},"level",[68,828,765],{"class":74},[68,830,831],{"class":373},"warning",[68,833,771],{"class":74},[68,835,826],{"class":730},[68,837,734],{"class":74},[68,839,840,842,845,847,850,852,854],{"class":70,"line":308},[68,841,749],{"class":74},[68,843,844],{"class":730},"console",[68,846,765],{"class":74},[68,848,849],{"class":373},"true",[68,851,771],{"class":74},[68,853,844],{"class":730},[68,855,734],{"class":74},[68,857,858,860,862],{"class":70,"line":314},[68,859,806],{"class":74},[68,861,817],{"class":730},[68,863,734],{"class":74},[68,865,866,868,871,875,878,880,882,884],{"class":70,"line":320},[68,867,739],{"class":74},[68,869,870],{"class":730},"asynchronous_metric_log",[68,872,874],{"class":873},"s9AJx"," remove",[68,876,877],{"class":74},"=",[68,879,89],{"class":112},[68,881,401],{"class":116},[68,883,89],{"class":112},[68,885,886],{"class":74}," \u002F>\n",[68,888,890,892,895,897,899,901,903,905],{"class":70,"line":889},13,[68,891,739],{"class":74},[68,893,894],{"class":730},"backup_log",[68,896,874],{"class":873},[68,898,877],{"class":74},[68,900,89],{"class":112},[68,902,401],{"class":116},[68,904,89],{"class":112},[68,906,886],{"class":74},[68,908,910,912,915,917,919,921,923,925],{"class":70,"line":909},14,[68,911,739],{"class":74},[68,913,914],{"class":730},"error_log",[68,916,874],{"class":873},[68,918,877],{"class":74},[68,920,89],{"class":112},[68,922,401],{"class":116},[68,924,89],{"class":112},[68,926,886],{"class":74},[68,928,930,932,935,937,939,941,943,945],{"class":70,"line":929},15,[68,931,739],{"class":74},[68,933,934],{"class":730},"metric_log",[68,936,874],{"class":873},[68,938,877],{"class":74},[68,940,89],{"class":112},[68,942,401],{"class":116},[68,944,89],{"class":112},[68,946,886],{"class":74},[68,948,950,952,955,957,959,961,963,965],{"class":70,"line":949},16,[68,951,739],{"class":74},[68,953,954],{"class":730},"query_thread_log",[68,956,874],{"class":873},[68,958,877],{"class":74},[68,960,89],{"class":112},[68,962,401],{"class":116},[68,964,89],{"class":112},[68,966,886],{"class":74},[68,968,970,972,975,977,979,981,983,985],{"class":70,"line":969},17,[68,971,739],{"class":74},[68,973,974],{"class":730},"query_log",[68,976,874],{"class":873},[68,978,877],{"class":74},[68,980,89],{"class":112},[68,982,401],{"class":116},[68,984,89],{"class":112},[68,986,886],{"class":74},[68,988,990,992,995,997,999,1001,1003,1005],{"class":70,"line":989},18,[68,991,739],{"class":74},[68,993,994],{"class":730},"query_views_log",[68,996,874],{"class":873},[68,998,877],{"class":74},[68,1000,89],{"class":112},[68,1002,401],{"class":116},[68,1004,89],{"class":112},[68,1006,886],{"class":74},[68,1008,1010,1012,1015,1017,1019,1021,1023,1025],{"class":70,"line":1009},19,[68,1011,739],{"class":74},[68,1013,1014],{"class":730},"part_log",[68,1016,874],{"class":873},[68,1018,877],{"class":74},[68,1020,89],{"class":112},[68,1022,401],{"class":116},[68,1024,89],{"class":112},[68,1026,886],{"class":74},[68,1028,1030,1032,1035,1037,1039,1041,1043,1045],{"class":70,"line":1029},20,[68,1031,739],{"class":74},[68,1033,1034],{"class":730},"session_log",[68,1036,874],{"class":873},[68,1038,877],{"class":74},[68,1040,89],{"class":112},[68,1042,401],{"class":116},[68,1044,89],{"class":112},[68,1046,886],{"class":74},[68,1048,1050,1052,1055,1057,1059,1061,1063,1065],{"class":70,"line":1049},21,[68,1051,739],{"class":74},[68,1053,1054],{"class":730},"text_log",[68,1056,874],{"class":873},[68,1058,877],{"class":74},[68,1060,89],{"class":112},[68,1062,401],{"class":116},[68,1064,89],{"class":112},[68,1066,886],{"class":74},[68,1068,1070,1072,1074,1076,1078,1080,1082,1084],{"class":70,"line":1069},22,[68,1071,739],{"class":74},[68,1073,562],{"class":730},[68,1075,874],{"class":873},[68,1077,877],{"class":74},[68,1079,89],{"class":112},[68,1081,401],{"class":116},[68,1083,89],{"class":112},[68,1085,886],{"class":74},[68,1087,1089,1091,1094,1096,1098,1100,1102,1104],{"class":70,"line":1088},23,[68,1090,739],{"class":74},[68,1092,1093],{"class":730},"crash_log",[68,1095,874],{"class":873},[68,1097,877],{"class":74},[68,1099,89],{"class":112},[68,1101,401],{"class":116},[68,1103,89],{"class":112},[68,1105,886],{"class":74},[68,1107,1109,1111,1114,1116,1118,1120,1122,1124],{"class":70,"line":1108},24,[68,1110,739],{"class":74},[68,1112,1113],{"class":730},"opentelemetry_span_log",[68,1115,874],{"class":873},[68,1117,877],{"class":74},[68,1119,89],{"class":112},[68,1121,401],{"class":116},[68,1123,89],{"class":112},[68,1125,886],{"class":74},[68,1127,1129,1131,1134,1136,1138,1140,1142,1144],{"class":70,"line":1128},25,[68,1130,739],{"class":74},[68,1132,1133],{"class":730},"zookeeper_log",[68,1135,874],{"class":873},[68,1137,877],{"class":74},[68,1139,89],{"class":112},[68,1141,401],{"class":116},[68,1143,89],{"class":112},[68,1145,886],{"class":74},[68,1147,1149,1151,1154,1156,1158,1160,1162,1164],{"class":70,"line":1148},26,[68,1150,739],{"class":74},[68,1152,1153],{"class":730},"processors_profile_log",[68,1155,874],{"class":873},[68,1157,877],{"class":74},[68,1159,89],{"class":112},[68,1161,401],{"class":116},[68,1163,89],{"class":112},[68,1165,886],{"class":74},[68,1167,1169,1171,1173,1175,1177,1179,1181,1183],{"class":70,"line":1168},27,[68,1170,739],{"class":74},[68,1172,714],{"class":730},[68,1174,874],{"class":873},[68,1176,877],{"class":74},[68,1178,89],{"class":112},[68,1180,401],{"class":116},[68,1182,89],{"class":112},[68,1184,886],{"class":74},[68,1186,1188,1190,1192],{"class":70,"line":1187},28,[68,1189,771],{"class":74},[68,1191,731],{"class":730},[68,1193,734],{"class":74},[28,1195,1196,1197,1200],{},"将上述配置挂载到 ClickHouse 容器的 ",[65,1198,1199],{},"\u002Fetc\u002Fclickhouse-server\u002Fconfig.d\u002Flogs.xml"," 后，重启容器即可。",[554,1202],{"filename":1203},"02.png",[28,1205,1206],{},"整个世界清净了。",[523,1208,1209],{},"html pre.shiki code .sw1J6, html code.shiki .sw1J6{--shiki-light:#F76D47;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s9AJx, html code.shiki .s9AJx{--shiki-light:#9C3EDA;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":63,"searchDepth":78,"depth":78,"links":1211},[],"png","2026-01-04",{},"\u002Fposts\u002F2026\u002Freduce-clickhouse-resource-usage-for-self-hosted-langfuse",{"text":1217,"minutes":1218,"time":1219,"words":1220},"2 min read",1.255,75300,251,{"title":547,"description":552},{"loc":1215,"lastmod":1213},"posts\u002F2026\u002F20260104.reduce-clickhouse-resource-usage-for-self-hosted-langfuse",[543],"7Hq01juLrC71nQLXMrZ1wFNCDCplNmp-JdCMvi12wuc",{"id":1227,"title":1228,"body":1229,"class":528,"cover":1445,"coverSize":528,"date":1446,"description":63,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":1447,"navigation":415,"path":1448,"readingTime":1449,"seo":1454,"sitemap":1455,"stem":1456,"tags":1457,"time":528,"weather":528,"__hash__":1461},"posts\u002Fposts\u002F2025\u002F20250707.homelab-disaster-postmortem.md","一次 HomeLab 灾难级事故的复盘",{"type":25,"value":1230,"toc":1439},[1231,1235,1353,1356,1402,1405,1425,1428],[1232,1233,1234],"h2",{"id":1234},"时间线",[1236,1237,1238,1246,1252,1258,1264,1270,1276,1282,1288,1294,1300,1305,1311,1317,1323,1329,1335,1341,1347],"ul",{},[1239,1240,1241,1245],"li",{},[1242,1243,1244],"strong",{},"2025-07-07 09:33",": TP-LINK 主路由设备上线告警（上次离线原因：设备重启）",[1239,1247,1248,1251],{},[1242,1249,1250],{},"2025-07-07 09:34",": 收到群晖异常关机的邮件通知（收到该通知说明群晖已经重启过了，实际重启时间会更早一点）",[1239,1253,1254,1257],{},[1242,1255,1256],{},"2025-07-07 09:36",": 尝试登录群晖 DSM，发现域名解析有问题，无法登录；尝试 ToDesk 远程连接家里的 PC，发现不在线（未开机）",[1239,1259,1260,1263],{},[1242,1261,1262],{},"2025-07-07 09:40",": 收到 Uptime Kuma 监控服务的各种告警通知，多项服务不可用",[1239,1265,1266,1269],{},[1242,1267,1268],{},"2025-07-07 09:52",": 通过 TP-LINK 商用云平台远程查看主路由，发现可连接，但由于之前为了 IPTV 改为了光猫的子路由（非桥接），无法查看到公网 IP；尝试通过电信的小翼管家查看公网 IP，发现没有入口可查",[1239,1271,1272,1275],{},[1242,1273,1274],{},"2025-07-07 09:54",": 尝试通过群晖的 QuickConnect 远程访问，发现之前被我关闭了",[1239,1277,1278,1281],{},[1242,1279,1280],{},"2025-07-07 10:30",": 查看自己写的 bots 服务代码（含 ddns 功能），请求失败时，有 backoff 策略，首次失败休眠 1 分钟，然后再失败休眠 10 分钟，再失败休眠 1 小时，决定再等一小时看看",[1239,1283,1284,1287],{},[1242,1285,1286],{},"2025-07-07 11:00",": 在 TP-LINK 主路由管理页面尝试通过网络唤醒服务唤醒家里的 PC，发现无法唤醒（事后发现之前记录的网卡 MAC 不对）",[1239,1289,1290,1293],{},[1242,1291,1292],{},"2025-07-07 11:30",": 通过米家控制办公桌的智能插座电源重启，尝试唤醒 PC，未成功；打算通过控制机柜的智能插座重启，实现所有服务的重启，但还打算再等等 bots 的 ddns 能否生效",[1239,1295,1296,1299],{},[1242,1297,1298],{},"2025-07-07 11:44",": 等了 2 个多小时了，感觉 bots 服务可能已经不在运行，再等下去也没用了，经过深思熟虑决定重启整个机柜电源",[1239,1301,1302,1304],{},[1242,1303,1298],{},": 通过米家控制智能插座关闭电源，发现状态未更新，再次点击发现操作失败，此时发现智能插座设备已离线，意识到机柜一旦断电，所有米家设备也无法控制了，再也无法打开",[1239,1306,1307,1310],{},[1242,1308,1309],{},"2025-07-07 11:50",": 出发回家，准备手动重启机柜电源",[1239,1312,1313,1316],{},[1242,1314,1315],{},"2025-07-07 12:49",": 到家，手动开启机柜智能插座电源",[1239,1318,1319,1322],{},[1242,1320,1321],{},"2025-07-07 12:50",": 打开 PC，发现 主板 PCI-E 设备唤醒是 Enabled",[1239,1324,1325,1328],{},[1242,1326,1327],{},"2025-07-07 12:51",": 进入 PC 系统，发现网卡的允许设备唤醒也是启用的，但网卡 MAC 地址和之前配置的不一样，原因后面详述",[1239,1330,1331,1334],{},[1242,1332,1333],{},"2025-07-07 12:53",": 通过 PC 内网登录 portainer，发现 bots 容器处于 stopped 状态（Stopped for 3 hours with exit code 127），finished 时间为 09:33:52",[1239,1336,1337,1340],{},[1242,1338,1339],{},"2025-07-07 12:54",": 手动重新启动 bots 容器，正常启动",[1239,1342,1343,1346],{},[1242,1344,1345],{},"2025-07-07 12:55",": bots 服务已正常更新域名解析，手机切换到蜂窝测试，已经可正常访问",[1239,1348,1349,1352],{},[1242,1350,1351],{},"2025-07-07 13:01",": 出门赶回公司",[1232,1354,1355],{"id":1355},"原因分析",[1236,1357,1358,1364,1370,1376,1396],{},[1239,1359,1360,1363],{},[1242,1361,1362],{},"导火索","：家里异常断电（TP-LINK 和群晖都在机柜里，他俩同时重启，可断定机柜掉电了；光猫在弱电箱里，查看光猫的启动时间，也在同一时间重启过，可判断是全屋断电了）",[1239,1365,1366,1369],{},[1242,1367,1368],{},"直接原因","：自建的 DDNS 服务在光猫重启后公网 IP 发生变化的情况下未更新解析，导致所有服务无法远程访问",[1239,1371,1372,1375],{},[1242,1373,1374],{},"根本原因","：包含了 DDNS 服务的 bots 容器在宿主机重启后未能重启成功，经过分析发现因为 bots 容器启动过程中挂载了群晖中的一个目录，用来更新 clash 的配置文件，但是群晖启动会比 bots 容器所在的宿主机慢，可能导致了启动失败",[1239,1377,1378,1381,1382],{},[1242,1379,1380],{},"处理慢的原因（多种补救措施失效）","：\n",[1236,1383,1384,1387,1390,1393],{},[1239,1385,1386],{},"家里的 PC 未开机，无法通过 ToDesk 远程连接处理（之前几次类似问题都是通过 ToDesk 远程修复）",[1239,1388,1389],{},"家里没人，无法帮忙手动启动 PC",[1239,1391,1392],{},"PC 的远程唤醒功能失效，原因是网卡 MAC 地址记录不正确，这是因为之前记录的是一个虚拟网卡的 MAC，上次去掉了虚拟网卡，直接走的物理网卡，但是忘记记录 MAC 地址",[1239,1394,1395],{},"群晖的 QuickConnect 远程访问服务失效，之前感觉用不到被我手动关闭了",[1239,1397,1398,1401],{},[1242,1399,1400],{},"故障升级原因","：由于多个补救方案失效，尝试通过机柜断电重启的方式补救，结果所有设备断电，断绝了任何远程补救的可能",[1232,1403,1404],{"id":1404},"改进措施",[1236,1406,1407,1410,1413,1416,1419,1422],{},[1239,1408,1409],{},"✅ 购买 UPS，确保机柜设备在短暂断电时能够继续供电，避免意外断电导致的服务中断（07-08 更新: 已购买山特 SANTAK TG-BOX850 UPS）",[1239,1411,1412],{},"✅ 提升 DDNS 服务的核心程度，从 bots 项目中独立出来，减少其他依赖（07-08 更新: 已完成）",[1239,1414,1415],{},"✅ 启用群晖的 QuickConnect 服务， DDNS 失效后可连接到群晖上进行一些处理",[1239,1417,1418],{},"✅ 确保 PC 的网络远程唤醒功能正常，可通过远程连接到 PC 解决问题",[1239,1420,1421],{},"✅ 部署一个 Cloudflare Tunnel 容器，作为 DDNS 失效后的备用方案",[1239,1423,1424],{},"✅ 把机柜的米家插座从米家 APP 首页移除，避免误操作关闭电源，吸取教训，以后不要再给机柜断电了",[1232,1426,1427],{"id":1427},"经验教训",[1236,1429,1430,1433,1436],{},[1239,1431,1432],{},"之前出现过一次机柜断电后 DDNS 服务不可用导致无法访问的问题，当时通过 ToDesk 远程连接到 PC，然后通过内网重启了 bots 服务解决了问题，但应该更进一步，看看为什么 bots 服务没有自动重启成功，从而可以避免这次的事故",[1239,1434,1435],{},"核心的服务需要保障高可用，例如公网访问这件事，除了自建的 DDNS 之外，还需要通过 QuickConnect、Cloudflare Tunnel 等多种手段保证可用性",[1239,1437,1438],{},"任何情况下都不要尝试给整个机柜断电这种操作，应该优先考虑其他补救措施",{"title":63,"searchDepth":78,"depth":78,"links":1440},[1441,1442,1443,1444],{"id":1234,"depth":78,"text":1234},{"id":1355,"depth":78,"text":1355},{"id":1404,"depth":78,"text":1404},{"id":1427,"depth":78,"text":1427},"jpg","2025-07-07",{},"\u002Fposts\u002F2025\u002Fhomelab-disaster-postmortem",{"text":1450,"minutes":1451,"time":1452,"words":1453},"8 min read",7.38,442800,1476,{"title":1228,"description":63},{"loc":1448,"lastmod":1446},"posts\u002F2025\u002F20250707.homelab-disaster-postmortem",[543,1458,1459,1460],"HomeLab","运维","复盘","ICyfDes8hfks9c7nlNUEMy8kuWktGhB5Iu1TriECazU",{"id":1463,"title":1464,"body":1465,"class":528,"cover":1212,"coverSize":528,"date":4962,"description":1469,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":4963,"navigation":415,"path":4964,"readingTime":4965,"seo":4970,"sitemap":4971,"stem":4973,"tags":4974,"time":528,"weather":528,"__hash__":4978},"posts\u002Fposts\u002F2025\u002F20250212.display-deepseek-r1-thinking.md","如何在 Open WebUI 中显示 DeepSeek-R1 的思考过程",{"type":25,"value":1466,"toc":4955},[1467,1470,1491,1495,1500,1550,1555,1651,1655,1672,1674,1687,1689,1693,1708,1711,1716,1719,1727,1730,1739,4847,4852,4855,4860,4863,4867,4882,4885,4890,4893,4897,4904,4920,4943,4946,4949,4952],[28,1468,1469],{},"书接上回，咱们部署好了 DeepSeek-R1 之后，发现没办法显示思考过程，只能等结果出来之后查看结果，这个体感上就会感觉响应很慢。",[28,1471,1472,1473,1478,1479,1484,1485,1490],{},"查看了 ",[38,1474,1477],{"href":1475,"rel":1476},"https:\u002F\u002Fgithub.com\u002Fopen-webui\u002Fopen-webui",[42],"open-webui"," 的 issues，发现已经有人提过这个问题，并且已有",[38,1480,1483],{"href":1481,"rel":1482},"https:\u002F\u002Fgithub.com\u002Fopen-webui\u002Fopen-webui\u002Fissues\u002F9488#issuecomment-2640537231",[42],"解决方案","，那就是通过 ",[38,1486,1489],{"href":1487,"rel":1488},"https:\u002F\u002Fdocs.openwebui.com\u002Fpipelines\u002F",[42],"Pipelines"," 来实现。具体步骤如下：",[1232,1492,1494],{"id":1493},"_1-部署-pipelines-容器","1. 部署 pipelines 容器",[1236,1496,1497],{},[1239,1498,1499],{},"docker run 命令：",[58,1501,1505],{"className":1502,"code":1503,"language":1504,"meta":63,"style":63},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","docker run -d -p 9099:9099 --add-host=host.docker.internal:host-gateway -v pipelines:\u002Fapp\u002Fpipelines --name pipelines --restart always ghcr.io\u002Fopen-webui\u002Fpipelines:main\n","bash",[65,1506,1507],{"__ignoreMap":63},[68,1508,1509,1513,1516,1520,1523,1526,1529,1532,1535,1538,1541,1544,1547],{"class":70,"line":71},[68,1510,1512],{"class":1511},"sbgvK","docker",[68,1514,1515],{"class":116}," run",[68,1517,1519],{"class":1518},"stzsN"," -d",[68,1521,1522],{"class":1518}," -p",[68,1524,1525],{"class":116}," 9099:9099",[68,1527,1528],{"class":1518}," --add-host=host.docker.internal:host-gateway",[68,1530,1531],{"class":1518}," -v",[68,1533,1534],{"class":116}," pipelines:\u002Fapp\u002Fpipelines",[68,1536,1537],{"class":1518}," --name",[68,1539,1540],{"class":116}," pipelines",[68,1542,1543],{"class":1518}," --restart",[68,1545,1546],{"class":116}," always",[68,1548,1549],{"class":116}," ghcr.io\u002Fopen-webui\u002Fpipelines:main\n",[1236,1551,1552],{},[1239,1553,1554],{},"docker-compose.yml 配置：",[58,1556,1560],{"className":1557,"code":1558,"language":1559,"meta":63,"style":63},"language-yaml shiki shiki-themes material-theme-lighter github-light github-dark","services:\n  open-webui-pipelines:\n    container_name: open-webui-pipelines\n    image: ghcr.io\u002Fopen-webui\u002Fpipelines:main\n    network_mode: bridge\n    ports:\n      - '9099:9099'\n    volumes:\n      - .\u002Fpipelines:\u002Fapp\u002Fpipelines\n    restart: always\n","yaml",[65,1561,1562,1570,1577,1587,1596,1606,1613,1627,1634,1641],{"__ignoreMap":63},[68,1563,1564,1567],{"class":70,"line":71},[68,1565,1566],{"class":730},"services",[68,1568,1569],{"class":74},":\n",[68,1571,1572,1575],{"class":70,"line":78},[68,1573,1574],{"class":730},"  open-webui-pipelines",[68,1576,1569],{"class":74},[68,1578,1579,1582,1584],{"class":70,"line":98},[68,1580,1581],{"class":730},"    container_name",[68,1583,92],{"class":74},[68,1585,1586],{"class":116}," open-webui-pipelines\n",[68,1588,1589,1592,1594],{"class":70,"line":123},[68,1590,1591],{"class":730},"    image",[68,1593,92],{"class":74},[68,1595,1549],{"class":116},[68,1597,1598,1601,1603],{"class":70,"line":129},[68,1599,1600],{"class":730},"    network_mode",[68,1602,92],{"class":74},[68,1604,1605],{"class":116}," bridge\n",[68,1607,1608,1611],{"class":70,"line":212},[68,1609,1610],{"class":730},"    ports",[68,1612,1569],{"class":74},[68,1614,1615,1618,1621,1624],{"class":70,"line":233},[68,1616,1617],{"class":74},"      -",[68,1619,1620],{"class":112}," '",[68,1622,1623],{"class":116},"9099:9099",[68,1625,1626],{"class":112},"'\n",[68,1628,1629,1632],{"class":70,"line":268},[68,1630,1631],{"class":730},"    volumes",[68,1633,1569],{"class":74},[68,1635,1636,1638],{"class":70,"line":289},[68,1637,1617],{"class":74},[68,1639,1640],{"class":116}," .\u002Fpipelines:\u002Fapp\u002Fpipelines\n",[68,1642,1643,1646,1648],{"class":70,"line":308},[68,1644,1645],{"class":730},"    restart",[68,1647,92],{"class":74},[68,1649,1650],{"class":116}," always\n",[1232,1652,1654],{"id":1653},"_2-配置管道连接","2. 配置管道连接",[1236,1656,1657],{},[1239,1658,1659,1660,1663,1664,1667,1668,1671],{},"在 ",[65,1661,1662],{},"管理员设置"," -> ",[65,1665,1666],{},"外部连接"," 中按 ",[65,1669,1670],{},"+"," 添加一个连接。",[554,1673],{"filename":556},[1236,1675,1676],{},[1239,1677,1678,1679,1682,1683,1686],{},"管道地址通过内网 IP、hostname 或域名等，加上前面 pipelines 容器的端口号 ",[65,1680,1681],{},"9099","；密钥是固定的 ",[65,1684,1685],{},"0p3n-w3bu!","。",[554,1688],{"filename":1203},[1232,1690,1692],{"id":1691},"_3-创建函数","3. 创建函数",[1236,1694,1695],{},[1239,1696,1697,1698,1703,1704,1707],{},"访问 ",[38,1699,1702],{"href":1700,"rel":1701},"https:\u002F\u002Fopenwebui.com\u002Ff\u002Fzgccrui\u002Fdeepseek_r1",[42],"DeepSeek R1 Function"," 函数页面，通过 ",[65,1705,1706],{},"Get"," 按钮完成注册。",[554,1709],{"filename":1710},"03.png",[1236,1712,1713],{},[1239,1714,1715],{},"注册完成之后，在弹出的页面中输入自己的站点地址自动跳转并导入函数。",[554,1717],{"filename":1718},"04.png",[1236,1720,1721],{},[1239,1722,1723,1724,1726],{},"也可以手动复制该函数代码，然后点击 ",[65,1725,1670],{}," 加号手动添加。",[554,1728],{"filename":1729},"05.png",[1236,1731,1732],{},[1239,1733,1734,1735,1738],{},"以下是我在 ",[65,1736,1737],{},"1.2.10"," 版本上修改过的代码，支持了模型显示名的配置，这样方便与原始版本区分",[58,1740,1744],{"className":1741,"code":1742,"language":1743,"meta":63,"style":63},"language-python shiki shiki-themes material-theme-lighter github-light github-dark","\"\"\"\ntitle: DeepSeek R1\nauthor: zgccrui\ndescription: 在OpwenWebUI中显示DeepSeek R1模型的思维链 - 仅支持0.5.6及以上版本\nversion: 1.2.10\nlicence: MIT\n\"\"\"\n\nimport json\nimport httpx\nimport re\nfrom typing import AsyncGenerator, Callable, Awaitable\nfrom pydantic import BaseModel, Field\nimport asyncio\n\nclass Pipe:\n    class Valves(BaseModel):\n        DEEPSEEK_API_BASE_URL: str = Field(\n            default=\"https:\u002F\u002Fapi.deepseek.com\u002Fv1\",\n            description=\"DeepSeek API的基础请求地址\",\n        )\n        DEEPSEEK_API_KEY: str = Field(\n            default=\"\", description=\"用于身份验证的DeepSeek API密钥，可从控制台获取\"\n        )\n        DEEPSEEK_API_MODEL: str = Field(\n            default=\"deepseek-reasoner\",\n            description=\"API请求的模型名称，默认为 deepseek-reasoner\",\n        )\n        DEEPSEEK_MODEL_DISPLAY_NAME: str = Field(\n            default=\"deepseek-reasoner-fix\",\n            description=\"模型名称，默认为 deepseek-reasoner-fix\",\n        )\n\n    def __init__(self):\n        self.valves = self.Valves()\n        self.data_prefix = \"data:\"\n        self.emitter = None\n\n    def pipes(self):\n        return [\n            {\n                \"id\": self.valves.DEEPSEEK_API_MODEL,\n                \"name\": self.valves.DEEPSEEK_MODEL_DISPLAY_NAME,\n            }\n        ]\n\n    async def pipe(\n        self, body: dict, __event_emitter__: Callable[[dict], Awaitable[None]] = None\n    ) -> AsyncGenerator[str, None]:\n        \"\"\"主处理管道（已移除缓冲）\"\"\"\n        thinking_state = {\"thinking\": -1}  # 使用字典来存储thinking状态\n        self.emitter = __event_emitter__\n\n        # 验证配置\n        if not self.valves.DEEPSEEK_API_KEY:\n            yield json.dumps({\"error\": \"未配置API密钥\"}, ensure_ascii=False)\n            return\n\n        # 准备请求参数\n        headers = {\n            \"Authorization\": f\"Bearer {self.valves.DEEPSEEK_API_KEY}\",\n            \"Content-Type\": \"application\u002Fjson\",\n        }\n\n        try:\n            # 模型ID提取\n            model_id = body[\"model\"].split(\".\", 1)[-1]\n            payload = {**body, \"model\": model_id}\n\n            # 处理消息以防止连续的相同角色\n            messages = payload[\"messages\"]\n            i = 0\n            while i \u003C len(messages) - 1:\n                if messages[i][\"role\"] == messages[i + 1][\"role\"]:\n                    # 插入具有替代角色的占位符消息\n                    alternate_role = (\n                        \"assistant\" if messages[i][\"role\"] == \"user\" else \"user\"\n                    )\n                    messages.insert(\n                        i + 1,\n                        {\"role\": alternate_role, \"content\": \"[Unfinished thinking]\"},\n                    )\n                i += 1\n\n            # yield json.dumps(payload, ensure_ascii=False)\n\n            # 发起API请求\n            async with httpx.AsyncClient(http2=True) as client:\n                async with client.stream(\n                    \"POST\",\n                    f\"{self.valves.DEEPSEEK_API_BASE_URL}\u002Fchat\u002Fcompletions\",\n                    json=payload,\n                    headers=headers,\n                    timeout=300,\n                ) as response:\n                    # 错误处理\n                    if response.status_code != 200:\n                        error = await response.aread()\n                        yield self._format_error(response.status_code, error)\n                        return\n\n                    # 流式处理响应\n                    async for line in response.aiter_lines():\n                        if not line.startswith(self.data_prefix):\n                            continue\n\n                        # 截取 JSON 字符串\n                        json_str = line[len(self.data_prefix) :]\n\n                        # 去除首尾空格后检查是否为结束标记\n                        if json_str.strip() == \"[DONE]\":\n                            return\n\n                        try:\n                            data = json.loads(json_str)\n                        except json.JSONDecodeError as e:\n                            # 格式化错误信息，这里传入错误类型和详细原因（包括出错内容和异常信息）\n                            error_detail = f\"解析失败 - 内容：{json_str}，原因：{e}\"\n                            yield self._format_error(\"JSONDecodeError\", error_detail)\n                            return\n\n                        choice = data.get(\"choices\", [{}])[0]\n\n                        # 结束条件判断\n                        if choice.get(\"finish_reason\"):\n                            return\n\n                        # 状态机处理\n                        state_output = await self._update_thinking_state(\n                            choice.get(\"delta\", {}), thinking_state\n                        )\n                        if state_output:\n                            yield state_output  # 直接发送状态标记\n                            if state_output == \"\u003Cthink>\":\n                                yield \"\\n\"\n\n                        # 内容处理并立即发送\n                        content = self._process_content(choice[\"delta\"])\n                        if content:\n                            if content.startswith(\"\u003Cthink>\"):\n                                match = re.match(r\"^\u003Cthink>\", content)\n                                if match:\n                                    content = re.sub(r\"^\u003Cthink>\", \"\", content)\n                                    yield \"\u003Cthink>\"\n                                    await asyncio.sleep(0.1)\n                                    yield \"\\n\"\n\n                            elif content.startswith(\"\u003C\u002Fthink>\"):\n                                match = re.match(r\"^\u003C\u002Fthink>\", content)\n                                if match:\n                                    content = re.sub(r\"^\u003C\u002Fthink>\", \"\", content)\n                                    yield \"\u003C\u002Fthink>\"\n                                    await asyncio.sleep(0.1)\n                                    yield \"\\n\"\n                            yield content\n\n        except Exception as e:\n            yield self._format_exception(e)\n\n    async def _update_thinking_state(self, delta: dict, thinking_state: dict) -> str:\n        \"\"\"更新思考状态机（简化版）\"\"\"\n        state_output = \"\"\n\n        # 状态转换：未开始 -> 思考中\n        if thinking_state[\"thinking\"] == -1 and delta.get(\"reasoning_content\"):\n            thinking_state[\"thinking\"] = 0\n            state_output = \"\u003Cthink>\"\n\n        # 状态转换：思考中 -> 已回答\n        elif (\n            thinking_state[\"thinking\"] == 0\n            and not delta.get(\"reasoning_content\")\n            and delta.get(\"content\")\n        ):\n            thinking_state[\"thinking\"] = 1\n            state_output = \"\\n\u003C\u002Fthink>\\n\\n\"\n\n        return state_output\n\n    def _process_content(self, delta: dict) -> str:\n        \"\"\"直接返回处理后的内容\"\"\"\n        return delta.get(\"reasoning_content\", \"\") or delta.get(\"content\", \"\")\n\n    def _format_error(self, status_code: int, error: bytes) -> str:\n        # 如果 error 已经是字符串，则无需 decode\n        if isinstance(error, str):\n            error_str = error\n        else:\n            error_str = error.decode(errors=\"ignore\")\n\n        try:\n            err_msg = json.loads(error_str).get(\"message\", error_str)[:200]\n        except Exception as e:\n            err_msg = error_str[:200]\n        return json.dumps(\n            {\"error\": f\"HTTP {status_code}: {err_msg}\"}, ensure_ascii=False\n        )\n\n    def _format_exception(self, e: Exception) -> str:\n        \"\"\"异常格式化保持不变\"\"\"\n        err_type = type(e).__name__\n        return json.dumps({\"error\": f\"{err_type}: {str(e)}\"}, ensure_ascii=False)\n\n","python",[65,1745,1746,1752,1758,1763,1768,1773,1778,1782,1786,1795,1802,1809,1832,1849,1856,1860,1871,1887,1908,1925,1941,1946,1961,1984,1988,2003,2018,2033,2037,2053,2069,2085,2090,2095,2112,2137,2156,2171,2176,2191,2199,2205,2232,2256,2262,2268,2273,2287,2337,2361,2372,2405,2419,2424,2430,2452,2498,2504,2509,2515,2525,2563,2584,2590,2595,2603,2609,2658,2689,2694,2700,2722,2733,2760,2811,2817,2827,2875,2881,2894,2906,2944,2949,2961,2966,2972,2977,2983,3020,3037,3050,3078,3091,3104,3117,3130,3136,3157,3177,3206,3212,3217,3223,3248,3274,3280,3285,3291,3319,3324,3330,3357,3363,3368,3376,3398,3418,3424,3455,3482,3487,3492,3526,3531,3537,3560,3565,3570,3576,3595,3622,3628,3638,3649,3670,3683,3688,3694,3725,3735,3756,3793,3804,3842,3854,3875,3886,3891,3914,3945,3954,3989,4000,4017,4028,4036,4041,4056,4074,4079,4119,4129,4140,4145,4151,4194,4214,4228,4233,4239,4247,4266,4290,4311,4317,4336,4354,4359,4367,4372,4400,4410,4458,4463,4502,4508,4526,4537,4545,4575,4580,4587,4633,4646,4662,4675,4721,4726,4731,4759,4769,4789],{"__ignoreMap":63},[68,1747,1748],{"class":70,"line":71},[68,1749,1751],{"class":1750},"s2W-s","\"\"\"\n",[68,1753,1754],{"class":70,"line":78},[68,1755,1757],{"class":1756},"sithA","title: DeepSeek R1\n",[68,1759,1760],{"class":70,"line":98},[68,1761,1762],{"class":1756},"author: zgccrui\n",[68,1764,1765],{"class":70,"line":123},[68,1766,1767],{"class":1756},"description: 在OpwenWebUI中显示DeepSeek R1模型的思维链 - 仅支持0.5.6及以上版本\n",[68,1769,1770],{"class":70,"line":129},[68,1771,1772],{"class":1756},"version: 1.2.10\n",[68,1774,1775],{"class":70,"line":212},[68,1776,1777],{"class":1756},"licence: MIT\n",[68,1779,1780],{"class":70,"line":233},[68,1781,1751],{"class":1750},[68,1783,1784],{"class":70,"line":268},[68,1785,416],{"emptyLinePlaceholder":415},[68,1787,1788,1792],{"class":70,"line":289},[68,1789,1791],{"class":1790},"sVHd0","import",[68,1793,1794],{"class":373}," json\n",[68,1796,1797,1799],{"class":70,"line":308},[68,1798,1791],{"class":1790},[68,1800,1801],{"class":373}," httpx\n",[68,1803,1804,1806],{"class":70,"line":314},[68,1805,1791],{"class":1790},[68,1807,1808],{"class":373}," re\n",[68,1810,1811,1814,1817,1819,1822,1824,1827,1829],{"class":70,"line":320},[68,1812,1813],{"class":1790},"from",[68,1815,1816],{"class":373}," typing ",[68,1818,1791],{"class":1790},[68,1820,1821],{"class":373}," AsyncGenerator",[68,1823,255],{"class":74},[68,1825,1826],{"class":373}," Callable",[68,1828,255],{"class":74},[68,1830,1831],{"class":373}," Awaitable\n",[68,1833,1834,1836,1839,1841,1844,1846],{"class":70,"line":889},[68,1835,1813],{"class":1790},[68,1837,1838],{"class":373}," pydantic ",[68,1840,1791],{"class":1790},[68,1842,1843],{"class":373}," BaseModel",[68,1845,255],{"class":74},[68,1847,1848],{"class":373}," Field\n",[68,1850,1851,1853],{"class":70,"line":909},[68,1852,1791],{"class":1790},[68,1854,1855],{"class":373}," asyncio\n",[68,1857,1858],{"class":70,"line":929},[68,1859,416],{"emptyLinePlaceholder":415},[68,1861,1862,1866,1869],{"class":70,"line":949},[68,1863,1865],{"class":1864},"sbsja","class",[68,1867,1868],{"class":1511}," Pipe",[68,1870,1569],{"class":74},[68,1872,1873,1876,1879,1881,1884],{"class":70,"line":969},[68,1874,1875],{"class":1864},"    class",[68,1877,1878],{"class":1511}," Valves",[68,1880,648],{"class":74},[68,1882,1883],{"class":1511},"BaseModel",[68,1885,1886],{"class":74},"):\n",[68,1888,1889,1892,1894,1897,1901,1905],{"class":70,"line":989},[68,1890,1891],{"class":665},"        DEEPSEEK_API_BASE_URL",[68,1893,92],{"class":74},[68,1895,1896],{"class":104}," str",[68,1898,1900],{"class":1899},"smGrS"," =",[68,1902,1904],{"class":1903},"slqww"," Field",[68,1906,1907],{"class":74},"(\n",[68,1909,1910,1914,1916,1918,1921,1923],{"class":70,"line":1009},[68,1911,1913],{"class":1912},"s99_P","            default",[68,1915,877],{"class":1899},[68,1917,89],{"class":112},[68,1919,1920],{"class":116},"https:\u002F\u002Fapi.deepseek.com\u002Fv1",[68,1922,89],{"class":112},[68,1924,169],{"class":74},[68,1926,1927,1930,1932,1934,1937,1939],{"class":70,"line":1029},[68,1928,1929],{"class":1912},"            description",[68,1931,877],{"class":1899},[68,1933,89],{"class":112},[68,1935,1936],{"class":116},"DeepSeek API的基础请求地址",[68,1938,89],{"class":112},[68,1940,169],{"class":74},[68,1942,1943],{"class":70,"line":1049},[68,1944,1945],{"class":74},"        )\n",[68,1947,1948,1951,1953,1955,1957,1959],{"class":70,"line":1069},[68,1949,1950],{"class":665},"        DEEPSEEK_API_KEY",[68,1952,92],{"class":74},[68,1954,1896],{"class":104},[68,1956,1900],{"class":1899},[68,1958,1904],{"class":1903},[68,1960,1907],{"class":74},[68,1962,1963,1965,1967,1970,1972,1975,1977,1979,1982],{"class":70,"line":1088},[68,1964,1913],{"class":1912},[68,1966,877],{"class":1899},[68,1968,1969],{"class":112},"\"\"",[68,1971,255],{"class":74},[68,1973,1974],{"class":1912}," description",[68,1976,877],{"class":1899},[68,1978,89],{"class":112},[68,1980,1981],{"class":116},"用于身份验证的DeepSeek API密钥，可从控制台获取",[68,1983,120],{"class":112},[68,1985,1986],{"class":70,"line":1108},[68,1987,1945],{"class":74},[68,1989,1990,1993,1995,1997,1999,2001],{"class":70,"line":1128},[68,1991,1992],{"class":665},"        DEEPSEEK_API_MODEL",[68,1994,92],{"class":74},[68,1996,1896],{"class":104},[68,1998,1900],{"class":1899},[68,2000,1904],{"class":1903},[68,2002,1907],{"class":74},[68,2004,2005,2007,2009,2011,2014,2016],{"class":70,"line":1148},[68,2006,1913],{"class":1912},[68,2008,877],{"class":1899},[68,2010,89],{"class":112},[68,2012,2013],{"class":116},"deepseek-reasoner",[68,2015,89],{"class":112},[68,2017,169],{"class":74},[68,2019,2020,2022,2024,2026,2029,2031],{"class":70,"line":1168},[68,2021,1929],{"class":1912},[68,2023,877],{"class":1899},[68,2025,89],{"class":112},[68,2027,2028],{"class":116},"API请求的模型名称，默认为 deepseek-reasoner",[68,2030,89],{"class":112},[68,2032,169],{"class":74},[68,2034,2035],{"class":70,"line":1187},[68,2036,1945],{"class":74},[68,2038,2040,2043,2045,2047,2049,2051],{"class":70,"line":2039},29,[68,2041,2042],{"class":665},"        DEEPSEEK_MODEL_DISPLAY_NAME",[68,2044,92],{"class":74},[68,2046,1896],{"class":104},[68,2048,1900],{"class":1899},[68,2050,1904],{"class":1903},[68,2052,1907],{"class":74},[68,2054,2056,2058,2060,2062,2065,2067],{"class":70,"line":2055},30,[68,2057,1913],{"class":1912},[68,2059,877],{"class":1899},[68,2061,89],{"class":112},[68,2063,2064],{"class":116},"deepseek-reasoner-fix",[68,2066,89],{"class":112},[68,2068,169],{"class":74},[68,2070,2072,2074,2076,2078,2081,2083],{"class":70,"line":2071},31,[68,2073,1929],{"class":1912},[68,2075,877],{"class":1899},[68,2077,89],{"class":112},[68,2079,2080],{"class":116},"模型名称，默认为 deepseek-reasoner-fix",[68,2082,89],{"class":112},[68,2084,169],{"class":74},[68,2086,2088],{"class":70,"line":2087},32,[68,2089,1945],{"class":74},[68,2091,2093],{"class":70,"line":2092},33,[68,2094,416],{"emptyLinePlaceholder":415},[68,2096,2098,2101,2104,2106,2110],{"class":70,"line":2097},34,[68,2099,2100],{"class":1864},"    def",[68,2102,2103],{"class":630}," __init__",[68,2105,648],{"class":74},[68,2107,2109],{"class":2108},"smCYv","self",[68,2111,1886],{"class":74},[68,2113,2115,2118,2120,2124,2126,2129,2131,2134],{"class":70,"line":2114},35,[68,2116,2117],{"class":665},"        self",[68,2119,404],{"class":74},[68,2121,2123],{"class":2122},"skxfh","valves",[68,2125,1900],{"class":1899},[68,2127,2128],{"class":665}," self",[68,2130,404],{"class":74},[68,2132,2133],{"class":1903},"Valves",[68,2135,2136],{"class":74},"()\n",[68,2138,2140,2142,2144,2147,2149,2151,2154],{"class":70,"line":2139},36,[68,2141,2117],{"class":665},[68,2143,404],{"class":74},[68,2145,2146],{"class":2122},"data_prefix",[68,2148,1900],{"class":1899},[68,2150,113],{"class":112},[68,2152,2153],{"class":116},"data:",[68,2155,120],{"class":112},[68,2157,2159,2161,2163,2166,2168],{"class":70,"line":2158},37,[68,2160,2117],{"class":665},[68,2162,404],{"class":74},[68,2164,2165],{"class":2122},"emitter",[68,2167,1900],{"class":1899},[68,2169,2170],{"class":81}," None\n",[68,2172,2174],{"class":70,"line":2173},38,[68,2175,416],{"emptyLinePlaceholder":415},[68,2177,2179,2181,2185,2187,2189],{"class":70,"line":2178},39,[68,2180,2100],{"class":1864},[68,2182,2184],{"class":2183},"sGLFI"," pipes",[68,2186,648],{"class":74},[68,2188,2109],{"class":2108},[68,2190,1886],{"class":74},[68,2192,2194,2197],{"class":70,"line":2193},40,[68,2195,2196],{"class":1790},"        return",[68,2198,183],{"class":74},[68,2200,2202],{"class":70,"line":2201},41,[68,2203,2204],{"class":74},"            {\n",[68,2206,2208,2211,2214,2216,2218,2220,2222,2224,2226,2230],{"class":70,"line":2207},42,[68,2209,2210],{"class":112},"                \"",[68,2212,2213],{"class":116},"id",[68,2215,89],{"class":112},[68,2217,92],{"class":74},[68,2219,2128],{"class":665},[68,2221,404],{"class":74},[68,2223,2123],{"class":2122},[68,2225,404],{"class":74},[68,2227,2229],{"class":2228},"swQdS","DEEPSEEK_API_MODEL",[68,2231,169],{"class":74},[68,2233,2235,2237,2239,2241,2243,2245,2247,2249,2251,2254],{"class":70,"line":2234},43,[68,2236,2210],{"class":112},[68,2238,217],{"class":116},[68,2240,89],{"class":112},[68,2242,92],{"class":74},[68,2244,2128],{"class":665},[68,2246,404],{"class":74},[68,2248,2123],{"class":2122},[68,2250,404],{"class":74},[68,2252,2253],{"class":2228},"DEEPSEEK_MODEL_DISPLAY_NAME",[68,2255,169],{"class":74},[68,2257,2259],{"class":70,"line":2258},44,[68,2260,2261],{"class":74},"            }\n",[68,2263,2265],{"class":70,"line":2264},45,[68,2266,2267],{"class":74},"        ]\n",[68,2269,2271],{"class":70,"line":2270},46,[68,2272,416],{"emptyLinePlaceholder":415},[68,2274,2276,2279,2282,2285],{"class":70,"line":2275},47,[68,2277,2278],{"class":1864},"    async",[68,2280,2281],{"class":1864}," def",[68,2283,2284],{"class":2183}," pipe",[68,2286,1907],{"class":74},[68,2288,2290,2292,2294,2298,2300,2303,2305,2308,2310,2312,2315,2318,2321,2324,2327,2330,2333,2335],{"class":70,"line":2289},48,[68,2291,2117],{"class":2108},[68,2293,255],{"class":74},[68,2295,2297],{"class":2296},"sFwrP"," body",[68,2299,92],{"class":74},[68,2301,2302],{"class":104}," dict",[68,2304,255],{"class":74},[68,2306,2307],{"class":2296}," __event_emitter__",[68,2309,92],{"class":74},[68,2311,1826],{"class":373},[68,2313,2314],{"class":74},"[[",[68,2316,2317],{"class":104},"dict",[68,2319,2320],{"class":74},"],",[68,2322,2323],{"class":373}," Awaitable",[68,2325,2326],{"class":74},"[",[68,2328,2329],{"class":81},"None",[68,2331,2332],{"class":74},"]]",[68,2334,1900],{"class":1899},[68,2336,2170],{"class":81},[68,2338,2340,2343,2346,2348,2350,2353,2355,2358],{"class":70,"line":2339},49,[68,2341,2342],{"class":74},"    )",[68,2344,2345],{"class":74}," ->",[68,2347,1821],{"class":373},[68,2349,2326],{"class":74},[68,2351,2352],{"class":104},"str",[68,2354,255],{"class":74},[68,2356,2357],{"class":81}," None",[68,2359,2360],{"class":74},"]:\n",[68,2362,2364,2367,2370],{"class":70,"line":2363},50,[68,2365,2366],{"class":1750},"        \"\"\"",[68,2368,2369],{"class":1756},"主处理管道（已移除缓冲）",[68,2371,1751],{"class":1750},[68,2373,2375,2378,2380,2383,2385,2388,2390,2392,2395,2398,2401],{"class":70,"line":2374},51,[68,2376,2377],{"class":373},"        thinking_state ",[68,2379,877],{"class":1899},[68,2381,2382],{"class":74}," {",[68,2384,89],{"class":112},[68,2386,2387],{"class":116},"thinking",[68,2389,89],{"class":112},[68,2391,92],{"class":74},[68,2393,2394],{"class":1899}," -",[68,2396,401],{"class":2397},"srdBf",[68,2399,2400],{"class":74},"}",[68,2402,2404],{"class":2403},"sutJx","  # 使用字典来存储thinking状态\n",[68,2406,2408,2410,2412,2414,2416],{"class":70,"line":2407},52,[68,2409,2117],{"class":665},[68,2411,404],{"class":74},[68,2413,2165],{"class":2122},[68,2415,1900],{"class":1899},[68,2417,2418],{"class":373}," __event_emitter__\n",[68,2420,2422],{"class":70,"line":2421},53,[68,2423,416],{"emptyLinePlaceholder":415},[68,2425,2427],{"class":70,"line":2426},54,[68,2428,2429],{"class":2403},"        # 验证配置\n",[68,2431,2433,2436,2439,2441,2443,2445,2447,2450],{"class":70,"line":2432},55,[68,2434,2435],{"class":1790},"        if",[68,2437,2438],{"class":1899}," not",[68,2440,2128],{"class":665},[68,2442,404],{"class":74},[68,2444,2123],{"class":2122},[68,2446,404],{"class":74},[68,2448,2449],{"class":2228},"DEEPSEEK_API_KEY",[68,2451,1569],{"class":74},[68,2453,2455,2458,2461,2463,2466,2469,2471,2474,2476,2478,2480,2483,2485,2488,2491,2493,2496],{"class":70,"line":2454},56,[68,2456,2457],{"class":1790},"            yield",[68,2459,2460],{"class":373}," json",[68,2462,404],{"class":74},[68,2464,2465],{"class":1903},"dumps",[68,2467,2468],{"class":74},"({",[68,2470,89],{"class":112},[68,2472,2473],{"class":116},"error",[68,2475,89],{"class":112},[68,2477,92],{"class":74},[68,2479,113],{"class":112},[68,2481,2482],{"class":116},"未配置API密钥",[68,2484,89],{"class":112},[68,2486,2487],{"class":74},"},",[68,2489,2490],{"class":1912}," ensure_ascii",[68,2492,877],{"class":1899},[68,2494,2495],{"class":81},"False",[68,2497,410],{"class":74},[68,2499,2501],{"class":70,"line":2500},57,[68,2502,2503],{"class":1790},"            return\n",[68,2505,2507],{"class":70,"line":2506},58,[68,2508,416],{"emptyLinePlaceholder":415},[68,2510,2512],{"class":70,"line":2511},59,[68,2513,2514],{"class":2403},"        # 准备请求参数\n",[68,2516,2518,2521,2523],{"class":70,"line":2517},60,[68,2519,2520],{"class":373},"        headers ",[68,2522,877],{"class":1899},[68,2524,95],{"class":74},[68,2526,2528,2531,2534,2536,2538,2541,2544,2547,2549,2551,2553,2555,2557,2559,2561],{"class":70,"line":2527},61,[68,2529,2530],{"class":112},"            \"",[68,2532,2533],{"class":116},"Authorization",[68,2535,89],{"class":112},[68,2537,92],{"class":74},[68,2539,2540],{"class":1864}," f",[68,2542,2543],{"class":116},"\"Bearer ",[68,2545,2546],{"class":2397},"{",[68,2548,2109],{"class":665},[68,2550,404],{"class":74},[68,2552,2123],{"class":2122},[68,2554,404],{"class":74},[68,2556,2449],{"class":2228},[68,2558,2400],{"class":2397},[68,2560,89],{"class":116},[68,2562,169],{"class":74},[68,2564,2566,2568,2571,2573,2575,2577,2580,2582],{"class":70,"line":2565},62,[68,2567,2530],{"class":112},[68,2569,2570],{"class":116},"Content-Type",[68,2572,89],{"class":112},[68,2574,92],{"class":74},[68,2576,113],{"class":112},[68,2578,2579],{"class":116},"application\u002Fjson",[68,2581,89],{"class":112},[68,2583,169],{"class":74},[68,2585,2587],{"class":70,"line":2586},63,[68,2588,2589],{"class":74},"        }\n",[68,2591,2593],{"class":70,"line":2592},64,[68,2594,416],{"emptyLinePlaceholder":415},[68,2596,2598,2601],{"class":70,"line":2597},65,[68,2599,2600],{"class":1790},"        try",[68,2602,1569],{"class":74},[68,2604,2606],{"class":70,"line":2605},66,[68,2607,2608],{"class":2403},"            # 模型ID提取\n",[68,2610,2612,2615,2617,2619,2621,2623,2626,2628,2631,2634,2636,2638,2640,2642,2644,2647,2650,2653,2655],{"class":70,"line":2611},67,[68,2613,2614],{"class":373},"            model_id ",[68,2616,877],{"class":1899},[68,2618,2297],{"class":373},[68,2620,2326],{"class":74},[68,2622,89],{"class":112},[68,2624,2625],{"class":116},"model",[68,2627,89],{"class":112},[68,2629,2630],{"class":74},"].",[68,2632,2633],{"class":1903},"split",[68,2635,648],{"class":74},[68,2637,89],{"class":112},[68,2639,404],{"class":116},[68,2641,89],{"class":112},[68,2643,255],{"class":74},[68,2645,2646],{"class":2397}," 1",[68,2648,2649],{"class":74},")[",[68,2651,2652],{"class":1899},"-",[68,2654,401],{"class":2397},[68,2656,2657],{"class":74},"]\n",[68,2659,2661,2664,2666,2668,2671,2674,2676,2678,2680,2682,2684,2687],{"class":70,"line":2660},68,[68,2662,2663],{"class":373},"            payload ",[68,2665,877],{"class":1899},[68,2667,2382],{"class":74},[68,2669,2670],{"class":1899},"**",[68,2672,2673],{"class":373},"body",[68,2675,255],{"class":74},[68,2677,113],{"class":112},[68,2679,2625],{"class":116},[68,2681,89],{"class":112},[68,2683,92],{"class":74},[68,2685,2686],{"class":373}," model_id",[68,2688,132],{"class":74},[68,2690,2692],{"class":70,"line":2691},69,[68,2693,416],{"emptyLinePlaceholder":415},[68,2695,2697],{"class":70,"line":2696},70,[68,2698,2699],{"class":2403},"            # 处理消息以防止连续的相同角色\n",[68,2701,2703,2706,2708,2711,2713,2715,2718,2720],{"class":70,"line":2702},71,[68,2704,2705],{"class":373},"            messages ",[68,2707,877],{"class":1899},[68,2709,2710],{"class":373}," payload",[68,2712,2326],{"class":74},[68,2714,89],{"class":112},[68,2716,2717],{"class":116},"messages",[68,2719,89],{"class":112},[68,2721,2657],{"class":74},[68,2723,2725,2728,2730],{"class":70,"line":2724},72,[68,2726,2727],{"class":373},"            i ",[68,2729,877],{"class":1899},[68,2731,2732],{"class":2397}," 0\n",[68,2734,2736,2739,2742,2744,2747,2749,2751,2754,2756,2758],{"class":70,"line":2735},73,[68,2737,2738],{"class":1790},"            while",[68,2740,2741],{"class":373}," i ",[68,2743,727],{"class":1899},[68,2745,2746],{"class":630}," len",[68,2748,648],{"class":74},[68,2750,2717],{"class":1903},[68,2752,2753],{"class":74},")",[68,2755,2394],{"class":1899},[68,2757,2646],{"class":2397},[68,2759,1569],{"class":74},[68,2761,2763,2766,2769,2771,2774,2777,2779,2782,2784,2787,2790,2792,2794,2797,2799,2801,2803,2805,2807,2809],{"class":70,"line":2762},74,[68,2764,2765],{"class":1790},"                if",[68,2767,2768],{"class":373}," messages",[68,2770,2326],{"class":74},[68,2772,2773],{"class":373},"i",[68,2775,2776],{"class":74},"][",[68,2778,89],{"class":112},[68,2780,2781],{"class":116},"role",[68,2783,89],{"class":112},[68,2785,2786],{"class":74},"]",[68,2788,2789],{"class":1899}," ==",[68,2791,2768],{"class":373},[68,2793,2326],{"class":74},[68,2795,2796],{"class":373},"i ",[68,2798,1670],{"class":1899},[68,2800,2646],{"class":2397},[68,2802,2776],{"class":74},[68,2804,89],{"class":112},[68,2806,2781],{"class":116},[68,2808,89],{"class":112},[68,2810,2360],{"class":74},[68,2812,2814],{"class":70,"line":2813},75,[68,2815,2816],{"class":2403},"                    # 插入具有替代角色的占位符消息\n",[68,2818,2820,2823,2825],{"class":70,"line":2819},76,[68,2821,2822],{"class":373},"                    alternate_role ",[68,2824,877],{"class":1899},[68,2826,606],{"class":74},[68,2828,2830,2833,2836,2838,2841,2843,2845,2847,2849,2851,2853,2855,2857,2859,2861,2864,2866,2869,2871,2873],{"class":70,"line":2829},77,[68,2831,2832],{"class":112},"                        \"",[68,2834,2835],{"class":116},"assistant",[68,2837,89],{"class":112},[68,2839,2840],{"class":1790}," if",[68,2842,2768],{"class":373},[68,2844,2326],{"class":74},[68,2846,2773],{"class":373},[68,2848,2776],{"class":74},[68,2850,89],{"class":112},[68,2852,2781],{"class":116},[68,2854,89],{"class":112},[68,2856,2786],{"class":74},[68,2858,2789],{"class":1899},[68,2860,113],{"class":112},[68,2862,2863],{"class":116},"user",[68,2865,89],{"class":112},[68,2867,2868],{"class":1790}," else",[68,2870,113],{"class":112},[68,2872,2863],{"class":116},[68,2874,120],{"class":112},[68,2876,2878],{"class":70,"line":2877},78,[68,2879,2880],{"class":74},"                    )\n",[68,2882,2884,2887,2889,2892],{"class":70,"line":2883},79,[68,2885,2886],{"class":373},"                    messages",[68,2888,404],{"class":74},[68,2890,2891],{"class":1903},"insert",[68,2893,1907],{"class":74},[68,2895,2897,2900,2902,2904],{"class":70,"line":2896},80,[68,2898,2899],{"class":1903},"                        i ",[68,2901,1670],{"class":1899},[68,2903,2646],{"class":2397},[68,2905,169],{"class":74},[68,2907,2909,2912,2914,2916,2918,2920,2923,2925,2927,2930,2932,2934,2936,2939,2941],{"class":70,"line":2908},81,[68,2910,2911],{"class":74},"                        {",[68,2913,89],{"class":112},[68,2915,2781],{"class":116},[68,2917,89],{"class":112},[68,2919,92],{"class":74},[68,2921,2922],{"class":1903}," alternate_role",[68,2924,255],{"class":74},[68,2926,113],{"class":112},[68,2928,2929],{"class":116},"content",[68,2931,89],{"class":112},[68,2933,92],{"class":74},[68,2935,113],{"class":112},[68,2937,2938],{"class":116},"[Unfinished thinking]",[68,2940,89],{"class":112},[68,2942,2943],{"class":74},"},\n",[68,2945,2947],{"class":70,"line":2946},82,[68,2948,2880],{"class":74},[68,2950,2952,2955,2958],{"class":70,"line":2951},83,[68,2953,2954],{"class":373},"                i ",[68,2956,2957],{"class":1899},"+=",[68,2959,2960],{"class":2397}," 1\n",[68,2962,2964],{"class":70,"line":2963},84,[68,2965,416],{"emptyLinePlaceholder":415},[68,2967,2969],{"class":70,"line":2968},85,[68,2970,2971],{"class":2403},"            # yield json.dumps(payload, ensure_ascii=False)\n",[68,2973,2975],{"class":70,"line":2974},86,[68,2976,416],{"emptyLinePlaceholder":415},[68,2978,2980],{"class":70,"line":2979},87,[68,2981,2982],{"class":2403},"            # 发起API请求\n",[68,2984,2986,2989,2992,2995,2997,3000,3002,3005,3007,3010,3012,3015,3018],{"class":70,"line":2985},88,[68,2987,2988],{"class":1790},"            async",[68,2990,2991],{"class":1790}," with",[68,2993,2994],{"class":373}," httpx",[68,2996,404],{"class":74},[68,2998,2999],{"class":1903},"AsyncClient",[68,3001,648],{"class":74},[68,3003,3004],{"class":1912},"http2",[68,3006,877],{"class":1899},[68,3008,3009],{"class":81},"True",[68,3011,2753],{"class":74},[68,3013,3014],{"class":1790}," as",[68,3016,3017],{"class":373}," client",[68,3019,1569],{"class":74},[68,3021,3023,3026,3028,3030,3032,3035],{"class":70,"line":3022},89,[68,3024,3025],{"class":1790},"                async",[68,3027,2991],{"class":1790},[68,3029,3017],{"class":373},[68,3031,404],{"class":74},[68,3033,3034],{"class":1903},"stream",[68,3036,1907],{"class":74},[68,3038,3040,3043,3046,3048],{"class":70,"line":3039},90,[68,3041,3042],{"class":112},"                    \"",[68,3044,3045],{"class":116},"POST",[68,3047,89],{"class":112},[68,3049,169],{"class":74},[68,3051,3053,3056,3058,3060,3062,3064,3066,3068,3071,3073,3076],{"class":70,"line":3052},91,[68,3054,3055],{"class":1864},"                    f",[68,3057,89],{"class":116},[68,3059,2546],{"class":2397},[68,3061,2109],{"class":665},[68,3063,404],{"class":74},[68,3065,2123],{"class":2122},[68,3067,404],{"class":74},[68,3069,3070],{"class":2228},"DEEPSEEK_API_BASE_URL",[68,3072,2400],{"class":2397},[68,3074,3075],{"class":116},"\u002Fchat\u002Fcompletions\"",[68,3077,169],{"class":74},[68,3079,3081,3084,3086,3089],{"class":70,"line":3080},92,[68,3082,3083],{"class":1912},"                    json",[68,3085,877],{"class":1899},[68,3087,3088],{"class":1903},"payload",[68,3090,169],{"class":74},[68,3092,3094,3097,3099,3102],{"class":70,"line":3093},93,[68,3095,3096],{"class":1912},"                    headers",[68,3098,877],{"class":1899},[68,3100,3101],{"class":1903},"headers",[68,3103,169],{"class":74},[68,3105,3107,3110,3112,3115],{"class":70,"line":3106},94,[68,3108,3109],{"class":1912},"                    timeout",[68,3111,877],{"class":1899},[68,3113,3114],{"class":2397},"300",[68,3116,169],{"class":74},[68,3118,3120,3123,3125,3128],{"class":70,"line":3119},95,[68,3121,3122],{"class":74},"                )",[68,3124,3014],{"class":1790},[68,3126,3127],{"class":373}," response",[68,3129,1569],{"class":74},[68,3131,3133],{"class":70,"line":3132},96,[68,3134,3135],{"class":2403},"                    # 错误处理\n",[68,3137,3139,3142,3144,3146,3149,3152,3155],{"class":70,"line":3138},97,[68,3140,3141],{"class":1790},"                    if",[68,3143,3127],{"class":373},[68,3145,404],{"class":74},[68,3147,3148],{"class":2122},"status_code",[68,3150,3151],{"class":1899}," !=",[68,3153,3154],{"class":2397}," 200",[68,3156,1569],{"class":74},[68,3158,3160,3163,3165,3168,3170,3172,3175],{"class":70,"line":3159},98,[68,3161,3162],{"class":373},"                        error ",[68,3164,877],{"class":1899},[68,3166,3167],{"class":1790}," await",[68,3169,3127],{"class":373},[68,3171,404],{"class":74},[68,3173,3174],{"class":1903},"aread",[68,3176,2136],{"class":74},[68,3178,3180,3183,3185,3187,3190,3192,3195,3197,3199,3201,3204],{"class":70,"line":3179},99,[68,3181,3182],{"class":1790},"                        yield",[68,3184,2128],{"class":665},[68,3186,404],{"class":74},[68,3188,3189],{"class":1903},"_format_error",[68,3191,648],{"class":74},[68,3193,3194],{"class":1903},"response",[68,3196,404],{"class":74},[68,3198,3148],{"class":2122},[68,3200,255],{"class":74},[68,3202,3203],{"class":1903}," error",[68,3205,410],{"class":74},[68,3207,3209],{"class":70,"line":3208},100,[68,3210,3211],{"class":1790},"                        return\n",[68,3213,3215],{"class":70,"line":3214},101,[68,3216,416],{"emptyLinePlaceholder":415},[68,3218,3220],{"class":70,"line":3219},102,[68,3221,3222],{"class":2403},"                    # 流式处理响应\n",[68,3224,3226,3229,3232,3235,3238,3240,3242,3245],{"class":70,"line":3225},103,[68,3227,3228],{"class":1790},"                    async",[68,3230,3231],{"class":1790}," for",[68,3233,3234],{"class":373}," line ",[68,3236,3237],{"class":1790},"in",[68,3239,3127],{"class":373},[68,3241,404],{"class":74},[68,3243,3244],{"class":1903},"aiter_lines",[68,3246,3247],{"class":74},"():\n",[68,3249,3251,3254,3256,3259,3261,3264,3266,3268,3270,3272],{"class":70,"line":3250},104,[68,3252,3253],{"class":1790},"                        if",[68,3255,2438],{"class":1899},[68,3257,3258],{"class":373}," line",[68,3260,404],{"class":74},[68,3262,3263],{"class":1903},"startswith",[68,3265,648],{"class":74},[68,3267,2109],{"class":665},[68,3269,404],{"class":74},[68,3271,2146],{"class":2122},[68,3273,1886],{"class":74},[68,3275,3277],{"class":70,"line":3276},105,[68,3278,3279],{"class":1790},"                            continue\n",[68,3281,3283],{"class":70,"line":3282},106,[68,3284,416],{"emptyLinePlaceholder":415},[68,3286,3288],{"class":70,"line":3287},107,[68,3289,3290],{"class":2403},"                        # 截取 JSON 字符串\n",[68,3292,3294,3297,3299,3301,3303,3306,3308,3310,3312,3314,3316],{"class":70,"line":3293},108,[68,3295,3296],{"class":373},"                        json_str ",[68,3298,877],{"class":1899},[68,3300,3258],{"class":373},[68,3302,2326],{"class":74},[68,3304,3305],{"class":630},"len",[68,3307,648],{"class":74},[68,3309,2109],{"class":665},[68,3311,404],{"class":74},[68,3313,2146],{"class":2122},[68,3315,2753],{"class":74},[68,3317,3318],{"class":74}," :]\n",[68,3320,3322],{"class":70,"line":3321},109,[68,3323,416],{"emptyLinePlaceholder":415},[68,3325,3327],{"class":70,"line":3326},110,[68,3328,3329],{"class":2403},"                        # 去除首尾空格后检查是否为结束标记\n",[68,3331,3333,3335,3338,3340,3343,3346,3348,3350,3353,3355],{"class":70,"line":3332},111,[68,3334,3253],{"class":1790},[68,3336,3337],{"class":373}," json_str",[68,3339,404],{"class":74},[68,3341,3342],{"class":1903},"strip",[68,3344,3345],{"class":74},"()",[68,3347,2789],{"class":1899},[68,3349,113],{"class":112},[68,3351,3352],{"class":116},"[DONE]",[68,3354,89],{"class":112},[68,3356,1569],{"class":74},[68,3358,3360],{"class":70,"line":3359},112,[68,3361,3362],{"class":1790},"                            return\n",[68,3364,3366],{"class":70,"line":3365},113,[68,3367,416],{"emptyLinePlaceholder":415},[68,3369,3371,3374],{"class":70,"line":3370},114,[68,3372,3373],{"class":1790},"                        try",[68,3375,1569],{"class":74},[68,3377,3379,3382,3384,3386,3388,3391,3393,3396],{"class":70,"line":3378},115,[68,3380,3381],{"class":373},"                            data ",[68,3383,877],{"class":1899},[68,3385,2460],{"class":373},[68,3387,404],{"class":74},[68,3389,3390],{"class":1903},"loads",[68,3392,648],{"class":74},[68,3394,3395],{"class":1903},"json_str",[68,3397,410],{"class":74},[68,3399,3401,3404,3406,3408,3411,3413,3416],{"class":70,"line":3400},116,[68,3402,3403],{"class":1790},"                        except",[68,3405,2460],{"class":373},[68,3407,404],{"class":74},[68,3409,3410],{"class":2122},"JSONDecodeError",[68,3412,3014],{"class":1790},[68,3414,3415],{"class":373}," e",[68,3417,1569],{"class":74},[68,3419,3421],{"class":70,"line":3420},117,[68,3422,3423],{"class":2403},"                            # 格式化错误信息，这里传入错误类型和详细原因（包括出错内容和异常信息）\n",[68,3425,3427,3430,3432,3434,3437,3439,3441,3443,3446,3448,3451,3453],{"class":70,"line":3426},118,[68,3428,3429],{"class":373},"                            error_detail ",[68,3431,877],{"class":1899},[68,3433,2540],{"class":1864},[68,3435,3436],{"class":116},"\"解析失败 - 内容：",[68,3438,2546],{"class":2397},[68,3440,3395],{"class":373},[68,3442,2400],{"class":2397},[68,3444,3445],{"class":116},"，原因：",[68,3447,2546],{"class":2397},[68,3449,3450],{"class":373},"e",[68,3452,2400],{"class":2397},[68,3454,120],{"class":116},[68,3456,3458,3461,3463,3465,3467,3469,3471,3473,3475,3477,3480],{"class":70,"line":3457},119,[68,3459,3460],{"class":1790},"                            yield",[68,3462,2128],{"class":665},[68,3464,404],{"class":74},[68,3466,3189],{"class":1903},[68,3468,648],{"class":74},[68,3470,89],{"class":112},[68,3472,3410],{"class":116},[68,3474,89],{"class":112},[68,3476,255],{"class":74},[68,3478,3479],{"class":1903}," error_detail",[68,3481,410],{"class":74},[68,3483,3485],{"class":70,"line":3484},120,[68,3486,3362],{"class":1790},[68,3488,3490],{"class":70,"line":3489},121,[68,3491,416],{"emptyLinePlaceholder":415},[68,3493,3495,3498,3500,3503,3505,3508,3510,3512,3515,3517,3519,3522,3524],{"class":70,"line":3494},122,[68,3496,3497],{"class":373},"                        choice ",[68,3499,877],{"class":1899},[68,3501,3502],{"class":373}," data",[68,3504,404],{"class":74},[68,3506,3507],{"class":1903},"get",[68,3509,648],{"class":74},[68,3511,89],{"class":112},[68,3513,3514],{"class":116},"choices",[68,3516,89],{"class":112},[68,3518,255],{"class":74},[68,3520,3521],{"class":74}," [{}])[",[68,3523,768],{"class":2397},[68,3525,2657],{"class":74},[68,3527,3529],{"class":70,"line":3528},123,[68,3530,416],{"emptyLinePlaceholder":415},[68,3532,3534],{"class":70,"line":3533},124,[68,3535,3536],{"class":2403},"                        # 结束条件判断\n",[68,3538,3540,3542,3545,3547,3549,3551,3553,3556,3558],{"class":70,"line":3539},125,[68,3541,3253],{"class":1790},[68,3543,3544],{"class":373}," choice",[68,3546,404],{"class":74},[68,3548,3507],{"class":1903},[68,3550,648],{"class":74},[68,3552,89],{"class":112},[68,3554,3555],{"class":116},"finish_reason",[68,3557,89],{"class":112},[68,3559,1886],{"class":74},[68,3561,3563],{"class":70,"line":3562},126,[68,3564,3362],{"class":1790},[68,3566,3568],{"class":70,"line":3567},127,[68,3569,416],{"emptyLinePlaceholder":415},[68,3571,3573],{"class":70,"line":3572},128,[68,3574,3575],{"class":2403},"                        # 状态机处理\n",[68,3577,3579,3582,3584,3586,3588,3590,3593],{"class":70,"line":3578},129,[68,3580,3581],{"class":373},"                        state_output ",[68,3583,877],{"class":1899},[68,3585,3167],{"class":1790},[68,3587,2128],{"class":665},[68,3589,404],{"class":74},[68,3591,3592],{"class":1903},"_update_thinking_state",[68,3594,1907],{"class":74},[68,3596,3598,3601,3603,3605,3607,3609,3612,3614,3616,3619],{"class":70,"line":3597},130,[68,3599,3600],{"class":1903},"                            choice",[68,3602,404],{"class":74},[68,3604,3507],{"class":1903},[68,3606,648],{"class":74},[68,3608,89],{"class":112},[68,3610,3611],{"class":116},"delta",[68,3613,89],{"class":112},[68,3615,255],{"class":74},[68,3617,3618],{"class":74}," {}),",[68,3620,3621],{"class":1903}," thinking_state\n",[68,3623,3625],{"class":70,"line":3624},131,[68,3626,3627],{"class":74},"                        )\n",[68,3629,3631,3633,3636],{"class":70,"line":3630},132,[68,3632,3253],{"class":1790},[68,3634,3635],{"class":373}," state_output",[68,3637,1569],{"class":74},[68,3639,3641,3643,3646],{"class":70,"line":3640},133,[68,3642,3460],{"class":1790},[68,3644,3645],{"class":373}," state_output  ",[68,3647,3648],{"class":2403},"# 直接发送状态标记\n",[68,3650,3652,3655,3658,3661,3663,3666,3668],{"class":70,"line":3651},134,[68,3653,3654],{"class":1790},"                            if",[68,3656,3657],{"class":373}," state_output ",[68,3659,3660],{"class":1899},"==",[68,3662,113],{"class":112},[68,3664,3665],{"class":116},"\u003Cthink>",[68,3667,89],{"class":112},[68,3669,1569],{"class":74},[68,3671,3673,3676,3678,3681],{"class":70,"line":3672},135,[68,3674,3675],{"class":1790},"                                yield",[68,3677,113],{"class":112},[68,3679,3680],{"class":665},"\\n",[68,3682,120],{"class":112},[68,3684,3686],{"class":70,"line":3685},136,[68,3687,416],{"emptyLinePlaceholder":415},[68,3689,3691],{"class":70,"line":3690},137,[68,3692,3693],{"class":2403},"                        # 内容处理并立即发送\n",[68,3695,3697,3700,3702,3704,3706,3709,3711,3714,3716,3718,3720,3722],{"class":70,"line":3696},138,[68,3698,3699],{"class":373},"                        content ",[68,3701,877],{"class":1899},[68,3703,2128],{"class":665},[68,3705,404],{"class":74},[68,3707,3708],{"class":1903},"_process_content",[68,3710,648],{"class":74},[68,3712,3713],{"class":1903},"choice",[68,3715,2326],{"class":74},[68,3717,89],{"class":112},[68,3719,3611],{"class":116},[68,3721,89],{"class":112},[68,3723,3724],{"class":74},"])\n",[68,3726,3728,3730,3733],{"class":70,"line":3727},139,[68,3729,3253],{"class":1790},[68,3731,3732],{"class":373}," content",[68,3734,1569],{"class":74},[68,3736,3738,3740,3742,3744,3746,3748,3750,3752,3754],{"class":70,"line":3737},140,[68,3739,3654],{"class":1790},[68,3741,3732],{"class":373},[68,3743,404],{"class":74},[68,3745,3263],{"class":1903},[68,3747,648],{"class":74},[68,3749,89],{"class":112},[68,3751,3665],{"class":116},[68,3753,89],{"class":112},[68,3755,1886],{"class":74},[68,3757,3759,3762,3764,3767,3769,3772,3774,3777,3779,3782,3785,3787,3789,3791],{"class":70,"line":3758},141,[68,3760,3761],{"class":373},"                                match ",[68,3763,877],{"class":1899},[68,3765,3766],{"class":373}," re",[68,3768,404],{"class":74},[68,3770,3771],{"class":1903},"match",[68,3773,648],{"class":74},[68,3775,3776],{"class":1864},"r",[68,3778,89],{"class":112},[68,3780,3781],{"class":1518},"^",[68,3783,3665],{"class":3784},"sQRbd",[68,3786,89],{"class":112},[68,3788,255],{"class":74},[68,3790,3732],{"class":1903},[68,3792,410],{"class":74},[68,3794,3796,3799,3802],{"class":70,"line":3795},142,[68,3797,3798],{"class":1790},"                                if",[68,3800,3801],{"class":373}," match",[68,3803,1569],{"class":74},[68,3805,3807,3810,3812,3814,3816,3819,3821,3823,3825,3827,3829,3831,3833,3836,3838,3840],{"class":70,"line":3806},143,[68,3808,3809],{"class":373},"                                    content ",[68,3811,877],{"class":1899},[68,3813,3766],{"class":373},[68,3815,404],{"class":74},[68,3817,3818],{"class":1903},"sub",[68,3820,648],{"class":74},[68,3822,3776],{"class":1864},[68,3824,89],{"class":112},[68,3826,3781],{"class":1518},[68,3828,3665],{"class":3784},[68,3830,89],{"class":112},[68,3832,255],{"class":74},[68,3834,3835],{"class":112}," \"\"",[68,3837,255],{"class":74},[68,3839,3732],{"class":1903},[68,3841,410],{"class":74},[68,3843,3845,3848,3850,3852],{"class":70,"line":3844},144,[68,3846,3847],{"class":1790},"                                    yield",[68,3849,113],{"class":112},[68,3851,3665],{"class":116},[68,3853,120],{"class":112},[68,3855,3857,3860,3863,3865,3868,3870,3873],{"class":70,"line":3856},145,[68,3858,3859],{"class":1790},"                                    await",[68,3861,3862],{"class":373}," asyncio",[68,3864,404],{"class":74},[68,3866,3867],{"class":1903},"sleep",[68,3869,648],{"class":74},[68,3871,3872],{"class":2397},"0.1",[68,3874,410],{"class":74},[68,3876,3878,3880,3882,3884],{"class":70,"line":3877},146,[68,3879,3847],{"class":1790},[68,3881,113],{"class":112},[68,3883,3680],{"class":665},[68,3885,120],{"class":112},[68,3887,3889],{"class":70,"line":3888},147,[68,3890,416],{"emptyLinePlaceholder":415},[68,3892,3894,3897,3899,3901,3903,3905,3907,3910,3912],{"class":70,"line":3893},148,[68,3895,3896],{"class":1790},"                            elif",[68,3898,3732],{"class":373},[68,3900,404],{"class":74},[68,3902,3263],{"class":1903},[68,3904,648],{"class":74},[68,3906,89],{"class":112},[68,3908,3909],{"class":116},"\u003C\u002Fthink>",[68,3911,89],{"class":112},[68,3913,1886],{"class":74},[68,3915,3917,3919,3921,3923,3925,3927,3929,3931,3933,3935,3937,3939,3941,3943],{"class":70,"line":3916},149,[68,3918,3761],{"class":373},[68,3920,877],{"class":1899},[68,3922,3766],{"class":373},[68,3924,404],{"class":74},[68,3926,3771],{"class":1903},[68,3928,648],{"class":74},[68,3930,3776],{"class":1864},[68,3932,89],{"class":112},[68,3934,3781],{"class":1518},[68,3936,3909],{"class":3784},[68,3938,89],{"class":112},[68,3940,255],{"class":74},[68,3942,3732],{"class":1903},[68,3944,410],{"class":74},[68,3946,3948,3950,3952],{"class":70,"line":3947},150,[68,3949,3798],{"class":1790},[68,3951,3801],{"class":373},[68,3953,1569],{"class":74},[68,3955,3957,3959,3961,3963,3965,3967,3969,3971,3973,3975,3977,3979,3981,3983,3985,3987],{"class":70,"line":3956},151,[68,3958,3809],{"class":373},[68,3960,877],{"class":1899},[68,3962,3766],{"class":373},[68,3964,404],{"class":74},[68,3966,3818],{"class":1903},[68,3968,648],{"class":74},[68,3970,3776],{"class":1864},[68,3972,89],{"class":112},[68,3974,3781],{"class":1518},[68,3976,3909],{"class":3784},[68,3978,89],{"class":112},[68,3980,255],{"class":74},[68,3982,3835],{"class":112},[68,3984,255],{"class":74},[68,3986,3732],{"class":1903},[68,3988,410],{"class":74},[68,3990,3992,3994,3996,3998],{"class":70,"line":3991},152,[68,3993,3847],{"class":1790},[68,3995,113],{"class":112},[68,3997,3909],{"class":116},[68,3999,120],{"class":112},[68,4001,4003,4005,4007,4009,4011,4013,4015],{"class":70,"line":4002},153,[68,4004,3859],{"class":1790},[68,4006,3862],{"class":373},[68,4008,404],{"class":74},[68,4010,3867],{"class":1903},[68,4012,648],{"class":74},[68,4014,3872],{"class":2397},[68,4016,410],{"class":74},[68,4018,4020,4022,4024,4026],{"class":70,"line":4019},154,[68,4021,3847],{"class":1790},[68,4023,113],{"class":112},[68,4025,3680],{"class":665},[68,4027,120],{"class":112},[68,4029,4031,4033],{"class":70,"line":4030},155,[68,4032,3460],{"class":1790},[68,4034,4035],{"class":373}," content\n",[68,4037,4039],{"class":70,"line":4038},156,[68,4040,416],{"emptyLinePlaceholder":415},[68,4042,4044,4047,4050,4052,4054],{"class":70,"line":4043},157,[68,4045,4046],{"class":1790},"        except",[68,4048,4049],{"class":104}," Exception",[68,4051,3014],{"class":1790},[68,4053,3415],{"class":373},[68,4055,1569],{"class":74},[68,4057,4059,4061,4063,4065,4068,4070,4072],{"class":70,"line":4058},158,[68,4060,2457],{"class":1790},[68,4062,2128],{"class":665},[68,4064,404],{"class":74},[68,4066,4067],{"class":1903},"_format_exception",[68,4069,648],{"class":74},[68,4071,3450],{"class":1903},[68,4073,410],{"class":74},[68,4075,4077],{"class":70,"line":4076},159,[68,4078,416],{"emptyLinePlaceholder":415},[68,4080,4082,4084,4086,4089,4091,4093,4095,4098,4100,4102,4104,4107,4109,4111,4113,4115,4117],{"class":70,"line":4081},160,[68,4083,2278],{"class":1864},[68,4085,2281],{"class":1864},[68,4087,4088],{"class":2183}," _update_thinking_state",[68,4090,648],{"class":74},[68,4092,2109],{"class":2108},[68,4094,255],{"class":74},[68,4096,4097],{"class":2296}," delta",[68,4099,92],{"class":74},[68,4101,2302],{"class":104},[68,4103,255],{"class":74},[68,4105,4106],{"class":2296}," thinking_state",[68,4108,92],{"class":74},[68,4110,2302],{"class":104},[68,4112,2753],{"class":74},[68,4114,2345],{"class":74},[68,4116,1896],{"class":104},[68,4118,1569],{"class":74},[68,4120,4122,4124,4127],{"class":70,"line":4121},161,[68,4123,2366],{"class":1750},[68,4125,4126],{"class":1756},"更新思考状态机（简化版）",[68,4128,1751],{"class":1750},[68,4130,4132,4135,4137],{"class":70,"line":4131},162,[68,4133,4134],{"class":373},"        state_output ",[68,4136,877],{"class":1899},[68,4138,4139],{"class":112}," \"\"\n",[68,4141,4143],{"class":70,"line":4142},163,[68,4144,416],{"emptyLinePlaceholder":415},[68,4146,4148],{"class":70,"line":4147},164,[68,4149,4150],{"class":2403},"        # 状态转换：未开始 -> 思考中\n",[68,4152,4154,4156,4158,4160,4162,4164,4166,4168,4170,4172,4174,4177,4179,4181,4183,4185,4187,4190,4192],{"class":70,"line":4153},165,[68,4155,2435],{"class":1790},[68,4157,4106],{"class":373},[68,4159,2326],{"class":74},[68,4161,89],{"class":112},[68,4163,2387],{"class":116},[68,4165,89],{"class":112},[68,4167,2786],{"class":74},[68,4169,2789],{"class":1899},[68,4171,2394],{"class":1899},[68,4173,401],{"class":2397},[68,4175,4176],{"class":1899}," and",[68,4178,4097],{"class":373},[68,4180,404],{"class":74},[68,4182,3507],{"class":1903},[68,4184,648],{"class":74},[68,4186,89],{"class":112},[68,4188,4189],{"class":116},"reasoning_content",[68,4191,89],{"class":112},[68,4193,1886],{"class":74},[68,4195,4197,4200,4202,4204,4206,4208,4210,4212],{"class":70,"line":4196},166,[68,4198,4199],{"class":373},"            thinking_state",[68,4201,2326],{"class":74},[68,4203,89],{"class":112},[68,4205,2387],{"class":116},[68,4207,89],{"class":112},[68,4209,2786],{"class":74},[68,4211,1900],{"class":1899},[68,4213,2732],{"class":2397},[68,4215,4217,4220,4222,4224,4226],{"class":70,"line":4216},167,[68,4218,4219],{"class":373},"            state_output ",[68,4221,877],{"class":1899},[68,4223,113],{"class":112},[68,4225,3665],{"class":116},[68,4227,120],{"class":112},[68,4229,4231],{"class":70,"line":4230},168,[68,4232,416],{"emptyLinePlaceholder":415},[68,4234,4236],{"class":70,"line":4235},169,[68,4237,4238],{"class":2403},"        # 状态转换：思考中 -> 已回答\n",[68,4240,4242,4245],{"class":70,"line":4241},170,[68,4243,4244],{"class":1790},"        elif",[68,4246,606],{"class":74},[68,4248,4250,4252,4254,4256,4258,4260,4262,4264],{"class":70,"line":4249},171,[68,4251,4199],{"class":373},[68,4253,2326],{"class":74},[68,4255,89],{"class":112},[68,4257,2387],{"class":116},[68,4259,89],{"class":112},[68,4261,2786],{"class":74},[68,4263,2789],{"class":1899},[68,4265,2732],{"class":2397},[68,4267,4269,4272,4274,4276,4278,4280,4282,4284,4286,4288],{"class":70,"line":4268},172,[68,4270,4271],{"class":1899},"            and",[68,4273,2438],{"class":1899},[68,4275,4097],{"class":373},[68,4277,404],{"class":74},[68,4279,3507],{"class":1903},[68,4281,648],{"class":74},[68,4283,89],{"class":112},[68,4285,4189],{"class":116},[68,4287,89],{"class":112},[68,4289,410],{"class":74},[68,4291,4293,4295,4297,4299,4301,4303,4305,4307,4309],{"class":70,"line":4292},173,[68,4294,4271],{"class":1899},[68,4296,4097],{"class":373},[68,4298,404],{"class":74},[68,4300,3507],{"class":1903},[68,4302,648],{"class":74},[68,4304,89],{"class":112},[68,4306,2929],{"class":116},[68,4308,89],{"class":112},[68,4310,410],{"class":74},[68,4312,4314],{"class":70,"line":4313},174,[68,4315,4316],{"class":74},"        ):\n",[68,4318,4320,4322,4324,4326,4328,4330,4332,4334],{"class":70,"line":4319},175,[68,4321,4199],{"class":373},[68,4323,2326],{"class":74},[68,4325,89],{"class":112},[68,4327,2387],{"class":116},[68,4329,89],{"class":112},[68,4331,2786],{"class":74},[68,4333,1900],{"class":1899},[68,4335,2960],{"class":2397},[68,4337,4339,4341,4343,4345,4347,4349,4352],{"class":70,"line":4338},176,[68,4340,4219],{"class":373},[68,4342,877],{"class":1899},[68,4344,113],{"class":112},[68,4346,3680],{"class":665},[68,4348,3909],{"class":116},[68,4350,4351],{"class":665},"\\n\\n",[68,4353,120],{"class":112},[68,4355,4357],{"class":70,"line":4356},177,[68,4358,416],{"emptyLinePlaceholder":415},[68,4360,4362,4364],{"class":70,"line":4361},178,[68,4363,2196],{"class":1790},[68,4365,4366],{"class":373}," state_output\n",[68,4368,4370],{"class":70,"line":4369},179,[68,4371,416],{"emptyLinePlaceholder":415},[68,4373,4375,4377,4380,4382,4384,4386,4388,4390,4392,4394,4396,4398],{"class":70,"line":4374},180,[68,4376,2100],{"class":1864},[68,4378,4379],{"class":2183}," _process_content",[68,4381,648],{"class":74},[68,4383,2109],{"class":2108},[68,4385,255],{"class":74},[68,4387,4097],{"class":2296},[68,4389,92],{"class":74},[68,4391,2302],{"class":104},[68,4393,2753],{"class":74},[68,4395,2345],{"class":74},[68,4397,1896],{"class":104},[68,4399,1569],{"class":74},[68,4401,4403,4405,4408],{"class":70,"line":4402},181,[68,4404,2366],{"class":1750},[68,4406,4407],{"class":1756},"直接返回处理后的内容",[68,4409,1751],{"class":1750},[68,4411,4413,4415,4417,4419,4421,4423,4425,4427,4429,4431,4433,4435,4438,4440,4442,4444,4446,4448,4450,4452,4454,4456],{"class":70,"line":4412},182,[68,4414,2196],{"class":1790},[68,4416,4097],{"class":373},[68,4418,404],{"class":74},[68,4420,3507],{"class":1903},[68,4422,648],{"class":74},[68,4424,89],{"class":112},[68,4426,4189],{"class":116},[68,4428,89],{"class":112},[68,4430,255],{"class":74},[68,4432,3835],{"class":112},[68,4434,2753],{"class":74},[68,4436,4437],{"class":1899}," or",[68,4439,4097],{"class":373},[68,4441,404],{"class":74},[68,4443,3507],{"class":1903},[68,4445,648],{"class":74},[68,4447,89],{"class":112},[68,4449,2929],{"class":116},[68,4451,89],{"class":112},[68,4453,255],{"class":74},[68,4455,3835],{"class":112},[68,4457,410],{"class":74},[68,4459,4461],{"class":70,"line":4460},183,[68,4462,416],{"emptyLinePlaceholder":415},[68,4464,4466,4468,4471,4473,4475,4477,4480,4482,4485,4487,4489,4491,4494,4496,4498,4500],{"class":70,"line":4465},184,[68,4467,2100],{"class":1864},[68,4469,4470],{"class":2183}," _format_error",[68,4472,648],{"class":74},[68,4474,2109],{"class":2108},[68,4476,255],{"class":74},[68,4478,4479],{"class":2296}," status_code",[68,4481,92],{"class":74},[68,4483,4484],{"class":104}," int",[68,4486,255],{"class":74},[68,4488,3203],{"class":2296},[68,4490,92],{"class":74},[68,4492,4493],{"class":104}," bytes",[68,4495,2753],{"class":74},[68,4497,2345],{"class":74},[68,4499,1896],{"class":104},[68,4501,1569],{"class":74},[68,4503,4505],{"class":70,"line":4504},185,[68,4506,4507],{"class":2403},"        # 如果 error 已经是字符串，则无需 decode\n",[68,4509,4511,4513,4516,4518,4520,4522,4524],{"class":70,"line":4510},186,[68,4512,2435],{"class":1790},[68,4514,4515],{"class":630}," isinstance",[68,4517,648],{"class":74},[68,4519,2473],{"class":1903},[68,4521,255],{"class":74},[68,4523,1896],{"class":104},[68,4525,1886],{"class":74},[68,4527,4529,4532,4534],{"class":70,"line":4528},187,[68,4530,4531],{"class":373},"            error_str ",[68,4533,877],{"class":1899},[68,4535,4536],{"class":373}," error\n",[68,4538,4540,4543],{"class":70,"line":4539},188,[68,4541,4542],{"class":1790},"        else",[68,4544,1569],{"class":74},[68,4546,4548,4550,4552,4554,4556,4559,4561,4564,4566,4568,4571,4573],{"class":70,"line":4547},189,[68,4549,4531],{"class":373},[68,4551,877],{"class":1899},[68,4553,3203],{"class":373},[68,4555,404],{"class":74},[68,4557,4558],{"class":1903},"decode",[68,4560,648],{"class":74},[68,4562,4563],{"class":1912},"errors",[68,4565,877],{"class":1899},[68,4567,89],{"class":112},[68,4569,4570],{"class":116},"ignore",[68,4572,89],{"class":112},[68,4574,410],{"class":74},[68,4576,4578],{"class":70,"line":4577},190,[68,4579,416],{"emptyLinePlaceholder":415},[68,4581,4583,4585],{"class":70,"line":4582},191,[68,4584,2600],{"class":1790},[68,4586,1569],{"class":74},[68,4588,4590,4593,4595,4597,4599,4601,4603,4606,4609,4611,4613,4615,4618,4620,4622,4625,4628,4631],{"class":70,"line":4589},192,[68,4591,4592],{"class":373},"            err_msg ",[68,4594,877],{"class":1899},[68,4596,2460],{"class":373},[68,4598,404],{"class":74},[68,4600,3390],{"class":1903},[68,4602,648],{"class":74},[68,4604,4605],{"class":1903},"error_str",[68,4607,4608],{"class":74},").",[68,4610,3507],{"class":1903},[68,4612,648],{"class":74},[68,4614,89],{"class":112},[68,4616,4617],{"class":116},"message",[68,4619,89],{"class":112},[68,4621,255],{"class":74},[68,4623,4624],{"class":1903}," error_str",[68,4626,4627],{"class":74},")[:",[68,4629,4630],{"class":2397},"200",[68,4632,2657],{"class":74},[68,4634,4636,4638,4640,4642,4644],{"class":70,"line":4635},193,[68,4637,4046],{"class":1790},[68,4639,4049],{"class":104},[68,4641,3014],{"class":1790},[68,4643,3415],{"class":373},[68,4645,1569],{"class":74},[68,4647,4649,4651,4653,4655,4658,4660],{"class":70,"line":4648},194,[68,4650,4592],{"class":373},[68,4652,877],{"class":1899},[68,4654,4624],{"class":373},[68,4656,4657],{"class":74},"[:",[68,4659,4630],{"class":2397},[68,4661,2657],{"class":74},[68,4663,4665,4667,4669,4671,4673],{"class":70,"line":4664},195,[68,4666,2196],{"class":1790},[68,4668,2460],{"class":373},[68,4670,404],{"class":74},[68,4672,2465],{"class":1903},[68,4674,1907],{"class":74},[68,4676,4678,4681,4683,4685,4687,4689,4691,4694,4696,4698,4700,4703,4705,4708,4710,4712,4714,4716,4718],{"class":70,"line":4677},196,[68,4679,4680],{"class":74},"            {",[68,4682,89],{"class":112},[68,4684,2473],{"class":116},[68,4686,89],{"class":112},[68,4688,92],{"class":74},[68,4690,2540],{"class":1864},[68,4692,4693],{"class":116},"\"HTTP ",[68,4695,2546],{"class":2397},[68,4697,3148],{"class":1903},[68,4699,2400],{"class":2397},[68,4701,4702],{"class":116},": ",[68,4704,2546],{"class":2397},[68,4706,4707],{"class":1903},"err_msg",[68,4709,2400],{"class":2397},[68,4711,89],{"class":116},[68,4713,2487],{"class":74},[68,4715,2490],{"class":1912},[68,4717,877],{"class":1899},[68,4719,4720],{"class":81},"False\n",[68,4722,4724],{"class":70,"line":4723},197,[68,4725,1945],{"class":74},[68,4727,4729],{"class":70,"line":4728},198,[68,4730,416],{"emptyLinePlaceholder":415},[68,4732,4734,4736,4739,4741,4743,4745,4747,4749,4751,4753,4755,4757],{"class":70,"line":4733},199,[68,4735,2100],{"class":1864},[68,4737,4738],{"class":2183}," _format_exception",[68,4740,648],{"class":74},[68,4742,2109],{"class":2108},[68,4744,255],{"class":74},[68,4746,3415],{"class":2296},[68,4748,92],{"class":74},[68,4750,4049],{"class":104},[68,4752,2753],{"class":74},[68,4754,2345],{"class":74},[68,4756,1896],{"class":104},[68,4758,1569],{"class":74},[68,4760,4762,4764,4767],{"class":70,"line":4761},200,[68,4763,2366],{"class":1750},[68,4765,4766],{"class":1756},"异常格式化保持不变",[68,4768,1751],{"class":1750},[68,4770,4772,4775,4777,4780,4782,4784,4786],{"class":70,"line":4771},201,[68,4773,4774],{"class":373},"        err_type ",[68,4776,877],{"class":1899},[68,4778,4779],{"class":104}," type",[68,4781,648],{"class":74},[68,4783,3450],{"class":1903},[68,4785,4608],{"class":74},[68,4787,4788],{"class":665},"__name__\n",[68,4790,4792,4794,4796,4798,4800,4802,4804,4806,4808,4810,4812,4814,4816,4819,4821,4823,4825,4827,4829,4831,4833,4835,4837,4839,4841,4843,4845],{"class":70,"line":4791},202,[68,4793,2196],{"class":1790},[68,4795,2460],{"class":373},[68,4797,404],{"class":74},[68,4799,2465],{"class":1903},[68,4801,2468],{"class":74},[68,4803,89],{"class":112},[68,4805,2473],{"class":116},[68,4807,89],{"class":112},[68,4809,92],{"class":74},[68,4811,2540],{"class":1864},[68,4813,89],{"class":116},[68,4815,2546],{"class":2397},[68,4817,4818],{"class":1903},"err_type",[68,4820,2400],{"class":2397},[68,4822,4702],{"class":116},[68,4824,2546],{"class":2397},[68,4826,2352],{"class":104},[68,4828,648],{"class":74},[68,4830,3450],{"class":1903},[68,4832,2753],{"class":74},[68,4834,2400],{"class":2397},[68,4836,89],{"class":116},[68,4838,2487],{"class":74},[68,4840,2490],{"class":1912},[68,4842,877],{"class":1899},[68,4844,2495],{"class":81},[68,4846,410],{"class":74},[1236,4848,4849],{},[1239,4850,4851],{},"函数配置中，Api URL、Api Key、模型名字和上个教程一样，输入阿里云百炼对应的即可。",[554,4853],{"filename":4854},"13.png",[1236,4856,4857],{},[1239,4858,4859],{},"启用该函数",[554,4861],{"filename":4862},"07.png",[1232,4864,4866],{"id":4865},"_4-设置新模型","4. 设置新模型",[1236,4868,4869],{},[1239,4870,4871,4872,1663,4874,4877,4878,4881],{},"前往 ",[65,4873,1662],{},[65,4875,4876],{},"模型","，将新的 ",[65,4879,4880],{},"deepseek-r1-fix"," 设为启用，还可手动修改 Logo 图片等。",[554,4883],{"filename":4884},"14.png",[1236,4886,4887],{},[1239,4888,4889],{},"然后就可以在对话中看到思考过程了，出现「正在思考」的时候，可以点击旁边的箭头展开思考过程。",[554,4891],{"filename":4892},"15.png",[1232,4894,4896],{"id":4895},"_5-联网搜索-error-searching-的问题","5. 联网搜索 Error searching 的问题",[28,4898,4899,4900,4903],{},"目前使用下来发现一个问题，配置了 Pipeline 之后，虽然能够正常显示深度思考过程了，但是使用「联网搜索」会出现 ",[65,4901,4902],{},"Error searching"," 的问题。",[28,4905,4906],{},[4907,4908,4909,4910,1663,4912,4915,4916,4919],"del",{},"目前发现一个临时解决方案，就是去 ",[65,4911,1662],{},[65,4913,4914],{},"界面"," 中关闭 ",[65,4917,4918],{},"网页搜索关键词生成","。初步判断是经过了 Pipeline 的时候，会导致联网搜索的关键词生成出现问题，从而传到搜索引擎的是空的内容，导致搜索失败。临时关闭搜索关键词生成，导致的问题是用户输入内容会直接作为搜索引擎的搜索关键词，不过这个倒能接受。",[28,4921,4922,4923,1663,4925,1663,4927,4930,4931,4934,4935,4938,4939,4942],{},"2025-02-18 更新：发现一个完美的解决方案，在 ",[65,4924,1662],{},[65,4926,4914],{},[65,4928,4929],{},"外部模型"," 中选择一个其他模型（非配置了 Pipeline 的这个模型，例如 ",[65,4932,4933],{},"qwen-max-latest"," 或 ",[65,4936,4937],{},"deepseek-v3","）即可，尽量不要用 ",[65,4940,4941],{},"deepseek-r1","，会比较慢。",[554,4944],{"filename":4945},"16.png",[4947,4948],"hr",{},[4950,4951],"reward-code",{},[523,4953,4954],{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s2W-s, html code.shiki .s2W-s{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#032F62;--shiki-default-font-style:inherit;--shiki-dark:#9ECBFF;--shiki-dark-font-style:inherit}html pre.shiki code .sithA, html code.shiki .sithA{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#032F62;--shiki-default-font-style:inherit;--shiki-dark:#9ECBFF;--shiki-dark-font-style:inherit}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .slqww, html code.shiki .slqww{--shiki-light:#6182B8;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .smCYv, html code.shiki .smCYv{--shiki-light:#E53935;--shiki-light-font-style:italic;--shiki-default:#24292E;--shiki-default-font-style:inherit;--shiki-dark:#E1E4E8;--shiki-dark-font-style:inherit}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .swQdS, html code.shiki .swQdS{--shiki-light:#E53935;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sFwrP, html code.shiki .sFwrP{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#24292E;--shiki-default-font-style:inherit;--shiki-dark:#E1E4E8;--shiki-dark-font-style:inherit}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sQRbd, html code.shiki .sQRbd{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#DBEDFF}",{"title":63,"searchDepth":78,"depth":78,"links":4956},[4957,4958,4959,4960,4961],{"id":1493,"depth":78,"text":1494},{"id":1653,"depth":78,"text":1654},{"id":1691,"depth":78,"text":1692},{"id":4865,"depth":78,"text":4866},{"id":4895,"depth":78,"text":4896},"2025-02-12",{},"\u002Fposts\u002F2025\u002Fdisplay-deepseek-r1-thinking",{"text":4966,"minutes":4967,"time":4968,"words":4969},"7 min read",6.775,406500,1355,{"title":1464,"description":1469},{"loc":4964,"lastmod":4972},"2025-02-18","posts\u002F2025\u002F20250212.display-deepseek-r1-thinking",[543,4975,4976,4977],"人工智能","大语言模型","DeepSeek","AqfdzW2_tEalBScyb92RvCd17nOnwUQjxUqN_MnKsVo",{"id":4980,"title":4981,"body":4982,"class":528,"cover":1212,"coverSize":528,"date":5327,"description":4986,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":5328,"navigation":415,"path":5329,"readingTime":5330,"seo":5335,"sitemap":5336,"stem":5337,"tags":5338,"time":528,"weather":528,"__hash__":5339},"posts\u002Fposts\u002F2025\u002F20250211.deploy-deepseek-r1-for-free.md","零成本部署！阿里云百炼 + Open WebUI 打造专属 DeepSeek-R1",{"type":25,"value":4983,"toc":5320},[4984,4987,4989,4992,4994,4997,5000,5003,5006,5009,5030,5034,5174,5178,5192,5194,5212,5215,5233,5236,5239,5242,5245,5285,5288,5293,5295,5298,5307,5310,5313,5315,5317],[28,4985,4986],{},"大家好，众所周知，国产之光 DeepSeek 现在的热度远比当时 ChatGPT 出来的时候要火多了。泼天的流量再加上各种恶意攻击，导致 DeepSeek 一直存在性能问题。",[554,4988],{"filename":1203},[28,4990,4991],{},"开启了深度思考就经常出现那句经典名言：服务器繁忙，请稍后再试。",[554,4993],{"filename":1710},[28,4995,4996],{},"以至于社区已经出现了各种搞笑段子：",[554,4998],{"filename":4999},"01.jpg",[28,5001,5002],{},"好了，废话不多说，在这样的情况下各大云厂商都纷纷推出了自己的模型部署服务，支持 DeepSeek，并且会赠送很多免费额度。",[28,5004,5005],{},"今天来教大家如何用阿里云百炼平台和开源工具 Open WebUI，零成本部署专属的 DeepSeek R1 模型！全程无需复杂代码，跟着我做就能拥有企业级 AI 服务！我只花了不到半小时就完成了整个服务的部署，并且本视频的脚本有一部分就是由我自己部署的 DeepSeek-R1 来写的。",[1232,5007,5008],{"id":5008},"前期准备",[5010,5011,5012,5015,5027],"ol",{},[1239,5013,5014],{},"注册阿里云账号并实名认证（已有账号可跳过）",[1239,5016,5017,5018,5023,5024],{},"进入 ",[38,5019,5022],{"href":5020,"rel":5021},"https:\u002F\u002Fwww.aliyun.com\u002Fproduct\u002Fbailian",[42],"阿里云百炼"," 的管理控制台，开通大模型服务，获取 ",[65,5025,5026],{},"API KEY",[1239,5028,5029],{},"准备一台有 Docker 环境的服务器，用于部署 WebUI（本机部署亦可）",[1232,5031,5033],{"id":5032},"open-webui-部署","Open WebUI 部署",[5010,5035,5036,5112,5167],{},[1239,5037,5038,5039,5042],{},"通过 Docker Compose 直接部署 ",[38,5040,1477],{"href":1475,"rel":5041},[42],[58,5043,5045],{"className":1557,"code":5044,"language":1559,"meta":63,"style":63},"version: '3'\nservices:\n  openwebui:\n    image: ghcr.io\u002Fopen-webui\u002Fopen-webui:0.5.12（版本号可更新为当前最新 tag 版本号，或使用 main 拉取最新）\n    ports:\n      - '3000:8080'\n    volumes:\n      - .\u002Fdata:\u002Fapp\u002Fbackend\u002Fdata\n",[65,5046,5047,5060,5066,5073,5082,5088,5099,5105],{"__ignoreMap":63},[68,5048,5049,5051,5053,5055,5058],{"class":70,"line":71},[68,5050,155],{"class":730},[68,5052,92],{"class":74},[68,5054,1620],{"class":112},[68,5056,5057],{"class":116},"3",[68,5059,1626],{"class":112},[68,5061,5062,5064],{"class":70,"line":78},[68,5063,1566],{"class":730},[68,5065,1569],{"class":74},[68,5067,5068,5071],{"class":70,"line":98},[68,5069,5070],{"class":730},"  openwebui",[68,5072,1569],{"class":74},[68,5074,5075,5077,5079],{"class":70,"line":123},[68,5076,1591],{"class":730},[68,5078,92],{"class":74},[68,5080,5081],{"class":116}," ghcr.io\u002Fopen-webui\u002Fopen-webui:0.5.12（版本号可更新为当前最新 tag 版本号，或使用 main 拉取最新）\n",[68,5083,5084,5086],{"class":70,"line":129},[68,5085,1610],{"class":730},[68,5087,1569],{"class":74},[68,5089,5090,5092,5094,5097],{"class":70,"line":212},[68,5091,1617],{"class":74},[68,5093,1620],{"class":112},[68,5095,5096],{"class":116},"3000:8080",[68,5098,1626],{"class":112},[68,5100,5101,5103],{"class":70,"line":233},[68,5102,1631],{"class":730},[68,5104,1569],{"class":74},[68,5106,5107,5109],{"class":70,"line":268},[68,5108,1617],{"class":74},[68,5110,5111],{"class":116}," .\u002Fdata:\u002Fapp\u002Fbackend\u002Fdata\n",[1239,5113,5114,5115,5118,5119],{},"启动好实例后，通过 ",[65,5116,5117],{},"http:\u002F\u002Flocalhost:3000\u002F"," 访问 Open WebUI，如果是在云服务器上部署，还有一些域名解析、nginx 代理等操作，这里不再赘述。如果手动设置的 nginx，需要加上 websocket 相关的请求头，具体不赘述，可搜索一下。",[58,5120,5124],{"className":5121,"code":5122,"language":5123,"meta":63,"style":63},"language-nginx shiki shiki-themes material-theme-lighter github-light github-dark","proxy_http_version 1.1;\nproxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection $connection_upgrade;\n","nginx",[65,5125,5126,5137,5153],{"__ignoreMap":63},[68,5127,5128,5131,5134],{"class":70,"line":71},[68,5129,5130],{"class":1899},"proxy_http_version ",[68,5132,5133],{"class":2397},"1.1",[68,5135,5136],{"class":74},";\n",[68,5138,5139,5142,5145,5148,5151],{"class":70,"line":78},[68,5140,5141],{"class":1899},"proxy_set_header ",[68,5143,5144],{"class":373},"Upgrade ",[68,5146,5147],{"class":74},"$",[68,5149,5150],{"class":373},"http_upgrade",[68,5152,5136],{"class":74},[68,5154,5155,5157,5160,5162,5165],{"class":70,"line":98},[68,5156,5141],{"class":1899},[68,5158,5159],{"class":373},"Connection ",[68,5161,5147],{"class":74},[68,5163,5164],{"class":373},"connection_upgrade",[68,5166,5136],{"class":74},[1239,5168,5169,5170,5173],{},"登录进去之后设置好管理员帐号，如果出现白屏需要等待一段时间，大概率是默认的 OpenAI 的接口卡住了，等后面把 API 改成阿里云百炼的，就不会卡住了。有条件的可以给容器设置好 ",[65,5171,5172],{},"HTTP_PROXY"," 的代理，后面联网搜索需要用到。",[1232,5175,5177],{"id":5176},"deepseek-r1-接入","DeepSeek-R1 接入",[5010,5179,5180],{},[1239,5181,5182,5183,1663,5186,1663,5189],{},"进入 Open WebUI 的 ",[65,5184,5185],{},"管理员面板",[65,5187,5188],{},"外部链接",[65,5190,5191],{},"设置",[554,5193],{"filename":1718},[5010,5195,5196],{"start":78},[1239,5197,5198,5199,5202,5203,5205,5206,5208,5209,5211],{},"将 OpenAI 的 API 地址改成阿里云百炼的 API 地址：",[65,5200,5201],{},"https:\u002F\u002Fdashscope.aliyuncs.com\u002Fcompatible-mode\u002Fv1","，秘钥输入百炼获取的 ",[65,5204,5026],{},"，模型 ID 输入 ",[65,5207,4941],{},"，记得一定要点输入框后面那个 ",[65,5210,1670],{}," 加号，不然添加不进去。最后点击“保存”按钮。",[554,5213],{"filename":5214},"12.png",[5010,5216,5217,5220],{"start":98},[1239,5218,5219],{},"打开新会话，测试模型响应。",[1239,5221,5222,5223,5225,5226,5228,5229,5232],{},"如果需要使用阿里云百炼支持的其他模型，可在上述模型 ID 中手动添加需要的模型 ID，或者也可以再添加一个外部连接，模型 ID 留空，就可以添加除了 ",[65,5224,4941],{}," 和 ",[65,5227,4937],{}," 之外的其他所有模型了。之所以需要这么操作是因为目前如果直接留空的话，默认拉不出 ",[65,5230,5231],{},"deepseek"," 相关的模型，手动设置 ID 才能使用，估计日后等阿里云百炼彻底支持 DeepSeek 了就不需要那么操作了。",[554,5234],{"filename":5235},"09.png",[554,5237],{"filename":5238},"10.png",[554,5240],{"filename":5241},"11.png",[1232,5243,5244],{"id":5244},"设置联网搜索",[5010,5246,5247,5255,5264,5271,5278],{},[1239,5248,5249,5250],{},"登录 ",[38,5251,5254],{"href":5252,"rel":5253},"https:\u002F\u002Fdevelopers.google.com\u002Fcustom-search",[42],"Google 开发者账号",[1239,5256,5257,5258,5263],{},"去 ",[38,5259,5262],{"href":5260,"rel":5261},"https:\u002F\u002Fprogrammablesearchengine.google.com\u002Fcontrolpanel\u002Fall",[42],"可编程搜索引擎"," 添加一个自定义搜索引擎",[1239,5265,5266],{},[38,5267,5270],{"href":5268,"rel":5269},"https:\u002F\u002Fdevelopers.google.com\u002Fcustom-search\u002Fv1\u002Fintroduction",[42],"获取密钥",[1239,5272,5273,5274],{},"点击引擎名称，",[38,5275,5277],{"href":5260,"rel":5276},[42],"获取搜索引擎 ID",[1239,5279,5280,5281,5284],{},"前往 Open WebUI 设置页面，联网搜索引擎中下拉选择 ",[65,5282,5283],{},"google_pse","，输入密钥和搜索引擎ID，点击保存。搜索结果数量可设置多一些（会导致 token 消耗多）",[554,5286],{"filename":5287},"06.png",[5010,5289,5290],{"start":212},[1239,5291,5292],{},"打开新会话，发现已经有「联网搜索」选项",[554,5294],{"filename":4862},[1232,5296,5297],{"id":5297},"结语",[28,5299,5300,5301,5306],{},"好了，现在你已经拥有属于自己的 DeepSeek-R1 模型了，并且可以免费使用 100 万 Token，自开通起半年有效期。可在 ",[38,5302,5305],{"href":5303,"rel":5304},"https:\u002F\u002Fbailian.console.aliyun.com\u002Fdetail\u002Fdeepseek-r1#\u002Fmodel-market\u002Fdetail\u002Fdeepseek-r1",[42],"阿里云百炼 DeepSeek-R1"," 页面实时查看自己剩余的免费额度数量以及过期时间。有一说一，这 Token 消耗还挺快的，一下午已经消耗了 5 万多 Token 了。",[554,5308],{"filename":5309},"08.png",[28,5311,5312],{},"同样的，你还可以白嫖腾讯云，同样也有 100万的免费 Token。",[4947,5314],{},[4950,5316],{},[523,5318,5319],{},"html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":63,"searchDepth":78,"depth":78,"links":5321},[5322,5323,5324,5325,5326],{"id":5008,"depth":78,"text":5008},{"id":5032,"depth":78,"text":5033},{"id":5176,"depth":78,"text":5177},{"id":5244,"depth":78,"text":5244},{"id":5297,"depth":78,"text":5297},"2025-02-11",{},"\u002Fposts\u002F2025\u002Fdeploy-deepseek-r1-for-free",{"text":5331,"minutes":5332,"time":5333,"words":5334},"5 min read",4.94,296400,988,{"title":4981,"description":4986},{"loc":5329,"lastmod":4972},"posts\u002F2025\u002F20250211.deploy-deepseek-r1-for-free",[543,4975,4976,4977],"leo4ZEdDEANgF-NvFKhxq93mMJOpiEnKbDqFfmqbgPw",{"id":5341,"title":5342,"body":5343,"class":528,"cover":1212,"coverSize":528,"date":5461,"description":5462,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":5463,"navigation":415,"path":5464,"readingTime":5465,"seo":5469,"sitemap":5470,"stem":5471,"tags":5472,"time":528,"weather":528,"__hash__":5473},"posts\u002Fposts\u002F2025\u002F20250107.change-font-of-vscode-sidebar.md","如何修改 VSCode 侧边栏字体",{"type":25,"value":5344,"toc":5459},[5345,5358,5373,5375,5396,5398,5401,5451,5454,5456],[28,5346,5347,5348,5351,5352,5357],{},"如果你折腾过 VSCode 自定义字体的话，你应该知道目前 VSCode 只能自定义编辑器以及终端的字体样式，而侧边栏的字体样式是无法自定义的。不过你可以通过 ",[65,5349,5350],{},"window.zoomLevel"," 来曲线救国实现侧边栏字号的调整，但字体依旧无法设置，关于这个问题的讨论，可以查看这个存在了近十年但仍未解决的 ",[38,5353,5356],{"href":5354,"rel":5355},"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode\u002Fissues\u002F519",[42],"issue#519","。上下文实在太长，有几百个评论，我也没仔细研究其原因。有兴趣的可以研究一下来龙去脉。",[28,5359,5360,5361,5366,5367,5372],{},"经过一顿探寻，发现可以通过 ",[38,5362,5365],{"href":5363,"rel":5364},"https:\u002F\u002Fmarketplace.visualstudio.com\u002Fitems?itemName=drcika.apc-extension",[42],"Apc Customize UI++"," 这个插件来实现侧边栏字体的调整，这个插件可以通过简单的配置来实现。另外还可以通过 ",[38,5368,5371],{"href":5369,"rel":5370},"https:\u002F\u002Fmarketplace.visualstudio.com\u002Fitems?itemName=be5invis.vscode-custom-css",[42],"Custom CSS and JS Loader"," 这个插件来实现，这个可以自定义 CSS 样式，可定制化程度更高，不过没有前者方便。我这里打算使用前者。",[554,5374],{"filename":556},[28,5376,5377,5378,5380,5381,5384,5385,5390,5391,1686],{},"遗憾的是，经过尝试，这个 ",[65,5379,5365],{}," 这个插件目前在 ",[65,5382,5383],{},"1.93"," 以上的版本中已经无法使用。在相关 ",[38,5386,5389],{"href":5387,"rel":5388},"https:\u002F\u002Fgithub.com\u002Fdrcika\u002Fapc-extension\u002Fissues\u002F230#issuecomment-2421377174",[42],"issue#230"," 中发现一个国人开发的可替代的插件：",[38,5392,5395],{"href":5393,"rel":5394},"https:\u002F\u002Fmarketplace.visualstudio.com\u002Fitems?itemName=subframe7536.custom-ui-style",[42],"Custom UI Style",[554,5397],{"filename":1203},[28,5399,5400],{},"对于我这种只需要修改字体的，配制就比较简单了，侵入性也比较小。配置如下：",[58,5402,5404],{"className":60,"code":5403,"language":62,"meta":63,"style":63},"{\n  \"custom-ui-style.font.monospace\": \"Jetbrains Mono\",\n  \"custom-ui-style.font.sansSerif\": \"Jetbrains Mono\"\n}\n",[65,5405,5406,5410,5430,5447],{"__ignoreMap":63},[68,5407,5408],{"class":70,"line":71},[68,5409,75],{"class":74},[68,5411,5412,5414,5417,5419,5421,5423,5426,5428],{"class":70,"line":78},[68,5413,82],{"class":81},[68,5415,5416],{"class":85},"custom-ui-style.font.monospace",[68,5418,89],{"class":81},[68,5420,92],{"class":74},[68,5422,113],{"class":112},[68,5424,5425],{"class":116},"Jetbrains Mono",[68,5427,89],{"class":112},[68,5429,169],{"class":74},[68,5431,5432,5434,5437,5439,5441,5443,5445],{"class":70,"line":98},[68,5433,82],{"class":81},[68,5435,5436],{"class":85},"custom-ui-style.font.sansSerif",[68,5438,89],{"class":81},[68,5440,92],{"class":74},[68,5442,113],{"class":112},[68,5444,5425],{"class":116},[68,5446,120],{"class":112},[68,5448,5449],{"class":70,"line":123},[68,5450,132],{"class":74},[28,5452,5453],{},"最后看下整体效果吧：",[554,5455],{"filename":1710},[523,5457,5458],{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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":63,"searchDepth":78,"depth":78,"links":5460},[],"2025-01-07","如果你折腾过 VSCode 自定义字体的话，你应该知道目前 VSCode 只能自定义编辑器以及终端的字体样式，而侧边栏的字体样式是无法自定义的。不过你可以通过 window.zoomLevel 来曲线救国实现侧边栏字号的调整，但字体依旧无法设置，关于这个问题的讨论，可以查看这个存在了近十年但仍未解决的 issue#519。上下文实在太长，有几百个评论，我也没仔细研究其原因。有兴趣的可以研究一下来龙去脉。",{},"\u002Fposts\u002F2025\u002Fchange-font-of-vscode-sidebar",{"text":1217,"minutes":5466,"time":5467,"words":5468},1.755,105300,351,{"title":5342,"description":5462},{"loc":5464},"posts\u002F2025\u002F20250107.change-font-of-vscode-sidebar",[543],"6_cHQtBHbF5RA_jswlXNVQQD0TOfXQSPE0WUEkfTWIc",{"id":5475,"title":5476,"body":5477,"class":528,"cover":528,"coverSize":528,"date":7141,"description":5481,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":7142,"navigation":415,"path":7143,"readingTime":7144,"seo":7149,"sitemap":7150,"stem":7151,"tags":7152,"time":528,"weather":528,"__hash__":7155},"posts\u002Fposts\u002F2024\u002F20241212.managing-containers-in-my-homelab.md","如何管理并自动更新 HomeLab 中的容器",{"type":25,"value":5478,"toc":7137},[5479,5482,5485,5488,5491,5494,5502,5505,5508,5511,5514,5516,5523,5529,5535,5559,5565,5934,5945,5950,7098,7108,7128,7131,7134],[28,5480,5481],{},"经过一段时间的探索和优化，我已经找到一个特别适合我的方法，来自动化管理我 HomeLab 中的众多容器。一直想写一篇文章来记录一下，拖到今天才开始动笔。",[28,5483,5484],{},"首先交代一下背景，我的 HomeLab 中目前总共管理着 50 多个容器，分布在群晖 NAS 以及另外两台 NUC 的虚拟机中。从最初的直接通过 compose 文件手动管理，到后来使用了 Portainer，再到现在的基于 GitLab CI 的自动化管理方式，逐渐变得更自动化、更方便、也更不容易出错。",[1232,5486,5487],{"id":5487},"为什么要自动化管理",[28,5489,5490],{},"一开始上 Portainer，是为了解决手动管理多个设备中的容器，频繁 SSH 到不同设备中比较麻烦的问题。用 Portainer 确实可以方便快捷地管理多个设备中的容器，当容器数量比较少的时候，还是非常推荐的。",[28,5492,5493],{},"随着容器数量的变多，Portainer 上我遇到两个不太好解决的问题：",[5010,5495,5496,5499],{},[1239,5497,5498],{},"容器的自动更新，Portainer 的 Business 版是提供了自动更新的功能的，但可惜 CE 版没有",[1239,5500,5501],{},"容器的配置文件管理、配置和 compose 文件的备份、版本记录等",[28,5503,5504],{},"第二个问题比较好解决，通过 git 管理 compose 文件和配置文件，结合 GitLab CI 在文件变更的时候自动 SSH 到对应宿主机上执行容器的更新操作。",[28,5506,5507],{},"第一个问题，是我在本博客项目上使用 renovate 来更新前端依赖的时候，从他们的文章中看到，renovate 也可以检测 Docker 镜像的更新，于是灵光一现，基于上一步所有 compose 文件都已经在 GitLab 中管理了，那再结合 renovate，就可以实现容器的自动更新了。",[1232,5509,5510],{"id":5510},"最终形态",[28,5512,5513],{},"中间摸索的步骤由于已经过了差不多几个月了，不太容易复盘了，就把最终的形态分享一下。",[554,5515],{":dark-supported":849,"filename":556},[28,5517,5518,5519,5522],{},"如上图，所有容器的配置文件、compose 文件都放在 ",[65,5520,5521],{},"config-files"," 这个项目中，该项目通过 GitLab 管理，renovate 在定期检测到镜像更新的时候，会自动提交一个 MR，可以通过一定规则配置成自动合并或手动合并，当然也支持手动修改配置文件或 compose 文件。MR 合并或配置文件修改后触发 GitLab CI，通过 SSH 登录对应宿主机上，执行对应的配置文件更新或容器更新的操作。",[28,5524,5525,5526,5528],{},"我的 ",[65,5527,5521],{}," 目录结构：",[58,5530,5533],{"className":5531,"code":5532,"language":516},[514],".\n├── aliyun\n│   ├── gateway\n│   └── hk\n├── homelab\n│   ├── scripts\n│   ├── synology\n│   ├── vm-rocky-01\n│   └── vm-rocky-02\n├── .gitlab-ci.yml\n└── renovate.json\n",[65,5534,5532],{"__ignoreMap":63},[28,5536,5537,5540,5541,5225,5544,5547,5548,5551,5552,5555,5556,5558],{},[65,5538,5539],{},"aliyun"," 目录中的 ",[65,5542,5543],{},"gateway",[65,5545,5546],{},"hk"," 是我在杭州和香港的两台服务器，部署一些外网项目，也是通过上述的方式管理。",[65,5549,5550],{},"homelab"," 中的 ",[65,5553,5554],{},"scripts"," 是一些脚本，与本文无关，但也是通过 ",[65,5557,5521],{}," 项目统一管理的。",[28,5560,5525,5561,5564],{},[65,5562,5563],{},"renovate.json"," 如下：",[58,5566,5568],{"className":60,"code":5567,"language":62,"meta":63,"style":63},"{\n  \"$schema\": \"https:\u002F\u002Fdocs.renovatebot.com\u002Frenovate-schema.json\",\n  \"extends\": [\"config:recommended\", \"group:allNonMajor\", \":semanticCommitTypeAll(chore)\"],\n  \"packageRules\": [\n    {\n      \"matchManagers\": [\"docker-compose\"],\n      \"matchPackageNames\": [\"sonatype\u002Fnexus3\", \"clickhouse\u002Fclickhouse-server\"],\n      \"enabled\": false\n    },\n    {\n      \"matchManagers\": [\"docker-compose\"],\n      \"matchPackageNames\": [\"gitlab\u002Fgitlab-runner\"],\n      \"versionCompatibility\": \"^(?\u003Ccompatibility>.*)-(?\u003Cversion>.*)$\"\n    },\n    {\n      \"matchManagers\": [\"docker-compose\"],\n      \"matchUpdateTypes\": [\"minor\", \"patch\"],\n      \"automerge\": true,\n      \"schedule\": [\"before 11am on Monday\"]\n    }\n  ],\n  \"rangeStrategy\": \"bump\",\n  \"timezone\": \"Asia\u002FShanghai\"\n}\n",[65,5569,5570,5574,5594,5634,5647,5651,5673,5704,5718,5723,5727,5747,5768,5786,5790,5794,5814,5845,5861,5883,5887,5892,5912,5930],{"__ignoreMap":63},[68,5571,5572],{"class":70,"line":71},[68,5573,75],{"class":74},[68,5575,5576,5578,5581,5583,5585,5587,5590,5592],{"class":70,"line":78},[68,5577,82],{"class":81},[68,5579,5580],{"class":85},"$schema",[68,5582,89],{"class":81},[68,5584,92],{"class":74},[68,5586,113],{"class":112},[68,5588,5589],{"class":116},"https:\u002F\u002Fdocs.renovatebot.com\u002Frenovate-schema.json",[68,5591,89],{"class":112},[68,5593,169],{"class":74},[68,5595,5596,5598,5601,5603,5605,5607,5609,5612,5614,5616,5618,5621,5623,5625,5627,5630,5632],{"class":70,"line":98},[68,5597,82],{"class":81},[68,5599,5600],{"class":85},"extends",[68,5602,89],{"class":81},[68,5604,92],{"class":74},[68,5606,245],{"class":74},[68,5608,89],{"class":112},[68,5610,5611],{"class":116},"config:recommended",[68,5613,89],{"class":112},[68,5615,255],{"class":74},[68,5617,113],{"class":112},[68,5619,5620],{"class":116},"group:allNonMajor",[68,5622,89],{"class":112},[68,5624,255],{"class":74},[68,5626,113],{"class":112},[68,5628,5629],{"class":116},":semanticCommitTypeAll(chore)",[68,5631,89],{"class":112},[68,5633,265],{"class":74},[68,5635,5636,5638,5641,5643,5645],{"class":70,"line":123},[68,5637,82],{"class":81},[68,5639,5640],{"class":85},"packageRules",[68,5642,89],{"class":81},[68,5644,92],{"class":74},[68,5646,183],{"class":74},[68,5648,5649],{"class":70,"line":129},[68,5650,188],{"class":74},[68,5652,5653,5655,5658,5660,5662,5664,5666,5669,5671],{"class":70,"line":212},[68,5654,193],{"class":81},[68,5656,5657],{"class":104},"matchManagers",[68,5659,89],{"class":81},[68,5661,92],{"class":74},[68,5663,245],{"class":74},[68,5665,89],{"class":112},[68,5667,5668],{"class":116},"docker-compose",[68,5670,89],{"class":112},[68,5672,265],{"class":74},[68,5674,5675,5677,5680,5682,5684,5686,5688,5691,5693,5695,5697,5700,5702],{"class":70,"line":233},[68,5676,193],{"class":81},[68,5678,5679],{"class":104},"matchPackageNames",[68,5681,89],{"class":81},[68,5683,92],{"class":74},[68,5685,245],{"class":74},[68,5687,89],{"class":112},[68,5689,5690],{"class":116},"sonatype\u002Fnexus3",[68,5692,89],{"class":112},[68,5694,255],{"class":74},[68,5696,113],{"class":112},[68,5698,5699],{"class":116},"clickhouse\u002Fclickhouse-server",[68,5701,89],{"class":112},[68,5703,265],{"class":74},[68,5705,5706,5708,5711,5713,5715],{"class":70,"line":268},[68,5707,193],{"class":81},[68,5709,5710],{"class":104},"enabled",[68,5712,89],{"class":81},[68,5714,92],{"class":74},[68,5716,5717],{"class":81}," false\n",[68,5719,5720],{"class":70,"line":289},[68,5721,5722],{"class":74},"    },\n",[68,5724,5725],{"class":70,"line":308},[68,5726,188],{"class":74},[68,5728,5729,5731,5733,5735,5737,5739,5741,5743,5745],{"class":70,"line":314},[68,5730,193],{"class":81},[68,5732,5657],{"class":104},[68,5734,89],{"class":81},[68,5736,92],{"class":74},[68,5738,245],{"class":74},[68,5740,89],{"class":112},[68,5742,5668],{"class":116},[68,5744,89],{"class":112},[68,5746,265],{"class":74},[68,5748,5749,5751,5753,5755,5757,5759,5761,5764,5766],{"class":70,"line":320},[68,5750,193],{"class":81},[68,5752,5679],{"class":104},[68,5754,89],{"class":81},[68,5756,92],{"class":74},[68,5758,245],{"class":74},[68,5760,89],{"class":112},[68,5762,5763],{"class":116},"gitlab\u002Fgitlab-runner",[68,5765,89],{"class":112},[68,5767,265],{"class":74},[68,5769,5770,5772,5775,5777,5779,5781,5784],{"class":70,"line":889},[68,5771,193],{"class":81},[68,5773,5774],{"class":104},"versionCompatibility",[68,5776,89],{"class":81},[68,5778,92],{"class":74},[68,5780,113],{"class":112},[68,5782,5783],{"class":116},"^(?\u003Ccompatibility>.*)-(?\u003Cversion>.*)$",[68,5785,120],{"class":112},[68,5787,5788],{"class":70,"line":909},[68,5789,5722],{"class":74},[68,5791,5792],{"class":70,"line":929},[68,5793,188],{"class":74},[68,5795,5796,5798,5800,5802,5804,5806,5808,5810,5812],{"class":70,"line":949},[68,5797,193],{"class":81},[68,5799,5657],{"class":104},[68,5801,89],{"class":81},[68,5803,92],{"class":74},[68,5805,245],{"class":74},[68,5807,89],{"class":112},[68,5809,5668],{"class":116},[68,5811,89],{"class":112},[68,5813,265],{"class":74},[68,5815,5816,5818,5821,5823,5825,5827,5829,5832,5834,5836,5838,5841,5843],{"class":70,"line":969},[68,5817,193],{"class":81},[68,5819,5820],{"class":104},"matchUpdateTypes",[68,5822,89],{"class":81},[68,5824,92],{"class":74},[68,5826,245],{"class":74},[68,5828,89],{"class":112},[68,5830,5831],{"class":116},"minor",[68,5833,89],{"class":112},[68,5835,255],{"class":74},[68,5837,113],{"class":112},[68,5839,5840],{"class":116},"patch",[68,5842,89],{"class":112},[68,5844,265],{"class":74},[68,5846,5847,5849,5852,5854,5856,5859],{"class":70,"line":989},[68,5848,193],{"class":81},[68,5850,5851],{"class":104},"automerge",[68,5853,89],{"class":81},[68,5855,92],{"class":74},[68,5857,5858],{"class":81}," true",[68,5860,169],{"class":74},[68,5862,5863,5865,5868,5870,5872,5874,5876,5879,5881],{"class":70,"line":1009},[68,5864,193],{"class":81},[68,5866,5867],{"class":104},"schedule",[68,5869,89],{"class":81},[68,5871,92],{"class":74},[68,5873,245],{"class":74},[68,5875,89],{"class":112},[68,5877,5878],{"class":116},"before 11am on Monday",[68,5880,89],{"class":112},[68,5882,2657],{"class":74},[68,5884,5885],{"class":70,"line":1029},[68,5886,311],{"class":74},[68,5888,5889],{"class":70,"line":1049},[68,5890,5891],{"class":74},"  ],\n",[68,5893,5894,5896,5899,5901,5903,5905,5908,5910],{"class":70,"line":1069},[68,5895,82],{"class":81},[68,5897,5898],{"class":85},"rangeStrategy",[68,5900,89],{"class":81},[68,5902,92],{"class":74},[68,5904,113],{"class":112},[68,5906,5907],{"class":116},"bump",[68,5909,89],{"class":112},[68,5911,169],{"class":74},[68,5913,5914,5916,5919,5921,5923,5925,5928],{"class":70,"line":1088},[68,5915,82],{"class":81},[68,5917,5918],{"class":85},"timezone",[68,5920,89],{"class":81},[68,5922,92],{"class":74},[68,5924,113],{"class":112},[68,5926,5927],{"class":116},"Asia\u002FShanghai",[68,5929,120],{"class":112},[68,5931,5932],{"class":70,"line":1108},[68,5933,132],{"class":74},[28,5935,5936,5937,5225,5939,5941,5942,5944],{},"里面有一些自定义的规则，比如禁用了 ",[65,5938,5690],{},[65,5940,5699],{}," 的自动更新，以及适配了 ",[65,5943,5763],{}," 的不规则的版本号。",[28,5946,5947,5564],{},[65,5948,5949],{},".gitlab-ci.yml",[58,5951,5953],{"className":1557,"code":5952,"language":1559,"meta":63,"style":63},"stages:\n  - pass\n  - deploy\n\npass:\n  stage: pass\n  script:\n    - echo \"Current branch is $CI_COMMIT_BRANCH, pass.\"\n  except:\n    - main\n\ndeploy:\n  stage: deploy\n  before_script:\n    - eval $(ssh-agent -s)\n    - echo \"$SSH_PRIVATE_KEY\" | ssh-add -\n    - mkdir -p ~\u002F.ssh\n    - echo \"$SSH_KNOWN_HOSTS\" > ~\u002F.ssh\u002Fknown_hosts\n\n  script:\n    - MODIFIED_FILES=$(git diff --name-only --diff-filter=ACMRT $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA)\n    - DELETED_FILES=$(git diff --name-status --diff-filter=DR $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA | awk '{print $2}')\n    - |\n      for FILE in $DELETED_FILES; do\n        echo \"文件移除 $FILE\"\n        # 如果文件是 compose.yaml，执行 docker compose down\n        if [[ \"$FILE\" == \"*\u002Fcompose.yaml\" ]]; then\n          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n            DEST_DIR=\"\u002Fvolume3\u002Fdocker\u002F$(dirname ${FILE#homelab\u002F})\"\n            echo \"退出容器 synology:$(dirname ${FILE#homelab\u002Fsynology\u002F})\"\n            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd $DEST_DIR && docker-compose down\"\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n            echo \"退出容器 vm-rocky-01:$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n            ssh root@vm-rocky-01.home \"cd $DEST_DIR && docker compose down\"\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n            echo \"退出容器 vm-rocky-02:$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n            ssh root@vm-rocky-02.home \"cd $DEST_DIR && docker compose down\"\n          fi\n\n          # if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n          #   DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n          #   echo \"退出容器 gateway:$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n          #   ssh root@gateway.aliyun \"cd $DEST_DIR && docker compose down\"\n          # fi\n        fi\n\n        # 删除对应文件\n        if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n          FILE_PATH=\"\u002Fvolume3\u002Fdocker\u002F${FILE#homelab\u002F}\"\n          echo \"从 Synology 删除文件: $FILE_PATH\"\n          ssh -p 5110 root@synology.home \"rm -f $FILE_PATH\"\n        fi\n\n        if [[  \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n          FILE_PATH=\"\u002Froot\u002F${FILE#homelab\u002Fvm-rocky-01\u002F}\"\n          echo \"从 vm-rocky-01 删除文件: $FILE_PATH\"\n          ssh root@vm-rocky-01.home \"rm -f $FILE_PATH\"\n        fi\n\n        if [[  \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n          FILE_PATH=\"\u002Froot\u002F${FILE#homelab\u002Fvm-rocky-02\u002F}\"\n          echo \"从 vm-rocky-02 删除文件: $FILE_PATH\"\n          ssh root@vm-rocky-02.home \"rm -f $FILE_PATH\"\n        fi\n\n        if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n          FILE_PATH=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F${FILE#aliyun\u002Fgateway\u002F}\"\n          echo \"从 gateway.aliyun 删除文件: $FILE_PATH\"\n          ssh root@gateway.aliyun \"rm -f $FILE_PATH\"\n        fi\n      done\n\n      for FILE in $MODIFIED_FILES; do\n        echo \"文件变更 $FILE\"\n        if [ -e \"$FILE\" ]; then\n          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n            DEST_DIR=\"\u002Fvolume3\u002Fdocker\u002F$(dirname ${FILE#homelab\u002F})\"\n            ssh -p 5110 root@synology.home \"mkdir -p $DEST_DIR\"\n            echo \"复制文件 $FILE 到 Synology: $DEST_DIR\"\n            scp -O -P 5110 $FILE root@synology.home:$DEST_DIR\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n            ssh root@vm-rocky-01.home \"mkdir -p $DEST_DIR\"\n            echo \"复制文件 $FILE 到 vm-rocky-01: $DEST_DIR\"\n            scp -O $FILE root@vm-rocky-01.home:$DEST_DIR\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n            ssh root@vm-rocky-02.home \"mkdir -p $DEST_DIR\"\n            echo \"复制文件 $FILE 到 vm-rocky-02: $DEST_DIR\"\n            scp -O $FILE root@vm-rocky-02.home:$DEST_DIR\n          fi\n\n          if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n            DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n            ssh root@gateway.aliyun \"mkdir -p $DEST_DIR\"\n            echo \"复制文件 $FILE 到 gateway.aliyun: $DEST_DIR\"\n            scp -O $FILE root@gateway.aliyun:$DEST_DIR\n          fi\n\n          if [[ \"$FILE\" == \"aliyun\u002Fhk\u002Fnginx\u002F*\" ]]; then\n            DEST_DIR=\"\u002Fetc\u002Fnginx\u002F$(dirname ${FILE#aliyun\u002Fhk\u002Fnginx\u002F})\"\n            ssh root@hk.aliyun \"mkdir -p $DEST_DIR\"\n            echo \"复制文件 $FILE 到 hk.aliyun: $DEST_DIR\"\n            scp -O $FILE root@hk.aliyun:$DEST_DIR\n          fi\n        fi\n      done\n\n      for FILE in $MODIFIED_FILES; do\n        if [ -e \"$FILE\" ]; then\n          # 如果文件是 compose.yaml，执行 docker compose up -d\n          if [[ \"$FILE\" == \"*\u002Fcompose.yaml\" ]]; then\n            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Frenovate\u002Fcompose.yaml\" ]]; then\n              echo \"跳过部署 vm-rocky-01:renovate\"\n              continue\n            fi\n\n            if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n              DEST_DIR=\"\u002Fvolume3\u002Fdocker\u002F$(dirname ${FILE#homelab\u002F})\"\n              echo \"部署容器 synology:$(dirname ${FILE#homelab\u002Fsynology\u002F})\"\n              ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd $DEST_DIR && docker-compose up -d\"\n            fi\n\n            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n              DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n              echo \"部署容器 vm-rocky-01:$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n              ssh root@vm-rocky-01.home \"cd $DEST_DIR && docker compose up -d\"\n            fi\n\n            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n              DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n              echo \"部署容器 vm-rocky-02:$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n              ssh root@vm-rocky-02.home \"cd $DEST_DIR && docker compose up -d\"\n            fi\n\n            # if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n            #   DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n            #   echo \"部署容器 gateway.aliyun:$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n            #   ssh root@gateway.aliyun \"cd $DEST_DIR && docker compose up -d\"\n            # fi\n\n            echo \"容器重新部署完成\"\n            continue\n          fi\n\n          # 如果文件是 homelab\u002Fsynology\u002Fnginx\u002F*, 重新启动 nginx\n          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Fnginx\u002F*\" ]]; then\n            echo \"重新启动 synology:nginx 服务\"\n            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && docker exec nginx nginx -s reload\"\n          fi\n\n          # 如果文件是 aliyun\u002Fgateway\u002Fnginx\u002F*, 重新启动 nginx\n          if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002Fnginx\u002F*\" ]]; then\n            echo \"重新启动 gateway.aliyun:nginx\"\n            ssh root@gateway.aliyun \"nginx -s reload\"\n          fi\n\n          # 如果文件是 aliyun\u002Fhk\u002Fnginx\u002F*, 重新启动 nginx\n          if [[ \"$FILE\" == \"aliyun\u002Fhk\u002Fnginx\u002F*\" ]]; then\n            echo \"重新启动 hk.aliyun:nginx\"\n            ssh root@hk.aliyun \"nginx -s reload\"\n          fi\n\n          # 如果文件是 rinetd.conf, 重新启动 rinetd\n          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Frinetd\u002Fconfig\u002Frinetd.conf\" ]]; then\n            echo \"重新启动 synology:rinetd 服务\"\n            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Frinetd && docker-compose restart\"\n          fi\n\n          # 如果文件是 gitlab.rb, 重新配置 gitlab\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Fgitlab\u002Fconfig\u002Fgitlab.rb\" ]]; then\n            echo \"重新配置 vm-rocky-01:gitlab\"\n            ssh root@vm-rocky-01.home \"docker exec gitlab gitlab-ctl reconfigure\"\n          fi\n\n          # 如果文件是 prometheus.yml, 重新启动 prometheus\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Fprometheus\u002Fconfig\u002Fprometheus.yml\" ]]; then\n            echo \"重新启动 vm-rocky-01:prometheus\"\n            ssh root@vm-rocky-01.home \"cd \u002Froot\u002Fprometheus && docker compose restart\"\n          fi\n\n          # 如果文件是 telegraf.conf, 重新启动 telegraf\n          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n            echo \"重新启动 synology:telegraf\"\n            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Ftelegraf && docker-compose restart\"\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n            echo \"重新启动 vm-rocky-01:telegraf\"\n            ssh root@vm-rocky-01.home \"cd \u002Froot\u002Ftelegraf && docker compose restart\"\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n            echo \"重新启动 vm-rocky-02:telegraf\"\n            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Ftelegraf && docker compose restart\"\n          fi\n\n          # 如果文件是 bots\u002Fconfig\u002Fclash\u002F*.yaml, 更新 clash 配置\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Fbots\u002Fconfig\u002Fclash\u002F*.yaml\" ]]; then\n            echo \"重新启动 vm-rocky-02:bots\"\n            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Fbots && docker compose restart\"\n            echo \"等待 10 秒\"\n            sleep 10\n            echo \"重新启动 synology:clash-premium\"\n            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Fclash-premium && docker-compose restart\"\n          fi\n\n          # 如果文件是 artalk\u002Fdata\u002Fartalk.yml, 重新启动 artalk\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Fartalk\u002Fdata\u002Fartalk.yml\" ]]; then\n            echo \"重新启动 vm-rocky-02:artalk\"\n            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Fartalk && docker compose restart\"\n          fi\n\n        fi\n      done\n    - echo \"部署完成\"\n  only:\n    - main\n",[65,5954,5955,5962,5970,5977,5981,5988,5997,6004,6012,6019,6026,6030,6037,6045,6052,6059,6066,6073,6080,6084,6090,6097,6104,6111,6116,6121,6126,6131,6136,6141,6146,6151,6156,6160,6165,6170,6175,6180,6184,6188,6193,6198,6203,6208,6212,6216,6221,6226,6231,6236,6241,6246,6250,6255,6260,6265,6270,6275,6279,6283,6288,6293,6298,6303,6307,6311,6316,6321,6326,6331,6335,6339,6344,6349,6354,6359,6363,6368,6372,6377,6382,6387,6391,6395,6400,6405,6410,6414,6418,6422,6426,6431,6436,6441,6445,6449,6453,6457,6462,6467,6472,6476,6480,6485,6490,6495,6500,6505,6509,6513,6518,6523,6528,6533,6538,6542,6546,6550,6554,6558,6562,6567,6572,6577,6582,6587,6592,6596,6601,6606,6611,6616,6620,6624,6629,6634,6639,6644,6648,6652,6657,6662,6667,6672,6676,6680,6685,6690,6695,6700,6705,6709,6714,6719,6723,6727,6732,6737,6742,6747,6751,6755,6760,6765,6770,6775,6779,6783,6788,6792,6797,6802,6806,6810,6815,6820,6825,6830,6834,6838,6843,6848,6853,6858,6862,6866,6871,6876,6881,6886,6890,6894,6899,6904,6909,6914,6918,6922,6927,6932,6937,6941,6945,6951,6957,6963,6968,6973,6979,6985,6991,6997,7003,7009,7015,7021,7026,7031,7037,7043,7049,7055,7060,7065,7070,7075,7083,7091],{"__ignoreMap":63},[68,5956,5957,5960],{"class":70,"line":71},[68,5958,5959],{"class":730},"stages",[68,5961,1569],{"class":74},[68,5963,5964,5967],{"class":70,"line":78},[68,5965,5966],{"class":74},"  -",[68,5968,5969],{"class":116}," pass\n",[68,5971,5972,5974],{"class":70,"line":98},[68,5973,5966],{"class":74},[68,5975,5976],{"class":116}," deploy\n",[68,5978,5979],{"class":70,"line":123},[68,5980,416],{"emptyLinePlaceholder":415},[68,5982,5983,5986],{"class":70,"line":129},[68,5984,5985],{"class":730},"pass",[68,5987,1569],{"class":74},[68,5989,5990,5993,5995],{"class":70,"line":212},[68,5991,5992],{"class":730},"  stage",[68,5994,92],{"class":74},[68,5996,5969],{"class":116},[68,5998,5999,6002],{"class":70,"line":233},[68,6000,6001],{"class":730},"  script",[68,6003,1569],{"class":74},[68,6005,6006,6009],{"class":70,"line":268},[68,6007,6008],{"class":74},"    -",[68,6010,6011],{"class":116}," echo \"Current branch is $CI_COMMIT_BRANCH, pass.\"\n",[68,6013,6014,6017],{"class":70,"line":289},[68,6015,6016],{"class":730},"  except",[68,6018,1569],{"class":74},[68,6020,6021,6023],{"class":70,"line":308},[68,6022,6008],{"class":74},[68,6024,6025],{"class":116}," main\n",[68,6027,6028],{"class":70,"line":314},[68,6029,416],{"emptyLinePlaceholder":415},[68,6031,6032,6035],{"class":70,"line":320},[68,6033,6034],{"class":730},"deploy",[68,6036,1569],{"class":74},[68,6038,6039,6041,6043],{"class":70,"line":889},[68,6040,5992],{"class":730},[68,6042,92],{"class":74},[68,6044,5976],{"class":116},[68,6046,6047,6050],{"class":70,"line":909},[68,6048,6049],{"class":730},"  before_script",[68,6051,1569],{"class":74},[68,6053,6054,6056],{"class":70,"line":929},[68,6055,6008],{"class":74},[68,6057,6058],{"class":116}," eval $(ssh-agent -s)\n",[68,6060,6061,6063],{"class":70,"line":949},[68,6062,6008],{"class":74},[68,6064,6065],{"class":116}," echo \"$SSH_PRIVATE_KEY\" | ssh-add -\n",[68,6067,6068,6070],{"class":70,"line":969},[68,6069,6008],{"class":74},[68,6071,6072],{"class":116}," mkdir -p ~\u002F.ssh\n",[68,6074,6075,6077],{"class":70,"line":989},[68,6076,6008],{"class":74},[68,6078,6079],{"class":116}," echo \"$SSH_KNOWN_HOSTS\" > ~\u002F.ssh\u002Fknown_hosts\n",[68,6081,6082],{"class":70,"line":1009},[68,6083,416],{"emptyLinePlaceholder":415},[68,6085,6086,6088],{"class":70,"line":1029},[68,6087,6001],{"class":730},[68,6089,1569],{"class":74},[68,6091,6092,6094],{"class":70,"line":1049},[68,6093,6008],{"class":74},[68,6095,6096],{"class":116}," MODIFIED_FILES=$(git diff --name-only --diff-filter=ACMRT $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA)\n",[68,6098,6099,6101],{"class":70,"line":1069},[68,6100,6008],{"class":74},[68,6102,6103],{"class":116}," DELETED_FILES=$(git diff --name-status --diff-filter=DR $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA | awk '{print $2}')\n",[68,6105,6106,6108],{"class":70,"line":1088},[68,6107,6008],{"class":74},[68,6109,6110],{"class":1790}," |\n",[68,6112,6113],{"class":70,"line":1108},[68,6114,6115],{"class":116},"      for FILE in $DELETED_FILES; do\n",[68,6117,6118],{"class":70,"line":1128},[68,6119,6120],{"class":116},"        echo \"文件移除 $FILE\"\n",[68,6122,6123],{"class":70,"line":1148},[68,6124,6125],{"class":116},"        # 如果文件是 compose.yaml，执行 docker compose down\n",[68,6127,6128],{"class":70,"line":1168},[68,6129,6130],{"class":116},"        if [[ \"$FILE\" == \"*\u002Fcompose.yaml\" ]]; then\n",[68,6132,6133],{"class":70,"line":1187},[68,6134,6135],{"class":116},"          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n",[68,6137,6138],{"class":70,"line":2039},[68,6139,6140],{"class":116},"            DEST_DIR=\"\u002Fvolume3\u002Fdocker\u002F$(dirname ${FILE#homelab\u002F})\"\n",[68,6142,6143],{"class":70,"line":2055},[68,6144,6145],{"class":116},"            echo \"退出容器 synology:$(dirname ${FILE#homelab\u002Fsynology\u002F})\"\n",[68,6147,6148],{"class":70,"line":2071},[68,6149,6150],{"class":116},"            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd $DEST_DIR && docker-compose down\"\n",[68,6152,6153],{"class":70,"line":2087},[68,6154,6155],{"class":116},"          fi\n",[68,6157,6158],{"class":70,"line":2092},[68,6159,416],{"emptyLinePlaceholder":415},[68,6161,6162],{"class":70,"line":2097},[68,6163,6164],{"class":116},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n",[68,6166,6167],{"class":70,"line":2114},[68,6168,6169],{"class":116},"            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n",[68,6171,6172],{"class":70,"line":2139},[68,6173,6174],{"class":116},"            echo \"退出容器 vm-rocky-01:$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n",[68,6176,6177],{"class":70,"line":2158},[68,6178,6179],{"class":116},"            ssh root@vm-rocky-01.home \"cd $DEST_DIR && docker compose down\"\n",[68,6181,6182],{"class":70,"line":2173},[68,6183,6155],{"class":116},[68,6185,6186],{"class":70,"line":2178},[68,6187,416],{"emptyLinePlaceholder":415},[68,6189,6190],{"class":70,"line":2193},[68,6191,6192],{"class":116},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n",[68,6194,6195],{"class":70,"line":2201},[68,6196,6197],{"class":116},"            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n",[68,6199,6200],{"class":70,"line":2207},[68,6201,6202],{"class":116},"            echo \"退出容器 vm-rocky-02:$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n",[68,6204,6205],{"class":70,"line":2234},[68,6206,6207],{"class":116},"            ssh root@vm-rocky-02.home \"cd $DEST_DIR && docker compose down\"\n",[68,6209,6210],{"class":70,"line":2258},[68,6211,6155],{"class":116},[68,6213,6214],{"class":70,"line":2264},[68,6215,416],{"emptyLinePlaceholder":415},[68,6217,6218],{"class":70,"line":2270},[68,6219,6220],{"class":116},"          # if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n",[68,6222,6223],{"class":70,"line":2275},[68,6224,6225],{"class":116},"          #   DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n",[68,6227,6228],{"class":70,"line":2289},[68,6229,6230],{"class":116},"          #   echo \"退出容器 gateway:$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n",[68,6232,6233],{"class":70,"line":2339},[68,6234,6235],{"class":116},"          #   ssh root@gateway.aliyun \"cd $DEST_DIR && docker compose down\"\n",[68,6237,6238],{"class":70,"line":2363},[68,6239,6240],{"class":116},"          # fi\n",[68,6242,6243],{"class":70,"line":2374},[68,6244,6245],{"class":116},"        fi\n",[68,6247,6248],{"class":70,"line":2407},[68,6249,416],{"emptyLinePlaceholder":415},[68,6251,6252],{"class":70,"line":2421},[68,6253,6254],{"class":116},"        # 删除对应文件\n",[68,6256,6257],{"class":70,"line":2426},[68,6258,6259],{"class":116},"        if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n",[68,6261,6262],{"class":70,"line":2432},[68,6263,6264],{"class":116},"          FILE_PATH=\"\u002Fvolume3\u002Fdocker\u002F${FILE#homelab\u002F}\"\n",[68,6266,6267],{"class":70,"line":2454},[68,6268,6269],{"class":116},"          echo \"从 Synology 删除文件: $FILE_PATH\"\n",[68,6271,6272],{"class":70,"line":2500},[68,6273,6274],{"class":116},"          ssh -p 5110 root@synology.home \"rm -f $FILE_PATH\"\n",[68,6276,6277],{"class":70,"line":2506},[68,6278,6245],{"class":116},[68,6280,6281],{"class":70,"line":2511},[68,6282,416],{"emptyLinePlaceholder":415},[68,6284,6285],{"class":70,"line":2517},[68,6286,6287],{"class":116},"        if [[  \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n",[68,6289,6290],{"class":70,"line":2527},[68,6291,6292],{"class":116},"          FILE_PATH=\"\u002Froot\u002F${FILE#homelab\u002Fvm-rocky-01\u002F}\"\n",[68,6294,6295],{"class":70,"line":2565},[68,6296,6297],{"class":116},"          echo \"从 vm-rocky-01 删除文件: $FILE_PATH\"\n",[68,6299,6300],{"class":70,"line":2586},[68,6301,6302],{"class":116},"          ssh root@vm-rocky-01.home \"rm -f $FILE_PATH\"\n",[68,6304,6305],{"class":70,"line":2592},[68,6306,6245],{"class":116},[68,6308,6309],{"class":70,"line":2597},[68,6310,416],{"emptyLinePlaceholder":415},[68,6312,6313],{"class":70,"line":2605},[68,6314,6315],{"class":116},"        if [[  \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n",[68,6317,6318],{"class":70,"line":2611},[68,6319,6320],{"class":116},"          FILE_PATH=\"\u002Froot\u002F${FILE#homelab\u002Fvm-rocky-02\u002F}\"\n",[68,6322,6323],{"class":70,"line":2660},[68,6324,6325],{"class":116},"          echo \"从 vm-rocky-02 删除文件: $FILE_PATH\"\n",[68,6327,6328],{"class":70,"line":2691},[68,6329,6330],{"class":116},"          ssh root@vm-rocky-02.home \"rm -f $FILE_PATH\"\n",[68,6332,6333],{"class":70,"line":2696},[68,6334,6245],{"class":116},[68,6336,6337],{"class":70,"line":2702},[68,6338,416],{"emptyLinePlaceholder":415},[68,6340,6341],{"class":70,"line":2724},[68,6342,6343],{"class":116},"        if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n",[68,6345,6346],{"class":70,"line":2735},[68,6347,6348],{"class":116},"          FILE_PATH=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F${FILE#aliyun\u002Fgateway\u002F}\"\n",[68,6350,6351],{"class":70,"line":2762},[68,6352,6353],{"class":116},"          echo \"从 gateway.aliyun 删除文件: $FILE_PATH\"\n",[68,6355,6356],{"class":70,"line":2813},[68,6357,6358],{"class":116},"          ssh root@gateway.aliyun \"rm -f $FILE_PATH\"\n",[68,6360,6361],{"class":70,"line":2819},[68,6362,6245],{"class":116},[68,6364,6365],{"class":70,"line":2829},[68,6366,6367],{"class":116},"      done\n",[68,6369,6370],{"class":70,"line":2877},[68,6371,416],{"emptyLinePlaceholder":415},[68,6373,6374],{"class":70,"line":2883},[68,6375,6376],{"class":116},"      for FILE in $MODIFIED_FILES; do\n",[68,6378,6379],{"class":70,"line":2896},[68,6380,6381],{"class":116},"        echo \"文件变更 $FILE\"\n",[68,6383,6384],{"class":70,"line":2908},[68,6385,6386],{"class":116},"        if [ -e \"$FILE\" ]; then\n",[68,6388,6389],{"class":70,"line":2946},[68,6390,6135],{"class":116},[68,6392,6393],{"class":70,"line":2951},[68,6394,6140],{"class":116},[68,6396,6397],{"class":70,"line":2963},[68,6398,6399],{"class":116},"            ssh -p 5110 root@synology.home \"mkdir -p $DEST_DIR\"\n",[68,6401,6402],{"class":70,"line":2968},[68,6403,6404],{"class":116},"            echo \"复制文件 $FILE 到 Synology: $DEST_DIR\"\n",[68,6406,6407],{"class":70,"line":2974},[68,6408,6409],{"class":116},"            scp -O -P 5110 $FILE root@synology.home:$DEST_DIR\n",[68,6411,6412],{"class":70,"line":2979},[68,6413,6155],{"class":116},[68,6415,6416],{"class":70,"line":2985},[68,6417,416],{"emptyLinePlaceholder":415},[68,6419,6420],{"class":70,"line":3022},[68,6421,6164],{"class":116},[68,6423,6424],{"class":70,"line":3039},[68,6425,6169],{"class":116},[68,6427,6428],{"class":70,"line":3052},[68,6429,6430],{"class":116},"            ssh root@vm-rocky-01.home \"mkdir -p $DEST_DIR\"\n",[68,6432,6433],{"class":70,"line":3080},[68,6434,6435],{"class":116},"            echo \"复制文件 $FILE 到 vm-rocky-01: $DEST_DIR\"\n",[68,6437,6438],{"class":70,"line":3093},[68,6439,6440],{"class":116},"            scp -O $FILE root@vm-rocky-01.home:$DEST_DIR\n",[68,6442,6443],{"class":70,"line":3106},[68,6444,6155],{"class":116},[68,6446,6447],{"class":70,"line":3119},[68,6448,416],{"emptyLinePlaceholder":415},[68,6450,6451],{"class":70,"line":3132},[68,6452,6192],{"class":116},[68,6454,6455],{"class":70,"line":3138},[68,6456,6197],{"class":116},[68,6458,6459],{"class":70,"line":3159},[68,6460,6461],{"class":116},"            ssh root@vm-rocky-02.home \"mkdir -p $DEST_DIR\"\n",[68,6463,6464],{"class":70,"line":3179},[68,6465,6466],{"class":116},"            echo \"复制文件 $FILE 到 vm-rocky-02: $DEST_DIR\"\n",[68,6468,6469],{"class":70,"line":3208},[68,6470,6471],{"class":116},"            scp -O $FILE root@vm-rocky-02.home:$DEST_DIR\n",[68,6473,6474],{"class":70,"line":3214},[68,6475,6155],{"class":116},[68,6477,6478],{"class":70,"line":3219},[68,6479,416],{"emptyLinePlaceholder":415},[68,6481,6482],{"class":70,"line":3225},[68,6483,6484],{"class":116},"          if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n",[68,6486,6487],{"class":70,"line":3250},[68,6488,6489],{"class":116},"            DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n",[68,6491,6492],{"class":70,"line":3276},[68,6493,6494],{"class":116},"            ssh root@gateway.aliyun \"mkdir -p $DEST_DIR\"\n",[68,6496,6497],{"class":70,"line":3282},[68,6498,6499],{"class":116},"            echo \"复制文件 $FILE 到 gateway.aliyun: $DEST_DIR\"\n",[68,6501,6502],{"class":70,"line":3287},[68,6503,6504],{"class":116},"            scp -O $FILE root@gateway.aliyun:$DEST_DIR\n",[68,6506,6507],{"class":70,"line":3293},[68,6508,6155],{"class":116},[68,6510,6511],{"class":70,"line":3321},[68,6512,416],{"emptyLinePlaceholder":415},[68,6514,6515],{"class":70,"line":3326},[68,6516,6517],{"class":116},"          if [[ \"$FILE\" == \"aliyun\u002Fhk\u002Fnginx\u002F*\" ]]; then\n",[68,6519,6520],{"class":70,"line":3332},[68,6521,6522],{"class":116},"            DEST_DIR=\"\u002Fetc\u002Fnginx\u002F$(dirname ${FILE#aliyun\u002Fhk\u002Fnginx\u002F})\"\n",[68,6524,6525],{"class":70,"line":3359},[68,6526,6527],{"class":116},"            ssh root@hk.aliyun \"mkdir -p $DEST_DIR\"\n",[68,6529,6530],{"class":70,"line":3365},[68,6531,6532],{"class":116},"            echo \"复制文件 $FILE 到 hk.aliyun: $DEST_DIR\"\n",[68,6534,6535],{"class":70,"line":3370},[68,6536,6537],{"class":116},"            scp -O $FILE root@hk.aliyun:$DEST_DIR\n",[68,6539,6540],{"class":70,"line":3378},[68,6541,6155],{"class":116},[68,6543,6544],{"class":70,"line":3400},[68,6545,6245],{"class":116},[68,6547,6548],{"class":70,"line":3420},[68,6549,6367],{"class":116},[68,6551,6552],{"class":70,"line":3426},[68,6553,416],{"emptyLinePlaceholder":415},[68,6555,6556],{"class":70,"line":3457},[68,6557,6376],{"class":116},[68,6559,6560],{"class":70,"line":3484},[68,6561,6386],{"class":116},[68,6563,6564],{"class":70,"line":3489},[68,6565,6566],{"class":116},"          # 如果文件是 compose.yaml，执行 docker compose up -d\n",[68,6568,6569],{"class":70,"line":3494},[68,6570,6571],{"class":116},"          if [[ \"$FILE\" == \"*\u002Fcompose.yaml\" ]]; then\n",[68,6573,6574],{"class":70,"line":3528},[68,6575,6576],{"class":116},"            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Frenovate\u002Fcompose.yaml\" ]]; then\n",[68,6578,6579],{"class":70,"line":3533},[68,6580,6581],{"class":116},"              echo \"跳过部署 vm-rocky-01:renovate\"\n",[68,6583,6584],{"class":70,"line":3539},[68,6585,6586],{"class":116},"              continue\n",[68,6588,6589],{"class":70,"line":3562},[68,6590,6591],{"class":116},"            fi\n",[68,6593,6594],{"class":70,"line":3567},[68,6595,416],{"emptyLinePlaceholder":415},[68,6597,6598],{"class":70,"line":3572},[68,6599,6600],{"class":116},"            if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n",[68,6602,6603],{"class":70,"line":3578},[68,6604,6605],{"class":116},"              DEST_DIR=\"\u002Fvolume3\u002Fdocker\u002F$(dirname ${FILE#homelab\u002F})\"\n",[68,6607,6608],{"class":70,"line":3597},[68,6609,6610],{"class":116},"              echo \"部署容器 synology:$(dirname ${FILE#homelab\u002Fsynology\u002F})\"\n",[68,6612,6613],{"class":70,"line":3624},[68,6614,6615],{"class":116},"              ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd $DEST_DIR && docker-compose up -d\"\n",[68,6617,6618],{"class":70,"line":3630},[68,6619,6591],{"class":116},[68,6621,6622],{"class":70,"line":3640},[68,6623,416],{"emptyLinePlaceholder":415},[68,6625,6626],{"class":70,"line":3651},[68,6627,6628],{"class":116},"            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n",[68,6630,6631],{"class":70,"line":3672},[68,6632,6633],{"class":116},"              DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n",[68,6635,6636],{"class":70,"line":3685},[68,6637,6638],{"class":116},"              echo \"部署容器 vm-rocky-01:$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n",[68,6640,6641],{"class":70,"line":3690},[68,6642,6643],{"class":116},"              ssh root@vm-rocky-01.home \"cd $DEST_DIR && docker compose up -d\"\n",[68,6645,6646],{"class":70,"line":3696},[68,6647,6591],{"class":116},[68,6649,6650],{"class":70,"line":3727},[68,6651,416],{"emptyLinePlaceholder":415},[68,6653,6654],{"class":70,"line":3737},[68,6655,6656],{"class":116},"            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n",[68,6658,6659],{"class":70,"line":3758},[68,6660,6661],{"class":116},"              DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n",[68,6663,6664],{"class":70,"line":3795},[68,6665,6666],{"class":116},"              echo \"部署容器 vm-rocky-02:$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n",[68,6668,6669],{"class":70,"line":3806},[68,6670,6671],{"class":116},"              ssh root@vm-rocky-02.home \"cd $DEST_DIR && docker compose up -d\"\n",[68,6673,6674],{"class":70,"line":3844},[68,6675,6591],{"class":116},[68,6677,6678],{"class":70,"line":3856},[68,6679,416],{"emptyLinePlaceholder":415},[68,6681,6682],{"class":70,"line":3877},[68,6683,6684],{"class":116},"            # if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n",[68,6686,6687],{"class":70,"line":3888},[68,6688,6689],{"class":116},"            #   DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n",[68,6691,6692],{"class":70,"line":3893},[68,6693,6694],{"class":116},"            #   echo \"部署容器 gateway.aliyun:$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n",[68,6696,6697],{"class":70,"line":3916},[68,6698,6699],{"class":116},"            #   ssh root@gateway.aliyun \"cd $DEST_DIR && docker compose up -d\"\n",[68,6701,6702],{"class":70,"line":3947},[68,6703,6704],{"class":116},"            # fi\n",[68,6706,6707],{"class":70,"line":3956},[68,6708,416],{"emptyLinePlaceholder":415},[68,6710,6711],{"class":70,"line":3991},[68,6712,6713],{"class":116},"            echo \"容器重新部署完成\"\n",[68,6715,6716],{"class":70,"line":4002},[68,6717,6718],{"class":116},"            continue\n",[68,6720,6721],{"class":70,"line":4019},[68,6722,6155],{"class":116},[68,6724,6725],{"class":70,"line":4030},[68,6726,416],{"emptyLinePlaceholder":415},[68,6728,6729],{"class":70,"line":4038},[68,6730,6731],{"class":116},"          # 如果文件是 homelab\u002Fsynology\u002Fnginx\u002F*, 重新启动 nginx\n",[68,6733,6734],{"class":70,"line":4043},[68,6735,6736],{"class":116},"          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Fnginx\u002F*\" ]]; then\n",[68,6738,6739],{"class":70,"line":4058},[68,6740,6741],{"class":116},"            echo \"重新启动 synology:nginx 服务\"\n",[68,6743,6744],{"class":70,"line":4076},[68,6745,6746],{"class":116},"            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && docker exec nginx nginx -s reload\"\n",[68,6748,6749],{"class":70,"line":4081},[68,6750,6155],{"class":116},[68,6752,6753],{"class":70,"line":4121},[68,6754,416],{"emptyLinePlaceholder":415},[68,6756,6757],{"class":70,"line":4131},[68,6758,6759],{"class":116},"          # 如果文件是 aliyun\u002Fgateway\u002Fnginx\u002F*, 重新启动 nginx\n",[68,6761,6762],{"class":70,"line":4142},[68,6763,6764],{"class":116},"          if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002Fnginx\u002F*\" ]]; then\n",[68,6766,6767],{"class":70,"line":4147},[68,6768,6769],{"class":116},"            echo \"重新启动 gateway.aliyun:nginx\"\n",[68,6771,6772],{"class":70,"line":4153},[68,6773,6774],{"class":116},"            ssh root@gateway.aliyun \"nginx -s reload\"\n",[68,6776,6777],{"class":70,"line":4196},[68,6778,6155],{"class":116},[68,6780,6781],{"class":70,"line":4216},[68,6782,416],{"emptyLinePlaceholder":415},[68,6784,6785],{"class":70,"line":4230},[68,6786,6787],{"class":116},"          # 如果文件是 aliyun\u002Fhk\u002Fnginx\u002F*, 重新启动 nginx\n",[68,6789,6790],{"class":70,"line":4235},[68,6791,6517],{"class":116},[68,6793,6794],{"class":70,"line":4241},[68,6795,6796],{"class":116},"            echo \"重新启动 hk.aliyun:nginx\"\n",[68,6798,6799],{"class":70,"line":4249},[68,6800,6801],{"class":116},"            ssh root@hk.aliyun \"nginx -s reload\"\n",[68,6803,6804],{"class":70,"line":4268},[68,6805,6155],{"class":116},[68,6807,6808],{"class":70,"line":4292},[68,6809,416],{"emptyLinePlaceholder":415},[68,6811,6812],{"class":70,"line":4313},[68,6813,6814],{"class":116},"          # 如果文件是 rinetd.conf, 重新启动 rinetd\n",[68,6816,6817],{"class":70,"line":4319},[68,6818,6819],{"class":116},"          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Frinetd\u002Fconfig\u002Frinetd.conf\" ]]; then\n",[68,6821,6822],{"class":70,"line":4338},[68,6823,6824],{"class":116},"            echo \"重新启动 synology:rinetd 服务\"\n",[68,6826,6827],{"class":70,"line":4356},[68,6828,6829],{"class":116},"            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Frinetd && docker-compose restart\"\n",[68,6831,6832],{"class":70,"line":4361},[68,6833,6155],{"class":116},[68,6835,6836],{"class":70,"line":4369},[68,6837,416],{"emptyLinePlaceholder":415},[68,6839,6840],{"class":70,"line":4374},[68,6841,6842],{"class":116},"          # 如果文件是 gitlab.rb, 重新配置 gitlab\n",[68,6844,6845],{"class":70,"line":4402},[68,6846,6847],{"class":116},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Fgitlab\u002Fconfig\u002Fgitlab.rb\" ]]; then\n",[68,6849,6850],{"class":70,"line":4412},[68,6851,6852],{"class":116},"            echo \"重新配置 vm-rocky-01:gitlab\"\n",[68,6854,6855],{"class":70,"line":4460},[68,6856,6857],{"class":116},"            ssh root@vm-rocky-01.home \"docker exec gitlab gitlab-ctl reconfigure\"\n",[68,6859,6860],{"class":70,"line":4465},[68,6861,6155],{"class":116},[68,6863,6864],{"class":70,"line":4504},[68,6865,416],{"emptyLinePlaceholder":415},[68,6867,6868],{"class":70,"line":4510},[68,6869,6870],{"class":116},"          # 如果文件是 prometheus.yml, 重新启动 prometheus\n",[68,6872,6873],{"class":70,"line":4528},[68,6874,6875],{"class":116},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Fprometheus\u002Fconfig\u002Fprometheus.yml\" ]]; then\n",[68,6877,6878],{"class":70,"line":4539},[68,6879,6880],{"class":116},"            echo \"重新启动 vm-rocky-01:prometheus\"\n",[68,6882,6883],{"class":70,"line":4547},[68,6884,6885],{"class":116},"            ssh root@vm-rocky-01.home \"cd \u002Froot\u002Fprometheus && docker compose restart\"\n",[68,6887,6888],{"class":70,"line":4577},[68,6889,6155],{"class":116},[68,6891,6892],{"class":70,"line":4582},[68,6893,416],{"emptyLinePlaceholder":415},[68,6895,6896],{"class":70,"line":4589},[68,6897,6898],{"class":116},"          # 如果文件是 telegraf.conf, 重新启动 telegraf\n",[68,6900,6901],{"class":70,"line":4635},[68,6902,6903],{"class":116},"          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n",[68,6905,6906],{"class":70,"line":4648},[68,6907,6908],{"class":116},"            echo \"重新启动 synology:telegraf\"\n",[68,6910,6911],{"class":70,"line":4664},[68,6912,6913],{"class":116},"            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Ftelegraf && docker-compose restart\"\n",[68,6915,6916],{"class":70,"line":4677},[68,6917,6155],{"class":116},[68,6919,6920],{"class":70,"line":4723},[68,6921,416],{"emptyLinePlaceholder":415},[68,6923,6924],{"class":70,"line":4728},[68,6925,6926],{"class":116},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n",[68,6928,6929],{"class":70,"line":4733},[68,6930,6931],{"class":116},"            echo \"重新启动 vm-rocky-01:telegraf\"\n",[68,6933,6934],{"class":70,"line":4761},[68,6935,6936],{"class":116},"            ssh root@vm-rocky-01.home \"cd \u002Froot\u002Ftelegraf && docker compose restart\"\n",[68,6938,6939],{"class":70,"line":4771},[68,6940,6155],{"class":116},[68,6942,6943],{"class":70,"line":4791},[68,6944,416],{"emptyLinePlaceholder":415},[68,6946,6948],{"class":70,"line":6947},203,[68,6949,6950],{"class":116},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n",[68,6952,6954],{"class":70,"line":6953},204,[68,6955,6956],{"class":116},"            echo \"重新启动 vm-rocky-02:telegraf\"\n",[68,6958,6960],{"class":70,"line":6959},205,[68,6961,6962],{"class":116},"            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Ftelegraf && docker compose restart\"\n",[68,6964,6966],{"class":70,"line":6965},206,[68,6967,6155],{"class":116},[68,6969,6971],{"class":70,"line":6970},207,[68,6972,416],{"emptyLinePlaceholder":415},[68,6974,6976],{"class":70,"line":6975},208,[68,6977,6978],{"class":116},"          # 如果文件是 bots\u002Fconfig\u002Fclash\u002F*.yaml, 更新 clash 配置\n",[68,6980,6982],{"class":70,"line":6981},209,[68,6983,6984],{"class":116},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Fbots\u002Fconfig\u002Fclash\u002F*.yaml\" ]]; then\n",[68,6986,6988],{"class":70,"line":6987},210,[68,6989,6990],{"class":116},"            echo \"重新启动 vm-rocky-02:bots\"\n",[68,6992,6994],{"class":70,"line":6993},211,[68,6995,6996],{"class":116},"            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Fbots && docker compose restart\"\n",[68,6998,7000],{"class":70,"line":6999},212,[68,7001,7002],{"class":116},"            echo \"等待 10 秒\"\n",[68,7004,7006],{"class":70,"line":7005},213,[68,7007,7008],{"class":116},"            sleep 10\n",[68,7010,7012],{"class":70,"line":7011},214,[68,7013,7014],{"class":116},"            echo \"重新启动 synology:clash-premium\"\n",[68,7016,7018],{"class":70,"line":7017},215,[68,7019,7020],{"class":116},"            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Fclash-premium && docker-compose restart\"\n",[68,7022,7024],{"class":70,"line":7023},216,[68,7025,6155],{"class":116},[68,7027,7029],{"class":70,"line":7028},217,[68,7030,416],{"emptyLinePlaceholder":415},[68,7032,7034],{"class":70,"line":7033},218,[68,7035,7036],{"class":116},"          # 如果文件是 artalk\u002Fdata\u002Fartalk.yml, 重新启动 artalk\n",[68,7038,7040],{"class":70,"line":7039},219,[68,7041,7042],{"class":116},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Fartalk\u002Fdata\u002Fartalk.yml\" ]]; then\n",[68,7044,7046],{"class":70,"line":7045},220,[68,7047,7048],{"class":116},"            echo \"重新启动 vm-rocky-02:artalk\"\n",[68,7050,7052],{"class":70,"line":7051},221,[68,7053,7054],{"class":116},"            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Fartalk && docker compose restart\"\n",[68,7056,7058],{"class":70,"line":7057},222,[68,7059,6155],{"class":116},[68,7061,7063],{"class":70,"line":7062},223,[68,7064,416],{"emptyLinePlaceholder":415},[68,7066,7068],{"class":70,"line":7067},224,[68,7069,6245],{"class":116},[68,7071,7073],{"class":70,"line":7072},225,[68,7074,6367],{"class":116},[68,7076,7078,7080],{"class":70,"line":7077},226,[68,7079,6008],{"class":74},[68,7081,7082],{"class":116}," echo \"部署完成\"\n",[68,7084,7086,7089],{"class":70,"line":7085},227,[68,7087,7088],{"class":730},"  only",[68,7090,1569],{"class":74},[68,7092,7094,7096],{"class":70,"line":7093},228,[68,7095,6008],{"class":74},[68,7097,6025],{"class":116},[28,7099,7100,7101,5225,7104,7107],{},"其中，",[65,7102,7103],{},"SSH_KNOWN_HOSTS",[65,7105,7106],{},"SSH_PRIVATE_KEY"," 存储在 GitLab CI\u002FCD 的变量中，用于 SSH 登录到对应的宿主机上。",[28,7109,7110,7111,7114,7115,7117,7118,7120,7121,7124,7125,7127],{},"当我需要改某个容器的配置文件时，例如当我需要修改 ",[65,7112,7113],{},"synology"," 上的 ",[65,7116,5123],{}," 配置，我只需要去 ",[65,7119,5521],{}," 项目中修改 ",[65,7122,7123],{},"homelab\u002Fsynology\u002Fnginx\u002Fnginx.conf"," 文件，然后 commit，push，等待几秒钟，GitLab CI 就会自动帮我完成配置文件的更新以及 ",[65,7126,5123],{}," 的重启。",[28,7129,7130],{},"每周一，renovate 会自动发起并合并更新容器镜像版本的 MR，然后 GitLab CI 会自动帮我更新容器。",[28,7132,7133],{},"对了，GitLab 、GitLab Runner、Renovate 也都是通过容器私有化部署在 HomeLab 中的，这些就不赘述。",[523,7135,7136],{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}",{"title":63,"searchDepth":78,"depth":78,"links":7138},[7139,7140],{"id":5487,"depth":78,"text":5487},{"id":5510,"depth":78,"text":5510},"2024-12-12",{},"\u002Fposts\u002F2024\u002Fmanaging-containers-in-my-homelab",{"text":7145,"minutes":7146,"time":7147,"words":7148},"11 min read",10.24,614400,2048,{"title":5476,"description":5481},{"loc":7143},"posts\u002F2024\u002F20241212.managing-containers-in-my-homelab",[543,7153,1458,7154],"DevOps","Docker","sSWijwbQSH5iLEufQ0ZZuj2tXgOjghNQbswIr9u7b4A",{"id":7157,"title":7158,"body":7159,"class":528,"cover":1445,"coverSize":528,"date":7293,"description":7163,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":7294,"navigation":415,"path":7295,"readingTime":7296,"seo":7301,"sitemap":7302,"stem":7303,"tags":7304,"time":528,"weather":528,"__hash__":7305},"posts\u002Fposts\u002F2024\u002F20240815.guide-for-beginners.md","编程初学者学习、就业指南",{"type":25,"value":7160,"toc":7283},[7161,7164,7167,7170,7173,7176,7179,7182,7185,7188,7191,7194,7198,7201,7204,7207,7210,7213,7216,7219,7222,7225,7228,7231,7234,7237,7240,7243,7246,7249,7252,7255,7258,7261,7264,7267,7274],[28,7162,7163],{},"最近面试了一些实习生，发现很多同学对于如何学习、如何找工作还是有不少困惑。这里我总结了一些经验，希望对初学者有所帮助。",[28,7165,7166],{},"经验这个东西，需要时间去摸索，需要实践去试错，在不断尝试不断学习中才能获得。如果在初学阶段能够有指路人提供一些建议和方向，那绝对会少走很多弯路。",[1232,7168,7169],{"id":7169},"技术要靠自学",[28,7171,7172],{},"一个非常突出的矛盾点是，学校所教的内容与实际工作所需完全脱节。这一点在我上学的时候就是这样，现在依然如此。不过我意识得比较早，在我刚接触学校专业课程之后就发现，这些课程太理论，教材太陈旧了，根本无法满足我对技术的需求。所以我在大学期间我基本全部在自学，自己研究技术。",[28,7174,7175],{},"究其原因，其实也不难理解。国内本科教育主要还是在培养基础理论知识，实践和应用基本都是辅助。在计算机技术这块，教材的更新迭代速度远远比不上技术的发展速度。尤其在前端领域，新框架、新轮子层出不穷，出教材的这些教授学习都来不及，更别说出教材。而且大学的专业也不会分那么细，基本上教一些通用的理论基础、编程基础就差不多了。专业对口肯定是比不上外面的培训班的，那些毕竟是针对就业速成的，都是干货，并且以实战为主。",[28,7177,7178],{},"所以，对于技术这块，还是要靠自学。尤其是想从事这个行业的话，一定要早做准备，积累技术经验，等到快毕业前再去学习，已经很晚了，和其他早就开始自己钻研技术的同学比起来，差距就会很明显。",[1232,7180,7181],{"id":7181},"怎么自学技术",[28,7183,7184],{},"兴趣是最好的老师，这句话绝对没错。对技术感兴趣的同学，甚至不需要指导，就已经在自学的路上了。因为兴趣，你会主动去尝试一些新技术，或者捣鼓一些新玩意，尝试的过程中会遇到各种各样的问题，在解决问题的过程中，你就已经在学习了，并且通过解决问题的过程来学习，效果是最好的。",[28,7186,7187],{},"对于兴趣没有那么浓厚，不知道该如何下手的同学，该怎么办呢？我的解法是，给自己创造需求。",[28,7189,7190],{},"比如，给自己建一个网站，用来写写博客，记录学习过程。在这个过程中，你会遇到很多问题。比如如何搭一个网站，用现成的博客系统，还是纯手撸？现成的博客系统的话如何部署？如何绑定域名？手撸的网站前后端分别用什么语言，什么框架，哪些组件？我需要实现哪些功能？页面是纯静态部署，还是带服务端渲染？各种方案各有哪些优缺点？……",[28,7192,7193],{},"在各种各样的问题中，尝试去找到自己的解决方案，在这个过程中，你就已经在学习各种各样的知识和技术了。",[1232,7195,7197],{"id":7196},"精力有限需要集中","精力有限，需要集中",[28,7199,7200],{},"前面也讲到，技术经验一定是需要时间去积累的，只能不断去学习去实践。但计算机技术上，永远有学不完的技术。对于技术方向的选择上，最好也要聚焦。全栈不是一件坏事，但是对于目前国内的就业环境来说，大部分的工作岗位还是比较细分的，专门的全栈岗位非常少。对于精力有限的同学来说，最好是专注于一个方向去打好基础，有余力再去横向扩展。不过有精力又喜欢折腾的同学，可以尝试各种方向，不要被一种技术所局限，实现目的有各种各样的方式，选择适合自己的就好。",[28,7202,7203],{},"另外，大学期间还要应对各种考试、作业、实验、论文，还要陪室友打游戏、陪男女朋友吃饭、逛街、看电影，还有各种社团活动，以及其他各种各样的事情。真正留给你自学技术的时间，其实很有限。我当年学校的课基本都是翘课，学业也基本上是应付考试为主，把这些时间都用来研究技术。但尽管如此，我还是花了很多的时间在玩游戏、看电影、社团活动上。人精力是有限的，很多分散精力的事情也是难以避免的。只能说尽可能去集中精力，减少分散精力的事情。",[28,7205,7206],{},"举一个例子，最近一段时间面试到的一些实习生，很多简历里都有各种各样的竞赛得奖，比如蓝桥杯。我具体不太了解这个竞赛的含金量，但是就我面到的一些同学来看，有很多非常水的同学。与其把精力花在这些竞赛上，不如把这些时间花在学习技术上，打好基础上，效果会更好。（能力特别强的同学除外）",[1232,7208,7209],{"id":7209},"关于开源",[28,7211,7212],{},"面试过程中，有几个同学向我讨教经验，说到想做一些开源项目，加入开源社区，问我有没有什么建议。",[28,7214,7215],{},"我觉得这个想法非常好，但顺序不太对。不是想做开源项目而做开源项目。而是当你遇到一个问题，并且通过你的项目解决了某类问题。这时候如果你想开源，帮助到其他人，那就直接代码提交到 GitHub 开源就完事了，你的项目足够好，能够解决别人的问题，自然而然会有人关注，会有人使用。",[28,7217,7218],{},"但对于大部分同学来说，很难做到。刚起步的你们，甚至很难遇到真正困难的问题，更别提做出解决这些问题的方案了。",[28,7220,7221],{},"其实，开源项目也并不是那么遥不可及，很多项目也是由各种各样的贡献者添砖加瓦不断完善不断补充而成。当你用到一些开源项目，并且遇到问题的时候，不要停留在搜索阶段。当你搜不到解决方案的时候，尝试去看下项目源码，看看原因出在哪里，如果这个问题还没有人提过，你可以提一个 issue，如果问题很明确，可以尝试去解决，然后提交 PR。在这样的过程中，你是真正遇到问题，并且去尝试解决问题，这样的经历对于你学习和成长是非常有帮助的，并且在这个过程中你还可以帮助到开源项目，成为开源项目贡献者大军的一员。",[28,7223,7224],{},"最近我在重新搭建我的博客系统的过程中，用到了 nuxt 生态的一些新项目，并且也遇到了各种各样的问题，给其中多个项目都提交了 PR。在这个过程中，我自己也学到了很多，我觉得对我成长最大的一件事情就是，我对开源项目“祛魅”了。我内心不再觉得那些开源项目有多么神秘，因为他们也是人写的，也是各种各样的贡献者提交 PR 组成的。项目未必能覆盖所有的情况，总会有一些未考虑到的情况，会被你遇到。随着使用的深入，你总会遇到各种各样的问题，这时候主动去找到问题，并且提出解决方案，在不知不觉中，你会发现，原来你已经融入这个社区了。",[1232,7226,7227],{"id":7227},"关于实习工作",[28,7229,7230],{},"有同学没有实习过，向我了解实习工作到底是什么样的，需要那些能力和知识。",[28,7232,7233],{},"先说一下我对实习这件事情的理解，实习是一件“双赢”的事情。对于公司来说，实习生是一个廉价的劳动力，可以帮助公司完成一些简单的工作，减轻正式员工的负担。对于实习生来说，实习是一个学习的过程，可以在公司里学到一些实际工作中的技术和流程，也可以锻炼自己的沟通能力、团队协作能力等等。",[28,7235,7236],{},"因此，其实公司对于实习生的要求没有那么高，主要还是做一些杂事。基础知识扎实，能够独立解决一些简单问题，能够良好沟通协作，就可以了。",[28,7238,7239],{},"但问题在于现在这个行业的竞争比较激烈，一个 HC 有很多人在竞争，这个情况下只能从里面挑能力更强的人进来。所以有时候，你没拿到那个 offer，并不是公司要求太高，而是你的竞争对手太卷，能力太强。",[28,7241,7242],{},"在这种情况下，只能不断提高自身的能力，才能增强竞争力，才能有更多的机会拿到心仪的实习 offer。",[1232,7244,7245],{"id":7245},"关于找工作",[28,7247,7248],{},"大厂还是小厂，我觉得有条件的情况下，还是优先选大厂。大厂相对规范，相对稳定，并且有利于提升简历的背景，也利于后面继续换别的大厂工作。一个都是不知名小厂的简历，和另一个都是知名大厂的简历，你觉得哪个简历被用人单位选中的概率更高？",[28,7250,7251],{},"但是，大厂的竞争会比较激烈，没拿到大厂 offer 的情况下，也可以先去小厂积累经验，等到有了更多的经验和能力，再去尝试大厂。最好不要去外包公司，去了外包公司，基本上就很难再去大厂了。",[1232,7253,7254],{"id":7254},"简历该怎么写",[28,7256,7257],{},"最近发现一些简历里面，都不约而同提到一个前端低代码平台，并且结构都很相似。问到实现细节上，又不太答得出来。不确定是不是一些教程上提到的这个项目。",[28,7259,7260],{},"大学生本身项目经历很少，确实简历内容会相对简单。但与其在简历中写一些这样教程中的看起来很高大上的项目，不如自己去做一些小项目，把这些项目写到简历上。这样的项目，你是真正参与过的，你是真正了解的，你是真正可以讲出来的。这样的项目，会让你在面试的时候更有底气。最好是能直接部署到线上，贴上访问地址，能够直接体验到效果，更有说服力。我记得我当年第一次去面试的时候，带了个 surface pro，给面试官看了下我之前做过的一些作品，面试官当场就说你这也太牛了，轻松通过了面试。",[1232,7262,7263],{"id":7263},"付费咨询",[28,7265,7266],{},"面试的过程中很多同学会问我很多问题，给他们解答之后，都觉得受益匪浅。受他们启发，我感觉可以尝试一下付费咨询。毕竟在这个行业摸爬滚打了这么多年，创过业、待过外企、上市公司，也面试过很多人，对于学习、就业方向有一些自己的看法和经验，可以分享给有需要的同学。",[28,7268,7269,7270,7273],{},"如果你有关于学习方向、就业方向等相关的问题，可以通过下面的方式联系我，约个时间聊个半小时到一小时。费用的话，先定个 ",[65,7271,7272],{},"¥198"," 吧。",[1236,7275,7276],{},[1239,7277,7278,7279],{},"邮箱： ",[7280,7281],"email",{"email":7282},"hi@hadb.me",{"title":63,"searchDepth":78,"depth":78,"links":7284},[7285,7286,7287,7288,7289,7290,7291,7292],{"id":7169,"depth":78,"text":7169},{"id":7181,"depth":78,"text":7181},{"id":7196,"depth":78,"text":7197},{"id":7209,"depth":78,"text":7209},{"id":7227,"depth":78,"text":7227},{"id":7245,"depth":78,"text":7245},{"id":7254,"depth":78,"text":7254},{"id":7263,"depth":78,"text":7263},"2024-08-15",{},"\u002Fposts\u002F2024\u002Fguide-for-beginners",{"text":7297,"minutes":7298,"time":7299,"words":7300},"16 min read",15.01,900600,3002,{"title":7158,"description":7163},{"loc":7295},"posts\u002F2024\u002F20240815.guide-for-beginners",[543],"yqTnnZyK-ma0NviMXz0KvrEgHuQJs3jphKXQ5UKC7ZY",{"id":7307,"title":7308,"body":7309,"class":528,"cover":528,"coverSize":528,"date":7519,"description":7313,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":7520,"navigation":415,"path":7521,"readingTime":7522,"seo":7526,"sitemap":7527,"stem":7528,"tags":7529,"time":528,"weather":528,"__hash__":7531},"posts\u002Fposts\u002F2024\u002F20240615.subsystem-request-failed-on-channel.md","subsystem request failed on channel",{"type":25,"value":7310,"toc":7517},[7311,7314,7317,7439,7442,7477,7484,7506,7514],[28,7312,7313],{},"问题：",[28,7315,7316],{},"以下代码是创建文件目录，并复制文件到 nas",[58,7318,7320],{"className":1502,"code":7319,"language":1504,"meta":63,"style":63},"DEST_DIR=\"\u002Fvolume3\u002Fdocker\u002F$(dirname ${FILE#synology\u002Fdocker\u002F})\"\necho \"准备创建目录 $DEST_DIR\"\nssh -p 5110 root@192.168.31.10 \"mkdir -p $DEST_DIR\"\n\necho \"准备复制 $FILE 到 $DEST_DIR\"\nscp -P 5110 $FILE root@192.168.31.10:$DEST_DIR\n",[65,7321,7322,7360,7375,7397,7401,7420],{"__ignoreMap":63},[68,7323,7324,7327,7329,7331,7334,7337,7340,7343,7346,7349,7351,7353,7355,7357],{"class":70,"line":71},[68,7325,7326],{"class":373},"DEST_DIR",[68,7328,877],{"class":1899},[68,7330,89],{"class":112},[68,7332,7333],{"class":116},"\u002Fvolume3\u002Fdocker\u002F",[68,7335,7336],{"class":112},"$(",[68,7338,7339],{"class":1511},"dirname",[68,7341,7342],{"class":112}," ${",[68,7344,7345],{"class":373},"FILE",[68,7347,7348],{"class":1899},"#",[68,7350,7113],{"class":373},[68,7352,6],{"class":1899},[68,7354,1512],{"class":373},[68,7356,6],{"class":1899},[68,7358,7359],{"class":112},"})\"\n",[68,7361,7362,7365,7367,7370,7373],{"class":70,"line":78},[68,7363,7364],{"class":630},"echo",[68,7366,113],{"class":112},[68,7368,7369],{"class":116},"准备创建目录 ",[68,7371,7372],{"class":373},"$DEST_DIR",[68,7374,120],{"class":112},[68,7376,7377,7380,7382,7385,7388,7390,7393,7395],{"class":70,"line":98},[68,7378,7379],{"class":1511},"ssh",[68,7381,1522],{"class":1518},[68,7383,7384],{"class":2397}," 5110",[68,7386,7387],{"class":116}," root@192.168.31.10",[68,7389,113],{"class":112},[68,7391,7392],{"class":116},"mkdir -p ",[68,7394,7372],{"class":373},[68,7396,120],{"class":112},[68,7398,7399],{"class":70,"line":123},[68,7400,416],{"emptyLinePlaceholder":415},[68,7402,7403,7405,7407,7410,7413,7416,7418],{"class":70,"line":129},[68,7404,7364],{"class":630},[68,7406,113],{"class":112},[68,7408,7409],{"class":116},"准备复制 ",[68,7411,7412],{"class":373},"$FILE",[68,7414,7415],{"class":116}," 到 ",[68,7417,7372],{"class":373},[68,7419,120],{"class":112},[68,7421,7422,7425,7428,7430,7433,7436],{"class":70,"line":212},[68,7423,7424],{"class":1511},"scp",[68,7426,7427],{"class":1518}," -P",[68,7429,7384],{"class":2397},[68,7431,7432],{"class":373}," $FILE ",[68,7434,7435],{"class":116},"root@192.168.31.10:",[68,7437,7438],{"class":373},"$DEST_DIR\n",[28,7440,7441],{},"ssh 命令没问题，但 scp 命令报如下错：",[58,7443,7445],{"className":1502,"code":7444,"language":1504,"meta":63,"style":63},"subsystem request failed on channel 0\nscp: Connection closed\n",[65,7446,7447,7466],{"__ignoreMap":63},[68,7448,7449,7452,7455,7458,7461,7464],{"class":70,"line":71},[68,7450,7451],{"class":1511},"subsystem",[68,7453,7454],{"class":116}," request",[68,7456,7457],{"class":116}," failed",[68,7459,7460],{"class":116}," on",[68,7462,7463],{"class":116}," channel",[68,7465,2732],{"class":2397},[68,7467,7468,7471,7474],{"class":70,"line":78},[68,7469,7470],{"class":1511},"scp:",[68,7472,7473],{"class":116}," Connection",[68,7475,7476],{"class":116}," closed\n",[28,7478,7479,7480,7483],{},"研究了半天，发现加个 ",[65,7481,7482],{},"-O"," 就可以解决了：",[58,7485,7487],{"className":1502,"code":7486,"language":1504,"meta":63,"style":63},"scp -O -P 5110 $FILE root@192.168.31.10:$DEST_DIR\n",[65,7488,7489],{"__ignoreMap":63},[68,7490,7491,7493,7496,7498,7500,7502,7504],{"class":70,"line":71},[68,7492,7424],{"class":1511},[68,7494,7495],{"class":1518}," -O",[68,7497,7427],{"class":1518},[68,7499,7384],{"class":2397},[68,7501,7432],{"class":373},[68,7503,7435],{"class":116},[68,7505,7438],{"class":373},[28,7507,7508,7509],{},"参考：",[38,7510,7513],{"href":7511,"rel":7512},"https:\u002F\u002Fstackoverflow.com\u002Fquestions\u002F74311661\u002Fsubsystem-request-failed-on-channel-0-scp-connection-closed",[42],"subsystem request failed on channel 0 scp: Connection closed",[523,7515,7516],{},"html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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":63,"searchDepth":78,"depth":78,"links":7518},[],"2024-06-15",{},"\u002Fposts\u002F2024\u002Fsubsystem-request-failed-on-channel",{"text":7523,"minutes":7524,"time":7525,"words":3276},"1 min read",0.525,31500,{"title":7308,"description":7313},{"loc":7521},"posts\u002F2024\u002F20240615.subsystem-request-failed-on-channel",[543,7530],"Linux","I3le_XL4p0q79nUP06GTofRHt6UBprDEI-au5i3GRGY",{"id":7533,"title":7534,"body":7535,"class":528,"cover":1445,"coverSize":528,"date":8415,"description":63,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":8416,"navigation":415,"path":8417,"readingTime":8418,"seo":8423,"sitemap":8424,"stem":8425,"tags":8426,"time":528,"weather":528,"__hash__":8429},"posts\u002Fposts\u002F2024\u002F20240522.get-video-rotation-by-mp4box-js.md","MP4Box.js 获取视频旋转信息",{"type":25,"value":7536,"toc":8408},[7537,7543,7546,7556,7560,7567,7583,7587,7598,7608,7613,7616,7681,7697,7700,8384,8387,8396,8405],[7538,7539,7540],"blockquote",{},[28,7541,7542],{},"声明：本文部分内容使用 ChatGPT 生成",[1232,7544,7545],{"id":7545},"序言",[28,7547,7548,7549,7552,7553,7555],{},"公司的一个项目中用到 ",[65,7550,7551],{},"MP4Box.js"," 在上传视频前去解析视频的宽高，并且根据宽高的比例做一些拦截，只允许 16:9 横屏的素材。后来发现一个问题，部分竖屏的素材也被提交上来了。经过研究，发现这类视频可能是由手机拍摄的，带了旋转信息，因此 ",[65,7554,7551],{}," 中的原始宽高有问题。",[1232,7557,7559],{"id":7558},"什么是-mp4boxjs","什么是 MP4Box.js",[28,7561,7562,7566],{},[38,7563,7551],{"href":7564,"rel":7565},"https:\u002F\u002Fgithub.com\u002Fgpac\u002Fmp4box.js",[42]," 是一个支持在浏览器中处理 MP4 文件的 JS 库，可以实现获取 MP4 文件的元数据信息、分割文件、提取媒体样本等高级处理能力。",[28,7568,7569,7570,7572,7573,5551,7576,5225,7579,7582],{},"通过 ",[65,7571,7551],{}," 可以从 ",[65,7574,7575],{},"videoTrack",[65,7577,7578],{},"width",[65,7580,7581],{},"height"," 中获取视频的宽高，对于一般的视频都是 OK 的，但是带了旋转信息，通过 MP4Box 读出的宽高仍是旋转前的宽高，导致在一些场景下的判断会有问题。那么，如何获取到视频的旋转信息呢？",[1232,7584,7586],{"id":7585},"mp4boxjs-如何获取旋转信息","MP4Box.js 如何获取旋转信息",[28,7588,7589,7590,7593,7594,7597],{},"在 MP4 和 MOV 文件中，旋转信息通常存储在 ",[65,7591,7592],{},"tkhd"," (Track Header Box) 或 ",[65,7595,7596],{},"mvhd"," (Movie Header Box) 中。这个信息会影响视频轨道的显示方式。",[28,7599,7600,7601,7603,7604,7607],{},"Track Header Box (",[65,7602,7592],{},")：包含一个 ",[65,7605,7606],{},"matrix"," 的矩阵，描述视频帧的旋转。",[28,7609,7610,7612],{},[65,7611,7606],{}," 字段中的旋转信息是通过一个 3x3 矩阵来表示的，具体可以参考 ISO\u002FIEC 14496-12 标准。",[28,7614,7615],{},"具体实现代码：",[58,7617,7621],{"className":7618,"code":7619,"language":7620,"meta":63,"style":63},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","Math.atan2(videoTrack.matrix[1], videoTrack.matrix[0]) * (180 \u002F Math.PI)\n","ts",[65,7622,7623],{"__ignoreMap":63},[68,7624,7625,7628,7630,7633,7636,7638,7641,7643,7645,7647,7650,7652,7654,7656,7659,7662,7665,7668,7671,7674,7676,7679],{"class":70,"line":71},[68,7626,7627],{"class":373},"Math",[68,7629,404],{"class":74},[68,7631,7632],{"class":2183},"atan2",[68,7634,7635],{"class":373},"(videoTrack",[68,7637,404],{"class":74},[68,7639,7640],{"class":373},"matrix[",[68,7642,401],{"class":2397},[68,7644,2786],{"class":373},[68,7646,255],{"class":74},[68,7648,7649],{"class":373}," videoTrack",[68,7651,404],{"class":74},[68,7653,7640],{"class":373},[68,7655,768],{"class":2397},[68,7657,7658],{"class":373},"]) ",[68,7660,7661],{"class":1899},"*",[68,7663,7664],{"class":373}," (",[68,7666,7667],{"class":2397},"180",[68,7669,7670],{"class":1899}," \u002F",[68,7672,7673],{"class":373}," Math",[68,7675,404],{"class":74},[68,7677,7678],{"class":665},"PI",[68,7680,410],{"class":373},[28,7682,7100,7683,7686,7687,7689,7690,7693,7694,7696],{},[65,7684,7685],{},"Math.atan2"," 是 JS 中的一个数学函数，用于计算从点 (0, 0) 到点 (x, y) 之间的直线与 x 轴正方向之间的角度，角度的单位为弧度。这个函数能够处理所有的象限，因此可以返回从 -π 到 π 之间的值。",[65,7688,7685],{}," 函数在计算几何、游戏开发、图形编程以及需要处理极坐标转换等场景中非常有用。与 ",[65,7691,7692],{},"Math.atan"," 不同，",[65,7695,7685],{}," 可以处理 (x, y) 都为零的情况，并根据 x 和 y 的符号确定正确的象限。",[1232,7698,7699],{"id":7699},"完整实现代码",[58,7701,7703],{"className":7618,"code":7702,"language":7620,"meta":63,"style":63},"function getVideoMetaInfo(file: File): Promise\u003Cany> {\n  return new Promise((resolve, reject) => {\n    const mp4boxFile = MP4Box.createFile()\n\n    mp4boxFile.onReady = function (info: any) {\n      if (info && info.videoTracks?.length) {\n        const videoTrack = info.videoTracks[0]\n        const result = {\n          duration: videoTrack.duration \u002F videoTrack.timescale,\n          codec: videoTrack.codec,\n          fps: videoTrack.nb_samples \u002F (videoTrack.duration \u002F videoTrack.timescale),\n          width: videoTrack.video.width,\n          height: videoTrack.video.height,\n          rotation: Math.atan2(videoTrack.matrix[1], videoTrack.matrix[0]) * (180 \u002F Math.PI),\n        }\n\n        resolve(result)\n      }\n    }\n\n    mp4boxFile.onError = function (info: any) {\n      console.error('mp4box.js error', info)\n      reject(info)\n    }\n\n    const reader = file.stream().getReader()\n    let offset = 0\n    reader.read().then(function getNextChunk({ done, value }: any) {\n      if (done) {\n        mp4boxFile.flush()\n        return\n      }\n\n      const copy = value.buffer\n      copy.fileStart = offset\n      offset += value.length\n      mp4boxFile.appendBuffer(copy)\n      reader.read().then(getNextChunk)\n    })\n  })\n}\n",[65,7704,7705,7739,7768,7788,7792,7821,7851,7872,7883,7908,7924,7960,7980,7999,8058,8062,8066,8078,8083,8087,8091,8116,8141,8152,8156,8160,8185,8197,8242,8255,8267,8272,8276,8280,8297,8312,8327,8344,8366,8373,8380],{"__ignoreMap":63},[68,7706,7707,7710,7713,7715,7718,7720,7723,7725,7727,7730,7732,7735,7737],{"class":70,"line":71},[68,7708,7709],{"class":1864},"function",[68,7711,7712],{"class":2183}," getVideoMetaInfo",[68,7714,648],{"class":74},[68,7716,7717],{"class":1912},"file",[68,7719,92],{"class":1899},[68,7721,7722],{"class":1511}," File",[68,7724,2753],{"class":74},[68,7726,92],{"class":1899},[68,7728,7729],{"class":1511}," Promise",[68,7731,727],{"class":74},[68,7733,7734],{"class":104},"any",[68,7736,765],{"class":74},[68,7738,95],{"class":74},[68,7740,7741,7744,7747,7749,7751,7753,7756,7758,7761,7763,7766],{"class":70,"line":78},[68,7742,7743],{"class":1790},"  return",[68,7745,7746],{"class":1899}," new",[68,7748,7729],{"class":104},[68,7750,648],{"class":2122},[68,7752,648],{"class":74},[68,7754,7755],{"class":1912},"resolve",[68,7757,255],{"class":74},[68,7759,7760],{"class":1912}," reject",[68,7762,2753],{"class":74},[68,7764,7765],{"class":1864}," =>",[68,7767,95],{"class":74},[68,7769,7770,7773,7776,7778,7781,7783,7786],{"class":70,"line":98},[68,7771,7772],{"class":1864},"    const",[68,7774,7775],{"class":665}," mp4boxFile",[68,7777,1900],{"class":1899},[68,7779,7780],{"class":373}," MP4Box",[68,7782,404],{"class":74},[68,7784,7785],{"class":2183},"createFile",[68,7787,2136],{"class":2122},[68,7789,7790],{"class":70,"line":123},[68,7791,416],{"emptyLinePlaceholder":415},[68,7793,7794,7797,7799,7802,7804,7807,7809,7812,7814,7817,7819],{"class":70,"line":129},[68,7795,7796],{"class":373},"    mp4boxFile",[68,7798,404],{"class":74},[68,7800,7801],{"class":2183},"onReady",[68,7803,1900],{"class":1899},[68,7805,7806],{"class":1864}," function",[68,7808,7664],{"class":74},[68,7810,7811],{"class":1912},"info",[68,7813,92],{"class":1899},[68,7815,7816],{"class":104}," any",[68,7818,2753],{"class":74},[68,7820,95],{"class":74},[68,7822,7823,7826,7828,7830,7833,7836,7838,7841,7844,7847,7849],{"class":70,"line":212},[68,7824,7825],{"class":1790},"      if",[68,7827,7664],{"class":2122},[68,7829,7811],{"class":373},[68,7831,7832],{"class":1899}," &&",[68,7834,7835],{"class":373}," info",[68,7837,404],{"class":74},[68,7839,7840],{"class":373},"videoTracks",[68,7842,7843],{"class":74},"?.",[68,7845,7846],{"class":665},"length",[68,7848,588],{"class":2122},[68,7850,75],{"class":74},[68,7852,7853,7856,7858,7860,7862,7864,7866,7868,7870],{"class":70,"line":233},[68,7854,7855],{"class":1864},"        const",[68,7857,7649],{"class":665},[68,7859,1900],{"class":1899},[68,7861,7835],{"class":373},[68,7863,404],{"class":74},[68,7865,7840],{"class":373},[68,7867,2326],{"class":2122},[68,7869,768],{"class":2397},[68,7871,2657],{"class":2122},[68,7873,7874,7876,7879,7881],{"class":70,"line":268},[68,7875,7855],{"class":1864},[68,7877,7878],{"class":665}," result",[68,7880,1900],{"class":1899},[68,7882,95],{"class":74},[68,7884,7885,7888,7890,7892,7894,7897,7899,7901,7903,7906],{"class":70,"line":289},[68,7886,7887],{"class":2122},"          duration",[68,7889,92],{"class":74},[68,7891,7649],{"class":373},[68,7893,404],{"class":74},[68,7895,7896],{"class":373},"duration",[68,7898,7670],{"class":1899},[68,7900,7649],{"class":373},[68,7902,404],{"class":74},[68,7904,7905],{"class":373},"timescale",[68,7907,169],{"class":74},[68,7909,7910,7913,7915,7917,7919,7922],{"class":70,"line":308},[68,7911,7912],{"class":2122},"          codec",[68,7914,92],{"class":74},[68,7916,7649],{"class":373},[68,7918,404],{"class":74},[68,7920,7921],{"class":373},"codec",[68,7923,169],{"class":74},[68,7925,7926,7929,7931,7933,7935,7938,7940,7942,7944,7946,7948,7950,7952,7954,7956,7958],{"class":70,"line":314},[68,7927,7928],{"class":2122},"          fps",[68,7930,92],{"class":74},[68,7932,7649],{"class":373},[68,7934,404],{"class":74},[68,7936,7937],{"class":373},"nb_samples",[68,7939,7670],{"class":1899},[68,7941,7664],{"class":2122},[68,7943,7575],{"class":373},[68,7945,404],{"class":74},[68,7947,7896],{"class":373},[68,7949,7670],{"class":1899},[68,7951,7649],{"class":373},[68,7953,404],{"class":74},[68,7955,7905],{"class":373},[68,7957,2753],{"class":2122},[68,7959,169],{"class":74},[68,7961,7962,7965,7967,7969,7971,7974,7976,7978],{"class":70,"line":320},[68,7963,7964],{"class":2122},"          width",[68,7966,92],{"class":74},[68,7968,7649],{"class":373},[68,7970,404],{"class":74},[68,7972,7973],{"class":373},"video",[68,7975,404],{"class":74},[68,7977,7578],{"class":373},[68,7979,169],{"class":74},[68,7981,7982,7985,7987,7989,7991,7993,7995,7997],{"class":70,"line":889},[68,7983,7984],{"class":2122},"          height",[68,7986,92],{"class":74},[68,7988,7649],{"class":373},[68,7990,404],{"class":74},[68,7992,7973],{"class":373},[68,7994,404],{"class":74},[68,7996,7581],{"class":373},[68,7998,169],{"class":74},[68,8000,8001,8004,8006,8008,8010,8012,8014,8016,8018,8020,8022,8024,8026,8028,8030,8032,8034,8036,8038,8040,8042,8044,8046,8048,8050,8052,8054,8056],{"class":70,"line":909},[68,8002,8003],{"class":2122},"          rotation",[68,8005,92],{"class":74},[68,8007,7673],{"class":373},[68,8009,404],{"class":74},[68,8011,7632],{"class":2183},[68,8013,648],{"class":2122},[68,8015,7575],{"class":373},[68,8017,404],{"class":74},[68,8019,7606],{"class":373},[68,8021,2326],{"class":2122},[68,8023,401],{"class":2397},[68,8025,2786],{"class":2122},[68,8027,255],{"class":74},[68,8029,7649],{"class":373},[68,8031,404],{"class":74},[68,8033,7606],{"class":373},[68,8035,2326],{"class":2122},[68,8037,768],{"class":2397},[68,8039,7658],{"class":2122},[68,8041,7661],{"class":1899},[68,8043,7664],{"class":2122},[68,8045,7667],{"class":2397},[68,8047,7670],{"class":1899},[68,8049,7673],{"class":373},[68,8051,404],{"class":74},[68,8053,7678],{"class":665},[68,8055,2753],{"class":2122},[68,8057,169],{"class":74},[68,8059,8060],{"class":70,"line":929},[68,8061,2589],{"class":74},[68,8063,8064],{"class":70,"line":949},[68,8065,416],{"emptyLinePlaceholder":415},[68,8067,8068,8071,8073,8076],{"class":70,"line":969},[68,8069,8070],{"class":2183},"        resolve",[68,8072,648],{"class":2122},[68,8074,8075],{"class":373},"result",[68,8077,410],{"class":2122},[68,8079,8080],{"class":70,"line":989},[68,8081,8082],{"class":74},"      }\n",[68,8084,8085],{"class":70,"line":1009},[68,8086,311],{"class":74},[68,8088,8089],{"class":70,"line":1029},[68,8090,416],{"emptyLinePlaceholder":415},[68,8092,8093,8095,8097,8100,8102,8104,8106,8108,8110,8112,8114],{"class":70,"line":1049},[68,8094,7796],{"class":373},[68,8096,404],{"class":74},[68,8098,8099],{"class":2183},"onError",[68,8101,1900],{"class":1899},[68,8103,7806],{"class":1864},[68,8105,7664],{"class":74},[68,8107,7811],{"class":1912},[68,8109,92],{"class":1899},[68,8111,7816],{"class":104},[68,8113,2753],{"class":74},[68,8115,95],{"class":74},[68,8117,8118,8121,8123,8125,8127,8130,8133,8135,8137,8139],{"class":70,"line":1069},[68,8119,8120],{"class":373},"      console",[68,8122,404],{"class":74},[68,8124,2473],{"class":2183},[68,8126,648],{"class":2122},[68,8128,8129],{"class":112},"'",[68,8131,8132],{"class":116},"mp4box.js error",[68,8134,8129],{"class":112},[68,8136,255],{"class":74},[68,8138,7835],{"class":373},[68,8140,410],{"class":2122},[68,8142,8143,8146,8148,8150],{"class":70,"line":1088},[68,8144,8145],{"class":2183},"      reject",[68,8147,648],{"class":2122},[68,8149,7811],{"class":373},[68,8151,410],{"class":2122},[68,8153,8154],{"class":70,"line":1108},[68,8155,311],{"class":74},[68,8157,8158],{"class":70,"line":1128},[68,8159,416],{"emptyLinePlaceholder":415},[68,8161,8162,8164,8167,8169,8172,8174,8176,8178,8180,8183],{"class":70,"line":1148},[68,8163,7772],{"class":1864},[68,8165,8166],{"class":665}," reader",[68,8168,1900],{"class":1899},[68,8170,8171],{"class":373}," file",[68,8173,404],{"class":74},[68,8175,3034],{"class":2183},[68,8177,3345],{"class":2122},[68,8179,404],{"class":74},[68,8181,8182],{"class":2183},"getReader",[68,8184,2136],{"class":2122},[68,8186,8187,8190,8193,8195],{"class":70,"line":1168},[68,8188,8189],{"class":1864},"    let",[68,8191,8192],{"class":373}," offset",[68,8194,1900],{"class":1899},[68,8196,2732],{"class":2397},[68,8198,8199,8202,8204,8207,8209,8211,8214,8216,8218,8221,8223,8226,8228,8231,8234,8236,8238,8240],{"class":70,"line":1187},[68,8200,8201],{"class":373},"    reader",[68,8203,404],{"class":74},[68,8205,8206],{"class":2183},"read",[68,8208,3345],{"class":2122},[68,8210,404],{"class":74},[68,8212,8213],{"class":2183},"then",[68,8215,648],{"class":2122},[68,8217,7709],{"class":1864},[68,8219,8220],{"class":2183}," getNextChunk",[68,8222,2468],{"class":74},[68,8224,8225],{"class":1912}," done",[68,8227,255],{"class":74},[68,8229,8230],{"class":1912}," value",[68,8232,8233],{"class":74}," }",[68,8235,92],{"class":1899},[68,8237,7816],{"class":104},[68,8239,2753],{"class":74},[68,8241,95],{"class":74},[68,8243,8244,8246,8248,8251,8253],{"class":70,"line":2039},[68,8245,7825],{"class":1790},[68,8247,7664],{"class":2122},[68,8249,8250],{"class":373},"done",[68,8252,588],{"class":2122},[68,8254,75],{"class":74},[68,8256,8257,8260,8262,8265],{"class":70,"line":2055},[68,8258,8259],{"class":373},"        mp4boxFile",[68,8261,404],{"class":74},[68,8263,8264],{"class":2183},"flush",[68,8266,2136],{"class":2122},[68,8268,8269],{"class":70,"line":2071},[68,8270,8271],{"class":1790},"        return\n",[68,8273,8274],{"class":70,"line":2087},[68,8275,8082],{"class":74},[68,8277,8278],{"class":70,"line":2092},[68,8279,416],{"emptyLinePlaceholder":415},[68,8281,8282,8285,8288,8290,8292,8294],{"class":70,"line":2097},[68,8283,8284],{"class":1864},"      const",[68,8286,8287],{"class":665}," copy",[68,8289,1900],{"class":1899},[68,8291,8230],{"class":373},[68,8293,404],{"class":74},[68,8295,8296],{"class":373},"buffer\n",[68,8298,8299,8302,8304,8307,8309],{"class":70,"line":2114},[68,8300,8301],{"class":373},"      copy",[68,8303,404],{"class":74},[68,8305,8306],{"class":373},"fileStart",[68,8308,1900],{"class":1899},[68,8310,8311],{"class":373}," offset\n",[68,8313,8314,8317,8320,8322,8324],{"class":70,"line":2139},[68,8315,8316],{"class":373},"      offset",[68,8318,8319],{"class":1899}," +=",[68,8321,8230],{"class":373},[68,8323,404],{"class":74},[68,8325,8326],{"class":665},"length\n",[68,8328,8329,8332,8334,8337,8339,8342],{"class":70,"line":2158},[68,8330,8331],{"class":373},"      mp4boxFile",[68,8333,404],{"class":74},[68,8335,8336],{"class":2183},"appendBuffer",[68,8338,648],{"class":2122},[68,8340,8341],{"class":373},"copy",[68,8343,410],{"class":2122},[68,8345,8346,8349,8351,8353,8355,8357,8359,8361,8364],{"class":70,"line":2173},[68,8347,8348],{"class":373},"      reader",[68,8350,404],{"class":74},[68,8352,8206],{"class":2183},[68,8354,3345],{"class":2122},[68,8356,404],{"class":74},[68,8358,8213],{"class":2183},[68,8360,648],{"class":2122},[68,8362,8363],{"class":373},"getNextChunk",[68,8365,410],{"class":2122},[68,8367,8368,8371],{"class":70,"line":2178},[68,8369,8370],{"class":74},"    }",[68,8372,410],{"class":2122},[68,8374,8375,8378],{"class":70,"line":2193},[68,8376,8377],{"class":74},"  }",[68,8379,410],{"class":2122},[68,8381,8382],{"class":70,"line":2201},[68,8383,132],{"class":74},[1232,8385,8386],{"id":8386},"后记",[28,8388,8389,8390,8395],{},"在解决这个问题的过程中，我发现另一个强大的 JS 库：",[38,8391,8394],{"href":8392,"rel":8393},"https:\u002F\u002Fgithub.com\u002Fbuzz\u002Fmediainfo.js",[42],"mediainfo.js","，已经帮我们做好了这一切，并且支持解析更多格式的文件。",[28,8397,8398,8399,8404],{},"我基于这个库，做了一个可视化的工具页：",[38,8400,8403],{"href":8401,"rel":8402},"https:\u002F\u002Ftools.yuanfen.net\u002Fmetadata",[42],"媒体文件元数据解析","，方便解析媒体文件的元数据，纯浏览器本地解析，性能优异，并且不需要读完整个文件，读完头就可以了。",[523,8406,8407],{},"html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":63,"searchDepth":78,"depth":78,"links":8409},[8410,8411,8412,8413,8414],{"id":7545,"depth":78,"text":7545},{"id":7558,"depth":78,"text":7559},{"id":7585,"depth":78,"text":7586},{"id":7699,"depth":78,"text":7699},{"id":8386,"depth":78,"text":8386},"2024-05-22",{},"\u002Fposts\u002F2024\u002Fget-video-rotation-by-mp4box-js",{"text":8419,"minutes":8420,"time":8421,"words":8422},"4 min read",3.68,220800,736,{"title":7534,"description":63},{"loc":8417},"posts\u002F2024\u002F20240522.get-video-rotation-by-mp4box-js",[543,8427,8428],"前端","音视频处理","D0zmDUaMjME7E7unN1UVePE8EFtBxbFmdtpj9iS2Dlc",{"id":8431,"title":8432,"body":8433,"class":528,"cover":528,"coverSize":528,"date":8459,"description":8437,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":8460,"navigation":415,"path":8461,"readingTime":8462,"seo":8466,"sitemap":8467,"stem":8468,"tags":8469,"time":528,"weather":528,"__hash__":8471},"posts\u002Fposts\u002F2024\u002F20240327.wordpress-hacked.md","记录一次 WordPress 被恶意代码注入的问题",{"type":25,"value":8434,"toc":8457},[8435,8438,8441,8448,8454],[28,8436,8437],{},"今天发现之前帮一个客户维护的服务器流量近期一直比较高，是平常的几十倍。看了下请求，都是一些奇奇怪怪的 URL，并且甚至还能返回 200。访问看了下，是一些别的产品的营销页，看了下请求来源，也都是一些营销机器人。",[28,8439,8440],{},"初步怀疑是客户 WordPress 的管理员密码被撞库了，然后 WordPress 本身又有一些漏洞导致代码文件被改了。上去看了下，发现篡改了很多文件。",[28,8442,8443,8444,8447],{},"后续就是将 WordPress 的代码恢复成之前的版本，清理了一些不用的管理员账号，并且把剩下唯一的管理员密码重新修改了。然后在 ",[65,8445,8446],{},"Apache"," 上把流量较高的一些请求的路由和 UA 做了限制，直接禁止访问降低带宽。",[58,8449,8452],{"className":8450,"code":8451,"language":516},[514],"RewriteCond %{HTTP_USER_AGENT} (DataForSeoBot|SemrushBot) [NC,OR]\nRewriteCond %{REQUEST_URI} ^\u002Fgodsend\u002F [NC]\nRewriteRule .* - [F]\n",[65,8453,8451],{"__ignoreMap":63},[28,8455,8456],{},"后面流量就恢复正常了。",{"title":63,"searchDepth":78,"depth":78,"links":8458},[],"2024-03-27",{},"\u002Fposts\u002F2024\u002Fwordpress-hacked",{"text":1217,"minutes":8463,"time":8464,"words":8465},1.225,73500,245,{"title":8432,"description":8437},{"loc":8461},"posts\u002F2024\u002F20240327.wordpress-hacked",[543,7153,8470],"WordPress","8M8kF_xksYyDPkt3CFRWkUA01rl8UzgjELJTcjEXnVY",{"id":8473,"title":8474,"body":8475,"class":528,"cover":528,"coverSize":528,"date":9989,"description":63,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":9990,"navigation":415,"path":9991,"readingTime":9992,"seo":9997,"sitemap":9998,"stem":9999,"tags":10000,"time":528,"weather":528,"__hash__":10001},"posts\u002Fposts\u002F2023\u002F20230719.font2svg-solution.md","Font2svg 特殊字体渲染方案",{"type":25,"value":8476,"toc":9983},[8477,8504,8507,8510,8513,8518,8521,8525,8532,8536,8539,8542,8545,8549,8552,8556,8559,8563,8566,8570,8573,8577,8580,8584,8587,8591,8594,8596,8600,8603,8605,8607,8611,8614,8617,8620,8623,8626,8629,8632,8635,8638,8641,8643,8646,8649,8652,8654,8657,8661,8665,8682,9432,9435,9437,9440,9442,9579,9593,9599,9795,9798,9801,9819,9821,9835,9837,9840,9843,9846,9849,9852,9855,9858,9925,9945,9948,9951,9954,9957,9959,9962,9965,9968,9971,9974,9977,9980],[7538,8478,8479,8493,8501],{},[28,8480,8481,8482,345,8487,8492],{},"2023-09-12 发表于 ",[38,8483,8486],{"href":8484,"rel":8485},"https:\u002F\u002Fmp.weixin.qq.com\u002Fs\u002FeAlceV6H0JO019m8TozwxA",[42],"哔哩哔哩技术公众号",[38,8488,8491],{"href":8489,"rel":8490},"https:\u002F\u002Fwww.bilibili.com\u002Fread\u002Fcv26464950\u002F",[42],"哔哩哔哩技术专栏"," 等处。",[28,8494,8495,8496],{},"GitHub 开源地址：",[38,8497,8500],{"href":8498,"rel":8499},"https:\u002F\u002Fgithub.com\u002Ffont2svg",[42],"Font2svg",[28,8502,8503],{},"注：开源版方案和文中略有差别，但更通用。API 服务已经完成，前端组件的开源版还没搞完。",[1232,8505,8506],{"id":8506},"背景介绍",[28,8508,8509],{},"在 Web 开发中，经常会需要在页面中引入一些特殊字体，这些字体通常不在系统字体库的范围内，并且动辄 4~5MB，甚至有些字体超过 10MB，会影响用户加载体验，尤其在手机端使用移动网络的情况下。",[28,8511,8512],{},"针对不同的业务场景，通常会有以下几种解决方案：",[8514,8515,8517],"h4",{"id":8516},"一使用切图","一、使用切图",[28,8519,8520],{},"当需要显示特殊字体的文案为固定文案时，直接使用切图可能是最常见的一种解决方案，无论是透明的 PNG 切图，还是用矢量的 SVG，都能在便捷度和加载体验上有不错的体验。当然，使用 PNG 的时候需要注意高分辨率屏幕的适配，使用 1 倍图在高分辨率的屏幕下肉眼很容易看出模糊。",[8514,8522,8524],{"id":8523},"二使用原始字体","二、使用原始字体",[28,8526,8527,8528,8531],{},"当需要显示特殊字体的文案是动态文案时，就没办法直接用文字切图来实现了。通常在对用户加载体验没有特别要求的场景下，可直接通过 ",[65,8529,8530],{},"@font-face"," 来直接引入特殊字体的文件进来。缺点也显而易见，正如前文所说，动辄 4~5MB，甚至超过 10MB 的字体文件，会比较影响用户的加载体验，在首屏需要显示的场景下，用户往往会先看到先渲染默认字体，再闪现成特殊字体的情况。",[8514,8533,8535],{"id":8534},"三使用裁剪压缩后的字体","三、使用裁剪\u002F压缩后的字体",[28,8537,8538],{},"字体裁剪技术，可通过省略字体变体（如某些字体会包含多个粗细）、裁剪字体字符集（省略不常用的字符）、通过其他压缩算法（如 woff、woff2）等方式来降低字体体积。当文案内容相对可控的情况下，用该方案可比直接加载原始字体要节省更多体积。但若字符裁剪过多，会导致缺少部分字符，在显示时出现字体不一致的情况；若字符裁剪过少，体积仍会较大。并且通常情况下，裁剪后的字符数也是远大于用户实际需要渲染的字符数，还是会有非常大的不必要的下载流量。",[1232,8540,8541],{"id":8541},"实现原理",[28,8543,8544],{},"在动态渲染特殊字体文案的场景下，为了提升用户的加载体验，我们研发了一个创新的解决方案：「Font2svg」。在介绍实现原理前，先简单介绍下字体相关的基础知识。",[8514,8546,8548],{"id":8547},"字符character","字符（Character）",[28,8550,8551],{},"在计算机中，字符是任意单个文本元素，例如字母、数字、标点符号、汉字甚至 Emoji 表情等。字符通常是构建文本的基本单位。",[8514,8553,8555],{"id":8554},"字符集character-set","字符集（Character Set）",[28,8557,8558],{},"字符集是一组预定义的字符的集合。它是对字符进行分类和组织的方式，以便在计算机系统中能够使用。会为每一个字符分配一个唯一的 ID，叫做 Code Point（码点、内码），常见字符集：ASCII、GB2312、GBK、Unicode 等。",[8514,8560,8562],{"id":8561},"字符编码character-encoding","字符编码（Character Encoding）",[28,8564,8565],{},"字符编码是一种将字符映射到特定二进制数的规则，以便在计算机中存储。通常字符集和字符编码都是成对出现，如 ASCII、GB2312、GBK 等，都是既代表了字符集，也代表了对应的字符编码。而 Unicode 比较特殊，有多种字符编码，如 UTF-8、UTF-16 等。",[8514,8567,8569],{"id":8568},"字体家族font-family","字体家族（Font Family）",[28,8571,8572],{},"字体家族是指具有相似设计特征的一组字体。这些字体共享类似的外观，但在细节上可能略有不同，如粗细、斜体等。例如：「思源宋体」就是一个字体家族。",[8514,8574,8576],{"id":8575},"字体样式font-style","字体样式（Font Style）",[28,8578,8579],{},"字体样式是字体家族中特定字体的变体，它定义了字体的外观。例如：Light、Regular、Bold、Heavy 等。",[8514,8581,8583],{"id":8582},"字型font","字型（Font）",[28,8585,8586],{},"字型是一个字体家族下的特定样式的字体，例如: 思源宋体-Bold",[8514,8588,8590],{"id":8589},"字形glyph","字形（Glyph）",[28,8592,8593],{},"字形是指一个单独字符在特定的字体（字型）中的具体外观或形状。每个字符都有对应的字形，它们描述了字符的细节和轮廓。如下图，就是「哔」这个字符在不同的字体下不同的字形：",[554,8595],{"filename":556},[8514,8597,8599],{"id":8598},"字体度量metrics","字体度量（Metrics）",[28,8601,8602],{},"字体度量是指字体中字符的尺寸和位置信息。它包括了字符的宽度、高度、上沿线（ascender）、下沿线（descender）、基线（baseline）等数据。字体度量在排版中非常重要，它决定了字符之间的间距和对齐方式，确保文本在屏幕或打印上的合理布局。",[554,8604],{"filename":1203},[554,8606],{"filename":1710},[8514,8608,8610],{"id":8609},"轮廓outline","轮廓（Outline）",[28,8612,8613],{},"字体轮廓是字形的矢量图形表示，一个字形由若干轮廓组成，每个轮廓由若干条线段或贝塞尔曲线闭合而成。根据字体格式不同，使用的贝塞尔曲线的阶数也有区别。TrueType（TTF）字体采用二次贝塞尔曲线（3 点控制），而 OpenType（OTF）字体采用三次贝塞尔曲线（4 点控制）。",[28,8615,8616],{},"一个轮廓组成的字母 C：",[554,8618],{"filename":8619},"04.gif",[28,8621,8622],{},"多个轮廓组成的字母 B：",[554,8624],{"filename":8625},"05.gif",[28,8627,8628],{},"二次贝塞尔曲线：",[554,8630],{"filename":8631},"06.gif",[28,8633,8634],{},"三次贝塞尔曲线：",[554,8636],{"filename":8637},"07.gif",[28,8639,8640],{},"本方案的原理本质上还是利用 SVG 来渲染特殊字体，通过上面的介绍，我们知道，字体的轮廓由若干线段和贝塞尔曲线组成，得知了线段的坐标和贝塞尔曲线的控制点，我们就可以绘制出轮廓的 SVG，而有了 SVG 我们就可以用来显示。因此我们可以通过解析字体文件，按需获取对应字符的 SVG 来避免加载整个字体文件。如何去唯一表示一个特定字符呢？那就是通过字符的 Unicode 编码。如下图所示，我们在渲染「哔哩哔哩乾杯」这几个字符的时候，只需要根据「哔」、「哩」、「乾」、「杯」这四个字符的 Unicode 编码去获取他们的 SVG，然后组合起来显示即可。",[554,8642],{"filename":5309},[28,8644,8645],{},"考虑到性能，如果每个字符都实时去请求服务端生成 SVG 的话，并发小的时候没影响，并发大了的话，这个字体转 SVG 的服务就会成为瓶颈。不过由于这些 SVG 生成之后就几乎不会变，所以可以预先全部生成好，放到 CDN 上，每次直接根据 Unicode 编码去 CDN 上获取指定字符的 SVG 即可。这样既避免了服务端的压力，还利用了 CDN 的特性，可以让用户更快的获取到所需的资源。",[28,8647,8648],{},"「字体转 SVG」只是第一步，在实际渲染中，还需要修改 SVG 的样式，例如字体颜色、下划线等，因此我们还做了一个前端的组件，用来对获取到的 SVG 进行样式的处理以还原字体的样式。这个组件实现了根据所需字符「动态加载 SVG」以及「SVG 复原字体样式」，在使用起来只需要传入字体名称、所需显示的字符、一些字体样式等，大大减少了前端接入的复杂度。",[28,8650,8651],{},"整体流程如下图，页面开发阶段，在 Font2svg 后台预先上传字体文件，解析所有字符的 SVG，并预先上传至对象存储\u002FCDN 上。在页面加载时，通过前端组件传入需要渲染的字符和对应字体的配置，从 CDN 拉取特定字体特定字符的 SVG，并根据样式对字符的 SVG 进行二次编辑，复原字体上色、描边、下划线等样式。",[554,8653],{"filename":5235},[28,8655,8656],{},"后台效果预览：",[8658,8659],"video-player",{":autoplay":849,":controls":849,":loop":849,"autoPlay":415,"filename":8660},"10.mp4",[8514,8662,8664],{"id":8663},"如何解析字体并生成字符的-svg","如何解析字体并生成字符的 SVG",[28,8666,8667,8668,8673,8674,8677,8678,8681],{},"字体解析服务通过 Python 实现，借助 ",[38,8669,8672],{"href":8670,"rel":8671},"https:\u002F\u002Fgithub.com\u002Frougier\u002Ffreetype-py",[42],"freetype"," 这个库对字体进行解析，遍历字体中的每一个字符，每个字符的字形（glyph）包含了轮廓（outline）和度量（metrics）数据，通过 ",[65,8675,8676],{},"outline.decompose"," 方法，可以将字符的轮廓数据分解为一系列的路径段，其中每个路径段都是一个点序列，表示字符的一部分。这些路径段可以是直线段、二次贝塞尔曲线或三次贝塞尔曲线。通过这些路径段的类型和点坐标，再通过 ",[65,8679,8680],{},"svgpathtools"," 这个库绘制成 SVG 的路径。",[58,8683,8685],{"className":1741,"code":8684,"language":1743,"meta":63,"style":63},"outline.decompose(\n    context=None,\n    move_to=self.callbackMoveTo,\n    line_to=self.callbackLineTo,\n    conic_to=self.callbackConicTo,\n    cubic_to=self.callbackCubicTo,\n)\n\ndef callbackMoveTo(self, *args):\n    self._verbose(\"MoveTo \", len(args), self.vectorsToPoints(args))\n    self._lastX, self._lastY = args[0].x, args[0].y\n\ndef callbackLineTo(self, *args):\n    self._verbose(\"LineTo \", len(args), self.vectorsToPoints(args))\n    line = Line(self.lastXyToComplex(), self.vectorToComplex(args[0]))\n    self.svg_paths.append(line)\n    self._lastX, self._lastY = args[0].x, args[0].y\n\ndef callbackConicTo(self, *args):\n    self._verbose(\"ConicTo\", len(args), self.vectorsToPoints(args))\n    curve = QuadraticBezier(self.lastXyToComplex(), self.vectorToComplex(args[0]), self.vectorToComplex(args[1]))\n    self.svg_paths.append(curve)\n    self._lastX, self._lastY = args[1].x, args[1].y\n\ndef callbackCubicTo(self, *args):\n    self._verbose(\"CubicTo\", len(args), self.vectorsToPoints(args))\n    curve = CubicBezier(\n        self.lastXyToComplex(),\n        self.vectorToComplex(args[0]),\n        self.vectorToComplex(args[1]),\n        self.vectorToComplex(args[2]),\n    )\n    self.svg_paths.append(curve)\n    self._lastX, self._lastY = args[2].x, args[2].y\n",[65,8686,8687,8699,8710,8726,8742,8758,8774,8778,8782,8804,8848,8893,8897,8916,8955,8995,9015,9055,9059,9078,9117,9170,9189,9229,9233,9252,9291,9302,9313,9332,9350,9369,9374,9392],{"__ignoreMap":63},[68,8688,8689,8692,8694,8697],{"class":70,"line":71},[68,8690,8691],{"class":373},"outline",[68,8693,404],{"class":74},[68,8695,8696],{"class":1903},"decompose",[68,8698,1907],{"class":74},[68,8700,8701,8704,8706,8708],{"class":70,"line":78},[68,8702,8703],{"class":1912},"    context",[68,8705,877],{"class":1899},[68,8707,2329],{"class":81},[68,8709,169],{"class":74},[68,8711,8712,8715,8717,8719,8721,8724],{"class":70,"line":98},[68,8713,8714],{"class":1912},"    move_to",[68,8716,877],{"class":1899},[68,8718,2109],{"class":665},[68,8720,404],{"class":74},[68,8722,8723],{"class":2122},"callbackMoveTo",[68,8725,169],{"class":74},[68,8727,8728,8731,8733,8735,8737,8740],{"class":70,"line":123},[68,8729,8730],{"class":1912},"    line_to",[68,8732,877],{"class":1899},[68,8734,2109],{"class":665},[68,8736,404],{"class":74},[68,8738,8739],{"class":2122},"callbackLineTo",[68,8741,169],{"class":74},[68,8743,8744,8747,8749,8751,8753,8756],{"class":70,"line":129},[68,8745,8746],{"class":1912},"    conic_to",[68,8748,877],{"class":1899},[68,8750,2109],{"class":665},[68,8752,404],{"class":74},[68,8754,8755],{"class":2122},"callbackConicTo",[68,8757,169],{"class":74},[68,8759,8760,8763,8765,8767,8769,8772],{"class":70,"line":212},[68,8761,8762],{"class":1912},"    cubic_to",[68,8764,877],{"class":1899},[68,8766,2109],{"class":665},[68,8768,404],{"class":74},[68,8770,8771],{"class":2122},"callbackCubicTo",[68,8773,169],{"class":74},[68,8775,8776],{"class":70,"line":233},[68,8777,410],{"class":74},[68,8779,8780],{"class":70,"line":268},[68,8781,416],{"emptyLinePlaceholder":415},[68,8783,8784,8787,8790,8792,8794,8796,8799,8802],{"class":70,"line":289},[68,8785,8786],{"class":1864},"def",[68,8788,8789],{"class":2183}," callbackMoveTo",[68,8791,648],{"class":74},[68,8793,2109],{"class":2108},[68,8795,255],{"class":74},[68,8797,8798],{"class":1899}," *",[68,8800,8801],{"class":2296},"args",[68,8803,1886],{"class":74},[68,8805,8806,8809,8811,8814,8816,8818,8821,8823,8825,8827,8829,8831,8834,8836,8838,8841,8843,8845],{"class":70,"line":308},[68,8807,8808],{"class":665},"    self",[68,8810,404],{"class":74},[68,8812,8813],{"class":1903},"_verbose",[68,8815,648],{"class":74},[68,8817,89],{"class":112},[68,8819,8820],{"class":116},"MoveTo ",[68,8822,89],{"class":112},[68,8824,255],{"class":74},[68,8826,2746],{"class":630},[68,8828,648],{"class":74},[68,8830,8801],{"class":1903},[68,8832,8833],{"class":74},"),",[68,8835,2128],{"class":665},[68,8837,404],{"class":74},[68,8839,8840],{"class":1903},"vectorsToPoints",[68,8842,648],{"class":74},[68,8844,8801],{"class":1903},[68,8846,8847],{"class":74},"))\n",[68,8849,8850,8852,8854,8857,8859,8861,8863,8866,8868,8871,8873,8875,8877,8880,8882,8884,8886,8888,8890],{"class":70,"line":314},[68,8851,8808],{"class":665},[68,8853,404],{"class":74},[68,8855,8856],{"class":2122},"_lastX",[68,8858,255],{"class":74},[68,8860,2128],{"class":665},[68,8862,404],{"class":74},[68,8864,8865],{"class":2122},"_lastY",[68,8867,1900],{"class":1899},[68,8869,8870],{"class":373}," args",[68,8872,2326],{"class":74},[68,8874,768],{"class":2397},[68,8876,2630],{"class":74},[68,8878,8879],{"class":2122},"x",[68,8881,255],{"class":74},[68,8883,8870],{"class":373},[68,8885,2326],{"class":74},[68,8887,768],{"class":2397},[68,8889,2630],{"class":74},[68,8891,8892],{"class":2122},"y\n",[68,8894,8895],{"class":70,"line":320},[68,8896,416],{"emptyLinePlaceholder":415},[68,8898,8899,8901,8904,8906,8908,8910,8912,8914],{"class":70,"line":889},[68,8900,8786],{"class":1864},[68,8902,8903],{"class":2183}," callbackLineTo",[68,8905,648],{"class":74},[68,8907,2109],{"class":2108},[68,8909,255],{"class":74},[68,8911,8798],{"class":1899},[68,8913,8801],{"class":2296},[68,8915,1886],{"class":74},[68,8917,8918,8920,8922,8924,8926,8928,8931,8933,8935,8937,8939,8941,8943,8945,8947,8949,8951,8953],{"class":70,"line":909},[68,8919,8808],{"class":665},[68,8921,404],{"class":74},[68,8923,8813],{"class":1903},[68,8925,648],{"class":74},[68,8927,89],{"class":112},[68,8929,8930],{"class":116},"LineTo ",[68,8932,89],{"class":112},[68,8934,255],{"class":74},[68,8936,2746],{"class":630},[68,8938,648],{"class":74},[68,8940,8801],{"class":1903},[68,8942,8833],{"class":74},[68,8944,2128],{"class":665},[68,8946,404],{"class":74},[68,8948,8840],{"class":1903},[68,8950,648],{"class":74},[68,8952,8801],{"class":1903},[68,8954,8847],{"class":74},[68,8956,8957,8960,8962,8965,8967,8969,8971,8974,8977,8979,8981,8984,8986,8988,8990,8992],{"class":70,"line":929},[68,8958,8959],{"class":373},"    line ",[68,8961,877],{"class":1899},[68,8963,8964],{"class":1903}," Line",[68,8966,648],{"class":74},[68,8968,2109],{"class":665},[68,8970,404],{"class":74},[68,8972,8973],{"class":1903},"lastXyToComplex",[68,8975,8976],{"class":74},"(),",[68,8978,2128],{"class":665},[68,8980,404],{"class":74},[68,8982,8983],{"class":1903},"vectorToComplex",[68,8985,648],{"class":74},[68,8987,8801],{"class":1903},[68,8989,2326],{"class":74},[68,8991,768],{"class":2397},[68,8993,8994],{"class":74},"]))\n",[68,8996,8997,8999,9001,9004,9006,9009,9011,9013],{"class":70,"line":949},[68,8998,8808],{"class":665},[68,9000,404],{"class":74},[68,9002,9003],{"class":2122},"svg_paths",[68,9005,404],{"class":74},[68,9007,9008],{"class":1903},"append",[68,9010,648],{"class":74},[68,9012,70],{"class":1903},[68,9014,410],{"class":74},[68,9016,9017,9019,9021,9023,9025,9027,9029,9031,9033,9035,9037,9039,9041,9043,9045,9047,9049,9051,9053],{"class":70,"line":969},[68,9018,8808],{"class":665},[68,9020,404],{"class":74},[68,9022,8856],{"class":2122},[68,9024,255],{"class":74},[68,9026,2128],{"class":665},[68,9028,404],{"class":74},[68,9030,8865],{"class":2122},[68,9032,1900],{"class":1899},[68,9034,8870],{"class":373},[68,9036,2326],{"class":74},[68,9038,768],{"class":2397},[68,9040,2630],{"class":74},[68,9042,8879],{"class":2122},[68,9044,255],{"class":74},[68,9046,8870],{"class":373},[68,9048,2326],{"class":74},[68,9050,768],{"class":2397},[68,9052,2630],{"class":74},[68,9054,8892],{"class":2122},[68,9056,9057],{"class":70,"line":989},[68,9058,416],{"emptyLinePlaceholder":415},[68,9060,9061,9063,9066,9068,9070,9072,9074,9076],{"class":70,"line":1009},[68,9062,8786],{"class":1864},[68,9064,9065],{"class":2183}," callbackConicTo",[68,9067,648],{"class":74},[68,9069,2109],{"class":2108},[68,9071,255],{"class":74},[68,9073,8798],{"class":1899},[68,9075,8801],{"class":2296},[68,9077,1886],{"class":74},[68,9079,9080,9082,9084,9086,9088,9090,9093,9095,9097,9099,9101,9103,9105,9107,9109,9111,9113,9115],{"class":70,"line":1029},[68,9081,8808],{"class":665},[68,9083,404],{"class":74},[68,9085,8813],{"class":1903},[68,9087,648],{"class":74},[68,9089,89],{"class":112},[68,9091,9092],{"class":116},"ConicTo",[68,9094,89],{"class":112},[68,9096,255],{"class":74},[68,9098,2746],{"class":630},[68,9100,648],{"class":74},[68,9102,8801],{"class":1903},[68,9104,8833],{"class":74},[68,9106,2128],{"class":665},[68,9108,404],{"class":74},[68,9110,8840],{"class":1903},[68,9112,648],{"class":74},[68,9114,8801],{"class":1903},[68,9116,8847],{"class":74},[68,9118,9119,9122,9124,9127,9129,9131,9133,9135,9137,9139,9141,9143,9145,9147,9149,9151,9154,9156,9158,9160,9162,9164,9166,9168],{"class":70,"line":1049},[68,9120,9121],{"class":373},"    curve ",[68,9123,877],{"class":1899},[68,9125,9126],{"class":1903}," QuadraticBezier",[68,9128,648],{"class":74},[68,9130,2109],{"class":665},[68,9132,404],{"class":74},[68,9134,8973],{"class":1903},[68,9136,8976],{"class":74},[68,9138,2128],{"class":665},[68,9140,404],{"class":74},[68,9142,8983],{"class":1903},[68,9144,648],{"class":74},[68,9146,8801],{"class":1903},[68,9148,2326],{"class":74},[68,9150,768],{"class":2397},[68,9152,9153],{"class":74},"]),",[68,9155,2128],{"class":665},[68,9157,404],{"class":74},[68,9159,8983],{"class":1903},[68,9161,648],{"class":74},[68,9163,8801],{"class":1903},[68,9165,2326],{"class":74},[68,9167,401],{"class":2397},[68,9169,8994],{"class":74},[68,9171,9172,9174,9176,9178,9180,9182,9184,9187],{"class":70,"line":1069},[68,9173,8808],{"class":665},[68,9175,404],{"class":74},[68,9177,9003],{"class":2122},[68,9179,404],{"class":74},[68,9181,9008],{"class":1903},[68,9183,648],{"class":74},[68,9185,9186],{"class":1903},"curve",[68,9188,410],{"class":74},[68,9190,9191,9193,9195,9197,9199,9201,9203,9205,9207,9209,9211,9213,9215,9217,9219,9221,9223,9225,9227],{"class":70,"line":1088},[68,9192,8808],{"class":665},[68,9194,404],{"class":74},[68,9196,8856],{"class":2122},[68,9198,255],{"class":74},[68,9200,2128],{"class":665},[68,9202,404],{"class":74},[68,9204,8865],{"class":2122},[68,9206,1900],{"class":1899},[68,9208,8870],{"class":373},[68,9210,2326],{"class":74},[68,9212,401],{"class":2397},[68,9214,2630],{"class":74},[68,9216,8879],{"class":2122},[68,9218,255],{"class":74},[68,9220,8870],{"class":373},[68,9222,2326],{"class":74},[68,9224,401],{"class":2397},[68,9226,2630],{"class":74},[68,9228,8892],{"class":2122},[68,9230,9231],{"class":70,"line":1108},[68,9232,416],{"emptyLinePlaceholder":415},[68,9234,9235,9237,9240,9242,9244,9246,9248,9250],{"class":70,"line":1128},[68,9236,8786],{"class":1864},[68,9238,9239],{"class":2183}," callbackCubicTo",[68,9241,648],{"class":74},[68,9243,2109],{"class":2108},[68,9245,255],{"class":74},[68,9247,8798],{"class":1899},[68,9249,8801],{"class":2296},[68,9251,1886],{"class":74},[68,9253,9254,9256,9258,9260,9262,9264,9267,9269,9271,9273,9275,9277,9279,9281,9283,9285,9287,9289],{"class":70,"line":1148},[68,9255,8808],{"class":665},[68,9257,404],{"class":74},[68,9259,8813],{"class":1903},[68,9261,648],{"class":74},[68,9263,89],{"class":112},[68,9265,9266],{"class":116},"CubicTo",[68,9268,89],{"class":112},[68,9270,255],{"class":74},[68,9272,2746],{"class":630},[68,9274,648],{"class":74},[68,9276,8801],{"class":1903},[68,9278,8833],{"class":74},[68,9280,2128],{"class":665},[68,9282,404],{"class":74},[68,9284,8840],{"class":1903},[68,9286,648],{"class":74},[68,9288,8801],{"class":1903},[68,9290,8847],{"class":74},[68,9292,9293,9295,9297,9300],{"class":70,"line":1168},[68,9294,9121],{"class":373},[68,9296,877],{"class":1899},[68,9298,9299],{"class":1903}," CubicBezier",[68,9301,1907],{"class":74},[68,9303,9304,9306,9308,9310],{"class":70,"line":1187},[68,9305,2117],{"class":665},[68,9307,404],{"class":74},[68,9309,8973],{"class":1903},[68,9311,9312],{"class":74},"(),\n",[68,9314,9315,9317,9319,9321,9323,9325,9327,9329],{"class":70,"line":2039},[68,9316,2117],{"class":665},[68,9318,404],{"class":74},[68,9320,8983],{"class":1903},[68,9322,648],{"class":74},[68,9324,8801],{"class":1903},[68,9326,2326],{"class":74},[68,9328,768],{"class":2397},[68,9330,9331],{"class":74},"]),\n",[68,9333,9334,9336,9338,9340,9342,9344,9346,9348],{"class":70,"line":2055},[68,9335,2117],{"class":665},[68,9337,404],{"class":74},[68,9339,8983],{"class":1903},[68,9341,648],{"class":74},[68,9343,8801],{"class":1903},[68,9345,2326],{"class":74},[68,9347,401],{"class":2397},[68,9349,9331],{"class":74},[68,9351,9352,9354,9356,9358,9360,9362,9364,9367],{"class":70,"line":2071},[68,9353,2117],{"class":665},[68,9355,404],{"class":74},[68,9357,8983],{"class":1903},[68,9359,648],{"class":74},[68,9361,8801],{"class":1903},[68,9363,2326],{"class":74},[68,9365,9366],{"class":2397},"2",[68,9368,9331],{"class":74},[68,9370,9371],{"class":70,"line":2087},[68,9372,9373],{"class":74},"    )\n",[68,9375,9376,9378,9380,9382,9384,9386,9388,9390],{"class":70,"line":2092},[68,9377,8808],{"class":665},[68,9379,404],{"class":74},[68,9381,9003],{"class":2122},[68,9383,404],{"class":74},[68,9385,9008],{"class":1903},[68,9387,648],{"class":74},[68,9389,9186],{"class":1903},[68,9391,410],{"class":74},[68,9393,9394,9396,9398,9400,9402,9404,9406,9408,9410,9412,9414,9416,9418,9420,9422,9424,9426,9428,9430],{"class":70,"line":2097},[68,9395,8808],{"class":665},[68,9397,404],{"class":74},[68,9399,8856],{"class":2122},[68,9401,255],{"class":74},[68,9403,2128],{"class":665},[68,9405,404],{"class":74},[68,9407,8865],{"class":2122},[68,9409,1900],{"class":1899},[68,9411,8870],{"class":373},[68,9413,2326],{"class":74},[68,9415,9366],{"class":2397},[68,9417,2630],{"class":74},[68,9419,8879],{"class":2122},[68,9421,255],{"class":74},[68,9423,8870],{"class":373},[68,9425,2326],{"class":74},[68,9427,9366],{"class":2397},[68,9429,2630],{"class":74},[68,9431,8892],{"class":2122},[28,9433,9434],{},"除了 SVG 的路径之外，还有一个很重要的内容，就是 viewBox。因为在字体中，表达一个字形，除了轮廓之外，还有一个很重要的 metrics 信息，metrics 信息没有还原好，在字体排版的时候就会出现问题。如果以轮廓本身的大小作为容器的话，一些未占满空间的文字，或者一些标点符号，在排版时，就会被缩放的很大；而如果以字体的上沿线和下沿线来计算一个方形容器显示的话，对于中文字符问题不大，但对于半角字符，其中间的间隙就会显得太大，如下图：",[554,9436],{"filename":5241},[28,9438,9439],{},"因此，我们需要从 Metrics 中提取每个字形的基本信息。以横排为例，取每个字形自己的宽度来绘制 viewBox，高度还是通过上下沿线相减来计算。这样在排版时就可以得到想要的效果了：",[554,9441],{"filename":5214},[58,9443,9445],{"className":1741,"code":9444,"language":1743,"meta":63,"style":63},"def calcViewBox(self, metrics):\n    view_box_min_x = 0\n    view_box_min_y = -self.face.ascender\n    view_box_width = metrics.horiAdvance\n    view_box_height = self.face.ascender - self.face.descender\n    return f\"{view_box_min_x} {view_box_min_y} {view_box_width} {view_box_height}\"\n",[65,9446,9447,9465,9474,9495,9509,9540],{"__ignoreMap":63},[68,9448,9449,9451,9454,9456,9458,9460,9463],{"class":70,"line":71},[68,9450,8786],{"class":1864},[68,9452,9453],{"class":2183}," calcViewBox",[68,9455,648],{"class":74},[68,9457,2109],{"class":2108},[68,9459,255],{"class":74},[68,9461,9462],{"class":2296}," metrics",[68,9464,1886],{"class":74},[68,9466,9467,9470,9472],{"class":70,"line":78},[68,9468,9469],{"class":373},"    view_box_min_x ",[68,9471,877],{"class":1899},[68,9473,2732],{"class":2397},[68,9475,9476,9479,9481,9483,9485,9487,9490,9492],{"class":70,"line":98},[68,9477,9478],{"class":373},"    view_box_min_y ",[68,9480,877],{"class":1899},[68,9482,2394],{"class":1899},[68,9484,2109],{"class":665},[68,9486,404],{"class":74},[68,9488,9489],{"class":2122},"face",[68,9491,404],{"class":74},[68,9493,9494],{"class":2122},"ascender\n",[68,9496,9497,9500,9502,9504,9506],{"class":70,"line":123},[68,9498,9499],{"class":373},"    view_box_width ",[68,9501,877],{"class":1899},[68,9503,9462],{"class":373},[68,9505,404],{"class":74},[68,9507,9508],{"class":2122},"horiAdvance\n",[68,9510,9511,9514,9516,9518,9520,9522,9524,9527,9529,9531,9533,9535,9537],{"class":70,"line":129},[68,9512,9513],{"class":373},"    view_box_height ",[68,9515,877],{"class":1899},[68,9517,2128],{"class":665},[68,9519,404],{"class":74},[68,9521,9489],{"class":2122},[68,9523,404],{"class":74},[68,9525,9526],{"class":2122},"ascender",[68,9528,2394],{"class":1899},[68,9530,2128],{"class":665},[68,9532,404],{"class":74},[68,9534,9489],{"class":2122},[68,9536,404],{"class":74},[68,9538,9539],{"class":2122},"descender\n",[68,9541,9542,9545,9547,9549,9551,9554,9556,9558,9561,9563,9565,9568,9570,9572,9575,9577],{"class":70,"line":212},[68,9543,9544],{"class":1790},"    return",[68,9546,2540],{"class":1864},[68,9548,89],{"class":116},[68,9550,2546],{"class":2397},[68,9552,9553],{"class":373},"view_box_min_x",[68,9555,2400],{"class":2397},[68,9557,2382],{"class":2397},[68,9559,9560],{"class":373},"view_box_min_y",[68,9562,2400],{"class":2397},[68,9564,2382],{"class":2397},[68,9566,9567],{"class":373},"view_box_width",[68,9569,2400],{"class":2397},[68,9571,2382],{"class":2397},[68,9573,9574],{"class":373},"view_box_height",[68,9576,2400],{"class":2397},[68,9578,120],{"class":116},[28,9580,9581,9582,345,9585,345,9588,345,9590,9592],{},"在有了 ",[65,9583,9584],{},"path",[65,9586,9587],{},"viewBox",[65,9589,7578],{},[65,9591,7581],{}," 等信息之后，我们就可以直接生成能表达具体字形所需要的 SVG 文件了。",[28,9594,9595,9596,9598],{},"还有一个特殊字符，那就是空格，他是不包含任何轮廓的，解析轮廓的时候结果是空的。在生成 SVG 的时候，如果 path 是为空会失败，这时候可以生成一个和 ",[65,9597,9587],{}," 同样大小的透明度为 0 的轮廓用来占位。",[58,9600,9602],{"className":1741,"code":9601,"language":1743,"meta":63,"style":63},"path = (\n    Path(*self.svg_paths).scaled(1, -1)\n    if len(self.svg_paths) > 0\n    else parse_path(\n        f\"M 0,{-self.face.ascender} L {metrics.horiAdvance},{-self.face.ascender} L {metrics.horiAdvance},{-self.face.descender} L 0,{-self.face.descender} Z\"\n    )\n)\n",[65,9603,9604,9613,9645,9667,9677,9787,9791],{"__ignoreMap":63},[68,9605,9606,9609,9611],{"class":70,"line":71},[68,9607,9608],{"class":373},"path ",[68,9610,877],{"class":1899},[68,9612,606],{"class":74},[68,9614,9615,9618,9620,9622,9624,9626,9628,9630,9633,9635,9637,9639,9641,9643],{"class":70,"line":78},[68,9616,9617],{"class":1903},"    Path",[68,9619,648],{"class":74},[68,9621,7661],{"class":1899},[68,9623,2109],{"class":665},[68,9625,404],{"class":74},[68,9627,9003],{"class":2122},[68,9629,4608],{"class":74},[68,9631,9632],{"class":1903},"scaled",[68,9634,648],{"class":74},[68,9636,401],{"class":2397},[68,9638,255],{"class":74},[68,9640,2394],{"class":1899},[68,9642,401],{"class":2397},[68,9644,410],{"class":74},[68,9646,9647,9650,9652,9654,9656,9658,9660,9662,9665],{"class":70,"line":98},[68,9648,9649],{"class":1790},"    if",[68,9651,2746],{"class":630},[68,9653,648],{"class":74},[68,9655,2109],{"class":665},[68,9657,404],{"class":74},[68,9659,9003],{"class":2122},[68,9661,2753],{"class":74},[68,9663,9664],{"class":1899}," >",[68,9666,2732],{"class":2397},[68,9668,9669,9672,9675],{"class":70,"line":123},[68,9670,9671],{"class":1790},"    else",[68,9673,9674],{"class":1903}," parse_path",[68,9676,1907],{"class":74},[68,9678,9679,9682,9685,9687,9689,9691,9693,9695,9697,9699,9701,9704,9706,9709,9711,9714,9716,9718,9720,9722,9724,9726,9728,9730,9732,9734,9736,9738,9740,9742,9744,9746,9748,9750,9752,9754,9756,9758,9760,9763,9765,9768,9770,9772,9774,9776,9778,9780,9782,9784],{"class":70,"line":129},[68,9680,9681],{"class":1864},"        f",[68,9683,9684],{"class":116},"\"M 0,",[68,9686,2546],{"class":2397},[68,9688,2652],{"class":1899},[68,9690,2109],{"class":665},[68,9692,404],{"class":74},[68,9694,9489],{"class":2122},[68,9696,404],{"class":74},[68,9698,9526],{"class":2122},[68,9700,2400],{"class":2397},[68,9702,9703],{"class":116}," L ",[68,9705,2546],{"class":2397},[68,9707,9708],{"class":1903},"metrics",[68,9710,404],{"class":74},[68,9712,9713],{"class":2122},"horiAdvance",[68,9715,2400],{"class":2397},[68,9717,255],{"class":116},[68,9719,2546],{"class":2397},[68,9721,2652],{"class":1899},[68,9723,2109],{"class":665},[68,9725,404],{"class":74},[68,9727,9489],{"class":2122},[68,9729,404],{"class":74},[68,9731,9526],{"class":2122},[68,9733,2400],{"class":2397},[68,9735,9703],{"class":116},[68,9737,2546],{"class":2397},[68,9739,9708],{"class":1903},[68,9741,404],{"class":74},[68,9743,9713],{"class":2122},[68,9745,2400],{"class":2397},[68,9747,255],{"class":116},[68,9749,2546],{"class":2397},[68,9751,2652],{"class":1899},[68,9753,2109],{"class":665},[68,9755,404],{"class":74},[68,9757,9489],{"class":2122},[68,9759,404],{"class":74},[68,9761,9762],{"class":2122},"descender",[68,9764,2400],{"class":2397},[68,9766,9767],{"class":116}," L 0,",[68,9769,2546],{"class":2397},[68,9771,2652],{"class":1899},[68,9773,2109],{"class":665},[68,9775,404],{"class":74},[68,9777,9489],{"class":2122},[68,9779,404],{"class":74},[68,9781,9762],{"class":2122},[68,9783,2400],{"class":2397},[68,9785,9786],{"class":116}," Z\"\n",[68,9788,9789],{"class":70,"line":212},[68,9790,9373],{"class":74},[68,9792,9793],{"class":70,"line":233},[68,9794,410],{"class":74},[8514,9796,9797],{"id":9797},"怎么设置字体样式",[28,9799,9800],{},"在前面的步骤中，我们为每个字形预先生成好了对应的 SVG 文件，但我们知道，SVG 文件是一个静态的文件，其字体颜色等样式是固定的，在前端使用的时候，如何去动态设置颜色等样式呢？",[28,9802,9803,9804,9807,9808,9810,9811,9814,9815,9818],{},"我们在前端去加载 SVG 字体文件的时候，并不是直接通过 ",[65,9805,9806],{},"img"," 标签的方式来引入 SVG，而是在前端组件中去下载 SVG 文件内容并解析 DOM 对象，然后根据需要去修改对应的属性或添加额外的样式，例如修改 ",[65,9809,9584],{}," 的 ",[65,9812,9813],{},"fill"," 属性来设置颜色，修改完样式后再把修改后的 SVG 标签直接插入到文档中。还有一些字体全局的样式信息，例如下划线的位置、粗细等，在不同的字体中，也是有不一样的配置的。我们需要想办法把这些信息也通过 SVG 传递过来。解决方法也很简单，我们在 SVG 的根节点上添加一个自定义的属性，将所需传递的信息转成 ",[65,9816,9817],{},"JSON"," 格式然后塞到这个属性里，前端在解析 SVG 的 DOM 时，把这个属性中的数据取出来解析并渲染就可以了。",[554,9820],{"filename":4854},[28,9822,9823,9824,5225,9827,9830,9831,9834],{},"上面截图中，",[65,9825,9826],{},"underline",[65,9828,9829],{},"underlineThickness"," 就分别代表了下划线位置和粗细信息，在解析到这两个信息后再做一些转换处理，可通过 ",[65,9832,9833],{},"::before"," 伪元素来绘制下划线：",[554,9836],{"filename":4884},[1232,9838,9839],{"id":9839},"实践案例",[8514,9841,9842],{"id":9842},"页面接入",[28,9844,9845],{},"创作中心每周会给 UP 主推送荣誉周报，页面中首屏有非常大的篇幅显示的是本周关键词，效果如下图：",[554,9847],{"filename":9848},"15.jpg",[28,9850,9851],{},"这个场景有两处使用了特殊字体「方正 FW 筑紫黑」，一个是卡片标题「本周关键词」这几个字，这个是固定标题，用传统的切图方式也是 OK 的，但下面的关键词是根据 UP 主上周的投稿、评论、弹幕、播放等数据由服务端动态下发的，这个就无法通过切图来实现了，需要穷举的关键词特别多。",[28,9853,9854],{},"在使用本方案之前，该页面使用的是直接引入字体文件来设置字体。由于该页面加载资源较多，在加载时不可避免会出现字体闪烁、图片资源加载过程页面抖动的问题，在手机上通过 4G 网络访问时尤其明显。为了避免这种情况，该页面渲染之前加入了一个 Loading 页，在 Loading 页上加载所需的所有资源，包括图片、字体文件等。",[28,9856,9857],{},"使用本方案优化前后的数据对比如下：",[9859,9860,9861,9879],"table",{},[9862,9863,9864],"thead",{},[9865,9866,9867,9870,9873,9876],"tr",{},[9868,9869],"th",{},[9868,9871,9872],{},"渲染文字所需资源",[9868,9874,9875],{},"首屏加载时长",[9868,9877,9878],{},"页面跳失率",[9880,9881,9882,9897,9911],"tbody",{},[9865,9883,9884,9888,9891,9894],{},[9885,9886,9887],"td",{},"优化前",[9885,9889,9890],{},"2855KB",[9885,9892,9893],{},"2268ms",[9885,9895,9896],{},"5.47%",[9865,9898,9899,9902,9905,9908],{},[9885,9900,9901],{},"优化后",[9885,9903,9904],{},"45KB",[9885,9906,9907],{},"1791ms",[9885,9909,9910],{},"3.11%",[9865,9912,9913,9916,9919,9922],{},[9885,9914,9915],{},"对比",[9885,9917,9918],{},"🔽 下降 98.4%",[9885,9920,9921],{},"🔽 下降 21%",[9885,9923,9924],{},"🔽 下降 2.36PP",[1236,9926,9927,9933,9939],{},[1239,9928,9929,9930,1686],{},"渲染文字所需资源：优化前字体文件为 2855KB，优化后 JS 组件库为 37KB，4 个 SVG 为 8KB，总共 45KB 的资源，",[1242,9931,9932],{},"下降了 98.4%",[1239,9934,9935,9936,1686],{},"首屏加载时长：从 2268ms 下降至 1791ms，",[1242,9937,9938],{},"下降了 21%",[1239,9940,9941,9942,1686],{},"页面跳失率：从 5.47% 降低至 3.11%，",[1242,9943,9944],{},"下降了 2.36PP",[28,9946,9947],{},"最终本方案不仅通过降低首屏加载时长提升了用户的加载体验，同时也获得了页面跳失率下降的业务结果。",[8514,9949,9950],{"id":9950},"失败兜底",[28,9952,9953],{},"尽管本方案所需加载的资源量相比直接引入字体包要小很多，但请求数量会根据字符数有所上升，在极端情况下，网络失败会导致资源请求失败；另外，一些字体包含的字符数有限，在请求这个字体本身就不存在的字符的时候，也会出现请求失败的情况。在缺失 SVG 的情况下如何进行兜底的显示也是我们需要考虑的内容。",[28,9955,9956],{},"在组件侧获取不到对应的 SVG 时，可通过系统默认字体直接在字符位置进行渲染作为兜底，与浏览器默认的处理方式一致。具体效果如下：",[554,9958],{"filename":4945},[28,9960,9961],{},"由于兜底的默认字体与目标字体不同，他们的容器大小和下划线位置、下划线粗细等设置均有可能不一样，所以在开启了下划线的情况下，会出现下划线高度和粗细不一致的现象，如下图：",[554,9963],{"filename":9964},"17.png",[28,9966,9967],{},"为了解决这个问题，我们在渲染下划线的时候，对于兜底的默认字体，不采用 css 样式来绘制下划线，而是与 SVG 一样，通过伪元素并且使用目标字体的下划线设置来进行绘制，最终效果如下图：",[554,9969],{"filename":9970},"18.png",[1232,9972,9973],{"id":9973},"总结",[28,9975,9976],{},"在本文中，我们深入探讨了 Font2svg 方案的技术原理和实现细节。我们通过将字体转换成 SVG 进行渲染，降低了用户渲染特殊字体动态文字所需下载的文件大小，提高了加载速度，从而优化了特殊字体在 Web 页面中的加载体验。",[28,9978,9979],{},"这套方案在动态渲染特殊字体文案的场景下具有广泛的应用前景，不只是在 Web 端，在客户端上也同样适用。它能为设计师和开发者提供更灵活、高效的特殊字体渲染方案，让设计师不会再因为字体包体积而放弃使用一些艺术字体，同时也让开发者不再为字体包的大小而头疼。",[523,9981,9982],{},"html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .slqww, html code.shiki .slqww{--shiki-light:#6182B8;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .smCYv, html code.shiki .smCYv{--shiki-light:#E53935;--shiki-light-font-style:italic;--shiki-default:#24292E;--shiki-default-font-style:inherit;--shiki-dark:#E1E4E8;--shiki-dark-font-style:inherit}html pre.shiki code .sFwrP, html code.shiki .sFwrP{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#24292E;--shiki-default-font-style:inherit;--shiki-dark:#E1E4E8;--shiki-dark-font-style:inherit}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}",{"title":63,"searchDepth":78,"depth":78,"links":9984},[9985,9986,9987,9988],{"id":8506,"depth":78,"text":8506},{"id":8541,"depth":78,"text":8541},{"id":9839,"depth":78,"text":9839},{"id":9973,"depth":78,"text":9973},"2023-07-19",{},"\u002Fposts\u002F2023\u002Ffont2svg-solution",{"text":9993,"minutes":9994,"time":9995,"words":9996},"21 min read",20.145,1208700,4029,{"title":8474,"description":63},{"loc":9991},"posts\u002F2023\u002F20230719.font2svg-solution",[543,8427],"ExIjK3gmZDqEqp_xAilxKHvuQ01bwCJnvj3dq-s3k6o",{"id":10003,"title":10004,"body":10005,"class":528,"cover":528,"coverSize":528,"date":10046,"description":10009,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":10047,"navigation":415,"path":10048,"readingTime":10049,"seo":10053,"sitemap":10054,"stem":10055,"tags":10056,"time":528,"weather":528,"__hash__":10058},"posts\u002Fposts\u002F2023\u002F20230518.use-cloudflare-speed-up-overseas-traffic.md","使用 Cloudflare 加速博客海外访问速度",{"type":25,"value":10006,"toc":10044},[10007,10010,10013,10016,10019,10022,10025,10028,10041],[28,10008,10009],{},"这几天对 hadb.me 博客又做了一次迁移和优化。",[28,10011,10012],{},"原先 hadb.me 是直接部署在阿里云的 k8s 上的，但为了统一博客的数据备份，决定迁移到 HomeLab ，数据存到 NAS 上，容器部署到 NUC 的 docker 中。这样数据可以跟着整个 NAS 的备份策略一起。所以这次架构调整的主要目的是为了方便博客数据的统一备份。",[28,10014,10015],{},"但是由于 HomeLab 无法直接暴露 443 端口，所以域名不能直接解析到家里的 IP。",[28,10017,10018],{},"一开始尝试了下直接接入 Cloudflare，发现境外访问速度很快，但境内直接访问的话，速度堪忧。于是研究了一下，最终根据访问者的位置使用不同的解析方式实现境内外的同时加速的目标。",[28,10020,10021],{},"最终的架构是这样：",[554,10023],{"description":10024,"filename":556},"hadb.me 博客网络架构",[28,10026,10027],{},"在境内，直接解析到阿里云的 SLB 上，阿里云上我是有一套 k8s 集群，里面起了个 nginx 容器，反向代理到 HomeLab 的非标端口上，HomeLab 的域名解析通过阿里云解析的 API 动态更新。",[28,10029,10030,10031,10034,10035,5225,10037,10040],{},"在境外，通过 CNAME 解析到 Cloudflare 上绑定一个其他域名 ",[65,10032,10033],{},"xxx.com","，这个域名通过 Cloudflare 的 API 会动态更新解析到 HomeLab 的外网 IP 上。在这个域名的 Origin Rules 里面设置主机名为 ",[65,10036,10033],{},[65,10038,10039],{},"hadb.me"," 的时候都重写端口到 HomeLab 的非标端口上。",[28,10042,10043],{},"至此，完成架构迁移。既满足了备份需求，海外访问速度上也有提升。",{"title":63,"searchDepth":78,"depth":78,"links":10045},[],"2023-05-18",{},"\u002Fposts\u002F2023\u002Fuse-cloudflare-speed-up-overseas-traffic",{"text":1217,"minutes":10050,"time":10051,"words":10052},1.95,117000,390,{"title":10004,"description":10009},{"loc":10048},"posts\u002F2023\u002F20230518.use-cloudflare-speed-up-overseas-traffic",[543,10057,7153],"博客","ACtCrCQ6aSo6KIRwPlOKjc7i5Yh2L3RAT_a91uJ9FWo",{"id":10060,"title":10061,"body":10062,"class":528,"cover":1445,"coverSize":528,"date":10640,"description":10066,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":10641,"navigation":415,"path":10642,"readingTime":10643,"seo":10647,"sitemap":10648,"stem":10649,"tags":10650,"time":528,"weather":528,"__hash__":10652},"posts\u002Fposts\u002F2023\u002F20230423.gitlab-ci-auto-deploy-python-lib.md","GitLab CI 配置自动化打包上传 Python 库",{"type":25,"value":10063,"toc":10638},[10064,10067,10078,10409,10414,10530,10548,10564,10567,10635],[28,10065,10066],{},"自己之前有些 python 脚本类的项目，会用到一些通用的能力，如读取配置、打日志等，每次都 copy 一份 utils 目录有些不够优雅，于是撸了一个公共库，方便自己使用。",[28,10068,10069,10070,10073,10074,10077],{},"为了能配合 GitLab CI，",[65,10071,10072],{},"setup.py"," 需要做一些小调整，版本号不需要手动输入了，直接读取 ",[65,10075,10076],{},"$CI_COMMIT_TAG","，代码如下：",[58,10079,10081],{"className":1741,"code":10080,"language":1743,"meta":63,"style":63},"import os\n\nimport setuptools\n\nwith open(\"README.md\", \"r\") as fh:\n    long_description = fh.read()\n\nsetuptools.setup(\n    name=\"yuanfen\",\n    version=os.environ.get(\"CI_COMMIT_TAG\", \"0.0.0\"),\n    author=\"Bean\",\n    author_email=\"bean@yuanfen.net\",\n    description=\"Yuanfen Python Library\",\n    long_description=long_description,\n    long_description_content_type=\"text\u002Fmarkdown\",\n    url=\"\",\n    install_requires=[\"pyyaml\", \"watchdog\"],\n    packages=setuptools.find_packages(),\n    classifiers=[\n        \"Programming Language :: Python :: 3\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Operating System :: OS Independent\",\n    ],\n)\n\n",[65,10082,10083,10090,10094,10101,10105,10139,10154,10158,10170,10186,10226,10242,10258,10274,10286,10302,10313,10340,10356,10366,10378,10389,10400,10405],{"__ignoreMap":63},[68,10084,10085,10087],{"class":70,"line":71},[68,10086,1791],{"class":1790},[68,10088,10089],{"class":373}," os\n",[68,10091,10092],{"class":70,"line":78},[68,10093,416],{"emptyLinePlaceholder":415},[68,10095,10096,10098],{"class":70,"line":98},[68,10097,1791],{"class":1790},[68,10099,10100],{"class":373}," setuptools\n",[68,10102,10103],{"class":70,"line":123},[68,10104,416],{"emptyLinePlaceholder":415},[68,10106,10107,10110,10113,10115,10117,10120,10122,10124,10126,10128,10130,10132,10134,10137],{"class":70,"line":129},[68,10108,10109],{"class":1790},"with",[68,10111,10112],{"class":630}," open",[68,10114,648],{"class":74},[68,10116,89],{"class":112},[68,10118,10119],{"class":116},"README.md",[68,10121,89],{"class":112},[68,10123,255],{"class":74},[68,10125,113],{"class":112},[68,10127,3776],{"class":116},[68,10129,89],{"class":112},[68,10131,2753],{"class":74},[68,10133,3014],{"class":1790},[68,10135,10136],{"class":373}," fh",[68,10138,1569],{"class":74},[68,10140,10141,10144,10146,10148,10150,10152],{"class":70,"line":212},[68,10142,10143],{"class":373},"    long_description ",[68,10145,877],{"class":1899},[68,10147,10136],{"class":373},[68,10149,404],{"class":74},[68,10151,8206],{"class":1903},[68,10153,2136],{"class":74},[68,10155,10156],{"class":70,"line":233},[68,10157,416],{"emptyLinePlaceholder":415},[68,10159,10160,10163,10165,10168],{"class":70,"line":268},[68,10161,10162],{"class":373},"setuptools",[68,10164,404],{"class":74},[68,10166,10167],{"class":1903},"setup",[68,10169,1907],{"class":74},[68,10171,10172,10175,10177,10179,10182,10184],{"class":70,"line":289},[68,10173,10174],{"class":1912},"    name",[68,10176,877],{"class":1899},[68,10178,89],{"class":112},[68,10180,10181],{"class":116},"yuanfen",[68,10183,89],{"class":112},[68,10185,169],{"class":74},[68,10187,10188,10191,10193,10196,10198,10201,10203,10205,10207,10209,10212,10214,10216,10218,10221,10223],{"class":70,"line":308},[68,10189,10190],{"class":1912},"    version",[68,10192,877],{"class":1899},[68,10194,10195],{"class":1903},"os",[68,10197,404],{"class":74},[68,10199,10200],{"class":2122},"environ",[68,10202,404],{"class":74},[68,10204,3507],{"class":1903},[68,10206,648],{"class":74},[68,10208,89],{"class":112},[68,10210,10211],{"class":116},"CI_COMMIT_TAG",[68,10213,89],{"class":112},[68,10215,255],{"class":74},[68,10217,113],{"class":112},[68,10219,10220],{"class":116},"0.0.0",[68,10222,89],{"class":112},[68,10224,10225],{"class":74},"),\n",[68,10227,10228,10231,10233,10235,10238,10240],{"class":70,"line":314},[68,10229,10230],{"class":1912},"    author",[68,10232,877],{"class":1899},[68,10234,89],{"class":112},[68,10236,10237],{"class":116},"Bean",[68,10239,89],{"class":112},[68,10241,169],{"class":74},[68,10243,10244,10247,10249,10251,10254,10256],{"class":70,"line":320},[68,10245,10246],{"class":1912},"    author_email",[68,10248,877],{"class":1899},[68,10250,89],{"class":112},[68,10252,10253],{"class":116},"bean@yuanfen.net",[68,10255,89],{"class":112},[68,10257,169],{"class":74},[68,10259,10260,10263,10265,10267,10270,10272],{"class":70,"line":889},[68,10261,10262],{"class":1912},"    description",[68,10264,877],{"class":1899},[68,10266,89],{"class":112},[68,10268,10269],{"class":116},"Yuanfen Python Library",[68,10271,89],{"class":112},[68,10273,169],{"class":74},[68,10275,10276,10279,10281,10284],{"class":70,"line":909},[68,10277,10278],{"class":1912},"    long_description",[68,10280,877],{"class":1899},[68,10282,10283],{"class":1903},"long_description",[68,10285,169],{"class":74},[68,10287,10288,10291,10293,10295,10298,10300],{"class":70,"line":929},[68,10289,10290],{"class":1912},"    long_description_content_type",[68,10292,877],{"class":1899},[68,10294,89],{"class":112},[68,10296,10297],{"class":116},"text\u002Fmarkdown",[68,10299,89],{"class":112},[68,10301,169],{"class":74},[68,10303,10304,10307,10309,10311],{"class":70,"line":949},[68,10305,10306],{"class":1912},"    url",[68,10308,877],{"class":1899},[68,10310,1969],{"class":112},[68,10312,169],{"class":74},[68,10314,10315,10318,10320,10322,10324,10327,10329,10331,10333,10336,10338],{"class":70,"line":969},[68,10316,10317],{"class":1912},"    install_requires",[68,10319,877],{"class":1899},[68,10321,2326],{"class":74},[68,10323,89],{"class":112},[68,10325,10326],{"class":116},"pyyaml",[68,10328,89],{"class":112},[68,10330,255],{"class":74},[68,10332,113],{"class":112},[68,10334,10335],{"class":116},"watchdog",[68,10337,89],{"class":112},[68,10339,265],{"class":74},[68,10341,10342,10345,10347,10349,10351,10354],{"class":70,"line":989},[68,10343,10344],{"class":1912},"    packages",[68,10346,877],{"class":1899},[68,10348,10162],{"class":1903},[68,10350,404],{"class":74},[68,10352,10353],{"class":1903},"find_packages",[68,10355,9312],{"class":74},[68,10357,10358,10361,10363],{"class":70,"line":1009},[68,10359,10360],{"class":1912},"    classifiers",[68,10362,877],{"class":1899},[68,10364,10365],{"class":74},"[\n",[68,10367,10368,10371,10374,10376],{"class":70,"line":1029},[68,10369,10370],{"class":112},"        \"",[68,10372,10373],{"class":116},"Programming Language :: Python :: 3",[68,10375,89],{"class":112},[68,10377,169],{"class":74},[68,10379,10380,10382,10385,10387],{"class":70,"line":1049},[68,10381,10370],{"class":112},[68,10383,10384],{"class":116},"License :: OSI Approved :: MIT License",[68,10386,89],{"class":112},[68,10388,169],{"class":74},[68,10390,10391,10393,10396,10398],{"class":70,"line":1069},[68,10392,10370],{"class":112},[68,10394,10395],{"class":116},"Operating System :: OS Independent",[68,10397,89],{"class":112},[68,10399,169],{"class":74},[68,10401,10402],{"class":70,"line":1088},[68,10403,10404],{"class":74},"    ],\n",[68,10406,10407],{"class":70,"line":1108},[68,10408,410],{"class":74},[28,10410,10411,10413],{},[65,10412,5949],{}," 代码如下：",[58,10415,10417],{"className":1557,"code":10416,"language":1559,"meta":63,"style":63},"image: python:3\n\nstages:\n  - deploy\n\ndeploy:\n  stage: deploy\n  variables:\n    TWINE_USERNAME: $TWINE_USERNAME\n    TWINE_PASSWORD: $TWINE_PASSWORD\n  script:\n    - python setup.py sdist bdist_wheel\n    - pip install twine -i https:\u002F\u002Fpypi.tuna.tsinghua.edu.cn\u002Fsimple\n    - twine upload dist\u002F*\n  only:\n    - tags\n",[65,10418,10419,10429,10433,10439,10445,10449,10455,10463,10470,10480,10490,10496,10503,10510,10517,10523],{"__ignoreMap":63},[68,10420,10421,10424,10426],{"class":70,"line":71},[68,10422,10423],{"class":730},"image",[68,10425,92],{"class":74},[68,10427,10428],{"class":116}," python:3\n",[68,10430,10431],{"class":70,"line":78},[68,10432,416],{"emptyLinePlaceholder":415},[68,10434,10435,10437],{"class":70,"line":98},[68,10436,5959],{"class":730},[68,10438,1569],{"class":74},[68,10440,10441,10443],{"class":70,"line":123},[68,10442,5966],{"class":74},[68,10444,5976],{"class":116},[68,10446,10447],{"class":70,"line":129},[68,10448,416],{"emptyLinePlaceholder":415},[68,10450,10451,10453],{"class":70,"line":212},[68,10452,6034],{"class":730},[68,10454,1569],{"class":74},[68,10456,10457,10459,10461],{"class":70,"line":233},[68,10458,5992],{"class":730},[68,10460,92],{"class":74},[68,10462,5976],{"class":116},[68,10464,10465,10468],{"class":70,"line":268},[68,10466,10467],{"class":730},"  variables",[68,10469,1569],{"class":74},[68,10471,10472,10475,10477],{"class":70,"line":289},[68,10473,10474],{"class":730},"    TWINE_USERNAME",[68,10476,92],{"class":74},[68,10478,10479],{"class":116}," $TWINE_USERNAME\n",[68,10481,10482,10485,10487],{"class":70,"line":308},[68,10483,10484],{"class":730},"    TWINE_PASSWORD",[68,10486,92],{"class":74},[68,10488,10489],{"class":116}," $TWINE_PASSWORD\n",[68,10491,10492,10494],{"class":70,"line":314},[68,10493,6001],{"class":730},[68,10495,1569],{"class":74},[68,10497,10498,10500],{"class":70,"line":320},[68,10499,6008],{"class":74},[68,10501,10502],{"class":116}," python setup.py sdist bdist_wheel\n",[68,10504,10505,10507],{"class":70,"line":889},[68,10506,6008],{"class":74},[68,10508,10509],{"class":116}," pip install twine -i https:\u002F\u002Fpypi.tuna.tsinghua.edu.cn\u002Fsimple\n",[68,10511,10512,10514],{"class":70,"line":909},[68,10513,6008],{"class":74},[68,10515,10516],{"class":116}," twine upload dist\u002F*\n",[68,10518,10519,10521],{"class":70,"line":929},[68,10520,7088],{"class":730},[68,10522,1569],{"class":74},[68,10524,10525,10527],{"class":70,"line":949},[68,10526,6008],{"class":74},[68,10528,10529],{"class":116}," tags\n",[28,10531,10532,10533,10536,10537,5225,10540,10543,10544,10547],{},"其中，需要在 GitLab 项目 ",[65,10534,10535],{},"Setting -> CI\u002FCD -> Variables"," 中配置 ",[65,10538,10539],{},"TWINE_USERNAME",[65,10541,10542],{},"TWINE_PASSWORD","，这个是你在 ",[65,10545,10546],{},"pypi.org"," 上的用户名和密码。",[28,10549,10550,10551,10553,10554,10556,10557,10560,10561,10563],{},"另外需要注意的是，仓库的目录结构，",[65,10552,10072],{}," 中指定的 ",[65,10555,217],{}," 是包名，而 ",[65,10558,10559],{},"__init__.py"," 所在的文件夹名是实际 ",[65,10562,1791],{}," 的导入名。上传包时，PyPI 会检查报名是否重复，但不会检查导入名，所以如果定义了一个容易导致重复的导入名的话，就有可能有冲突。所以一般情况下该文件夹尽量是与包名统一，这样不容易有冲突。",[28,10565,10566],{},"以下是 ChatGPT 给出的目录结构的建议：",[7538,10568,10569,10572,10578,10597,10603,10609,10618,10623,10629],{},[28,10570,10571],{},"当你创建一个 Python 库时，通常需要遵循一些标准的目录结构和文件命名规范。下面是一个常见的建议目录结构，包括了一些常见文件和目录：",[58,10573,10576],{"className":10574,"code":10575,"language":516},[514],"your_library\u002F\n   your_library\u002F\n       __init__.py\n       module1.py\n       module2.py\n       ...\n   examples\u002F\n       example1.py\n       example2.py\n       ...\n   tests\u002F\n       test_module1.py\n       test_module2.py\n       ...\n   README.md\n   LICENSE\n   setup.py\n   requirements.txt\n   .gitignore\n\n",[65,10577,10575],{"__ignoreMap":63},[28,10579,10580,10581,10584,10585,10587,10588,10590,10591,10593,10594,10596],{},"在这个目录结构中，",[65,10582,10583],{},"your_library"," 是你的库的名称，它应该与 ",[65,10586,10072],{}," 文件中定义的名称相同。",[65,10589,10583],{}," 目录包含了你的库的所有源代码和模块文件，以及 ",[65,10592,10559],{}," 文件，用于将 ",[65,10595,10583],{}," 目录转换为 Python 包。",[28,10598,10599,10602],{},[65,10600,10601],{},"examples"," 目录包含了一些示例代码，用于演示如何使用你的库。这些示例代码可以帮助用户更快地了解和使用你的库。",[28,10604,10605,10608],{},[65,10606,10607],{},"tests"," 目录包含了单元测试和集成测试代码，用于确保你的库的功能和质量。在这个目录中，你可以为每个模块编写一个或多个测试文件，并使用测试框架运行这些测试。",[28,10610,10611,10613,10614,10617],{},[65,10612,10119],{}," 文件是你的库的说明文档，用于介绍你的库的功能、用法和示例。",[65,10615,10616],{},"LICENSE"," 文件包含了你的库的许可证信息，用于明确你的库的使用和分发条件。",[28,10619,10620,10622],{},[65,10621,10072],{}," 文件是你的库的安装脚本，用于将你的库安装到 Python 环境中。在这个文件中，你需要指定库的名称、版本、作者、描述、依赖等信息。",[28,10624,10625,10628],{},[65,10626,10627],{},"requirements.txt"," 文件包含了你的库依赖的其他 Python 包的名称和版本号。在使用 pip 安装你的库时，pip 将自动安装这些依赖包。",[28,10630,10631,10634],{},[65,10632,10633],{},".gitignore"," 文件是 Git 版本控制系统的忽略文件，用于指定哪些文件或目录不需要被 Git 跟踪和提交。",[523,10636,10637],{},"html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .slqww, html code.shiki .slqww{--shiki-light:#6182B8;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":63,"searchDepth":78,"depth":78,"links":10639},[],"2023-04-23",{},"\u002Fposts\u002F2023\u002Fgitlab-ci-auto-deploy-python-lib",{"text":8419,"minutes":10644,"time":10645,"words":10646},3.835,230100,767,{"title":10061,"description":10066},{"loc":10642},"posts\u002F2023\u002F20230423.gitlab-ci-auto-deploy-python-lib",[543,7153,10651],"Python","o6Yb6IDvjBzm2kTY_GeQKG92soCUwh_mh3f6D78Xm-s",{"id":10654,"title":10655,"body":10656,"class":528,"cover":1445,"coverSize":528,"date":10640,"description":10660,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":11069,"navigation":415,"path":11070,"readingTime":11071,"seo":11075,"sitemap":11076,"stem":11077,"tags":11078,"time":528,"weather":528,"__hash__":11079},"posts\u002Fposts\u002F2023\u002F20230423.timedrotatingfilehandler-backupcount-problem.md","TimedRotatingFileHandler 不会自动清除旧日志的问题",{"type":25,"value":10657,"toc":11067},[10658,10661,10778,10788,10975,10985,10996,10999,11048,11051,11064],[28,10659,10660],{},"话不多说，直接贴代码：",[58,10662,10664],{"className":1741,"code":10663,"language":1743,"meta":63,"style":63},"file_handler = TimedRotatingFileHandler(\n    \"logs\u002Flog\",\n    when=\"midnight\",\n    backupCount=365,\n    encoding=\"utf-8\",\n)\nfile_handler.suffix = \"%Y%m%d.log\"\nfile_handler.setFormatter(log_formatter)\n",[65,10665,10666,10678,10689,10705,10717,10733,10737,10762],{"__ignoreMap":63},[68,10667,10668,10671,10673,10676],{"class":70,"line":71},[68,10669,10670],{"class":373},"file_handler ",[68,10672,877],{"class":1899},[68,10674,10675],{"class":1903}," TimedRotatingFileHandler",[68,10677,1907],{"class":74},[68,10679,10680,10682,10685,10687],{"class":70,"line":78},[68,10681,101],{"class":112},[68,10683,10684],{"class":116},"logs\u002Flog",[68,10686,89],{"class":112},[68,10688,169],{"class":74},[68,10690,10691,10694,10696,10698,10701,10703],{"class":70,"line":98},[68,10692,10693],{"class":1912},"    when",[68,10695,877],{"class":1899},[68,10697,89],{"class":112},[68,10699,10700],{"class":116},"midnight",[68,10702,89],{"class":112},[68,10704,169],{"class":74},[68,10706,10707,10710,10712,10715],{"class":70,"line":123},[68,10708,10709],{"class":1912},"    backupCount",[68,10711,877],{"class":1899},[68,10713,10714],{"class":2397},"365",[68,10716,169],{"class":74},[68,10718,10719,10722,10724,10726,10729,10731],{"class":70,"line":129},[68,10720,10721],{"class":1912},"    encoding",[68,10723,877],{"class":1899},[68,10725,89],{"class":112},[68,10727,10728],{"class":116},"utf-8",[68,10730,89],{"class":112},[68,10732,169],{"class":74},[68,10734,10735],{"class":70,"line":212},[68,10736,410],{"class":74},[68,10738,10739,10742,10744,10747,10749,10751,10754,10757,10760],{"class":70,"line":233},[68,10740,10741],{"class":373},"file_handler",[68,10743,404],{"class":74},[68,10745,10746],{"class":2122},"suffix",[68,10748,1900],{"class":1899},[68,10750,113],{"class":112},[68,10752,10753],{"class":116},"%Y%m",[68,10755,10756],{"class":2397},"%d",[68,10758,10759],{"class":116},".log",[68,10761,120],{"class":112},[68,10763,10764,10766,10768,10771,10773,10776],{"class":70,"line":268},[68,10765,10741],{"class":373},[68,10767,404],{"class":74},[68,10769,10770],{"class":1903},"setFormatter",[68,10772,648],{"class":74},[68,10774,10775],{"class":1903},"log_formatter",[68,10777,410],{"class":74},[28,10779,10780,10781,10784,10785,10787],{},"这是我几年前写的一段写日志文件的代码，前几天发现并没有按照预期只保留 365 个日志文件。研究了一下，发现了问题所在。",[65,10782,10783],{},"TimedRotatingFileHandler"," 中对于 ",[65,10786,10700],{}," 的操作是这样的：",[58,10789,10791],{"className":1741,"code":10790,"language":1743,"meta":63,"style":63},"elif self.when == 'D' or self.when == 'MIDNIGHT':\n    self.interval = 60 * 60 * 24 # one day\n    self.suffix = \"%Y-%m-%d\"\n    self.extMatch = r\"^\\d{4}-\\d{2}-\\d{2}(\\.\\w+)?$\"\n# ... other code\nself.extMatch = re.compile(self.extMatch, re.ASCII)\n",[65,10792,10793,10833,10859,10878,10934,10939],{"__ignoreMap":63},[68,10794,10795,10798,10800,10802,10805,10807,10809,10812,10814,10816,10818,10820,10822,10824,10826,10829,10831],{"class":70,"line":71},[68,10796,10797],{"class":1790},"elif",[68,10799,2128],{"class":665},[68,10801,404],{"class":74},[68,10803,10804],{"class":2122},"when",[68,10806,2789],{"class":1899},[68,10808,1620],{"class":112},[68,10810,10811],{"class":116},"D",[68,10813,8129],{"class":112},[68,10815,4437],{"class":1899},[68,10817,2128],{"class":665},[68,10819,404],{"class":74},[68,10821,10804],{"class":2122},[68,10823,2789],{"class":1899},[68,10825,1620],{"class":112},[68,10827,10828],{"class":116},"MIDNIGHT",[68,10830,8129],{"class":112},[68,10832,1569],{"class":74},[68,10834,10835,10837,10839,10842,10844,10847,10849,10851,10853,10856],{"class":70,"line":78},[68,10836,8808],{"class":665},[68,10838,404],{"class":74},[68,10840,10841],{"class":2122},"interval",[68,10843,1900],{"class":1899},[68,10845,10846],{"class":2397}," 60",[68,10848,8798],{"class":1899},[68,10850,10846],{"class":2397},[68,10852,8798],{"class":1899},[68,10854,10855],{"class":2397}," 24",[68,10857,10858],{"class":2403}," # one day\n",[68,10860,10861,10863,10865,10867,10869,10871,10874,10876],{"class":70,"line":98},[68,10862,8808],{"class":665},[68,10864,404],{"class":74},[68,10866,10746],{"class":2122},[68,10868,1900],{"class":1899},[68,10870,113],{"class":112},[68,10872,10873],{"class":116},"%Y-%m-",[68,10875,10756],{"class":2397},[68,10877,120],{"class":112},[68,10879,10880,10882,10884,10887,10889,10892,10894,10897,10900,10902,10905,10908,10910,10912,10914,10916,10920,10923,10925,10927,10930,10932],{"class":70,"line":123},[68,10881,8808],{"class":665},[68,10883,404],{"class":74},[68,10885,10886],{"class":2122},"extMatch",[68,10888,1900],{"class":1899},[68,10890,10891],{"class":1864}," r",[68,10893,89],{"class":112},[68,10895,10896],{"class":1518},"^\\d",[68,10898,10899],{"class":1899},"{4}",[68,10901,2652],{"class":3784},[68,10903,10904],{"class":1518},"\\d",[68,10906,10907],{"class":1899},"{2}",[68,10909,2652],{"class":3784},[68,10911,10904],{"class":1518},[68,10913,10907],{"class":1899},[68,10915,648],{"class":81},[68,10917,10919],{"class":10918},"sjYin","\\.",[68,10921,10922],{"class":1518},"\\w",[68,10924,1670],{"class":1899},[68,10926,2753],{"class":81},[68,10928,10929],{"class":1899},"?",[68,10931,5147],{"class":1518},[68,10933,120],{"class":112},[68,10935,10936],{"class":70,"line":129},[68,10937,10938],{"class":2403},"# ... other code\n",[68,10940,10941,10943,10945,10947,10949,10951,10953,10956,10958,10960,10962,10964,10966,10968,10970,10973],{"class":70,"line":212},[68,10942,2109],{"class":665},[68,10944,404],{"class":74},[68,10946,10886],{"class":2122},[68,10948,1900],{"class":1899},[68,10950,3766],{"class":373},[68,10952,404],{"class":74},[68,10954,10955],{"class":1903},"compile",[68,10957,648],{"class":74},[68,10959,2109],{"class":665},[68,10961,404],{"class":74},[68,10963,10886],{"class":2122},[68,10965,255],{"class":74},[68,10967,3766],{"class":1903},[68,10969,404],{"class":74},[68,10971,10972],{"class":2228},"ASCII",[68,10974,410],{"class":74},[28,10976,10977,10978,10981,10982,10984],{},"可以看到，其默认的 suffix 是 ",[65,10979,10980],{},"%Y-%m-%d","，我之所以要加 ",[65,10983,10759],{}," 后缀，是因为如果不带后缀的话，群晖的文本编辑器默认不能直接打开这个文件，不太方便看日志。",[28,10986,10987,10988,10991,10992,10995],{},"它默认的匹配正则是 ",[65,10989,10990],{},"r\"^\\d{4}-\\d{2}-\\d{2}(\\.\\w+)?$\"","，当我把 suffix 改成 ",[65,10993,10994],{},"%Y%m%d.log"," 之后，它就无法匹配到旧日志的数量了，所以保留日志个数的功能会失效。有两种办法解决：",[28,10997,10998],{},"一是加一行：",[58,11000,11002],{"className":1741,"code":11001,"language":1743,"meta":63,"style":63},"self.extMatch = re.compile(r\"^\\d{4}\\d{2}\\d{2}\\.log$\")\n",[65,11003,11004],{"__ignoreMap":63},[68,11005,11006,11008,11010,11012,11014,11016,11018,11020,11022,11024,11026,11028,11030,11032,11034,11036,11038,11040,11042,11044,11046],{"class":70,"line":71},[68,11007,2109],{"class":665},[68,11009,404],{"class":74},[68,11011,10886],{"class":2122},[68,11013,1900],{"class":1899},[68,11015,3766],{"class":373},[68,11017,404],{"class":74},[68,11019,10955],{"class":1903},[68,11021,648],{"class":74},[68,11023,3776],{"class":1864},[68,11025,89],{"class":112},[68,11027,10896],{"class":1518},[68,11029,10899],{"class":1899},[68,11031,10904],{"class":1518},[68,11033,10907],{"class":1899},[68,11035,10904],{"class":1518},[68,11037,10907],{"class":1899},[68,11039,10919],{"class":10918},[68,11041,363],{"class":3784},[68,11043,5147],{"class":1518},[68,11045,89],{"class":112},[68,11047,410],{"class":74},[28,11049,11050],{},"另一种更简单：",[28,11052,11053,11054,11056,11057,11060,11061,11063],{},"把 ",[65,11055,10746],{}," 改成 ",[65,11058,11059],{},"%Y-%m-%d.log"," 即可，因为默认的 ",[65,11062,10886],{}," 是可以匹配到后缀名的。",[523,11065,11066],{},"html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .slqww, html code.shiki .slqww{--shiki-light:#6182B8;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sQRbd, html code.shiki .sQRbd{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#DBEDFF}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjYin, html code.shiki .sjYin{--shiki-light:#90A4AE;--shiki-light-font-weight:inherit;--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold}html pre.shiki code .swQdS, html code.shiki .swQdS{--shiki-light:#E53935;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":63,"searchDepth":78,"depth":78,"links":11068},[],{},"\u002Fposts\u002F2023\u002Ftimedrotatingfilehandler-backupcount-problem",{"text":1217,"minutes":11072,"time":11073,"words":11074},1.28,76800,256,{"title":10655,"description":10660},{"loc":11070},"posts\u002F2023\u002F20230423.timedrotatingfilehandler-backupcount-problem",[543,10651],"4wede-nsE8KJljBACI9w26cVkEu6w_4hgpuuTibyohw",{"id":11081,"title":11082,"body":11083,"class":528,"cover":1445,"coverSize":528,"date":11271,"description":11087,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":11272,"navigation":415,"path":11273,"readingTime":11274,"seo":11278,"sitemap":11279,"stem":11280,"tags":11281,"time":528,"weather":528,"__hash__":11283},"posts\u002Fposts\u002F2023\u002F20230408.upgrade-ghost-5-0-to-5-42.md","Ghost 5.0 升级到 5.42",{"type":25,"value":11084,"toc":11269},[11085,11088,11224,11232,11235,11260,11263,11266],[28,11086,11087],{},"距离上次升级 Ghost 已经过去快 1 年了，上次是 Ghost 5.0 刚发布的时候升级的，这次直接把容器镜像版本改为最新的 5.42 时，报了个错：",[58,11089,11091],{"className":361,"code":11090,"language":363,"meta":63,"style":63},"ERROR connect ECONNREFUSED 127.0.0.1:3306\nconnect ECONNREFUSED 127.0.0.1:3306\n\n\"Unknown database error\"\n\nError ID:\n500\n\nError Code:\nECONNREFUSED\n\nError: connect ECONNREFUSED 127.0.0.1:3306\nat \u002Fvar\u002Flib\u002Fghost\u002Fversions\u002F5.42.0\u002Fnode_modules\u002Fknex-migrator\u002Flib\u002Fdatabase.js:57:19\nat TCPConnectWrap.afterConnect [as oncomplete] (node:net:1278:16)\n",[65,11092,11093,11121,11144,11148,11153,11157,11164,11169,11173,11180,11185,11189,11214,11219],{"__ignoreMap":63},[68,11094,11095,11098,11101,11104,11106,11108,11110,11112,11114,11116,11118],{"class":70,"line":71},[68,11096,11097],{"class":116},"ERROR",[68,11099,11100],{"class":373}," connect ECONNREFUSED ",[68,11102,11103],{"class":81},"127",[68,11105,404],{"class":373},[68,11107,768],{"class":81},[68,11109,404],{"class":373},[68,11111,768],{"class":81},[68,11113,404],{"class":373},[68,11115,401],{"class":81},[68,11117,92],{"class":373},[68,11119,11120],{"class":81},"3306\n",[68,11122,11123,11126,11128,11130,11132,11134,11136,11138,11140,11142],{"class":70,"line":78},[68,11124,11125],{"class":373},"connect ECONNREFUSED ",[68,11127,11103],{"class":81},[68,11129,404],{"class":373},[68,11131,768],{"class":81},[68,11133,404],{"class":373},[68,11135,768],{"class":81},[68,11137,404],{"class":373},[68,11139,401],{"class":81},[68,11141,92],{"class":373},[68,11143,11120],{"class":81},[68,11145,11146],{"class":70,"line":98},[68,11147,416],{"emptyLinePlaceholder":415},[68,11149,11150],{"class":70,"line":123},[68,11151,11152],{"class":116},"\"Unknown database error\"\n",[68,11154,11155],{"class":70,"line":129},[68,11156,416],{"emptyLinePlaceholder":415},[68,11158,11159,11161],{"class":70,"line":212},[68,11160,370],{"class":116},[68,11162,11163],{"class":373}," ID:\n",[68,11165,11166],{"class":70,"line":233},[68,11167,11168],{"class":81},"500\n",[68,11170,11171],{"class":70,"line":268},[68,11172,416],{"emptyLinePlaceholder":415},[68,11174,11175,11177],{"class":70,"line":289},[68,11176,370],{"class":116},[68,11178,11179],{"class":373}," Code:\n",[68,11181,11182],{"class":70,"line":308},[68,11183,11184],{"class":373},"ECONNREFUSED\n",[68,11186,11187],{"class":70,"line":314},[68,11188,416],{"emptyLinePlaceholder":415},[68,11190,11191,11193,11196,11198,11200,11202,11204,11206,11208,11210,11212],{"class":70,"line":320},[68,11192,370],{"class":116},[68,11194,11195],{"class":373},": connect ECONNREFUSED ",[68,11197,11103],{"class":81},[68,11199,404],{"class":373},[68,11201,768],{"class":81},[68,11203,404],{"class":373},[68,11205,768],{"class":81},[68,11207,404],{"class":373},[68,11209,401],{"class":81},[68,11211,92],{"class":373},[68,11213,11120],{"class":81},[68,11215,11216],{"class":70,"line":889},[68,11217,11218],{"class":116},"at \u002Fvar\u002Flib\u002Fghost\u002Fversions\u002F5.42.0\u002Fnode_modules\u002Fknex-migrator\u002Flib\u002Fdatabase.js:57:19\n",[68,11220,11221],{"class":70,"line":909},[68,11222,11223],{"class":116},"at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1278:16)\n",[28,11225,11226,11227],{},"重试了几次都不行，在 Ghost 的文档中找了半天也没发现从 5.0 升级到 5.42 中间有什么 breaking changes。最后从 Ghost 的 Docker 镜像维护仓库找到了相关的 Issue：",[38,11228,11231],{"href":11229,"rel":11230},"https:\u002F\u002Fgithub.com\u002Fdocker-library\u002Fghost\u002Fpull\u002F323",[42],"#323",[28,11233,11234],{},"大致原因主要是这样，Ghost 5.0 之后其实是有个 breaking change，就是原先数据库是支持 SQLite3 和 MySQL 5 的，在 5.0 之后，数据库只支持 MySQL 8 了，但是 SQLite3 在开发环境还是支持的。对应的 Docker 镜像，在 5.9 之前，都还是可以继续用之前的 SQLite3 的，但是在 5.9 这个版本中，Docker 镜像将默认数据库改为了 MySQL 8，这就导致从低版本升到高于 5.9 版本的镜像之后，数据库会直接找不到。在这个 PR 中，其实给出了一个临时的解决方案，就是在环境变量中加两个变量，就可以继续使用 SQLite3，这两个变量是：",[58,11236,11238],{"className":1557,"code":11237,"language":1559,"meta":63,"style":63},"database__client: sqlite3\ndatabase__connection__filename: \u002Fvar\u002Flib\u002Fghost\u002Fcontent\u002Fdata\u002Fghost.db\n",[65,11239,11240,11250],{"__ignoreMap":63},[68,11241,11242,11245,11247],{"class":70,"line":71},[68,11243,11244],{"class":730},"database__client",[68,11246,92],{"class":74},[68,11248,11249],{"class":116}," sqlite3\n",[68,11251,11252,11255,11257],{"class":70,"line":78},[68,11253,11254],{"class":730},"database__connection__filename",[68,11256,92],{"class":74},[68,11258,11259],{"class":116}," \u002Fvar\u002Flib\u002Fghost\u002Fcontent\u002Fdata\u002Fghost.db\n",[28,11261,11262],{},"添加完之后，容器可以正常升级了。",[28,11264,11265],{},"但，既然官方已经将数据库支持重点改为了 MySQL 8，不怕麻烦的话，也可以升级下数据库。原本准备写个 MySQL 8 的升级教程的，想想不折腾了，我的博客用轻量的 SQLite3 就够了。",[523,11267,11268],{},"html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":63,"searchDepth":78,"depth":78,"links":11270},[],"2023-04-08",{},"\u002Fposts\u002F2023\u002Fupgrade-ghost-5-0-to-5-42",{"text":1217,"minutes":11275,"time":11276,"words":11277},1.995,119700,399,{"title":11082,"description":11087},{"loc":11273},"posts\u002F2023\u002F20230408.upgrade-ghost-5-0-to-5-42",[543,10057,11282],"Ghost","jTG5prR9EJe76ga8Usd6FU3xLZllrEAUvtzpDIDc-io",{"id":11285,"title":11286,"body":11287,"class":528,"cover":1445,"coverSize":528,"date":13213,"description":63,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":13214,"navigation":415,"path":13215,"readingTime":13216,"seo":13220,"sitemap":13221,"stem":13222,"tags":13223,"time":528,"weather":528,"__hash__":13224},"posts\u002Fposts\u002F2022\u002F20220524.use-gitlab-to-deploy-ghost-theme-automatically.md","使用 GitLab CI 自动部署 Ghost 主题",{"type":25,"value":11288,"toc":13211},[11289,11294,11297,11306,12010,12020,12029,12031,12041,12043,12046,12065,12071,12975,12980,13075,13081,13192,13205,13208],[7538,11290,11291],{},[28,11292,11293],{},"居家隔离的第 N 天",[28,11295,11296],{},"今天收到了 Ghost 5.0 发布的邮件，第一时间更新了下，发现主题里有些功能已经不兼容了，于是准备对主题做下更新。在看 Ghost Integrations 的时候发现有个 GitHub 的插件特别好用，支持通过 GitHub Actions 自动部署你的主题。但是我自己的项目用的都是 GitLab，找了一圈，没有官方的插件。于是尝试自己通过 GitLab CI 来实现。",[28,11298,11299,11300,11305],{},"大致看了下基于 GitHub Actions 自动部署的实现方式，通过官方提供的一个 ",[38,11301,11304],{"href":11302,"rel":11303},"https:\u002F\u002Fgithub.com\u002FTryGhost\u002Faction-deploy-theme\u002Fblob\u002Fmain\u002Findex.js",[42],"TryGhost\u002Faction-deploy-theme"," 的步骤，代码很简单，总共 40 行，我们来看下它做了什么：",[58,11307,11311],{"className":11308,"code":11309,"language":11310,"meta":63,"style":63},"language-javascript shiki shiki-themes material-theme-lighter github-light github-dark","const path = require('node:path')\nconst core = require('@actions\u002Fcore')\nconst exec = require('@actions\u002Fexec')\nconst GhostAdminApi = require('@tryghost\u002Fadmin-api');\n\n(async function main() {\n  try {\n    const url = core.getInput('api-url')\n    const api = new GhostAdminApi({\n      url,\n      key: core.getInput('api-key'),\n      version: 'canary'\n    })\n\n    const basePath = process.env.GITHUB_WORKSPACE\n    const pkgPath = path.join(process.env.GITHUB_WORKSPACE, 'package.json')\n\n    let zipPath = core.getInput('file')\n\n    \u002F\u002F Zip file was not provided - zip everything up!\n    if (!zipPath) {\n      const themeName = core.getInput('theme-name') || require(pkgPath).name\n      const themeZip = `${themeName}.zip`\n      const exclude = core.getInput('exclude') || ''\n      zipPath = themeZip\n\n      \u002F\u002F Create a zip\n      await exec.exec(`zip -r ${themeZip} ${core.getInput('working-directory') || '.'} -x *.git* *.zip yarn* npm* node_modules* *routes.yaml *redirects.yaml *redirects.json ${exclude}`, [], { cwd: basePath })\n    }\n\n    zipPath = path.join(basePath, zipPath)\n\n    \u002F\u002F Deploy it to the configured site\n    await api.themes.upload({ file: zipPath })\n    console.log(`${zipPath} successfully uploaded.`)\n  }\n  catch (err) {\n    console.error(err)\n    process.exit(1)\n  }\n}())\n","javascript",[65,11312,11313,11337,11359,11381,11405,11409,11425,11432,11459,11476,11483,11509,11523,11529,11533,11555,11596,11600,11625,11629,11634,11650,11693,11716,11747,11757,11761,11766,11854,11858,11862,11886,11890,11895,11926,11951,11955,11969,11983,11999,12003],{"__ignoreMap":63},[68,11314,11315,11318,11321,11323,11326,11328,11330,11333,11335],{"class":70,"line":71},[68,11316,11317],{"class":1864},"const",[68,11319,11320],{"class":665}," path",[68,11322,1900],{"class":1899},[68,11324,11325],{"class":2183}," require",[68,11327,648],{"class":373},[68,11329,8129],{"class":112},[68,11331,11332],{"class":116},"node:path",[68,11334,8129],{"class":112},[68,11336,410],{"class":373},[68,11338,11339,11341,11344,11346,11348,11350,11352,11355,11357],{"class":70,"line":78},[68,11340,11317],{"class":1864},[68,11342,11343],{"class":665}," core",[68,11345,1900],{"class":1899},[68,11347,11325],{"class":2183},[68,11349,648],{"class":373},[68,11351,8129],{"class":112},[68,11353,11354],{"class":116},"@actions\u002Fcore",[68,11356,8129],{"class":112},[68,11358,410],{"class":373},[68,11360,11361,11363,11366,11368,11370,11372,11374,11377,11379],{"class":70,"line":98},[68,11362,11317],{"class":1864},[68,11364,11365],{"class":665}," exec",[68,11367,1900],{"class":1899},[68,11369,11325],{"class":2183},[68,11371,648],{"class":373},[68,11373,8129],{"class":112},[68,11375,11376],{"class":116},"@actions\u002Fexec",[68,11378,8129],{"class":112},[68,11380,410],{"class":373},[68,11382,11383,11385,11388,11390,11392,11394,11396,11399,11401,11403],{"class":70,"line":123},[68,11384,11317],{"class":1864},[68,11386,11387],{"class":665}," GhostAdminApi",[68,11389,1900],{"class":1899},[68,11391,11325],{"class":2183},[68,11393,648],{"class":373},[68,11395,8129],{"class":112},[68,11397,11398],{"class":116},"@tryghost\u002Fadmin-api",[68,11400,8129],{"class":112},[68,11402,2753],{"class":373},[68,11404,5136],{"class":74},[68,11406,11407],{"class":70,"line":129},[68,11408,416],{"emptyLinePlaceholder":415},[68,11410,11411,11413,11416,11418,11421,11423],{"class":70,"line":212},[68,11412,648],{"class":373},[68,11414,11415],{"class":1864},"async",[68,11417,7806],{"class":1864},[68,11419,11420],{"class":2183}," main",[68,11422,3345],{"class":74},[68,11424,95],{"class":74},[68,11426,11427,11430],{"class":70,"line":233},[68,11428,11429],{"class":1790},"  try",[68,11431,95],{"class":74},[68,11433,11434,11436,11439,11441,11443,11445,11448,11450,11452,11455,11457],{"class":70,"line":268},[68,11435,7772],{"class":1864},[68,11437,11438],{"class":665}," url",[68,11440,1900],{"class":1899},[68,11442,11343],{"class":373},[68,11444,404],{"class":74},[68,11446,11447],{"class":2183},"getInput",[68,11449,648],{"class":2122},[68,11451,8129],{"class":112},[68,11453,11454],{"class":116},"api-url",[68,11456,8129],{"class":112},[68,11458,410],{"class":2122},[68,11460,11461,11463,11466,11468,11470,11472,11474],{"class":70,"line":289},[68,11462,7772],{"class":1864},[68,11464,11465],{"class":665}," api",[68,11467,1900],{"class":1899},[68,11469,7746],{"class":1899},[68,11471,11387],{"class":2183},[68,11473,648],{"class":2122},[68,11475,75],{"class":74},[68,11477,11478,11481],{"class":70,"line":308},[68,11479,11480],{"class":373},"      url",[68,11482,169],{"class":74},[68,11484,11485,11488,11490,11492,11494,11496,11498,11500,11503,11505,11507],{"class":70,"line":314},[68,11486,11487],{"class":2122},"      key",[68,11489,92],{"class":74},[68,11491,11343],{"class":373},[68,11493,404],{"class":74},[68,11495,11447],{"class":2183},[68,11497,648],{"class":2122},[68,11499,8129],{"class":112},[68,11501,11502],{"class":116},"api-key",[68,11504,8129],{"class":112},[68,11506,2753],{"class":2122},[68,11508,169],{"class":74},[68,11510,11511,11514,11516,11518,11521],{"class":70,"line":320},[68,11512,11513],{"class":2122},"      version",[68,11515,92],{"class":74},[68,11517,1620],{"class":112},[68,11519,11520],{"class":116},"canary",[68,11522,1626],{"class":112},[68,11524,11525,11527],{"class":70,"line":889},[68,11526,8370],{"class":74},[68,11528,410],{"class":2122},[68,11530,11531],{"class":70,"line":909},[68,11532,416],{"emptyLinePlaceholder":415},[68,11534,11535,11537,11540,11542,11545,11547,11550,11552],{"class":70,"line":929},[68,11536,7772],{"class":1864},[68,11538,11539],{"class":665}," basePath",[68,11541,1900],{"class":1899},[68,11543,11544],{"class":373}," process",[68,11546,404],{"class":74},[68,11548,11549],{"class":373},"env",[68,11551,404],{"class":74},[68,11553,11554],{"class":665},"GITHUB_WORKSPACE\n",[68,11556,11557,11559,11562,11564,11566,11568,11571,11573,11576,11578,11580,11582,11585,11587,11589,11592,11594],{"class":70,"line":949},[68,11558,7772],{"class":1864},[68,11560,11561],{"class":665}," pkgPath",[68,11563,1900],{"class":1899},[68,11565,11320],{"class":373},[68,11567,404],{"class":74},[68,11569,11570],{"class":2183},"join",[68,11572,648],{"class":2122},[68,11574,11575],{"class":373},"process",[68,11577,404],{"class":74},[68,11579,11549],{"class":373},[68,11581,404],{"class":74},[68,11583,11584],{"class":665},"GITHUB_WORKSPACE",[68,11586,255],{"class":74},[68,11588,1620],{"class":112},[68,11590,11591],{"class":116},"package.json",[68,11593,8129],{"class":112},[68,11595,410],{"class":2122},[68,11597,11598],{"class":70,"line":969},[68,11599,416],{"emptyLinePlaceholder":415},[68,11601,11602,11604,11607,11609,11611,11613,11615,11617,11619,11621,11623],{"class":70,"line":989},[68,11603,8189],{"class":1864},[68,11605,11606],{"class":373}," zipPath",[68,11608,1900],{"class":1899},[68,11610,11343],{"class":373},[68,11612,404],{"class":74},[68,11614,11447],{"class":2183},[68,11616,648],{"class":2122},[68,11618,8129],{"class":112},[68,11620,7717],{"class":116},[68,11622,8129],{"class":112},[68,11624,410],{"class":2122},[68,11626,11627],{"class":70,"line":1009},[68,11628,416],{"emptyLinePlaceholder":415},[68,11630,11631],{"class":70,"line":1029},[68,11632,11633],{"class":2403},"    \u002F\u002F Zip file was not provided - zip everything up!\n",[68,11635,11636,11638,11640,11643,11646,11648],{"class":70,"line":1049},[68,11637,9649],{"class":1790},[68,11639,7664],{"class":2122},[68,11641,11642],{"class":1899},"!",[68,11644,11645],{"class":373},"zipPath",[68,11647,588],{"class":2122},[68,11649,75],{"class":74},[68,11651,11652,11654,11657,11659,11661,11663,11665,11667,11669,11672,11674,11676,11679,11681,11683,11686,11688,11690],{"class":70,"line":1069},[68,11653,8284],{"class":1864},[68,11655,11656],{"class":665}," themeName",[68,11658,1900],{"class":1899},[68,11660,11343],{"class":373},[68,11662,404],{"class":74},[68,11664,11447],{"class":2183},[68,11666,648],{"class":2122},[68,11668,8129],{"class":112},[68,11670,11671],{"class":116},"theme-name",[68,11673,8129],{"class":112},[68,11675,588],{"class":2122},[68,11677,11678],{"class":1899},"||",[68,11680,11325],{"class":2183},[68,11682,648],{"class":2122},[68,11684,11685],{"class":373},"pkgPath",[68,11687,2753],{"class":2122},[68,11689,404],{"class":74},[68,11691,11692],{"class":373},"name\n",[68,11694,11695,11697,11700,11702,11705,11708,11710,11713],{"class":70,"line":1088},[68,11696,8284],{"class":1864},[68,11698,11699],{"class":665}," themeZip",[68,11701,1900],{"class":1899},[68,11703,11704],{"class":112}," `${",[68,11706,11707],{"class":373},"themeName",[68,11709,2400],{"class":112},[68,11711,11712],{"class":116},".zip",[68,11714,11715],{"class":112},"`\n",[68,11717,11718,11720,11723,11725,11727,11729,11731,11733,11735,11738,11740,11742,11744],{"class":70,"line":1108},[68,11719,8284],{"class":1864},[68,11721,11722],{"class":665}," exclude",[68,11724,1900],{"class":1899},[68,11726,11343],{"class":373},[68,11728,404],{"class":74},[68,11730,11447],{"class":2183},[68,11732,648],{"class":2122},[68,11734,8129],{"class":112},[68,11736,11737],{"class":116},"exclude",[68,11739,8129],{"class":112},[68,11741,588],{"class":2122},[68,11743,11678],{"class":1899},[68,11745,11746],{"class":112}," ''\n",[68,11748,11749,11752,11754],{"class":70,"line":1128},[68,11750,11751],{"class":373},"      zipPath",[68,11753,1900],{"class":1899},[68,11755,11756],{"class":373}," themeZip\n",[68,11758,11759],{"class":70,"line":1148},[68,11760,416],{"emptyLinePlaceholder":415},[68,11762,11763],{"class":70,"line":1168},[68,11764,11765],{"class":2403},"      \u002F\u002F Create a zip\n",[68,11767,11768,11771,11773,11775,11778,11780,11783,11786,11789,11792,11794,11796,11799,11801,11803,11806,11808,11811,11813,11815,11817,11819,11821,11824,11827,11829,11831,11834,11836,11839,11841,11843,11846,11848,11850,11852],{"class":70,"line":1187},[68,11769,11770],{"class":1790},"      await",[68,11772,11365],{"class":373},[68,11774,404],{"class":74},[68,11776,11777],{"class":2183},"exec",[68,11779,648],{"class":2122},[68,11781,11782],{"class":112},"`",[68,11784,11785],{"class":116},"zip -r ",[68,11787,11788],{"class":112},"${",[68,11790,11791],{"class":373},"themeZip",[68,11793,2400],{"class":112},[68,11795,7342],{"class":112},[68,11797,11798],{"class":373},"core",[68,11800,404],{"class":112},[68,11802,11447],{"class":2183},[68,11804,648],{"class":11805},"sfo-9",[68,11807,8129],{"class":112},[68,11809,11810],{"class":116},"working-directory",[68,11812,8129],{"class":112},[68,11814,588],{"class":11805},[68,11816,11678],{"class":1899},[68,11818,1620],{"class":112},[68,11820,404],{"class":116},[68,11822,11823],{"class":112},"'}",[68,11825,11826],{"class":116}," -x *.git* *.zip yarn* npm* node_modules* *routes.yaml *redirects.yaml *redirects.json ",[68,11828,11788],{"class":112},[68,11830,11737],{"class":373},[68,11832,11833],{"class":112},"}`",[68,11835,255],{"class":74},[68,11837,11838],{"class":2122}," []",[68,11840,255],{"class":74},[68,11842,2382],{"class":74},[68,11844,11845],{"class":2122}," cwd",[68,11847,92],{"class":74},[68,11849,11539],{"class":373},[68,11851,8233],{"class":74},[68,11853,410],{"class":2122},[68,11855,11856],{"class":70,"line":2039},[68,11857,311],{"class":74},[68,11859,11860],{"class":70,"line":2055},[68,11861,416],{"emptyLinePlaceholder":415},[68,11863,11864,11867,11869,11871,11873,11875,11877,11880,11882,11884],{"class":70,"line":2071},[68,11865,11866],{"class":373},"    zipPath",[68,11868,1900],{"class":1899},[68,11870,11320],{"class":373},[68,11872,404],{"class":74},[68,11874,11570],{"class":2183},[68,11876,648],{"class":2122},[68,11878,11879],{"class":373},"basePath",[68,11881,255],{"class":74},[68,11883,11606],{"class":373},[68,11885,410],{"class":2122},[68,11887,11888],{"class":70,"line":2087},[68,11889,416],{"emptyLinePlaceholder":415},[68,11891,11892],{"class":70,"line":2092},[68,11893,11894],{"class":2403},"    \u002F\u002F Deploy it to the configured site\n",[68,11896,11897,11900,11902,11904,11907,11909,11912,11914,11916,11918,11920,11922,11924],{"class":70,"line":2097},[68,11898,11899],{"class":1790},"    await",[68,11901,11465],{"class":373},[68,11903,404],{"class":74},[68,11905,11906],{"class":373},"themes",[68,11908,404],{"class":74},[68,11910,11911],{"class":2183},"upload",[68,11913,648],{"class":2122},[68,11915,2546],{"class":74},[68,11917,8171],{"class":2122},[68,11919,92],{"class":74},[68,11921,11606],{"class":373},[68,11923,8233],{"class":74},[68,11925,410],{"class":2122},[68,11927,11928,11931,11933,11935,11937,11940,11942,11944,11947,11949],{"class":70,"line":2114},[68,11929,11930],{"class":373},"    console",[68,11932,404],{"class":74},[68,11934,363],{"class":2183},[68,11936,648],{"class":2122},[68,11938,11939],{"class":112},"`${",[68,11941,11645],{"class":373},[68,11943,2400],{"class":112},[68,11945,11946],{"class":116}," successfully uploaded.",[68,11948,11782],{"class":112},[68,11950,410],{"class":2122},[68,11952,11953],{"class":70,"line":2139},[68,11954,126],{"class":74},[68,11956,11957,11960,11962,11965,11967],{"class":70,"line":2158},[68,11958,11959],{"class":1790},"  catch",[68,11961,7664],{"class":2122},[68,11963,11964],{"class":373},"err",[68,11966,588],{"class":2122},[68,11968,75],{"class":74},[68,11970,11971,11973,11975,11977,11979,11981],{"class":70,"line":2173},[68,11972,11930],{"class":373},[68,11974,404],{"class":74},[68,11976,2473],{"class":2183},[68,11978,648],{"class":2122},[68,11980,11964],{"class":373},[68,11982,410],{"class":2122},[68,11984,11985,11988,11990,11993,11995,11997],{"class":70,"line":2178},[68,11986,11987],{"class":373},"    process",[68,11989,404],{"class":74},[68,11991,11992],{"class":2183},"exit",[68,11994,648],{"class":2122},[68,11996,401],{"class":2397},[68,11998,410],{"class":2122},[68,12000,12001],{"class":70,"line":2193},[68,12002,126],{"class":74},[68,12004,12005,12007],{"class":70,"line":2201},[68,12006,2400],{"class":74},[68,12008,12009],{"class":373},"())\n",[28,12011,12012,12013,5225,12016,12019],{},"把主题打包成 zip 包，然后提供 Ghost 上创建的 ",[65,12014,12015],{},"Admin API Key",[65,12017,12018],{},"API URL","，通过 API 去上传，那么我们应该也可以自己去实现。",[28,12021,12022,12023,5225,12026,12028],{},"首先，我们也需要去 Ghost 后台创建一个自定义的 Integration，比如取名叫 GitLab CI，目的是为了获得 ",[65,12024,12025],{},"Admin API key",[65,12027,12018],{},"，后面在 GitLab CI 中需要用到。",[554,12030],{"filename":556},[28,12032,12033,12034,5225,12037,12040],{},"下一步，去 GitLab CI 中，把这两个内容配置成变量，取名 ",[65,12035,12036],{},"GHOST_ADMIN_API_KEY",[65,12038,12039],{},"GHOST_API_URL"," 以便在 CI 脚本中使用。",[554,12042],{"filename":1203},[28,12044,12045],{},"在项目中添加 Ghost Admin API 库：",[58,12047,12049],{"className":1502,"code":12048,"language":1504,"meta":63,"style":63},"yarn add @tryghost\u002Fadmin-api --dev\n",[65,12050,12051],{"__ignoreMap":63},[68,12052,12053,12056,12059,12062],{"class":70,"line":71},[68,12054,12055],{"class":1511},"yarn",[68,12057,12058],{"class":116}," add",[68,12060,12061],{"class":116}," @tryghost\u002Fadmin-api",[68,12063,12064],{"class":1518}," --dev\n",[28,12066,1659,12067,12070],{},[65,12068,12069],{},"gulpfile.js"," 中插入部署的任务：",[58,12072,12074],{"className":11308,"code":12073,"language":11310,"meta":63,"style":63},"const GhostAdminApi = require('@tryghost\u002Fadmin-api')\nconst { series, src, dest } = require('gulp')\nconst less = require('gulp-less')\nconst zip = require('gulp-zip')\nconst pump = require('pump')\n\nconst handleError = (done) => {\n  return function (err) {\n    if (err) {\n      console.error(err)\n    }\n    return done(err)\n  }\n}\n\nfunction css(done) {\n  pump(\n    [\n      src('.\u002Fassets\u002Fcss\u002F*.less', { sourcemaps: true }),\n      less({}),\n      dest('assets\u002Fcss', { sourcemaps: '.\u002F' }),\n    ],\n    handleError(done)\n  )\n}\n\nfunction zipper(done) {\n  const targetDir = 'dist\u002F'\n  const themeName = require('.\u002Fpackage.json').name\n  const filename = `${themeName}.zip`\n\n  pump(\n    [\n      src(['**', '!node_modules', '!node_modules\u002F**', '!dist', '!dist\u002F**']),\n      zip(filename),\n      dest(targetDir),\n    ],\n    handleError(done)\n  )\n}\n\nasync function deploy(done) {\n  try {\n    const zipFile = `dist\u002F${require('.\u002Fpackage.json').name}.zip`\n    const api = new GhostAdminApi({\n      url: process.env.GHOST_API_URL,\n      key: process.env.GHOST_ADMIN_API_KEY,\n      version: `v${require('.\u002Fpackage.json').version}`,\n    })\n\n    await api.themes.upload({ file: zipFile })\n    console.log(`${zipFile} successfully uploaded.`)\n    done()\n  }\n  catch (err) {\n    console.error(err)\n    done(err)\n  }\n}\n\nconst build = series(css)\n\nexports.build = build\nexports.zip = series(build, zipper)\nexports.deploy = deploy\nexports.default = build\n",[65,12075,12076,12096,12132,12154,12176,12198,12202,12222,12236,12248,12262,12266,12278,12282,12286,12290,12305,12312,12317,12349,12363,12398,12405,12416,12421,12425,12429,12444,12461,12486,12505,12509,12515,12519,12573,12587,12600,12606,12616,12620,12624,12628,12645,12651,12690,12706,12724,12742,12775,12781,12785,12813,12836,12843,12847,12859,12873,12883,12887,12891,12895,12909,12913,12928,12949,12962],{"__ignoreMap":63},[68,12077,12078,12080,12082,12084,12086,12088,12090,12092,12094],{"class":70,"line":71},[68,12079,11317],{"class":1864},[68,12081,11387],{"class":665},[68,12083,1900],{"class":1899},[68,12085,11325],{"class":2183},[68,12087,648],{"class":373},[68,12089,8129],{"class":112},[68,12091,11398],{"class":116},[68,12093,8129],{"class":112},[68,12095,410],{"class":373},[68,12097,12098,12100,12102,12105,12107,12110,12112,12115,12117,12119,12121,12123,12125,12128,12130],{"class":70,"line":78},[68,12099,11317],{"class":1864},[68,12101,2382],{"class":74},[68,12103,12104],{"class":665}," series",[68,12106,255],{"class":74},[68,12108,12109],{"class":665}," src",[68,12111,255],{"class":74},[68,12113,12114],{"class":665}," dest",[68,12116,8233],{"class":74},[68,12118,1900],{"class":1899},[68,12120,11325],{"class":2183},[68,12122,648],{"class":373},[68,12124,8129],{"class":112},[68,12126,12127],{"class":116},"gulp",[68,12129,8129],{"class":112},[68,12131,410],{"class":373},[68,12133,12134,12136,12139,12141,12143,12145,12147,12150,12152],{"class":70,"line":98},[68,12135,11317],{"class":1864},[68,12137,12138],{"class":665}," less",[68,12140,1900],{"class":1899},[68,12142,11325],{"class":2183},[68,12144,648],{"class":373},[68,12146,8129],{"class":112},[68,12148,12149],{"class":116},"gulp-less",[68,12151,8129],{"class":112},[68,12153,410],{"class":373},[68,12155,12156,12158,12161,12163,12165,12167,12169,12172,12174],{"class":70,"line":123},[68,12157,11317],{"class":1864},[68,12159,12160],{"class":665}," zip",[68,12162,1900],{"class":1899},[68,12164,11325],{"class":2183},[68,12166,648],{"class":373},[68,12168,8129],{"class":112},[68,12170,12171],{"class":116},"gulp-zip",[68,12173,8129],{"class":112},[68,12175,410],{"class":373},[68,12177,12178,12180,12183,12185,12187,12189,12191,12194,12196],{"class":70,"line":129},[68,12179,11317],{"class":1864},[68,12181,12182],{"class":665}," pump",[68,12184,1900],{"class":1899},[68,12186,11325],{"class":2183},[68,12188,648],{"class":373},[68,12190,8129],{"class":112},[68,12192,12193],{"class":116},"pump",[68,12195,8129],{"class":112},[68,12197,410],{"class":373},[68,12199,12200],{"class":70,"line":212},[68,12201,416],{"emptyLinePlaceholder":415},[68,12203,12204,12206,12210,12212,12214,12216,12218,12220],{"class":70,"line":233},[68,12205,11317],{"class":1864},[68,12207,12209],{"class":12208},"sfCm-"," handleError",[68,12211,1900],{"class":1899},[68,12213,7664],{"class":74},[68,12215,8250],{"class":1912},[68,12217,2753],{"class":74},[68,12219,7765],{"class":1864},[68,12221,95],{"class":74},[68,12223,12224,12226,12228,12230,12232,12234],{"class":70,"line":268},[68,12225,7743],{"class":1790},[68,12227,7806],{"class":1864},[68,12229,7664],{"class":74},[68,12231,11964],{"class":1912},[68,12233,2753],{"class":74},[68,12235,95],{"class":74},[68,12237,12238,12240,12242,12244,12246],{"class":70,"line":289},[68,12239,9649],{"class":1790},[68,12241,7664],{"class":2122},[68,12243,11964],{"class":373},[68,12245,588],{"class":2122},[68,12247,75],{"class":74},[68,12249,12250,12252,12254,12256,12258,12260],{"class":70,"line":308},[68,12251,8120],{"class":373},[68,12253,404],{"class":74},[68,12255,2473],{"class":2183},[68,12257,648],{"class":2122},[68,12259,11964],{"class":373},[68,12261,410],{"class":2122},[68,12263,12264],{"class":70,"line":314},[68,12265,311],{"class":74},[68,12267,12268,12270,12272,12274,12276],{"class":70,"line":320},[68,12269,9544],{"class":1790},[68,12271,8225],{"class":2183},[68,12273,648],{"class":2122},[68,12275,11964],{"class":373},[68,12277,410],{"class":2122},[68,12279,12280],{"class":70,"line":889},[68,12281,126],{"class":74},[68,12283,12284],{"class":70,"line":909},[68,12285,132],{"class":74},[68,12287,12288],{"class":70,"line":929},[68,12289,416],{"emptyLinePlaceholder":415},[68,12291,12292,12294,12297,12299,12301,12303],{"class":70,"line":949},[68,12293,7709],{"class":1864},[68,12295,12296],{"class":2183}," css",[68,12298,648],{"class":74},[68,12300,8250],{"class":1912},[68,12302,2753],{"class":74},[68,12304,95],{"class":74},[68,12306,12307,12310],{"class":70,"line":969},[68,12308,12309],{"class":2183},"  pump",[68,12311,1907],{"class":2122},[68,12313,12314],{"class":70,"line":989},[68,12315,12316],{"class":2122},"    [\n",[68,12318,12319,12322,12324,12326,12329,12331,12333,12335,12338,12340,12343,12345,12347],{"class":70,"line":1009},[68,12320,12321],{"class":2183},"      src",[68,12323,648],{"class":2122},[68,12325,8129],{"class":112},[68,12327,12328],{"class":116},".\u002Fassets\u002Fcss\u002F*.less",[68,12330,8129],{"class":112},[68,12332,255],{"class":74},[68,12334,2382],{"class":74},[68,12336,12337],{"class":2122}," sourcemaps",[68,12339,92],{"class":74},[68,12341,5858],{"class":12342},"syTEX",[68,12344,8233],{"class":74},[68,12346,2753],{"class":2122},[68,12348,169],{"class":74},[68,12350,12351,12354,12356,12359,12361],{"class":70,"line":1029},[68,12352,12353],{"class":2183},"      less",[68,12355,648],{"class":2122},[68,12357,12358],{"class":74},"{}",[68,12360,2753],{"class":2122},[68,12362,169],{"class":74},[68,12364,12365,12368,12370,12372,12375,12377,12379,12381,12383,12385,12387,12390,12392,12394,12396],{"class":70,"line":1049},[68,12366,12367],{"class":2183},"      dest",[68,12369,648],{"class":2122},[68,12371,8129],{"class":112},[68,12373,12374],{"class":116},"assets\u002Fcss",[68,12376,8129],{"class":112},[68,12378,255],{"class":74},[68,12380,2382],{"class":74},[68,12382,12337],{"class":2122},[68,12384,92],{"class":74},[68,12386,1620],{"class":112},[68,12388,12389],{"class":116},".\u002F",[68,12391,8129],{"class":112},[68,12393,8233],{"class":74},[68,12395,2753],{"class":2122},[68,12397,169],{"class":74},[68,12399,12400,12403],{"class":70,"line":1069},[68,12401,12402],{"class":2122},"    ]",[68,12404,169],{"class":74},[68,12406,12407,12410,12412,12414],{"class":70,"line":1088},[68,12408,12409],{"class":2183},"    handleError",[68,12411,648],{"class":2122},[68,12413,8250],{"class":373},[68,12415,410],{"class":2122},[68,12417,12418],{"class":70,"line":1108},[68,12419,12420],{"class":2122},"  )\n",[68,12422,12423],{"class":70,"line":1128},[68,12424,132],{"class":74},[68,12426,12427],{"class":70,"line":1148},[68,12428,416],{"emptyLinePlaceholder":415},[68,12430,12431,12433,12436,12438,12440,12442],{"class":70,"line":1168},[68,12432,7709],{"class":1864},[68,12434,12435],{"class":2183}," zipper",[68,12437,648],{"class":74},[68,12439,8250],{"class":1912},[68,12441,2753],{"class":74},[68,12443,95],{"class":74},[68,12445,12446,12449,12452,12454,12456,12459],{"class":70,"line":1187},[68,12447,12448],{"class":1864},"  const",[68,12450,12451],{"class":665}," targetDir",[68,12453,1900],{"class":1899},[68,12455,1620],{"class":112},[68,12457,12458],{"class":116},"dist\u002F",[68,12460,1626],{"class":112},[68,12462,12463,12465,12467,12469,12471,12473,12475,12478,12480,12482,12484],{"class":70,"line":2039},[68,12464,12448],{"class":1864},[68,12466,11656],{"class":665},[68,12468,1900],{"class":1899},[68,12470,11325],{"class":2183},[68,12472,648],{"class":2122},[68,12474,8129],{"class":112},[68,12476,12477],{"class":116},".\u002Fpackage.json",[68,12479,8129],{"class":112},[68,12481,2753],{"class":2122},[68,12483,404],{"class":74},[68,12485,11692],{"class":373},[68,12487,12488,12490,12493,12495,12497,12499,12501,12503],{"class":70,"line":2055},[68,12489,12448],{"class":1864},[68,12491,12492],{"class":665}," filename",[68,12494,1900],{"class":1899},[68,12496,11704],{"class":112},[68,12498,11707],{"class":373},[68,12500,2400],{"class":112},[68,12502,11712],{"class":116},[68,12504,11715],{"class":112},[68,12506,12507],{"class":70,"line":2071},[68,12508,416],{"emptyLinePlaceholder":415},[68,12510,12511,12513],{"class":70,"line":2087},[68,12512,12309],{"class":2183},[68,12514,1907],{"class":2122},[68,12516,12517],{"class":70,"line":2092},[68,12518,12316],{"class":2122},[68,12520,12521,12523,12526,12528,12530,12532,12534,12536,12539,12541,12543,12545,12548,12550,12552,12554,12557,12559,12561,12563,12566,12568,12571],{"class":70,"line":2097},[68,12522,12321],{"class":2183},[68,12524,12525],{"class":2122},"([",[68,12527,8129],{"class":112},[68,12529,2670],{"class":116},[68,12531,8129],{"class":112},[68,12533,255],{"class":74},[68,12535,1620],{"class":112},[68,12537,12538],{"class":116},"!node_modules",[68,12540,8129],{"class":112},[68,12542,255],{"class":74},[68,12544,1620],{"class":112},[68,12546,12547],{"class":116},"!node_modules\u002F**",[68,12549,8129],{"class":112},[68,12551,255],{"class":74},[68,12553,1620],{"class":112},[68,12555,12556],{"class":116},"!dist",[68,12558,8129],{"class":112},[68,12560,255],{"class":74},[68,12562,1620],{"class":112},[68,12564,12565],{"class":116},"!dist\u002F**",[68,12567,8129],{"class":112},[68,12569,12570],{"class":2122},"])",[68,12572,169],{"class":74},[68,12574,12575,12578,12580,12583,12585],{"class":70,"line":2114},[68,12576,12577],{"class":2183},"      zip",[68,12579,648],{"class":2122},[68,12581,12582],{"class":373},"filename",[68,12584,2753],{"class":2122},[68,12586,169],{"class":74},[68,12588,12589,12591,12593,12596,12598],{"class":70,"line":2139},[68,12590,12367],{"class":2183},[68,12592,648],{"class":2122},[68,12594,12595],{"class":373},"targetDir",[68,12597,2753],{"class":2122},[68,12599,169],{"class":74},[68,12601,12602,12604],{"class":70,"line":2158},[68,12603,12402],{"class":2122},[68,12605,169],{"class":74},[68,12607,12608,12610,12612,12614],{"class":70,"line":2173},[68,12609,12409],{"class":2183},[68,12611,648],{"class":2122},[68,12613,8250],{"class":373},[68,12615,410],{"class":2122},[68,12617,12618],{"class":70,"line":2178},[68,12619,12420],{"class":2122},[68,12621,12622],{"class":70,"line":2193},[68,12623,132],{"class":74},[68,12625,12626],{"class":70,"line":2201},[68,12627,416],{"emptyLinePlaceholder":415},[68,12629,12630,12632,12634,12637,12639,12641,12643],{"class":70,"line":2207},[68,12631,11415],{"class":1864},[68,12633,7806],{"class":1864},[68,12635,12636],{"class":2183}," deploy",[68,12638,648],{"class":74},[68,12640,8250],{"class":1912},[68,12642,2753],{"class":74},[68,12644,95],{"class":74},[68,12646,12647,12649],{"class":70,"line":2234},[68,12648,11429],{"class":1790},[68,12650,95],{"class":74},[68,12652,12653,12655,12658,12660,12663,12665,12667,12670,12672,12674,12676,12678,12680,12682,12684,12686,12688],{"class":70,"line":2258},[68,12654,7772],{"class":1864},[68,12656,12657],{"class":665}," zipFile",[68,12659,1900],{"class":1899},[68,12661,12662],{"class":112}," `",[68,12664,12458],{"class":116},[68,12666,11788],{"class":112},[68,12668,12669],{"class":2183},"require",[68,12671,648],{"class":11805},[68,12673,8129],{"class":112},[68,12675,12477],{"class":116},[68,12677,8129],{"class":112},[68,12679,2753],{"class":11805},[68,12681,404],{"class":112},[68,12683,217],{"class":373},[68,12685,2400],{"class":112},[68,12687,11712],{"class":116},[68,12689,11715],{"class":112},[68,12691,12692,12694,12696,12698,12700,12702,12704],{"class":70,"line":2264},[68,12693,7772],{"class":1864},[68,12695,11465],{"class":665},[68,12697,1900],{"class":1899},[68,12699,7746],{"class":1899},[68,12701,11387],{"class":2183},[68,12703,648],{"class":2122},[68,12705,75],{"class":74},[68,12707,12708,12710,12712,12714,12716,12718,12720,12722],{"class":70,"line":2270},[68,12709,11480],{"class":2122},[68,12711,92],{"class":74},[68,12713,11544],{"class":373},[68,12715,404],{"class":74},[68,12717,11549],{"class":373},[68,12719,404],{"class":74},[68,12721,12039],{"class":665},[68,12723,169],{"class":74},[68,12725,12726,12728,12730,12732,12734,12736,12738,12740],{"class":70,"line":2275},[68,12727,11487],{"class":2122},[68,12729,92],{"class":74},[68,12731,11544],{"class":373},[68,12733,404],{"class":74},[68,12735,11549],{"class":373},[68,12737,404],{"class":74},[68,12739,12036],{"class":665},[68,12741,169],{"class":74},[68,12743,12744,12746,12748,12750,12753,12755,12757,12759,12761,12763,12765,12767,12769,12771,12773],{"class":70,"line":2289},[68,12745,11513],{"class":2122},[68,12747,92],{"class":74},[68,12749,12662],{"class":112},[68,12751,12752],{"class":116},"v",[68,12754,11788],{"class":112},[68,12756,12669],{"class":2183},[68,12758,648],{"class":11805},[68,12760,8129],{"class":112},[68,12762,12477],{"class":116},[68,12764,8129],{"class":112},[68,12766,2753],{"class":11805},[68,12768,404],{"class":112},[68,12770,155],{"class":373},[68,12772,11833],{"class":112},[68,12774,169],{"class":74},[68,12776,12777,12779],{"class":70,"line":2339},[68,12778,8370],{"class":74},[68,12780,410],{"class":2122},[68,12782,12783],{"class":70,"line":2363},[68,12784,416],{"emptyLinePlaceholder":415},[68,12786,12787,12789,12791,12793,12795,12797,12799,12801,12803,12805,12807,12809,12811],{"class":70,"line":2374},[68,12788,11899],{"class":1790},[68,12790,11465],{"class":373},[68,12792,404],{"class":74},[68,12794,11906],{"class":373},[68,12796,404],{"class":74},[68,12798,11911],{"class":2183},[68,12800,648],{"class":2122},[68,12802,2546],{"class":74},[68,12804,8171],{"class":2122},[68,12806,92],{"class":74},[68,12808,12657],{"class":373},[68,12810,8233],{"class":74},[68,12812,410],{"class":2122},[68,12814,12815,12817,12819,12821,12823,12825,12828,12830,12832,12834],{"class":70,"line":2407},[68,12816,11930],{"class":373},[68,12818,404],{"class":74},[68,12820,363],{"class":2183},[68,12822,648],{"class":2122},[68,12824,11939],{"class":112},[68,12826,12827],{"class":373},"zipFile",[68,12829,2400],{"class":112},[68,12831,11946],{"class":116},[68,12833,11782],{"class":112},[68,12835,410],{"class":2122},[68,12837,12838,12841],{"class":70,"line":2421},[68,12839,12840],{"class":2183},"    done",[68,12842,2136],{"class":2122},[68,12844,12845],{"class":70,"line":2426},[68,12846,126],{"class":74},[68,12848,12849,12851,12853,12855,12857],{"class":70,"line":2432},[68,12850,11959],{"class":1790},[68,12852,7664],{"class":2122},[68,12854,11964],{"class":373},[68,12856,588],{"class":2122},[68,12858,75],{"class":74},[68,12860,12861,12863,12865,12867,12869,12871],{"class":70,"line":2454},[68,12862,11930],{"class":373},[68,12864,404],{"class":74},[68,12866,2473],{"class":2183},[68,12868,648],{"class":2122},[68,12870,11964],{"class":373},[68,12872,410],{"class":2122},[68,12874,12875,12877,12879,12881],{"class":70,"line":2500},[68,12876,12840],{"class":2183},[68,12878,648],{"class":2122},[68,12880,11964],{"class":373},[68,12882,410],{"class":2122},[68,12884,12885],{"class":70,"line":2506},[68,12886,126],{"class":74},[68,12888,12889],{"class":70,"line":2511},[68,12890,132],{"class":74},[68,12892,12893],{"class":70,"line":2517},[68,12894,416],{"emptyLinePlaceholder":415},[68,12896,12897,12899,12902,12904,12906],{"class":70,"line":2527},[68,12898,11317],{"class":1864},[68,12900,12901],{"class":665}," build",[68,12903,1900],{"class":1899},[68,12905,12104],{"class":2183},[68,12907,12908],{"class":373},"(css)\n",[68,12910,12911],{"class":70,"line":2565},[68,12912,416],{"emptyLinePlaceholder":415},[68,12914,12915,12918,12920,12923,12925],{"class":70,"line":2586},[68,12916,12917],{"class":81},"exports",[68,12919,404],{"class":74},[68,12921,12922],{"class":373},"build ",[68,12924,877],{"class":1899},[68,12926,12927],{"class":373}," build\n",[68,12929,12930,12932,12934,12937,12939,12941,12944,12946],{"class":70,"line":2592},[68,12931,12917],{"class":81},[68,12933,404],{"class":74},[68,12935,12936],{"class":373},"zip ",[68,12938,877],{"class":1899},[68,12940,12104],{"class":2183},[68,12942,12943],{"class":373},"(build",[68,12945,255],{"class":74},[68,12947,12948],{"class":373}," zipper)\n",[68,12950,12951,12953,12955,12958,12960],{"class":70,"line":2597},[68,12952,12917],{"class":81},[68,12954,404],{"class":74},[68,12956,12957],{"class":373},"deploy ",[68,12959,877],{"class":1899},[68,12961,5976],{"class":373},[68,12963,12964,12966,12968,12971,12973],{"class":70,"line":2605},[68,12965,12917],{"class":81},[68,12967,404],{"class":74},[68,12969,12970],{"class":373},"default ",[68,12972,877],{"class":1899},[68,12974,12927],{"class":373},[28,12976,1659,12977,12979],{},[65,12978,11591],{}," 中插入脚本：",[58,12981,12983],{"className":60,"code":12982,"language":62,"meta":63,"style":63},"{\n  \u002F* ... *\u002F\n  \"scripts\": {\n    \"build\": \"gulp build\",\n    \"zip\": \"gulp zip\",\n    \"deploy\": \"gulp deploy\"\n  }\n  \u002F* ... *\u002F\n}\n",[65,12984,12985,12989,12994,13006,13026,13046,13063,13067,13071],{"__ignoreMap":63},[68,12986,12987],{"class":70,"line":71},[68,12988,75],{"class":74},[68,12990,12991],{"class":70,"line":78},[68,12992,12993],{"class":2403},"  \u002F* ... *\u002F\n",[68,12995,12996,12998,13000,13002,13004],{"class":70,"line":98},[68,12997,82],{"class":81},[68,12999,5554],{"class":85},[68,13001,89],{"class":81},[68,13003,92],{"class":74},[68,13005,95],{"class":74},[68,13007,13008,13010,13013,13015,13017,13019,13022,13024],{"class":70,"line":123},[68,13009,101],{"class":81},[68,13011,13012],{"class":104},"build",[68,13014,89],{"class":81},[68,13016,92],{"class":74},[68,13018,113],{"class":112},[68,13020,13021],{"class":116},"gulp build",[68,13023,89],{"class":112},[68,13025,169],{"class":74},[68,13027,13028,13030,13033,13035,13037,13039,13042,13044],{"class":70,"line":129},[68,13029,101],{"class":81},[68,13031,13032],{"class":104},"zip",[68,13034,89],{"class":81},[68,13036,92],{"class":74},[68,13038,113],{"class":112},[68,13040,13041],{"class":116},"gulp zip",[68,13043,89],{"class":112},[68,13045,169],{"class":74},[68,13047,13048,13050,13052,13054,13056,13058,13061],{"class":70,"line":212},[68,13049,101],{"class":81},[68,13051,6034],{"class":104},[68,13053,89],{"class":81},[68,13055,92],{"class":74},[68,13057,113],{"class":112},[68,13059,13060],{"class":116},"gulp deploy",[68,13062,120],{"class":112},[68,13064,13065],{"class":70,"line":233},[68,13066,126],{"class":74},[68,13068,13069],{"class":70,"line":268},[68,13070,12993],{"class":2403},[68,13072,13073],{"class":70,"line":289},[68,13074,132],{"class":74},[28,13076,13077,13078,13080],{},"添加 ",[65,13079,5949],{}," 文件：",[58,13082,13084],{"className":1557,"code":13083,"language":1559,"meta":63,"style":63},"image: node:14-slim # 注意：不要用 alpine 的镜像，上传至 https 站点会有问题\n\nstages:\n  - deploy\n\ndeploy:\n  stage: deploy\n  script:\n    - yarn install\n    - yarn zip\n    - yarn deploy\n  only:\n    - tags\n  cache:\n    paths:\n      - node_modules\u002F\n",[65,13085,13086,13098,13102,13108,13114,13118,13124,13132,13138,13145,13152,13159,13165,13171,13178,13185],{"__ignoreMap":63},[68,13087,13088,13090,13092,13095],{"class":70,"line":71},[68,13089,10423],{"class":730},[68,13091,92],{"class":74},[68,13093,13094],{"class":116}," node:14-slim",[68,13096,13097],{"class":2403}," # 注意：不要用 alpine 的镜像，上传至 https 站点会有问题\n",[68,13099,13100],{"class":70,"line":78},[68,13101,416],{"emptyLinePlaceholder":415},[68,13103,13104,13106],{"class":70,"line":98},[68,13105,5959],{"class":730},[68,13107,1569],{"class":74},[68,13109,13110,13112],{"class":70,"line":123},[68,13111,5966],{"class":74},[68,13113,5976],{"class":116},[68,13115,13116],{"class":70,"line":129},[68,13117,416],{"emptyLinePlaceholder":415},[68,13119,13120,13122],{"class":70,"line":212},[68,13121,6034],{"class":730},[68,13123,1569],{"class":74},[68,13125,13126,13128,13130],{"class":70,"line":233},[68,13127,5992],{"class":730},[68,13129,92],{"class":74},[68,13131,5976],{"class":116},[68,13133,13134,13136],{"class":70,"line":268},[68,13135,6001],{"class":730},[68,13137,1569],{"class":74},[68,13139,13140,13142],{"class":70,"line":289},[68,13141,6008],{"class":74},[68,13143,13144],{"class":116}," yarn install\n",[68,13146,13147,13149],{"class":70,"line":308},[68,13148,6008],{"class":74},[68,13150,13151],{"class":116}," yarn zip\n",[68,13153,13154,13156],{"class":70,"line":314},[68,13155,6008],{"class":74},[68,13157,13158],{"class":116}," yarn deploy\n",[68,13160,13161,13163],{"class":70,"line":320},[68,13162,7088],{"class":730},[68,13164,1569],{"class":74},[68,13166,13167,13169],{"class":70,"line":889},[68,13168,6008],{"class":74},[68,13170,10529],{"class":116},[68,13172,13173,13176],{"class":70,"line":909},[68,13174,13175],{"class":730},"  cache",[68,13177,1569],{"class":74},[68,13179,13180,13183],{"class":70,"line":929},[68,13181,13182],{"class":730},"    paths",[68,13184,1569],{"class":74},[68,13186,13187,13189],{"class":70,"line":949},[68,13188,1617],{"class":74},[68,13190,13191],{"class":116}," node_modules\u002F\n",[28,13193,13194,13195,13197,13198,9810,13201,13204],{},"注意，为了避免每次提交代码都部署，",[65,13196,6034],{}," 任务限制了只有打了 ",[65,13199,13200],{},"tag",[65,13202,13203],{},"commit"," 才会触发。",[28,13206,13207],{},"好了，更新代码，打个 tag 就会自动打包上传至 Ghost 后台了！",[523,13209,13210],{},"html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sfo-9, html code.shiki .sfo-9{--shiki-light:#90A4AE;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sfCm-, html code.shiki .sfCm-{--shiki-light:#90A4AE;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":63,"searchDepth":78,"depth":78,"links":13212},[],"2022-05-24",{},"\u002Fposts\u002F2022\u002Fuse-gitlab-to-deploy-ghost-theme-automatically",{"text":8419,"minutes":13217,"time":13218,"words":13219},3.575,214500,715,{"title":11286,"description":63},{"loc":13215},"posts\u002F2022\u002F20220524.use-gitlab-to-deploy-ghost-theme-automatically",[543,11282,7153,10057],"2cB9yXMzvXfyQ8XEXx9VDbtCvf4CKC-2FRBZ833gE-Q",{"id":13226,"title":13227,"body":13228,"class":528,"cover":1445,"coverSize":528,"date":13504,"description":13505,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":13506,"navigation":415,"path":13507,"readingTime":13508,"seo":13512,"sitemap":13513,"stem":13514,"tags":13515,"time":528,"weather":528,"__hash__":13518},"posts\u002Fposts\u002F2021\u002F20210108.synology-letsencrypt-multiple-domain-cert-configuration.md","群晖 Let's Encrypt 配置多个泛域名 SSL 证书自动更新",{"type":25,"value":13229,"toc":13502},[13230,13242,13268,13271,13277,13312,13318,13486,13496,13499],[28,13231,13232,13233,13238,13239,13241],{},"之前一直用的 ",[38,13234,13237],{"href":13235,"rel":13236},"https:\u002F\u002Fgithub.com\u002Fandyzhshg\u002Fsyno-acme",[42],"syno-acme"," 配合群晖的计划任务实现泛域名 SSL 证书的更新，但是最近想切换域名，但是又要保持原有域名一段时间可用。",[65,13240,13237],{}," 的方案只支持默认证书的配置，群晖上多个证书的配置确实比较麻烦，几年前也折腾过。",[28,13243,13244,13245,13247,13248,13253,13254,13256,13257,13262,13263,1686],{},"不过调研了下发现，Let's Encrypt 支持将多个域名绑定到同一个证书里，于是找了下解决方案，果然有位兄弟基于 ",[65,13246,13237],{}," 做了些",[38,13249,13252],{"href":13250,"rel":13251},"https:\u002F\u002F10001blog.xslinc.com\u002F?p=89",[42],"修改","，支持多个域名。不过这位兄弟是 Hard Code 的，不够通用化，于是对 ",[65,13255,13237],{}," 做了些改进，并提交了 ",[38,13258,13261],{"href":13259,"rel":13260},"https:\u002F\u002Fgithub.com\u002Fandyzhshg\u002Fsyno-acme\u002Fpull\u002F58",[42],"Pull request","，希望对大家有帮助，",[38,13264,13267],{"href":13265,"rel":13266},"https:\u002F\u002Fgithub.com\u002FHADB\u002Fsyno-acme",[42],"Fork 仓库",[28,13269,13270],{},"主要修改内容：",[28,13272,13273,13274,5564],{},"配置时可通过逗号分隔多个域名，",[65,13275,13276],{},"config",[58,13278,13282],{"className":13279,"code":13280,"language":13281,"meta":63,"style":63},"language-shell shiki shiki-themes material-theme-lighter github-light github-dark","# 你域名，如 baidu.com sina.com.cn 等，多个域名之间逗号分隔，支持泛域名\nexport DOMAIN=your_domain1,*.your_domain1,your_domain2,*.your_domain2\n","shell",[65,13283,13284,13289],{"__ignoreMap":63},[68,13285,13286],{"class":70,"line":71},[68,13287,13288],{"class":2403},"# 你域名，如 baidu.com sina.com.cn 等，多个域名之间逗号分隔，支持泛域名\n",[68,13290,13291,13294,13297,13299,13302,13304,13307,13309],{"class":70,"line":78},[68,13292,13293],{"class":1864},"export",[68,13295,13296],{"class":373}," DOMAIN",[68,13298,877],{"class":1899},[68,13300,13301],{"class":373},"your_domain1,",[68,13303,7661],{"class":1899},[68,13305,13306],{"class":373},".your_domain1,your_domain2,",[68,13308,7661],{"class":1899},[68,13310,13311],{"class":373},".your_domain2\n",[28,13313,13314,13317],{},[65,13315,13316],{},"cert-up.sh"," 主要修改了如下的地方：",[58,13319,13321],{"className":13279,"code":13320,"language":13281,"meta":63,"style":63},"for d in ${DOMAIN\u002F\u002F,\u002F }\ndo\n  domain_params=\"${domain_params} -d ${d}\"\ndone\n${ACME_BIN_PATH}\u002Facme.sh --force --log --issue --dns ${DNS} --dnssleep ${DNS_SLEEP} ${domain_params}\n${ACME_BIN_PATH}\u002Facme.sh --force --installcert ${domain_params} \\\n  --certpath ${CRT_PATH}\u002Fcert.pem \\\n  --key-file ${CRT_PATH}\u002Fprivkey.pem \\\n  --fullchain-file ${CRT_PATH}\u002Ffullchain.pem\n",[65,13322,13323,13348,13353,13379,13384,13419,13439,13456,13472],{"__ignoreMap":63},[68,13324,13325,13328,13331,13333,13335,13338,13341,13343,13345],{"class":70,"line":71},[68,13326,13327],{"class":1790},"for",[68,13329,13330],{"class":373}," d ",[68,13332,3237],{"class":1790},[68,13334,7342],{"class":74},[68,13336,13337],{"class":373},"DOMAIN",[68,13339,13340],{"class":1899},"\u002F\u002F",[68,13342,255],{"class":373},[68,13344,6],{"class":1899},[68,13346,13347],{"class":74}," }\n",[68,13349,13350],{"class":70,"line":78},[68,13351,13352],{"class":1790},"do\n",[68,13354,13355,13358,13360,13363,13366,13368,13371,13373,13376],{"class":70,"line":98},[68,13356,13357],{"class":373},"  domain_params",[68,13359,877],{"class":1899},[68,13361,13362],{"class":112},"\"${",[68,13364,13365],{"class":373},"domain_params",[68,13367,2400],{"class":112},[68,13369,13370],{"class":116}," -d ",[68,13372,11788],{"class":112},[68,13374,13375],{"class":373},"d",[68,13377,13378],{"class":112},"}\"\n",[68,13380,13381],{"class":70,"line":123},[68,13382,13383],{"class":1790},"done\n",[68,13385,13386,13388,13391,13393,13396,13398,13401,13403,13406,13408,13411,13413,13415,13417],{"class":70,"line":129},[68,13387,11788],{"class":74},[68,13389,13390],{"class":373},"ACME_BIN_PATH",[68,13392,2400],{"class":74},[68,13394,13395],{"class":373},"\u002Facme.sh --force --log --issue --dns ",[68,13397,11788],{"class":74},[68,13399,13400],{"class":373},"DNS",[68,13402,2400],{"class":74},[68,13404,13405],{"class":373}," --dnssleep ",[68,13407,11788],{"class":74},[68,13409,13410],{"class":373},"DNS_SLEEP",[68,13412,2400],{"class":74},[68,13414,7342],{"class":74},[68,13416,13365],{"class":373},[68,13418,132],{"class":74},[68,13420,13421,13423,13425,13427,13430,13432,13434,13436],{"class":70,"line":212},[68,13422,11788],{"class":74},[68,13424,13390],{"class":373},[68,13426,2400],{"class":74},[68,13428,13429],{"class":373},"\u002Facme.sh --force --installcert ",[68,13431,11788],{"class":74},[68,13433,13365],{"class":373},[68,13435,2400],{"class":74},[68,13437,13438],{"class":665}," \\\n",[68,13440,13441,13444,13446,13449,13451,13454],{"class":70,"line":233},[68,13442,13443],{"class":1511},"  --certpath",[68,13445,7342],{"class":74},[68,13447,13448],{"class":373},"CRT_PATH",[68,13450,2400],{"class":74},[68,13452,13453],{"class":116},"\u002Fcert.pem",[68,13455,13438],{"class":665},[68,13457,13458,13461,13463,13465,13467,13470],{"class":70,"line":268},[68,13459,13460],{"class":1518},"  --key-file",[68,13462,7342],{"class":74},[68,13464,13448],{"class":373},[68,13466,2400],{"class":74},[68,13468,13469],{"class":116},"\u002Fprivkey.pem",[68,13471,13438],{"class":665},[68,13473,13474,13477,13479,13481,13483],{"class":70,"line":289},[68,13475,13476],{"class":1518},"  --fullchain-file",[68,13478,7342],{"class":74},[68,13480,13448],{"class":373},[68,13482,2400],{"class":74},[68,13484,13485],{"class":116},"\u002Ffullchain.pem\n",[28,13487,13488,13489,13491,13492,13495],{},"通过逗号分隔 ",[65,13490,13337],{}," 中的多个域名，并循环拼接多个 ",[65,13493,13494],{},"-d"," 参数即可。",[28,13497,13498],{},"这么修改后，群晖就可以愉快的支持多个主域名的 SSL 证书啦，爽！",[523,13500,13501],{},"html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":63,"searchDepth":78,"depth":78,"links":13503},[],"2021-01-08","之前一直用的 syno-acme 配合群晖的计划任务实现泛域名 SSL 证书的更新，但是最近想切换域名，但是又要保持原有域名一段时间可用。syno-acme 的方案只支持默认证书的配置，群晖上多个证书的配置确实比较麻烦，几年前也折腾过。",{},"\u002Fposts\u002F2021\u002Fsynology-letsencrypt-multiple-domain-cert-configuration",{"text":1217,"minutes":13509,"time":13510,"words":13511},1.595,95700,319,{"title":13227,"description":13505},{"loc":13507},"posts\u002F2021\u002F20210108.synology-letsencrypt-multiple-domain-cert-configuration",[543,13516,13517,1458],"群晖","NAS","9T15Ab9v8M2cX6qAsb3w1Oqj78Mu9pxorqz3fCUWoKo",{"id":13520,"title":13521,"body":13522,"class":528,"cover":1212,"coverSize":528,"date":13748,"description":13526,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":13749,"navigation":415,"path":13750,"readingTime":13751,"seo":13755,"sitemap":13756,"stem":13757,"tags":13758,"time":528,"weather":528,"__hash__":13760},"posts\u002Fposts\u002F2021\u002F20210105.fastboot-failed-remote-operation-not-permitted.md","fastboot FAILED (remote: Operation not permitted) 的问题",{"type":25,"value":13523,"toc":13746},[13524,13527,13533,13718,13725,13728,13731,13734,13740,13743],[28,13525,13526],{},"这两天买了台安卓测试设备，由于我们的项目需要系统签名，所以不得不重新刷系统。",[28,13528,1659,13529,13532],{},[65,13530,13531],{},"fastboot flash system"," 的时候，每次都在最后要完成的时候报一个错误：",[58,13534,13536],{"className":361,"code":13535,"language":363,"meta":63,"style":63},"target reported max download size of 536870912 bytes\nSending sparse 'system' 1\u002F4 (524284 KB)...\nOKAY [ 15.543s]\nWriting 'system' 1\u002F4...\nOKAY [  3.548s]\nSending sparse 'system' 2\u002F4 (524284 KB)...\nOKAY [ 15.483s]\nWriting 'system' 2\u002F4...\nOKAY [  3.644s]\nSending sparse 'system' 3\u002F4 (524284 KB)...\nOKAY [ 15.103s]\nWriting 'system' 3\u002F4...\nFAILED (remote: Operation not permitted)\nFinished. Total time: 58.650s\n",[65,13537,13538,13549,13572,13583,13599,13609,13628,13637,13651,13660,13679,13688,13702,13707],{"__ignoreMap":63},[68,13539,13540,13543,13546],{"class":70,"line":71},[68,13541,13542],{"class":373},"target reported max download size of ",[68,13544,13545],{"class":81},"536870912",[68,13547,13548],{"class":373}," bytes\n",[68,13550,13551,13554,13557,13559,13561,13564,13566,13569],{"class":70,"line":78},[68,13552,13553],{"class":373},"Sending sparse ",[68,13555,13556],{"class":116},"'system'",[68,13558,2646],{"class":81},[68,13560,6],{"class":373},[68,13562,13563],{"class":81},"4",[68,13565,7664],{"class":373},[68,13567,13568],{"class":81},"524284",[68,13570,13571],{"class":373}," KB)...\n",[68,13573,13574,13577,13580],{"class":70,"line":98},[68,13575,13576],{"class":373},"OKAY [ ",[68,13578,13579],{"class":81},"15",[68,13581,13582],{"class":373},".543s]\n",[68,13584,13585,13588,13590,13592,13594,13596],{"class":70,"line":123},[68,13586,13587],{"class":373},"Writing ",[68,13589,13556],{"class":116},[68,13591,2646],{"class":81},[68,13593,6],{"class":373},[68,13595,13563],{"class":81},[68,13597,13598],{"class":373},"...\n",[68,13600,13601,13604,13606],{"class":70,"line":129},[68,13602,13603],{"class":373},"OKAY [  ",[68,13605,5057],{"class":81},[68,13607,13608],{"class":373},".548s]\n",[68,13610,13611,13613,13615,13618,13620,13622,13624,13626],{"class":70,"line":212},[68,13612,13553],{"class":373},[68,13614,13556],{"class":116},[68,13616,13617],{"class":81}," 2",[68,13619,6],{"class":373},[68,13621,13563],{"class":81},[68,13623,7664],{"class":373},[68,13625,13568],{"class":81},[68,13627,13571],{"class":373},[68,13629,13630,13632,13634],{"class":70,"line":233},[68,13631,13576],{"class":373},[68,13633,13579],{"class":81},[68,13635,13636],{"class":373},".483s]\n",[68,13638,13639,13641,13643,13645,13647,13649],{"class":70,"line":268},[68,13640,13587],{"class":373},[68,13642,13556],{"class":116},[68,13644,13617],{"class":81},[68,13646,6],{"class":373},[68,13648,13563],{"class":81},[68,13650,13598],{"class":373},[68,13652,13653,13655,13657],{"class":70,"line":289},[68,13654,13603],{"class":373},[68,13656,5057],{"class":81},[68,13658,13659],{"class":373},".644s]\n",[68,13661,13662,13664,13666,13669,13671,13673,13675,13677],{"class":70,"line":308},[68,13663,13553],{"class":373},[68,13665,13556],{"class":116},[68,13667,13668],{"class":81}," 3",[68,13670,6],{"class":373},[68,13672,13563],{"class":81},[68,13674,7664],{"class":373},[68,13676,13568],{"class":81},[68,13678,13571],{"class":373},[68,13680,13681,13683,13685],{"class":70,"line":314},[68,13682,13576],{"class":373},[68,13684,13579],{"class":81},[68,13686,13687],{"class":373},".103s]\n",[68,13689,13690,13692,13694,13696,13698,13700],{"class":70,"line":320},[68,13691,13587],{"class":373},[68,13693,13556],{"class":116},[68,13695,13668],{"class":81},[68,13697,6],{"class":373},[68,13699,13563],{"class":81},[68,13701,13598],{"class":373},[68,13703,13704],{"class":70,"line":889},[68,13705,13706],{"class":373},"FAILED (remote: Operation not permitted)\n",[68,13708,13709,13712,13715],{"class":70,"line":909},[68,13710,13711],{"class":373},"Finished. Total time: ",[68,13713,13714],{"class":81},"58",[68,13716,13717],{"class":373},".650s\n",[28,13719,13720,13721,13724],{},"不管怎么 ",[65,13722,13723],{},"-S"," 给多少，最后总在 70% 左右的时候报这个错，网上的资料也是查不到。",[28,13726,13727],{},"后来想，会不会和 system 分区大小有关，尝试了半天又没结果。",[28,13729,13730],{},"最终下了个最新版的 platform-tools，解决了。问题确实是 system 分区大小的问题，最新版的 fastboot 在烧录前会自动调整 system 分区大小。",[28,13732,13733],{},"附个最新版 platform-tools 的下载地址：",[28,13735,13736],{},[38,13737,13738],{"href":13738,"rel":13739},"https:\u002F\u002Fdeveloper.android.com\u002Fstudio\u002Freleases\u002Fplatform-tools",[42],[28,13741,13742],{},"记录一下，方便后人。如果帮助到你的话，留个言再走吧。",[523,13744,13745],{},"html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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":63,"searchDepth":78,"depth":78,"links":13747},[],"2021-01-05",{},"\u002Fposts\u002F2021\u002Ffastboot-failed-remote-operation-not-permitted",{"text":1217,"minutes":13752,"time":13753,"words":13754},1.24,74400,248,{"title":13521,"description":13526},{"loc":13750},"posts\u002F2021\u002F20210105.fastboot-failed-remote-operation-not-permitted",[543,13759],"Android","YV00Q09feWoUxyWhCUlxF32Tk1GXdjRSj33StuPrWdo",{"id":13762,"title":13763,"body":13764,"class":528,"cover":1212,"coverSize":528,"date":14779,"description":14780,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":14781,"navigation":415,"path":14782,"readingTime":14783,"seo":14787,"sitemap":14788,"stem":14789,"tags":14790,"time":528,"weather":528,"__hash__":14792},"posts\u002Fposts\u002F2020\u002F20201227.devops-gitlab-ci-aliyun-k8s.md","基于 GitLab CI 和阿里云 k8s 的持续交付解决方案",{"type":25,"value":13765,"toc":14774},[13766,13773,13776,13790,13793,13796,13800,13805,14115,14130,14133,14137,14141,14244,14247,14412,14416,14419,14513,14516,14754,14758,14761,14763,14766,14768,14771],[28,13767,13768,13769,13772],{},"今年对于我个人而言，在 DevOps 上的最大收获，莫过于摸索了这套基于 GitLab CI 和 k8s 的持续交付解决方案，其实原理都很简单，在我去年的方案里又做了改进，实现了基于 ",[65,13770,13771],{},"git tag"," 的触发方式，并且把原先的本地打包推镜像改为在 GitLab Runner 上打包推镜像。",[28,13774,13775],{},"这套解决方案大致流程是这样的：",[5010,13777,13778,13784,13787],{},[1239,13779,13780,13781],{},"推送代码，在代码中配置 ",[65,13782,13783],{},"gitlab-ci.yml",[1239,13785,13786],{},"推送 tag，触发 GitLab Runner 编译 docker 镜像，并推送至阿里云镜像仓库",[1239,13788,13789],{},"在阿里云 k8s 上基于镜像仓库创建应用，并创建重新部署的触发器，在镜像更新时触发该触发器",[28,13791,13792],{},"这样，以后每次推送新的 tag 上去，就可以实现自动打包&部署了。",[28,13794,13795],{},"下面，我来详细讲解下所有步骤。",[1232,13797,13799],{"id":13798},"配置-gitlab-runner","配置 GitLab Runner",[13801,13802,13804],"h5",{"id":13803},"configtoml","config.toml",[58,13806,13810],{"className":13807,"code":13808,"language":13809,"meta":63,"style":63},"language-toml shiki shiki-themes material-theme-lighter github-light github-dark","concurrent = 1\ncheck_interval = 0\n\n[session_server]\nsession_timeout = 1800\n\n[[runners]]\nname = \"common-runner\"\nurl = \"https:\u002F\u002Fgit.xxx.xxx\"\ntoken = \"TOKEN\"\nexecutor = \"docker\"\n\n[runners.custom_build_dir]\n\n[runners.cache]\n\n[runners.cache.s3]\n\n[runners.cache.gcs]\n\n[runners.docker]\ntls_verify = false\nimage = \"docker:latest\"\nprivileged = false\ndisable_entrypoint_overwrite = false\noom_kill_disable = false\ndisable_cache = false\nvolumes = [\n  \"\u002Fvar\u002Frun\u002Fdocker.sock:\u002Fvar\u002Frun\u002Fdocker.sock\",\n  \"\u002Fxxx\u002Fgitlab-runner\u002Fcache:\u002Fcache\"\n]\nshm_size = 0\n","toml",[65,13811,13812,13821,13830,13834,13843,13853,13857,13867,13881,13895,13909,13922,13926,13939,13943,13956,13960,13977,13981,13998,14002,14014,14023,14037,14046,14055,14064,14073,14082,14093,14102,14106],{"__ignoreMap":63},[68,13813,13814,13817,13819],{"class":70,"line":71},[68,13815,13816],{"class":373},"concurrent ",[68,13818,877],{"class":74},[68,13820,2960],{"class":2397},[68,13822,13823,13826,13828],{"class":70,"line":78},[68,13824,13825],{"class":373},"check_interval ",[68,13827,877],{"class":74},[68,13829,2732],{"class":2397},[68,13831,13832],{"class":70,"line":98},[68,13833,416],{"emptyLinePlaceholder":415},[68,13835,13836,13838,13841],{"class":70,"line":123},[68,13837,2326],{"class":74},[68,13839,13840],{"class":1511},"session_server",[68,13842,2657],{"class":74},[68,13844,13845,13848,13850],{"class":70,"line":129},[68,13846,13847],{"class":373},"session_timeout ",[68,13849,877],{"class":74},[68,13851,13852],{"class":2397}," 1800\n",[68,13854,13855],{"class":70,"line":212},[68,13856,416],{"emptyLinePlaceholder":415},[68,13858,13859,13861,13864],{"class":70,"line":233},[68,13860,2314],{"class":74},[68,13862,13863],{"class":1511},"runners",[68,13865,13866],{"class":74},"]]\n",[68,13868,13869,13872,13874,13876,13879],{"class":70,"line":268},[68,13870,13871],{"class":373},"name ",[68,13873,877],{"class":74},[68,13875,113],{"class":112},[68,13877,13878],{"class":116},"common-runner",[68,13880,120],{"class":112},[68,13882,13883,13886,13888,13890,13893],{"class":70,"line":289},[68,13884,13885],{"class":373},"url ",[68,13887,877],{"class":74},[68,13889,113],{"class":112},[68,13891,13892],{"class":116},"https:\u002F\u002Fgit.xxx.xxx",[68,13894,120],{"class":112},[68,13896,13897,13900,13902,13904,13907],{"class":70,"line":308},[68,13898,13899],{"class":373},"token ",[68,13901,877],{"class":74},[68,13903,113],{"class":112},[68,13905,13906],{"class":116},"TOKEN",[68,13908,120],{"class":112},[68,13910,13911,13914,13916,13918,13920],{"class":70,"line":314},[68,13912,13913],{"class":373},"executor ",[68,13915,877],{"class":74},[68,13917,113],{"class":112},[68,13919,1512],{"class":116},[68,13921,120],{"class":112},[68,13923,13924],{"class":70,"line":320},[68,13925,416],{"emptyLinePlaceholder":415},[68,13927,13928,13930,13932,13934,13937],{"class":70,"line":889},[68,13929,2326],{"class":74},[68,13931,13863],{"class":1511},[68,13933,404],{"class":373},[68,13935,13936],{"class":1511},"custom_build_dir",[68,13938,2657],{"class":74},[68,13940,13941],{"class":70,"line":909},[68,13942,416],{"emptyLinePlaceholder":415},[68,13944,13945,13947,13949,13951,13954],{"class":70,"line":929},[68,13946,2326],{"class":74},[68,13948,13863],{"class":1511},[68,13950,404],{"class":373},[68,13952,13953],{"class":1511},"cache",[68,13955,2657],{"class":74},[68,13957,13958],{"class":70,"line":949},[68,13959,416],{"emptyLinePlaceholder":415},[68,13961,13962,13964,13966,13968,13970,13972,13975],{"class":70,"line":969},[68,13963,2326],{"class":74},[68,13965,13863],{"class":1511},[68,13967,404],{"class":373},[68,13969,13953],{"class":1511},[68,13971,404],{"class":373},[68,13973,13974],{"class":1511},"s3",[68,13976,2657],{"class":74},[68,13978,13979],{"class":70,"line":989},[68,13980,416],{"emptyLinePlaceholder":415},[68,13982,13983,13985,13987,13989,13991,13993,13996],{"class":70,"line":1009},[68,13984,2326],{"class":74},[68,13986,13863],{"class":1511},[68,13988,404],{"class":373},[68,13990,13953],{"class":1511},[68,13992,404],{"class":373},[68,13994,13995],{"class":1511},"gcs",[68,13997,2657],{"class":74},[68,13999,14000],{"class":70,"line":1029},[68,14001,416],{"emptyLinePlaceholder":415},[68,14003,14004,14006,14008,14010,14012],{"class":70,"line":1049},[68,14005,2326],{"class":74},[68,14007,13863],{"class":1511},[68,14009,404],{"class":373},[68,14011,1512],{"class":1511},[68,14013,2657],{"class":74},[68,14015,14016,14019,14021],{"class":70,"line":1069},[68,14017,14018],{"class":373},"tls_verify ",[68,14020,877],{"class":74},[68,14022,5717],{"class":12342},[68,14024,14025,14028,14030,14032,14035],{"class":70,"line":1088},[68,14026,14027],{"class":373},"image ",[68,14029,877],{"class":74},[68,14031,113],{"class":112},[68,14033,14034],{"class":116},"docker:latest",[68,14036,120],{"class":112},[68,14038,14039,14042,14044],{"class":70,"line":1108},[68,14040,14041],{"class":373},"privileged ",[68,14043,877],{"class":74},[68,14045,5717],{"class":12342},[68,14047,14048,14051,14053],{"class":70,"line":1128},[68,14049,14050],{"class":373},"disable_entrypoint_overwrite ",[68,14052,877],{"class":74},[68,14054,5717],{"class":12342},[68,14056,14057,14060,14062],{"class":70,"line":1148},[68,14058,14059],{"class":373},"oom_kill_disable ",[68,14061,877],{"class":74},[68,14063,5717],{"class":12342},[68,14065,14066,14069,14071],{"class":70,"line":1168},[68,14067,14068],{"class":373},"disable_cache ",[68,14070,877],{"class":74},[68,14072,5717],{"class":12342},[68,14074,14075,14078,14080],{"class":70,"line":1187},[68,14076,14077],{"class":373},"volumes ",[68,14079,877],{"class":74},[68,14081,183],{"class":74},[68,14083,14084,14086,14089,14091],{"class":70,"line":2039},[68,14085,82],{"class":112},[68,14087,14088],{"class":116},"\u002Fvar\u002Frun\u002Fdocker.sock:\u002Fvar\u002Frun\u002Fdocker.sock",[68,14090,89],{"class":112},[68,14092,169],{"class":74},[68,14094,14095,14097,14100],{"class":70,"line":2055},[68,14096,82],{"class":112},[68,14098,14099],{"class":116},"\u002Fxxx\u002Fgitlab-runner\u002Fcache:\u002Fcache",[68,14101,120],{"class":112},[68,14103,14104],{"class":70,"line":2071},[68,14105,2657],{"class":74},[68,14107,14108,14111,14113],{"class":70,"line":2087},[68,14109,14110],{"class":373},"shm_size ",[68,14112,877],{"class":74},[68,14114,2732],{"class":2397},[28,14116,14117,14118,14121,14122,14125,14126,14129],{},"其中 ",[65,14119,14120],{},"token"," 从 ",[65,14123,14124],{},"GitLab Admin Area \u002F Overview \u002F Runners"," 中可以找到，或者也可以从 ",[65,14127,14128],{},"Project \u002F Settings \u002F CI\u002FCD"," 中找到项目专用的 Runner token。",[1232,14131,14132],{"id":14132},"代码配置",[8514,14134,14136],{"id":14135},"前端node","前端（node）",[13801,14138,14140],{"id":14139},"dockerfile","Dockerfile",[58,14142,14145],{"className":14143,"code":14144,"language":14139,"meta":63,"style":63},"language-dockerfile shiki shiki-themes material-theme-lighter github-light github-dark","FROM node:10-alpine\n\nWORKDIR \u002Fapp\n\nCOPY package.json \u002Fapp\nCOPY yarn.lock \u002Fapp\nRUN yarn install\nCOPY . \u002Fapp\nRUN yarn build\n\nEXPOSE 8888\nENV APP_ENV $APP_ENV\nCMD [\"yarn\", \"start\"]\n",[65,14146,14147,14155,14159,14167,14171,14179,14186,14193,14200,14207,14211,14219,14227],{"__ignoreMap":63},[68,14148,14149,14152],{"class":70,"line":71},[68,14150,14151],{"class":575},"FROM",[68,14153,14154],{"class":373}," node:10-alpine\n",[68,14156,14157],{"class":70,"line":78},[68,14158,416],{"emptyLinePlaceholder":415},[68,14160,14161,14164],{"class":70,"line":98},[68,14162,14163],{"class":575},"WORKDIR",[68,14165,14166],{"class":373}," \u002Fapp\n",[68,14168,14169],{"class":70,"line":123},[68,14170,416],{"emptyLinePlaceholder":415},[68,14172,14173,14176],{"class":70,"line":129},[68,14174,14175],{"class":575},"COPY",[68,14177,14178],{"class":373}," package.json \u002Fapp\n",[68,14180,14181,14183],{"class":70,"line":212},[68,14182,14175],{"class":575},[68,14184,14185],{"class":373}," yarn.lock \u002Fapp\n",[68,14187,14188,14191],{"class":70,"line":233},[68,14189,14190],{"class":575},"RUN",[68,14192,13144],{"class":373},[68,14194,14195,14197],{"class":70,"line":268},[68,14196,14175],{"class":575},[68,14198,14199],{"class":373}," . \u002Fapp\n",[68,14201,14202,14204],{"class":70,"line":289},[68,14203,14190],{"class":575},[68,14205,14206],{"class":373}," yarn build\n",[68,14208,14209],{"class":70,"line":308},[68,14210,416],{"emptyLinePlaceholder":415},[68,14212,14213,14216],{"class":70,"line":314},[68,14214,14215],{"class":575},"EXPOSE",[68,14217,14218],{"class":373}," 8888\n",[68,14220,14221,14224],{"class":70,"line":320},[68,14222,14223],{"class":575},"ENV",[68,14225,14226],{"class":373}," APP_ENV $APP_ENV\n",[68,14228,14229,14232,14234,14237,14239,14242],{"class":70,"line":889},[68,14230,14231],{"class":575},"CMD",[68,14233,245],{"class":373},[68,14235,14236],{"class":116},"\"yarn\"",[68,14238,597],{"class":373},[68,14240,14241],{"class":116},"\"start\"",[68,14243,2657],{"class":373},[13801,14245,5949],{"id":14246},"gitlab-ciyml",[58,14248,14250],{"className":1557,"code":14249,"language":1559,"meta":63,"style":63},"image: docker:latest\n\nvariables:\n  REGISTRY: registry.cn-hangzhou.aliyuncs.com\n  USERNAME: your username\n  PASSWORD: your password\n  NAMESPACE: your namespace\n  PROJECT_NAME: your project name\n\nstages:\n  - build\n\ndocker-build:\n  stage: build\n  image: docker:latest\n  script:\n    - docker login --username=$USERNAME $REGISTRY -p $PASSWORD\n    - docker build -t $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:$CI_COMMIT_REF_NAME -t $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:latest .\n    - docker push $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:$CI_COMMIT_REF_NAME\n    - docker push $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:latest\n  only:\n    - tags\n",[65,14251,14252,14261,14265,14272,14282,14292,14302,14312,14322,14326,14332,14338,14342,14349,14357,14366,14372,14379,14386,14393,14400,14406],{"__ignoreMap":63},[68,14253,14254,14256,14258],{"class":70,"line":71},[68,14255,10423],{"class":730},[68,14257,92],{"class":74},[68,14259,14260],{"class":116}," docker:latest\n",[68,14262,14263],{"class":70,"line":78},[68,14264,416],{"emptyLinePlaceholder":415},[68,14266,14267,14270],{"class":70,"line":98},[68,14268,14269],{"class":730},"variables",[68,14271,1569],{"class":74},[68,14273,14274,14277,14279],{"class":70,"line":123},[68,14275,14276],{"class":730},"  REGISTRY",[68,14278,92],{"class":74},[68,14280,14281],{"class":116}," registry.cn-hangzhou.aliyuncs.com\n",[68,14283,14284,14287,14289],{"class":70,"line":129},[68,14285,14286],{"class":730},"  USERNAME",[68,14288,92],{"class":74},[68,14290,14291],{"class":116}," your username\n",[68,14293,14294,14297,14299],{"class":70,"line":212},[68,14295,14296],{"class":730},"  PASSWORD",[68,14298,92],{"class":74},[68,14300,14301],{"class":116}," your password\n",[68,14303,14304,14307,14309],{"class":70,"line":233},[68,14305,14306],{"class":730},"  NAMESPACE",[68,14308,92],{"class":74},[68,14310,14311],{"class":116}," your namespace\n",[68,14313,14314,14317,14319],{"class":70,"line":268},[68,14315,14316],{"class":730},"  PROJECT_NAME",[68,14318,92],{"class":74},[68,14320,14321],{"class":116}," your project name\n",[68,14323,14324],{"class":70,"line":289},[68,14325,416],{"emptyLinePlaceholder":415},[68,14327,14328,14330],{"class":70,"line":308},[68,14329,5959],{"class":730},[68,14331,1569],{"class":74},[68,14333,14334,14336],{"class":70,"line":314},[68,14335,5966],{"class":74},[68,14337,12927],{"class":116},[68,14339,14340],{"class":70,"line":320},[68,14341,416],{"emptyLinePlaceholder":415},[68,14343,14344,14347],{"class":70,"line":889},[68,14345,14346],{"class":730},"docker-build",[68,14348,1569],{"class":74},[68,14350,14351,14353,14355],{"class":70,"line":909},[68,14352,5992],{"class":730},[68,14354,92],{"class":74},[68,14356,12927],{"class":116},[68,14358,14359,14362,14364],{"class":70,"line":929},[68,14360,14361],{"class":730},"  image",[68,14363,92],{"class":74},[68,14365,14260],{"class":116},[68,14367,14368,14370],{"class":70,"line":949},[68,14369,6001],{"class":730},[68,14371,1569],{"class":74},[68,14373,14374,14376],{"class":70,"line":969},[68,14375,6008],{"class":74},[68,14377,14378],{"class":116}," docker login --username=$USERNAME $REGISTRY -p $PASSWORD\n",[68,14380,14381,14383],{"class":70,"line":989},[68,14382,6008],{"class":74},[68,14384,14385],{"class":116}," docker build -t $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:$CI_COMMIT_REF_NAME -t $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:latest .\n",[68,14387,14388,14390],{"class":70,"line":1009},[68,14389,6008],{"class":74},[68,14391,14392],{"class":116}," docker push $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:$CI_COMMIT_REF_NAME\n",[68,14394,14395,14397],{"class":70,"line":1029},[68,14396,6008],{"class":74},[68,14398,14399],{"class":116}," docker push $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:latest\n",[68,14401,14402,14404],{"class":70,"line":1049},[68,14403,7088],{"class":730},[68,14405,1569],{"class":74},[68,14407,14408,14410],{"class":70,"line":1069},[68,14409,6008],{"class":74},[68,14411,10529],{"class":116},[8514,14413,14415],{"id":14414},"后端spring-boot","后端（spring boot）",[13801,14417,14140],{"id":14418},"dockerfile-1",[58,14420,14422],{"className":14143,"code":14421,"language":14139,"meta":63,"style":63},"FROM openjdk:11-jre-slim\n\nRUN ln -sf \u002Fusr\u002Fshare\u002Fzoneinfo\u002FAsia\u002FShanghai \u002Fetc\u002Flocaltime\n\nVOLUME \u002Ftmp\n\nCOPY target\u002Fxxx-api.jar app.jar\nENV SPRING_PROFILES_ACTIVE=\"prd\"\nENV JAVA_OPTS=\"-Xmx256m\"\nENTRYPOINT [ \"java\", \"-Djava.security.egd=file:\u002Fdev\u002F.\u002Furandom\", \"-jar\", \"\u002Fapp.jar\"]\n",[65,14423,14424,14431,14435,14442,14446,14454,14458,14465,14475,14485],{"__ignoreMap":63},[68,14425,14426,14428],{"class":70,"line":71},[68,14427,14151],{"class":575},[68,14429,14430],{"class":373}," openjdk:11-jre-slim\n",[68,14432,14433],{"class":70,"line":78},[68,14434,416],{"emptyLinePlaceholder":415},[68,14436,14437,14439],{"class":70,"line":98},[68,14438,14190],{"class":575},[68,14440,14441],{"class":373}," ln -sf \u002Fusr\u002Fshare\u002Fzoneinfo\u002FAsia\u002FShanghai \u002Fetc\u002Flocaltime\n",[68,14443,14444],{"class":70,"line":123},[68,14445,416],{"emptyLinePlaceholder":415},[68,14447,14448,14451],{"class":70,"line":129},[68,14449,14450],{"class":575},"VOLUME",[68,14452,14453],{"class":373}," \u002Ftmp\n",[68,14455,14456],{"class":70,"line":212},[68,14457,416],{"emptyLinePlaceholder":415},[68,14459,14460,14462],{"class":70,"line":233},[68,14461,14175],{"class":575},[68,14463,14464],{"class":373}," target\u002Fxxx-api.jar app.jar\n",[68,14466,14467,14469,14472],{"class":70,"line":268},[68,14468,14223],{"class":575},[68,14470,14471],{"class":373}," SPRING_PROFILES_ACTIVE=",[68,14473,14474],{"class":116},"\"prd\"\n",[68,14476,14477,14479,14482],{"class":70,"line":289},[68,14478,14223],{"class":575},[68,14480,14481],{"class":373}," JAVA_OPTS=",[68,14483,14484],{"class":116},"\"-Xmx256m\"\n",[68,14486,14487,14490,14493,14496,14498,14501,14503,14506,14508,14511],{"class":70,"line":308},[68,14488,14489],{"class":575},"ENTRYPOINT",[68,14491,14492],{"class":373}," [ ",[68,14494,14495],{"class":116},"\"java\"",[68,14497,597],{"class":373},[68,14499,14500],{"class":116},"\"-Djava.security.egd=file:\u002Fdev\u002F.\u002Furandom\"",[68,14502,597],{"class":373},[68,14504,14505],{"class":116},"\"-jar\"",[68,14507,597],{"class":373},[68,14509,14510],{"class":116},"\"\u002Fapp.jar\"",[68,14512,2657],{"class":373},[13801,14514,5949],{"id":14515},"gitlab-ciyml-1",[58,14517,14519],{"className":1557,"code":14518,"language":1559,"meta":63,"style":63},"image: docker:latest\n\nvariables:\n  MAVEN_OPTS: -Dmaven.repo.local=\u002Fcache\u002F.m2\u002Frepository\n  REGISTRY: registry.cn-hangzhou.aliyuncs.com\n  USERNAME: your username\n  PASSWORD: your password\n  NAMESPACE: your namespace\n  PROJECT_NAME: your project name\n\nstages:\n  - package\n  - build\n\nmaven-package:\n  image: maven:3.6-jdk-11-slim\n  stage: package\n  script:\n    - mvn $MAVEN_OPTS clean package -Dmaven.test.skip=true\n    - cp target\u002F$PROJECT_NAME.jar \u002Fcache\u002Fjars\u002F\n  only:\n    - tags\n\ndocker-build:\n  stage: build\n  image: docker:latest\n  script:\n    - docker login --username=$USERNAME $REGISTRY -p $PASSWORD\n    - mkdir target\n    - cp \u002Fcache\u002Fjars\u002F$PROJECT_NAME.jar target\n    - docker build -t $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:$CI_COMMIT_REF_NAME -t $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:latest .\n    - docker push $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:$CI_COMMIT_REF_NAME\n    - docker push $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:latest\n  only:\n    - tags\n",[65,14520,14521,14529,14533,14539,14549,14557,14565,14573,14581,14589,14593,14599,14606,14612,14616,14623,14632,14640,14646,14653,14660,14666,14672,14676,14682,14690,14698,14704,14710,14717,14724,14730,14736,14742,14748],{"__ignoreMap":63},[68,14522,14523,14525,14527],{"class":70,"line":71},[68,14524,10423],{"class":730},[68,14526,92],{"class":74},[68,14528,14260],{"class":116},[68,14530,14531],{"class":70,"line":78},[68,14532,416],{"emptyLinePlaceholder":415},[68,14534,14535,14537],{"class":70,"line":98},[68,14536,14269],{"class":730},[68,14538,1569],{"class":74},[68,14540,14541,14544,14546],{"class":70,"line":123},[68,14542,14543],{"class":730},"  MAVEN_OPTS",[68,14545,92],{"class":74},[68,14547,14548],{"class":116}," -Dmaven.repo.local=\u002Fcache\u002F.m2\u002Frepository\n",[68,14550,14551,14553,14555],{"class":70,"line":129},[68,14552,14276],{"class":730},[68,14554,92],{"class":74},[68,14556,14281],{"class":116},[68,14558,14559,14561,14563],{"class":70,"line":212},[68,14560,14286],{"class":730},[68,14562,92],{"class":74},[68,14564,14291],{"class":116},[68,14566,14567,14569,14571],{"class":70,"line":233},[68,14568,14296],{"class":730},[68,14570,92],{"class":74},[68,14572,14301],{"class":116},[68,14574,14575,14577,14579],{"class":70,"line":268},[68,14576,14306],{"class":730},[68,14578,92],{"class":74},[68,14580,14311],{"class":116},[68,14582,14583,14585,14587],{"class":70,"line":289},[68,14584,14316],{"class":730},[68,14586,92],{"class":74},[68,14588,14321],{"class":116},[68,14590,14591],{"class":70,"line":308},[68,14592,416],{"emptyLinePlaceholder":415},[68,14594,14595,14597],{"class":70,"line":314},[68,14596,5959],{"class":730},[68,14598,1569],{"class":74},[68,14600,14601,14603],{"class":70,"line":320},[68,14602,5966],{"class":74},[68,14604,14605],{"class":116}," package\n",[68,14607,14608,14610],{"class":70,"line":889},[68,14609,5966],{"class":74},[68,14611,12927],{"class":116},[68,14613,14614],{"class":70,"line":909},[68,14615,416],{"emptyLinePlaceholder":415},[68,14617,14618,14621],{"class":70,"line":929},[68,14619,14620],{"class":730},"maven-package",[68,14622,1569],{"class":74},[68,14624,14625,14627,14629],{"class":70,"line":949},[68,14626,14361],{"class":730},[68,14628,92],{"class":74},[68,14630,14631],{"class":116}," maven:3.6-jdk-11-slim\n",[68,14633,14634,14636,14638],{"class":70,"line":969},[68,14635,5992],{"class":730},[68,14637,92],{"class":74},[68,14639,14605],{"class":116},[68,14641,14642,14644],{"class":70,"line":989},[68,14643,6001],{"class":730},[68,14645,1569],{"class":74},[68,14647,14648,14650],{"class":70,"line":1009},[68,14649,6008],{"class":74},[68,14651,14652],{"class":116}," mvn $MAVEN_OPTS clean package -Dmaven.test.skip=true\n",[68,14654,14655,14657],{"class":70,"line":1029},[68,14656,6008],{"class":74},[68,14658,14659],{"class":116}," cp target\u002F$PROJECT_NAME.jar \u002Fcache\u002Fjars\u002F\n",[68,14661,14662,14664],{"class":70,"line":1049},[68,14663,7088],{"class":730},[68,14665,1569],{"class":74},[68,14667,14668,14670],{"class":70,"line":1069},[68,14669,6008],{"class":74},[68,14671,10529],{"class":116},[68,14673,14674],{"class":70,"line":1088},[68,14675,416],{"emptyLinePlaceholder":415},[68,14677,14678,14680],{"class":70,"line":1108},[68,14679,14346],{"class":730},[68,14681,1569],{"class":74},[68,14683,14684,14686,14688],{"class":70,"line":1128},[68,14685,5992],{"class":730},[68,14687,92],{"class":74},[68,14689,12927],{"class":116},[68,14691,14692,14694,14696],{"class":70,"line":1148},[68,14693,14361],{"class":730},[68,14695,92],{"class":74},[68,14697,14260],{"class":116},[68,14699,14700,14702],{"class":70,"line":1168},[68,14701,6001],{"class":730},[68,14703,1569],{"class":74},[68,14705,14706,14708],{"class":70,"line":1187},[68,14707,6008],{"class":74},[68,14709,14378],{"class":116},[68,14711,14712,14714],{"class":70,"line":2039},[68,14713,6008],{"class":74},[68,14715,14716],{"class":116}," mkdir target\n",[68,14718,14719,14721],{"class":70,"line":2055},[68,14720,6008],{"class":74},[68,14722,14723],{"class":116}," cp \u002Fcache\u002Fjars\u002F$PROJECT_NAME.jar target\n",[68,14725,14726,14728],{"class":70,"line":2071},[68,14727,6008],{"class":74},[68,14729,14385],{"class":116},[68,14731,14732,14734],{"class":70,"line":2087},[68,14733,6008],{"class":74},[68,14735,14392],{"class":116},[68,14737,14738,14740],{"class":70,"line":2092},[68,14739,6008],{"class":74},[68,14741,14399],{"class":116},[68,14743,14744,14746],{"class":70,"line":2097},[68,14745,7088],{"class":730},[68,14747,1569],{"class":74},[68,14749,14750,14752],{"class":70,"line":2114},[68,14751,6008],{"class":74},[68,14753,10529],{"class":116},[1232,14755,14757],{"id":14756},"阿里云-k8s-配置","阿里云 k8s 配置",[28,14759,14760],{},"应用创建触发器：",[554,14762],{"filename":556},[28,14764,14765],{},"复制触发器 URL 到镜像仓库中创建推送触发器：",[554,14767],{"filename":1203},[28,14769,14770],{},"完成。",[523,14772,14773],{},"html pre.shiki code .sw1J6, html code.shiki .sw1J6{--shiki-light:#F76D47;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":63,"searchDepth":78,"depth":78,"links":14775},[14776,14777,14778],{"id":13798,"depth":78,"text":13799},{"id":14132,"depth":78,"text":14132},{"id":14756,"depth":78,"text":14757},"2020-12-27","今年对于我个人而言，在 DevOps 上的最大收获，莫过于摸索了这套基于 GitLab CI 和 k8s 的持续交付解决方案，其实原理都很简单，在我去年的方案里又做了改进，实现了基于 git tag 的触发方式，并且把原先的本地打包推镜像改为在 GitLab Runner 上打包推镜像。",{},"\u002Fposts\u002F2020\u002Fdevops-gitlab-ci-aliyun-k8s",{"text":535,"minutes":14784,"time":14785,"words":14786},2.635,158100,527,{"title":13763,"description":14780},{"loc":14782},"posts\u002F2020\u002F20201227.devops-gitlab-ci-aliyun-k8s",[543,7153,14791],"k8s","Jz27CSI5_8F6FrpDl81aWz-abbc2KGOJHbYI2yw83E8",{"id":14794,"title":14795,"body":14796,"class":528,"cover":1212,"coverSize":528,"date":15799,"description":14800,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":15800,"navigation":415,"path":15801,"readingTime":15802,"seo":15806,"sitemap":15807,"stem":15808,"tags":15809,"time":528,"weather":528,"__hash__":15810},"posts\u002Fposts\u002F2020\u002F20201218.android-webview-picture-cache.md","安卓 WebView 图片离线缓存方案",{"type":25,"value":14797,"toc":15797},[14798,14801,14804,14807,14828,14831,15794],[28,14799,14800],{},"有这样一个项目，UI 渲染全部由 WebView 来完成，套个安卓的壳，壳子里面做一些和硬件交互的功能，例如摄像头、麦克风等。WebView 加载的页面走的本地打包的文件。不过 WebView 中的图片等资源走的是网络访问。",[28,14802,14803],{},"为了减少网络访问的流量，以及提升在弱网络或无网络情况下的体验，需要对网络访问的图片进行本地缓存。",[28,14805,14806],{},"原先采用的是 WebView 自带的缓存机制来实现，但并不可靠，于是需要通过拦截网络请求，通过本地缓存干预的方式来实现。具体原理如下：",[5010,14808,14809,14815,14825],{},[1239,14810,7569,14811,14814],{},[65,14812,14813],{},"shouldInterceptRequest"," 拦截请求，判断是否是访问网络图片，如果是则进行干预",[1239,14816,14817,14818,14821,14822,14824],{},"取请求地址的 ",[65,14819,14820],{},"md5"," 值加图片文件扩展名组成的文件名，拼接 ",[65,14823,13953],{}," 目录获得一个本地资源地址，判断该资源是否存在，若存在则直接返回该资源",[1239,14826,14827],{},"若该资源不存在，说明是首次访问，则将该网络图片下载到该地址下，并返回该资源",[28,14829,14830],{},"具体代码如下：",[58,14832,14836],{"className":14833,"code":14834,"language":14835,"meta":63,"style":63},"language-kotlin shiki shiki-themes material-theme-lighter github-light github-dark","import android.content.Context\nimport android.net.http.SslError\nimport android.webkit.*\nimport androidx.webkit.WebViewAssetLoader\nimport java.io.File\nimport java.io.FileOutputStream\nimport java.math.BigInteger\nimport java.net.HttpURLConnection\nimport java.net.URL\nimport java.security.MessageDigest\n\nclass CommonWebClient(context: Context) : WebViewClient() {\n    private var assetLoader: WebViewAssetLoader = WebViewAssetLoader.Builder()\n            .addPathHandler(\"\u002Fassets\u002F\", WebViewAssetLoader.AssetsPathHandler(context))\n            .build()\n\n    private fun md5(input: String): String {\n        return BigInteger(1, MessageDigest.getInstance(\"MD5\").digest(input.toByteArray())).toString(16).padStart(32, '0')\n    }\n\n    override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {\n        return true\n    }\n\n    override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {\n        return request?.url?.let { url ->\n            val urlString = url.toString()\n            if (!urlString.contains(\"appassets.androidplatform.net\") && urlString.contains(\"aliyuncs.com\")) {\n                try {\n                    var extension = urlString.substring(urlString.lastIndexOf(\".\"))\n                    if (extension.lastIndexOf(\"?\") > -1) {\n                        extension = extension.substring(0, extension.lastIndexOf(\"?\"))\n                    }\n                    val fileName = \"${md5(urlString)}${extension}\"\n                    val file = File(view?.context?.externalCacheDir, fileName)\n                    if (!file.exists()) {\n                        val conn = URL(urlString).openConnection() as HttpURLConnection\n                        conn.connectTimeout = 5000\n                        conn.requestMethod = \"GET\"\n                        conn.doInput = true\n                        if (conn.responseCode == 200) {\n                            val fos = FileOutputStream(file)\n                            val buffer = ByteArray(1024)\n                            var len = 0\n                            while (conn.inputStream.read(buffer).also { len = it } != -1) {\n                                fos.write(buffer, 0, len)\n                            }\n                            conn.inputStream.close()\n                            fos.close()\n                        }\n                    }\n                    WebResourceResponse(MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension), \"UTF-8\", file.inputStream())\n                } catch (ex: Exception) {\n                    assetLoader.shouldInterceptRequest(url)\n                }\n            } else {\n                val response = assetLoader.shouldInterceptRequest(url)\n                if (url.path?.endsWith(\".js\") == true && response != null) {\n                    response.mimeType = \"text\u002Fjavascript\"\n                }\n                response\n            }\n        }\n    }\n\n    override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {\n        handler?.proceed()\n        super.onReceivedSslError(view, handler, error)\n    }\n}\n","kotlin",[65,14837,14838,14845,14852,14862,14869,14876,14883,14890,14897,14904,14911,14915,14937,14961,14983,14991,14995,15018,15079,15083,15087,15115,15122,15126,15130,15158,15174,15191,15229,15236,15264,15289,15316,15321,15350,15364,15381,15408,15418,15428,15437,15450,15466,15485,15497,15530,15546,15551,15561,15570,15575,15579,15610,15626,15636,15641,15651,15668,15700,15710,15714,15719,15723,15727,15731,15735,15763,15773,15786,15790],{"__ignoreMap":63},[68,14839,14840,14842],{"class":70,"line":71},[68,14841,1791],{"class":1899},[68,14843,14844],{"class":1511}," android.content.Context\n",[68,14846,14847,14849],{"class":70,"line":78},[68,14848,1791],{"class":1899},[68,14850,14851],{"class":1511}," android.net.http.SslError\n",[68,14853,14854,14856,14859],{"class":70,"line":98},[68,14855,1791],{"class":1899},[68,14857,14858],{"class":1511}," android.webkit.",[68,14860,14861],{"class":665},"*\n",[68,14863,14864,14866],{"class":70,"line":123},[68,14865,1791],{"class":1899},[68,14867,14868],{"class":1511}," androidx.webkit.WebViewAssetLoader\n",[68,14870,14871,14873],{"class":70,"line":129},[68,14872,1791],{"class":1899},[68,14874,14875],{"class":1511}," java.io.File\n",[68,14877,14878,14880],{"class":70,"line":212},[68,14879,1791],{"class":1899},[68,14881,14882],{"class":1511}," java.io.FileOutputStream\n",[68,14884,14885,14887],{"class":70,"line":233},[68,14886,1791],{"class":1899},[68,14888,14889],{"class":1511}," java.math.BigInteger\n",[68,14891,14892,14894],{"class":70,"line":268},[68,14893,1791],{"class":1899},[68,14895,14896],{"class":1511}," java.net.HttpURLConnection\n",[68,14898,14899,14901],{"class":70,"line":289},[68,14900,1791],{"class":1899},[68,14902,14903],{"class":1511}," java.net.URL\n",[68,14905,14906,14908],{"class":70,"line":308},[68,14907,1791],{"class":1899},[68,14909,14910],{"class":1511}," java.security.MessageDigest\n",[68,14912,14913],{"class":70,"line":314},[68,14914,416],{"emptyLinePlaceholder":415},[68,14916,14917,14919,14922,14925,14928,14931,14934],{"class":70,"line":320},[68,14918,1865],{"class":1899},[68,14920,14921],{"class":1511}," CommonWebClient",[68,14923,14924],{"class":373},"(context: ",[68,14926,14927],{"class":1511},"Context",[68,14929,14930],{"class":373},") : ",[68,14932,14933],{"class":1511},"WebViewClient",[68,14935,14936],{"class":373},"() {\n",[68,14938,14939,14942,14945,14948,14951,14953,14956,14959],{"class":70,"line":889},[68,14940,14941],{"class":1864},"    private",[68,14943,14944],{"class":1899}," var",[68,14946,14947],{"class":373}," assetLoader: ",[68,14949,14950],{"class":1511},"WebViewAssetLoader",[68,14952,1900],{"class":1899},[68,14954,14955],{"class":373}," WebViewAssetLoader.",[68,14957,14958],{"class":2183},"Builder",[68,14960,2136],{"class":373},[68,14962,14963,14966,14969,14971,14974,14977,14980],{"class":70,"line":909},[68,14964,14965],{"class":373},"            .",[68,14967,14968],{"class":2183},"addPathHandler",[68,14970,648],{"class":373},[68,14972,14973],{"class":116},"\"\u002Fassets\u002F\"",[68,14975,14976],{"class":373},", WebViewAssetLoader.",[68,14978,14979],{"class":2183},"AssetsPathHandler",[68,14981,14982],{"class":373},"(context))\n",[68,14984,14985,14987,14989],{"class":70,"line":929},[68,14986,14965],{"class":373},[68,14988,13012],{"class":2183},[68,14990,2136],{"class":373},[68,14992,14993],{"class":70,"line":949},[68,14994,416],{"emptyLinePlaceholder":415},[68,14996,14997,14999,15002,15005,15008,15011,15014,15016],{"class":70,"line":969},[68,14998,14941],{"class":1864},[68,15000,15001],{"class":1899}," fun",[68,15003,15004],{"class":2183}," md5",[68,15006,15007],{"class":373},"(input: ",[68,15009,15010],{"class":1511},"String",[68,15012,15013],{"class":373},"): ",[68,15015,15010],{"class":1511},[68,15017,95],{"class":373},[68,15019,15020,15022,15025,15027,15029,15032,15035,15037,15040,15042,15045,15048,15051,15054,15057,15059,15062,15064,15067,15069,15072,15074,15077],{"class":70,"line":989},[68,15021,2196],{"class":1790},[68,15023,15024],{"class":2183}," BigInteger",[68,15026,648],{"class":373},[68,15028,401],{"class":2397},[68,15030,15031],{"class":373},", MessageDigest.",[68,15033,15034],{"class":2183},"getInstance",[68,15036,648],{"class":373},[68,15038,15039],{"class":116},"\"MD5\"",[68,15041,4608],{"class":373},[68,15043,15044],{"class":2183},"digest",[68,15046,15047],{"class":373},"(input.",[68,15049,15050],{"class":2183},"toByteArray",[68,15052,15053],{"class":373},"())).",[68,15055,15056],{"class":2183},"toString",[68,15058,648],{"class":373},[68,15060,15061],{"class":2397},"16",[68,15063,4608],{"class":373},[68,15065,15066],{"class":2183},"padStart",[68,15068,648],{"class":373},[68,15070,15071],{"class":2397},"32",[68,15073,597],{"class":373},[68,15075,15076],{"class":116},"'0'",[68,15078,410],{"class":373},[68,15080,15081],{"class":70,"line":1009},[68,15082,311],{"class":373},[68,15084,15085],{"class":70,"line":1029},[68,15086,416],{"emptyLinePlaceholder":415},[68,15088,15089,15092,15094,15097,15100,15103,15106,15108,15110,15113],{"class":70,"line":1049},[68,15090,15091],{"class":1864},"    override",[68,15093,15001],{"class":1899},[68,15095,15096],{"class":2183}," shouldOverrideUrlLoading",[68,15098,15099],{"class":373},"(view: ",[68,15101,15102],{"class":1511},"WebView",[68,15104,15105],{"class":373},", url: ",[68,15107,15010],{"class":1511},[68,15109,15013],{"class":373},[68,15111,15112],{"class":1511},"Boolean",[68,15114,95],{"class":373},[68,15116,15117,15119],{"class":70,"line":1069},[68,15118,2196],{"class":1790},[68,15120,15121],{"class":12342}," true\n",[68,15123,15124],{"class":70,"line":1088},[68,15125,311],{"class":373},[68,15127,15128],{"class":70,"line":1108},[68,15129,416],{"emptyLinePlaceholder":415},[68,15131,15132,15134,15136,15139,15141,15143,15146,15149,15152,15155],{"class":70,"line":1128},[68,15133,15091],{"class":1864},[68,15135,15001],{"class":1899},[68,15137,15138],{"class":2183}," shouldInterceptRequest",[68,15140,15099],{"class":373},[68,15142,15102],{"class":1511},[68,15144,15145],{"class":373},"?, request: ",[68,15147,15148],{"class":1511},"WebResourceRequest",[68,15150,15151],{"class":373},"?): ",[68,15153,15154],{"class":1511},"WebResourceResponse",[68,15156,15157],{"class":373},"? {\n",[68,15159,15160,15162,15165,15168,15171],{"class":70,"line":1148},[68,15161,2196],{"class":1790},[68,15163,15164],{"class":373}," request?.url?.",[68,15166,15167],{"class":2183},"let",[68,15169,15170],{"class":373}," { url ",[68,15172,15173],{"class":1864},"->\n",[68,15175,15176,15179,15182,15184,15187,15189],{"class":70,"line":1168},[68,15177,15178],{"class":1899},"            val",[68,15180,15181],{"class":373}," urlString ",[68,15183,877],{"class":1899},[68,15185,15186],{"class":373}," url.",[68,15188,15056],{"class":2183},[68,15190,2136],{"class":373},[68,15192,15193,15196,15198,15200,15203,15206,15208,15211,15213,15216,15219,15221,15223,15226],{"class":70,"line":1187},[68,15194,15195],{"class":1790},"            if",[68,15197,7664],{"class":373},[68,15199,11642],{"class":1899},[68,15201,15202],{"class":373},"urlString.",[68,15204,15205],{"class":2183},"contains",[68,15207,648],{"class":373},[68,15209,15210],{"class":116},"\"appassets.androidplatform.net\"",[68,15212,588],{"class":373},[68,15214,15215],{"class":1899},"&&",[68,15217,15218],{"class":373}," urlString.",[68,15220,15205],{"class":2183},[68,15222,648],{"class":373},[68,15224,15225],{"class":116},"\"aliyuncs.com\"",[68,15227,15228],{"class":373},")) {\n",[68,15230,15231,15234],{"class":70,"line":2039},[68,15232,15233],{"class":1790},"                try",[68,15235,95],{"class":373},[68,15237,15238,15241,15244,15246,15248,15251,15254,15257,15259,15262],{"class":70,"line":2055},[68,15239,15240],{"class":1899},"                    var",[68,15242,15243],{"class":373}," extension ",[68,15245,877],{"class":1899},[68,15247,15218],{"class":373},[68,15249,15250],{"class":2183},"substring",[68,15252,15253],{"class":373},"(urlString.",[68,15255,15256],{"class":2183},"lastIndexOf",[68,15258,648],{"class":373},[68,15260,15261],{"class":116},"\".\"",[68,15263,8847],{"class":373},[68,15265,15266,15268,15271,15273,15275,15278,15280,15282,15284,15286],{"class":70,"line":2071},[68,15267,3141],{"class":1790},[68,15269,15270],{"class":373}," (extension.",[68,15272,15256],{"class":2183},[68,15274,648],{"class":373},[68,15276,15277],{"class":116},"\"?\"",[68,15279,588],{"class":373},[68,15281,765],{"class":1899},[68,15283,2394],{"class":1899},[68,15285,401],{"class":2397},[68,15287,15288],{"class":373},") {\n",[68,15290,15291,15294,15296,15299,15301,15303,15305,15308,15310,15312,15314],{"class":70,"line":2087},[68,15292,15293],{"class":373},"                        extension ",[68,15295,877],{"class":1899},[68,15297,15298],{"class":373}," extension.",[68,15300,15250],{"class":2183},[68,15302,648],{"class":373},[68,15304,768],{"class":2397},[68,15306,15307],{"class":373},", extension.",[68,15309,15256],{"class":2183},[68,15311,648],{"class":373},[68,15313,15277],{"class":116},[68,15315,8847],{"class":373},[68,15317,15318],{"class":70,"line":2092},[68,15319,15320],{"class":373},"                    }\n",[68,15322,15323,15326,15329,15331,15333,15335,15337,15340,15343,15346,15348],{"class":70,"line":2097},[68,15324,15325],{"class":1899},"                    val",[68,15327,15328],{"class":373}," fileName ",[68,15330,877],{"class":1899},[68,15332,113],{"class":116},[68,15334,11788],{"class":112},[68,15336,14820],{"class":2183},[68,15338,15339],{"class":11805},"(urlString)",[68,15341,15342],{"class":112},"}${",[68,15344,15345],{"class":11805},"extension",[68,15347,2400],{"class":112},[68,15349,120],{"class":116},[68,15351,15352,15354,15357,15359,15361],{"class":70,"line":2114},[68,15353,15325],{"class":1899},[68,15355,15356],{"class":373}," file ",[68,15358,877],{"class":1899},[68,15360,7722],{"class":2183},[68,15362,15363],{"class":373},"(view?.context?.externalCacheDir, fileName)\n",[68,15365,15366,15368,15370,15372,15375,15378],{"class":70,"line":2139},[68,15367,3141],{"class":1790},[68,15369,7664],{"class":373},[68,15371,11642],{"class":1899},[68,15373,15374],{"class":373},"file.",[68,15376,15377],{"class":2183},"exists",[68,15379,15380],{"class":373},"()) {\n",[68,15382,15383,15386,15389,15391,15394,15397,15400,15403,15405],{"class":70,"line":2158},[68,15384,15385],{"class":1899},"                        val",[68,15387,15388],{"class":373}," conn ",[68,15390,877],{"class":1899},[68,15392,15393],{"class":2183}," URL",[68,15395,15396],{"class":373},"(urlString).",[68,15398,15399],{"class":2183},"openConnection",[68,15401,15402],{"class":373},"() ",[68,15404,591],{"class":1899},[68,15406,15407],{"class":373}," HttpURLConnection\n",[68,15409,15410,15413,15415],{"class":70,"line":2173},[68,15411,15412],{"class":373},"                        conn.connectTimeout ",[68,15414,877],{"class":1899},[68,15416,15417],{"class":2397}," 5000\n",[68,15419,15420,15423,15425],{"class":70,"line":2178},[68,15421,15422],{"class":373},"                        conn.requestMethod ",[68,15424,877],{"class":1899},[68,15426,15427],{"class":116}," \"GET\"\n",[68,15429,15430,15433,15435],{"class":70,"line":2193},[68,15431,15432],{"class":373},"                        conn.doInput ",[68,15434,877],{"class":1899},[68,15436,15121],{"class":12342},[68,15438,15439,15441,15444,15446,15448],{"class":70,"line":2201},[68,15440,3253],{"class":1790},[68,15442,15443],{"class":373}," (conn.responseCode ",[68,15445,3660],{"class":1899},[68,15447,3154],{"class":2397},[68,15449,15288],{"class":373},[68,15451,15452,15455,15458,15460,15463],{"class":70,"line":2207},[68,15453,15454],{"class":1899},"                            val",[68,15456,15457],{"class":373}," fos ",[68,15459,877],{"class":1899},[68,15461,15462],{"class":2183}," FileOutputStream",[68,15464,15465],{"class":373},"(file)\n",[68,15467,15468,15470,15473,15475,15478,15480,15483],{"class":70,"line":2234},[68,15469,15454],{"class":1899},[68,15471,15472],{"class":373}," buffer ",[68,15474,877],{"class":1899},[68,15476,15477],{"class":2183}," ByteArray",[68,15479,648],{"class":373},[68,15481,15482],{"class":2397},"1024",[68,15484,410],{"class":373},[68,15486,15487,15490,15493,15495],{"class":70,"line":2258},[68,15488,15489],{"class":1899},"                            var",[68,15491,15492],{"class":373}," len ",[68,15494,877],{"class":1899},[68,15496,2732],{"class":2397},[68,15498,15499,15502,15505,15507,15510,15513,15516,15518,15521,15524,15526,15528],{"class":70,"line":2264},[68,15500,15501],{"class":1790},"                            while",[68,15503,15504],{"class":373}," (conn.inputStream.",[68,15506,8206],{"class":2183},[68,15508,15509],{"class":373},"(buffer).",[68,15511,15512],{"class":2183},"also",[68,15514,15515],{"class":373}," { len ",[68,15517,877],{"class":1899},[68,15519,15520],{"class":373}," it } ",[68,15522,15523],{"class":1899},"!=",[68,15525,2394],{"class":1899},[68,15527,401],{"class":2397},[68,15529,15288],{"class":373},[68,15531,15532,15535,15538,15541,15543],{"class":70,"line":2270},[68,15533,15534],{"class":373},"                                fos.",[68,15536,15537],{"class":2183},"write",[68,15539,15540],{"class":373},"(buffer, ",[68,15542,768],{"class":2397},[68,15544,15545],{"class":373},", len)\n",[68,15547,15548],{"class":70,"line":2275},[68,15549,15550],{"class":373},"                            }\n",[68,15552,15553,15556,15559],{"class":70,"line":2289},[68,15554,15555],{"class":373},"                            conn.inputStream.",[68,15557,15558],{"class":2183},"close",[68,15560,2136],{"class":373},[68,15562,15563,15566,15568],{"class":70,"line":2339},[68,15564,15565],{"class":373},"                            fos.",[68,15567,15558],{"class":2183},[68,15569,2136],{"class":373},[68,15571,15572],{"class":70,"line":2363},[68,15573,15574],{"class":373},"                        }\n",[68,15576,15577],{"class":70,"line":2374},[68,15578,15320],{"class":373},[68,15580,15581,15584,15587,15590,15593,15596,15599,15602,15605,15608],{"class":70,"line":2407},[68,15582,15583],{"class":2183},"                    WebResourceResponse",[68,15585,15586],{"class":373},"(MimeTypeMap.",[68,15588,15589],{"class":2183},"getSingleton",[68,15591,15592],{"class":373},"().",[68,15594,15595],{"class":2183},"getMimeTypeFromExtension",[68,15597,15598],{"class":373},"(extension), ",[68,15600,15601],{"class":116},"\"UTF-8\"",[68,15603,15604],{"class":373},", file.",[68,15606,15607],{"class":2183},"inputStream",[68,15609,12009],{"class":373},[68,15611,15612,15615,15618,15621,15624],{"class":70,"line":2421},[68,15613,15614],{"class":373},"                } ",[68,15616,15617],{"class":1899},"catch",[68,15619,15620],{"class":373}," (ex: ",[68,15622,15623],{"class":1511},"Exception",[68,15625,15288],{"class":373},[68,15627,15628,15631,15633],{"class":70,"line":2426},[68,15629,15630],{"class":373},"                    assetLoader.",[68,15632,14813],{"class":2183},[68,15634,15635],{"class":373},"(url)\n",[68,15637,15638],{"class":70,"line":2432},[68,15639,15640],{"class":373},"                }\n",[68,15642,15643,15646,15649],{"class":70,"line":2454},[68,15644,15645],{"class":373},"            } ",[68,15647,15648],{"class":1790},"else",[68,15650,95],{"class":373},[68,15652,15653,15656,15659,15661,15664,15666],{"class":70,"line":2500},[68,15654,15655],{"class":1899},"                val",[68,15657,15658],{"class":373}," response ",[68,15660,877],{"class":1899},[68,15662,15663],{"class":373}," assetLoader.",[68,15665,14813],{"class":2183},[68,15667,15635],{"class":373},[68,15669,15670,15672,15675,15678,15680,15683,15685,15687,15689,15691,15693,15695,15698],{"class":70,"line":2506},[68,15671,2765],{"class":1790},[68,15673,15674],{"class":373}," (url.path?.",[68,15676,15677],{"class":2183},"endsWith",[68,15679,648],{"class":373},[68,15681,15682],{"class":116},"\".js\"",[68,15684,588],{"class":373},[68,15686,3660],{"class":1899},[68,15688,5858],{"class":12342},[68,15690,7832],{"class":1899},[68,15692,15658],{"class":373},[68,15694,15523],{"class":1899},[68,15696,15697],{"class":81}," null",[68,15699,15288],{"class":373},[68,15701,15702,15705,15707],{"class":70,"line":2511},[68,15703,15704],{"class":373},"                    response.mimeType ",[68,15706,877],{"class":1899},[68,15708,15709],{"class":116}," \"text\u002Fjavascript\"\n",[68,15711,15712],{"class":70,"line":2517},[68,15713,15640],{"class":373},[68,15715,15716],{"class":70,"line":2527},[68,15717,15718],{"class":373},"                response\n",[68,15720,15721],{"class":70,"line":2565},[68,15722,2261],{"class":373},[68,15724,15725],{"class":70,"line":2586},[68,15726,2589],{"class":373},[68,15728,15729],{"class":70,"line":2592},[68,15730,311],{"class":373},[68,15732,15733],{"class":70,"line":2597},[68,15734,416],{"emptyLinePlaceholder":415},[68,15736,15737,15739,15741,15744,15746,15748,15751,15754,15757,15760],{"class":70,"line":2605},[68,15738,15091],{"class":1864},[68,15740,15001],{"class":1899},[68,15742,15743],{"class":2183}," onReceivedSslError",[68,15745,15099],{"class":373},[68,15747,15102],{"class":1511},[68,15749,15750],{"class":373},"?, handler: ",[68,15752,15753],{"class":1511},"SslErrorHandler",[68,15755,15756],{"class":373},"?, error: ",[68,15758,15759],{"class":1511},"SslError",[68,15761,15762],{"class":373},"?) {\n",[68,15764,15765,15768,15771],{"class":70,"line":2611},[68,15766,15767],{"class":373},"        handler?.",[68,15769,15770],{"class":2183},"proceed",[68,15772,2136],{"class":373},[68,15774,15775,15778,15780,15783],{"class":70,"line":2660},[68,15776,15777],{"class":81},"        super",[68,15779,404],{"class":373},[68,15781,15782],{"class":2183},"onReceivedSslError",[68,15784,15785],{"class":373},"(view, handler, error)\n",[68,15787,15788],{"class":70,"line":2691},[68,15789,311],{"class":373},[68,15791,15792],{"class":70,"line":2696},[68,15793,132],{"class":373},[523,15795,15796],{},"html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sfo-9, html code.shiki .sfo-9{--shiki-light:#90A4AE;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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":63,"searchDepth":78,"depth":78,"links":15798},[],"2020-12-18",{},"\u002Fposts\u002F2020\u002Fandroid-webview-picture-cache",{"text":535,"minutes":15803,"time":15804,"words":15805},2.52,151200,504,{"title":14795,"description":14800},{"loc":15801},"posts\u002F2020\u002F20201218.android-webview-picture-cache",[543,13759],"uWP_CIUYC6vbsshGYZ4ykWXKhER8hR9mpP1uhdAGLKI",{"id":15812,"title":15813,"body":15814,"class":528,"cover":1445,"coverSize":528,"date":16004,"description":63,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":16005,"navigation":415,"path":16006,"readingTime":16007,"seo":16011,"sitemap":16012,"stem":16013,"tags":16014,"time":528,"weather":528,"__hash__":16015},"posts\u002Fposts\u002F2020\u002F20201120.how-to-become-a-fullstack-developer.md","如何成为一名全栈开发工程师",{"type":25,"value":15815,"toc":15997},[15816,15820,15831,15834,15837,15840,15843,15846,15848,15851,15854,15857,15871,15874,15877,15879,15882,15885,15888,15891,15911,15914,15934,15937,15939,15942,15945,15948,15951,15953,15956,15958,15961,15978,15981,15983,15986,15989,15992,15994],[15817,15818,15819],"h3",{"id":15819},"什么是全栈开发工程师",[28,15821,15822,15823,1686],{},"“全栈”这个概念最早来源于 Facebook 工程师 Carlos Bueno 在 2010 年底写的一篇文章：",[38,15824,15827],{"href":15825,"rel":15826},"http:\u002F\u002Fcarlos.bueno.org\u002F2010\u002F11\u002Ffull-stack.html",[42],[15828,15829,15830],"em",{},"The Full Stack",[28,15832,15833],{},"作者认为全栈是一个通才，能够自己创建不平凡的应用程序。",[28,15835,15836],{},"他也指出，没人能够熟悉所有方方面面，但作为一个全栈，能够看清每个栈的上下之间是如何运作的。",[28,15838,15839],{},"我们再看看百度百科的定义：全栈工程师是指掌握多种技能，胜任前端与后端，能利用多种技能独立完成产品的人。",[28,15841,15842],{},"百度百科的定义稍微有些狭义，全栈的技能远远不止前端与后端，但在大多数情况下，熟练掌握前后端就可以成为一名别人眼中的全栈。",[28,15844,15845],{},"在我的理解中，全栈开发工程师除了传统大家理解的前端、后端、移动端之外，还必须具备设计与运维的技能。若不具备这两项技能，还是没办法独立完成一个产品的，在设计与运维阶段，还是得依赖于其他人，或者说无法更独立、更高效地完成一个产品。",[554,15847],{"filename":556},[15817,15849,15850],{"id":15850},"全栈开发工程师存在的意义",[28,15852,15853],{},"我们了解了什么是全栈开发工程师，那么，为什么会存在全栈开发工程师呢？或者说为什么需要全栈开发工程师呢？",[28,15855,15856],{},"我认为全栈具有下面几个至关重要的作用：",[5010,15858,15859,15862,15865,15868],{},[1239,15860,15861],{},"极大减少沟通成本，全栈开发工程师独立完成一个项目的时候，沟通成本为零，全部自己干",[1239,15863,15864],{},"恐怖的开发效率，全栈开发工程师可以从他的若干技能中找到最高效完成任务的方式",[1239,15866,15867],{},"资源紧张时的万金油，在公司资源紧张的时候，可以充当万金油，哪里缺人顶哪里",[1239,15869,15870],{},"救火灭火的能力，全栈开发工程师在面对线上问题的时候，能够更快地定位问题所在",[15817,15872,15873],{"id":15873},"全栈与专家的区别",[28,15875,15876],{},"相信大家都听说过“木桶理论”，专家强调的是术业有专攻，衡量一个专家主要看专攻领域的能力；而全栈强调综合能力，主要取决于技术的短板，也就是木桶理论中最短的那块板子。",[554,15878],{"filename":1203},[28,15880,15881],{},"另外，“二八定律”也可以用来解释全栈与专家。在很多情况下，一个有成为全栈或专家能力的人，花 20% 的时间可以将一项技术从零开始学习达到 80 分，而从 80 分提高到 100 分，则需要花 80% 的时间。于是，在某一项领域成为一名专家所花的时间与成为一名在多个领域都达到 80 分的全栈是差不多的。",[28,15883,15884],{},"因此，想成为一名全栈，也必然需要做一些取舍，需要更加注重技术的广度，而不能陷进技术的深度中无法自拔，毕竟人的精力是有限的。",[15817,15886,15887],{"id":15887},"全栈开发工程师的进阶之路",[28,15889,15890],{},"想要成为一名全栈，我认为需要具备以下几点能力：",[5010,15892,15893,15896,15899,15902,15905,15908],{},[1239,15894,15895],{},"超强的学习能力：全栈需要快速掌握很多技能，所以必须具有超强的学习能力",[1239,15897,15898],{},"发现并解决问题的能力：遇到未知领域的问题，不要退缩，去发现问题，并寻找解决办法",[1239,15900,15901],{},"良好的沟通能力：需要在团队中与各种人员进行沟通，起到团队不同成员的桥梁作用",[1239,15903,15904],{},"全局思维的能力：全栈最大的价值就是全局思维，不局限于某一个面，而是从全局去考虑问题",[1239,15906,15907],{},"技能迁移能力：不局限于某一个技能领域，可以根据需要快速迁移到其他领域",[1239,15909,15910],{},"勇于探索的能力：对新技术的渴望与追求，不断勇于探索新技能",[28,15912,15913],{},"在我个人的成长上面，我有以下几点经验：",[5010,15915,15916,15919,15922,15925,15928,15931],{},[1239,15917,15918],{},"实践大于理论：与其花大量的时间看文档，停留在理论上，不如直接开始实践，会更快上手一门新技术",[1239,15920,15921],{},"善于利用搜素引擎：翻墙！翻墙！翻墙！Google！Google！Google！",[1239,15923,15924],{},"独自完成一个产品：从零开始打造一个产品，独自完成其中的每个环节",[1239,15926,15927],{},"决心、耐心、虚心、细心：成为一名全栈需要下决心、有耐心，学习要虚心、做事情要细心，才能更上一层楼",[1239,15929,15930],{},"良好的人际关系：与各个领域的技术专家打好关系，你可以从他们身上获得很多干货",[1239,15932,15933],{},"时间积累：任何一个领域都需要花时间去积累，没有速成之法",[28,15935,15936],{},"下面这个图是我推荐的技术上的进阶路线：",[554,15938],{"filename":1710},[15817,15940,15941],{"id":15941},"全栈开发的一个实际案例",[28,15943,15944],{},"下面是我的一个黑客马拉松比赛作品的实际案例，一个人完成该作品的全部生命周期。",[554,15946],{"filename":15947},"04.jpg",[28,15949,15950],{},"产品设计：",[554,15952],{"filename":1729},[28,15954,15955],{},"UI 设计：",[554,15957],{"filename":5287},[28,15959,15960],{},"小程序注册：",[5010,15962,15963,15966,15969,15972,15975],{},[1239,15964,15965],{},"注册小程序账号",[1239,15967,15968],{},"公司实名认证，申请营业执照扫描件、授权书盖章等",[1239,15970,15971],{},"配置服务器合法域名",[1239,15973,15974],{},"本地打了一个含有域名验证的 nginx 镜像并发布到 k8s",[1239,15976,15977],{},"配置扫普通链接二维码打开小程序",[28,15979,15980],{},"Coding：",[554,15982],{"filename":4862},[28,15984,15985],{},"硬件调试：",[554,15987],{"filename":15988},"08.jpg",[28,15990,15991],{},"测试部署&发布上线：",[554,15993],{"filename":5235},[28,15995,15996],{},"之后有时间我整理下，详细讲下这个项目的细节，将各个环节的文件都开源出来。",{"title":63,"searchDepth":78,"depth":78,"links":15998},[15999,16000,16001,16002,16003],{"id":15819,"depth":98,"text":15819},{"id":15850,"depth":98,"text":15850},{"id":15873,"depth":98,"text":15873},{"id":15887,"depth":98,"text":15887},{"id":15941,"depth":98,"text":15941},"2020-11-20",{},"\u002Fposts\u002F2020\u002Fhow-to-become-a-fullstack-developer",{"text":1450,"minutes":16008,"time":16009,"words":16010},7.095,425700,1419,{"title":15813,"description":63},{"loc":16006},"posts\u002F2020\u002F20201120.how-to-become-a-fullstack-developer",[543],"69cHng1QvmZ6Cxqpg8ohfTwntpMe2TjCyRGPV9JRYto",{"id":16017,"title":16018,"body":16019,"class":528,"cover":528,"coverSize":528,"date":16165,"description":16023,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":16166,"navigation":415,"path":16167,"readingTime":16168,"seo":16171,"sitemap":16172,"stem":16173,"tags":16174,"time":528,"weather":528,"__hash__":16175},"posts\u002Fposts\u002F2020\u002F20200227.iterm2login.md","iTerm2login",{"type":25,"value":16020,"toc":16163},[16021,16024,16144,16147,16150,16153,16160],[28,16022,16023],{},"iterm2login.sh 文件：",[58,16025,16027],{"className":1502,"code":16026,"language":1504,"meta":63,"style":63},"#!\u002Fusr\u002Fbin\u002Fexpect\n\nset timeout 30\nspawn ssh -p [lindex $argv 0] [lindex $argv 1]@[lindex $argv 2]\nexpect {\n        \"(yes\u002Fno)?\"\n        {send \"yes\\n\";exp_continue}\n        \"password:\"\n        {send \"[lindex $argv 3]\\n\"}\n}\ninteract\n",[65,16028,16029,16034,16038,16049,16076,16083,16088,16109,16114,16135,16139],{"__ignoreMap":63},[68,16030,16031],{"class":70,"line":71},[68,16032,16033],{"class":2403},"#!\u002Fusr\u002Fbin\u002Fexpect\n",[68,16035,16036],{"class":70,"line":78},[68,16037,416],{"emptyLinePlaceholder":415},[68,16039,16040,16043,16046],{"class":70,"line":98},[68,16041,16042],{"class":630},"set",[68,16044,16045],{"class":116}," timeout",[68,16047,16048],{"class":2397}," 30\n",[68,16050,16051,16054,16057,16059,16062,16065,16067,16070,16073],{"class":70,"line":123},[68,16052,16053],{"class":1511},"spawn",[68,16055,16056],{"class":116}," ssh",[68,16058,1522],{"class":1518},[68,16060,16061],{"class":373}," [lindex $argv ",[68,16063,16064],{"class":116},"0]",[68,16066,16061],{"class":373},[68,16068,16069],{"class":116},"1]@[lindex",[68,16071,16072],{"class":373}," $argv ",[68,16074,16075],{"class":116},"2]\n",[68,16077,16078,16081],{"class":70,"line":129},[68,16079,16080],{"class":1511},"expect",[68,16082,95],{"class":116},[68,16084,16085],{"class":70,"line":212},[68,16086,16087],{"class":1511},"        \"(yes\u002Fno)?\"\n",[68,16089,16090,16093,16096,16098,16101,16103,16106],{"class":70,"line":233},[68,16091,16092],{"class":373},"        {",[68,16094,16095],{"class":1511},"send",[68,16097,113],{"class":112},[68,16099,16100],{"class":116},"yes\\n",[68,16102,89],{"class":112},[68,16104,16105],{"class":74},";",[68,16107,16108],{"class":1511},"exp_continue}\n",[68,16110,16111],{"class":70,"line":268},[68,16112,16113],{"class":1511},"        \"password:\"\n",[68,16115,16116,16118,16120,16122,16125,16128,16131,16133],{"class":70,"line":289},[68,16117,16092],{"class":373},[68,16119,16095],{"class":1511},[68,16121,113],{"class":112},[68,16123,16124],{"class":116},"[lindex ",[68,16126,16127],{"class":373},"$argv",[68,16129,16130],{"class":116}," 3]\\n",[68,16132,89],{"class":112},[68,16134,132],{"class":116},[68,16136,16137],{"class":70,"line":308},[68,16138,132],{"class":373},[68,16140,16141],{"class":70,"line":314},[68,16142,16143],{"class":1511},"interact\n",[28,16145,16146],{},"复制配置到其他 Profile：Other Actions -> Bulk Copy from Selected Profile",[28,16148,16149],{},"Session -> Status Bar",[28,16151,16152],{},"iTerm2 -> Install Shell Integration",[28,16154,7508,16155],{},[38,16156,16159],{"href":16157,"rel":16158},"https:\u002F\u002Fwww.iterm2.com\u002Fdocumentation-shell-integration.html",[42],"Shell Integration",[523,16161,16162],{},"html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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":63,"searchDepth":78,"depth":78,"links":16164},[],"2020-02-27",{},"\u002Fposts\u002F2020\u002Fiterm2login",{"text":7523,"minutes":16169,"time":16170,"words":2454},0.28,16800,{"title":16018,"description":16023},{"loc":16167},"posts\u002F2020\u002F20200227.iterm2login",[543],"Ly5K46t_3fQzySH4kw4_kMN3_AT7NDrMY8AejHBaZ_k",{"id":16177,"title":16178,"body":16179,"class":528,"cover":1212,"coverSize":528,"date":16165,"description":18119,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":18120,"navigation":415,"path":18121,"readingTime":18122,"seo":18126,"sitemap":18127,"stem":18128,"tags":18129,"time":528,"weather":18131,"__hash__":18132},"posts\u002Fposts\u002F2020\u002F20200227.k8s-cert-manager-tls.md","k8s 上利用 cert-manager 自动签发 TLS 证书",{"type":25,"value":16180,"toc":18117},[16181,16188,16195,16203,16211,16220,16224,16231,16249,16252,16353,16358,16387,16393,16477,16481,16567,16571,16593,16746,16751,16769,16772,16808,16811,16818,16958,16962,16980,16983,17029,17042,17048,17056,17510,17532,17550,17559,17562,17568,17571,17575,17578,17613,17658,17663,17727,17735,17740,17950,17954,17970,17972,18002,18005,18008,18050,18068,18079,18114],[28,16182,16183,16184,16187],{},"很多博主的 ",[65,16185,16186],{},"https"," 证书经常容易忘记更新，虽说证书过期前都会有邮件提醒，但是万一确实忙得没时间去处理，忘记了，就会出现证书过期的情况了。",[28,16189,16190,16191,16194],{},"之前在服务器上自己搭博客服务的时候，用 ",[65,16192,16193],{},"Let's Encrypt"," 来自动创建并续签证书，确实省了不少事。",[28,16196,16197,16198,16200,16201,1686],{},"在我的博客部署到 ",[65,16199,14791],{}," 之后，就一直用的一年一签的免费证书，每年更新一次，也不算特别麻烦，但是总归不够高端，我又怀念起了 ",[65,16202,16193],{},[28,16204,16205,16207,16208,16210],{},[65,16206,16193],{}," 是个好东西，",[65,16209,14791],{}," 也是个好东西，两个好东西怎么结合呢？搜寻了一番确实有方案，经过几天的尝试，终于弄好了。花了几天是因为第一天因为有个粗心导致的问题，导致搞了好久没成功，休息了几天再次尝试，才找到问题。",[28,16212,16213,16214,16216,16217,16219],{},"有关 ",[65,16215,14791],{}," 的基础知识，这里不做赘述，网上教程很多，这里假设大家对 ",[65,16218,14791],{}," 都有一定了解。",[8514,16221,16223],{"id":16222},"安装-cert-manager","安装 cert-manager",[28,16225,16226,16227,16230],{},"安装 ",[65,16228,16229],{},"helm"," 到本地",[58,16232,16234],{"className":1502,"code":16233,"language":1504,"meta":63,"style":63},"$ brew install helm\n",[65,16235,16236],{"__ignoreMap":63},[68,16237,16238,16240,16243,16246],{"class":70,"line":71},[68,16239,5147],{"class":1511},[68,16241,16242],{"class":116}," brew",[68,16244,16245],{"class":116}," install",[68,16247,16248],{"class":116}," helm\n",[28,16250,16251],{},"添加仓库和命名空间",[58,16253,16255],{"className":1502,"code":16254,"language":1504,"meta":63,"style":63},"$ kubectl create namespace cert-manager # 创建 cert-manager 命名空间\n$ kubectl label namespace cert-manager certmanager.io\u002Fdisable-validation=true # 标记 cert-manager 命名空间以禁用资源验证\n$ kubectl apply --validate=false -f https:\u002F\u002Fgithub.com\u002Fjetstack\u002Fcert-manager\u002Freleases\u002Fdownload\u002Fv0.14.1\u002Fcert-manager-legacy.crds.yaml # 安装 CustomResourceDefinition 资源，注意 k8s 版本低于 1.15 需要用 legacy 版本\n$ helm repo add jetstack https:\u002F\u002Fcharts.jetstack.io # 添加 Jetstack Helm repository\n$ helm repo update # 更新本地 Helm chart repository\n",[65,16256,16257,16276,16297,16318,16339],{"__ignoreMap":63},[68,16258,16259,16261,16264,16267,16270,16273],{"class":70,"line":71},[68,16260,5147],{"class":1511},[68,16262,16263],{"class":116}," kubectl",[68,16265,16266],{"class":116}," create",[68,16268,16269],{"class":116}," namespace",[68,16271,16272],{"class":116}," cert-manager",[68,16274,16275],{"class":2403}," # 创建 cert-manager 命名空间\n",[68,16277,16278,16280,16282,16285,16287,16289,16292,16294],{"class":70,"line":78},[68,16279,5147],{"class":1511},[68,16281,16263],{"class":116},[68,16283,16284],{"class":116}," label",[68,16286,16269],{"class":116},[68,16288,16272],{"class":116},[68,16290,16291],{"class":116}," certmanager.io\u002Fdisable-validation=",[68,16293,849],{"class":81},[68,16295,16296],{"class":2403}," # 标记 cert-manager 命名空间以禁用资源验证\n",[68,16298,16299,16301,16303,16306,16309,16312,16315],{"class":70,"line":98},[68,16300,5147],{"class":1511},[68,16302,16263],{"class":116},[68,16304,16305],{"class":116}," apply",[68,16307,16308],{"class":1518}," --validate=false",[68,16310,16311],{"class":1518}," -f",[68,16313,16314],{"class":116}," https:\u002F\u002Fgithub.com\u002Fjetstack\u002Fcert-manager\u002Freleases\u002Fdownload\u002Fv0.14.1\u002Fcert-manager-legacy.crds.yaml",[68,16316,16317],{"class":2403}," # 安装 CustomResourceDefinition 资源，注意 k8s 版本低于 1.15 需要用 legacy 版本\n",[68,16319,16320,16322,16325,16328,16330,16333,16336],{"class":70,"line":123},[68,16321,5147],{"class":1511},[68,16323,16324],{"class":116}," helm",[68,16326,16327],{"class":116}," repo",[68,16329,12058],{"class":116},[68,16331,16332],{"class":116}," jetstack",[68,16334,16335],{"class":116}," https:\u002F\u002Fcharts.jetstack.io",[68,16337,16338],{"class":2403}," # 添加 Jetstack Helm repository\n",[68,16340,16341,16343,16345,16347,16350],{"class":70,"line":129},[68,16342,5147],{"class":1511},[68,16344,16324],{"class":116},[68,16346,16327],{"class":116},[68,16348,16349],{"class":116}," update",[68,16351,16352],{"class":2403}," # 更新本地 Helm chart repository\n",[28,16354,16226,16355],{},[65,16356,16357],{},"cert-manager",[58,16359,16361],{"className":1502,"code":16360,"language":1504,"meta":63,"style":63},"$ helm install cert-manager --namespace cert-manager --version v0.14.1 jetstack\u002Fcert-manager\n",[65,16362,16363],{"__ignoreMap":63},[68,16364,16365,16367,16369,16371,16373,16376,16378,16381,16384],{"class":70,"line":71},[68,16366,5147],{"class":1511},[68,16368,16324],{"class":116},[68,16370,16245],{"class":116},[68,16372,16272],{"class":116},[68,16374,16375],{"class":1518}," --namespace",[68,16377,16272],{"class":116},[68,16379,16380],{"class":1518}," --version",[68,16382,16383],{"class":116}," v0.14.1",[68,16385,16386],{"class":116}," jetstack\u002Fcert-manager\n",[28,16388,16389,16390,16392],{},"查看 ",[65,16391,16357],{}," 安装情况",[58,16394,16396],{"className":1502,"code":16395,"language":1504,"meta":63,"style":63},"$ kubectl get pods --namespace cert-manager\nNAME                                       READY   STATUS    RESTARTS   AGE\ncert-manager-6cff8dc7b9-8vxws              1\u002F1     Running   0          4d10h\ncert-manager-cainjector-795c46858f-txczb   1\u002F1     Running   0          4d10h\ncert-manager-webhook-5dfc77cd74-skgsv      1\u002F1     Running   0          4d10h\n",[65,16397,16398,16415,16432,16449,16463],{"__ignoreMap":63},[68,16399,16400,16402,16404,16407,16410,16412],{"class":70,"line":71},[68,16401,5147],{"class":1511},[68,16403,16263],{"class":116},[68,16405,16406],{"class":116}," get",[68,16408,16409],{"class":116}," pods",[68,16411,16375],{"class":1518},[68,16413,16414],{"class":116}," cert-manager\n",[68,16416,16417,16420,16423,16426,16429],{"class":70,"line":78},[68,16418,16419],{"class":1511},"NAME",[68,16421,16422],{"class":116},"                                       READY",[68,16424,16425],{"class":116},"   STATUS",[68,16427,16428],{"class":116},"    RESTARTS",[68,16430,16431],{"class":116},"   AGE\n",[68,16433,16434,16437,16440,16443,16446],{"class":70,"line":98},[68,16435,16436],{"class":1511},"cert-manager-6cff8dc7b9-8vxws",[68,16438,16439],{"class":116},"              1\u002F1",[68,16441,16442],{"class":116},"     Running",[68,16444,16445],{"class":2397},"   0",[68,16447,16448],{"class":116},"          4d10h\n",[68,16450,16451,16454,16457,16459,16461],{"class":70,"line":123},[68,16452,16453],{"class":1511},"cert-manager-cainjector-795c46858f-txczb",[68,16455,16456],{"class":116},"   1\u002F1",[68,16458,16442],{"class":116},[68,16460,16445],{"class":2397},[68,16462,16448],{"class":116},[68,16464,16465,16468,16471,16473,16475],{"class":70,"line":129},[68,16466,16467],{"class":1511},"cert-manager-webhook-5dfc77cd74-skgsv",[68,16469,16470],{"class":116},"      1\u002F1",[68,16472,16442],{"class":116},[68,16474,16445],{"class":2397},[68,16476,16448],{"class":116},[8514,16478,16480],{"id":16479},"更新-cert-manager","更新 cert-manager",[58,16482,16484],{"className":1502,"code":16483,"language":1504,"meta":63,"style":63},"$ kubectl delete -n cert-manager deployment cert-manager cert-manager-cainjector cert-manager-webhook\n\n$ kubectl apply --validate=false -f https:\u002F\u002Fgithub.com\u002Fjetstack\u002Fcert-manager\u002Freleases\u002Fdownload\u002Fv0.14.1\u002Fcert-manager-legacy.crds.yaml\n\n$ helm repo update\n$ helm upgrade --version v0.14.1 cert-manager jetstack\u002Fcert-manager -n cert-manager\n",[65,16485,16486,16511,16515,16530,16534,16545],{"__ignoreMap":63},[68,16487,16488,16490,16492,16495,16498,16500,16503,16505,16508],{"class":70,"line":71},[68,16489,5147],{"class":1511},[68,16491,16263],{"class":116},[68,16493,16494],{"class":116}," delete",[68,16496,16497],{"class":1518}," -n",[68,16499,16272],{"class":116},[68,16501,16502],{"class":116}," deployment",[68,16504,16272],{"class":116},[68,16506,16507],{"class":116}," cert-manager-cainjector",[68,16509,16510],{"class":116}," cert-manager-webhook\n",[68,16512,16513],{"class":70,"line":78},[68,16514,416],{"emptyLinePlaceholder":415},[68,16516,16517,16519,16521,16523,16525,16527],{"class":70,"line":98},[68,16518,5147],{"class":1511},[68,16520,16263],{"class":116},[68,16522,16305],{"class":116},[68,16524,16308],{"class":1518},[68,16526,16311],{"class":1518},[68,16528,16529],{"class":116}," https:\u002F\u002Fgithub.com\u002Fjetstack\u002Fcert-manager\u002Freleases\u002Fdownload\u002Fv0.14.1\u002Fcert-manager-legacy.crds.yaml\n",[68,16531,16532],{"class":70,"line":123},[68,16533,416],{"emptyLinePlaceholder":415},[68,16535,16536,16538,16540,16542],{"class":70,"line":129},[68,16537,5147],{"class":1511},[68,16539,16324],{"class":116},[68,16541,16327],{"class":116},[68,16543,16544],{"class":116}," update\n",[68,16546,16547,16549,16551,16554,16556,16558,16560,16563,16565],{"class":70,"line":212},[68,16548,5147],{"class":1511},[68,16550,16324],{"class":116},[68,16552,16553],{"class":116}," upgrade",[68,16555,16380],{"class":1518},[68,16557,16383],{"class":116},[68,16559,16272],{"class":116},[68,16561,16562],{"class":116}," jetstack\u002Fcert-manager",[68,16564,16497],{"class":1518},[68,16566,16414],{"class":116},[8514,16568,16570],{"id":16569},"创建-clusterissuer","创建 ClusterIssuer",[28,16572,16573,16574,16576,16577,5225,16580,16583,16584,16586,16587,16589,16590,13080],{},"我们需要创建一个签发机构，",[65,16575,16357],{}," 提供了",[65,16578,16579],{},"Issuer",[65,16581,16582],{},"ClusterIssuer"," 两种类型的签发机构，",[65,16585,16579],{}," 只能用来签发自己所在命名空间下的证书，ClusterIssuer 可以签发任意命名空间下的证书，我这里用 ",[65,16588,16582],{}," 为例，创建 ",[65,16591,16592],{},"letsencrypt-prod.yaml",[58,16594,16596],{"className":1557,"code":16595,"language":1559,"meta":63,"style":63},"apiVersion: cert-manager.io\u002Fv1alpha2\nkind: ClusterIssuer\nmetadata:\n  labels:\n    name: letsencrypt-prod\n  name: letsencrypt-prod # 自定义的签发机构名称，后面会引用\nspec:\n  acme:\n    email: yourname@youremail.com # 你的邮箱，证书快过期的时候会邮件提醒，不过我们可以设置自动续期\n    solvers:\n      - http01:\n          ingress:\n            class: nginx\n    privateKeySecretRef:\n      name: letsencrypt-prod # 指示此签发机构的私钥将要存储到哪个 Secret 对象中\n    server: https:\u002F\u002Facme-v02.api.letsencrypt.org\u002Fdirectory # acme 协议的服务端，我们用 Let's Encrypt\n",[65,16597,16598,16608,16618,16625,16632,16641,16654,16661,16668,16681,16688,16697,16704,16714,16721,16733],{"__ignoreMap":63},[68,16599,16600,16603,16605],{"class":70,"line":71},[68,16601,16602],{"class":730},"apiVersion",[68,16604,92],{"class":74},[68,16606,16607],{"class":116}," cert-manager.io\u002Fv1alpha2\n",[68,16609,16610,16613,16615],{"class":70,"line":78},[68,16611,16612],{"class":730},"kind",[68,16614,92],{"class":74},[68,16616,16617],{"class":116}," ClusterIssuer\n",[68,16619,16620,16623],{"class":70,"line":98},[68,16621,16622],{"class":730},"metadata",[68,16624,1569],{"class":74},[68,16626,16627,16630],{"class":70,"line":123},[68,16628,16629],{"class":730},"  labels",[68,16631,1569],{"class":74},[68,16633,16634,16636,16638],{"class":70,"line":129},[68,16635,10174],{"class":730},[68,16637,92],{"class":74},[68,16639,16640],{"class":116}," letsencrypt-prod\n",[68,16642,16643,16646,16648,16651],{"class":70,"line":212},[68,16644,16645],{"class":730},"  name",[68,16647,92],{"class":74},[68,16649,16650],{"class":116}," letsencrypt-prod",[68,16652,16653],{"class":2403}," # 自定义的签发机构名称，后面会引用\n",[68,16655,16656,16659],{"class":70,"line":233},[68,16657,16658],{"class":730},"spec",[68,16660,1569],{"class":74},[68,16662,16663,16666],{"class":70,"line":268},[68,16664,16665],{"class":730},"  acme",[68,16667,1569],{"class":74},[68,16669,16670,16673,16675,16678],{"class":70,"line":289},[68,16671,16672],{"class":730},"    email",[68,16674,92],{"class":74},[68,16676,16677],{"class":116}," yourname@youremail.com",[68,16679,16680],{"class":2403}," # 你的邮箱，证书快过期的时候会邮件提醒，不过我们可以设置自动续期\n",[68,16682,16683,16686],{"class":70,"line":308},[68,16684,16685],{"class":730},"    solvers",[68,16687,1569],{"class":74},[68,16689,16690,16692,16695],{"class":70,"line":314},[68,16691,1617],{"class":74},[68,16693,16694],{"class":730}," http01",[68,16696,1569],{"class":74},[68,16698,16699,16702],{"class":70,"line":320},[68,16700,16701],{"class":730},"          ingress",[68,16703,1569],{"class":74},[68,16705,16706,16709,16711],{"class":70,"line":889},[68,16707,16708],{"class":730},"            class",[68,16710,92],{"class":74},[68,16712,16713],{"class":116}," nginx\n",[68,16715,16716,16719],{"class":70,"line":909},[68,16717,16718],{"class":730},"    privateKeySecretRef",[68,16720,1569],{"class":74},[68,16722,16723,16726,16728,16730],{"class":70,"line":929},[68,16724,16725],{"class":730},"      name",[68,16727,92],{"class":74},[68,16729,16650],{"class":116},[68,16731,16732],{"class":2403}," # 指示此签发机构的私钥将要存储到哪个 Secret 对象中\n",[68,16734,16735,16738,16740,16743],{"class":70,"line":949},[68,16736,16737],{"class":730},"    server",[68,16739,92],{"class":74},[68,16741,16742],{"class":116}," https:\u002F\u002Facme-v02.api.letsencrypt.org\u002Fdirectory",[68,16744,16745],{"class":2403}," # acme 协议的服务端，我们用 Let's Encrypt\n",[28,16747,16748,16749],{},"应用 ",[65,16750,1559],{},[58,16752,16754],{"className":1502,"code":16753,"language":1504,"meta":63,"style":63},"$ kubectl create -f letsencrypt-prod.yaml\n",[65,16755,16756],{"__ignoreMap":63},[68,16757,16758,16760,16762,16764,16766],{"class":70,"line":71},[68,16759,5147],{"class":1511},[68,16761,16263],{"class":116},[68,16763,16266],{"class":116},[68,16765,16311],{"class":1518},[68,16767,16768],{"class":116}," letsencrypt-prod.yaml\n",[28,16770,16771],{},"查看状态",[58,16773,16775],{"className":1502,"code":16774,"language":1504,"meta":63,"style":63},"$ kubectl get clusterissuer\nNAME               READY   AGE\nletsencrypt-prod   True    51s\n",[65,16776,16777,16788,16797],{"__ignoreMap":63},[68,16778,16779,16781,16783,16785],{"class":70,"line":71},[68,16780,5147],{"class":1511},[68,16782,16263],{"class":116},[68,16784,16406],{"class":116},[68,16786,16787],{"class":116}," clusterissuer\n",[68,16789,16790,16792,16795],{"class":70,"line":78},[68,16791,16419],{"class":1511},[68,16793,16794],{"class":116},"               READY",[68,16796,16431],{"class":116},[68,16798,16799,16802,16805],{"class":70,"line":98},[68,16800,16801],{"class":1511},"letsencrypt-prod",[68,16803,16804],{"class":116},"   True",[68,16806,16807],{"class":116},"    51s\n",[8514,16809,16810],{"id":16810},"手动签发证书",[28,16812,16813,16814,16817],{},"手动签发证书，创建 ",[65,16815,16816],{},"test-monkeyrun-net-cert.yaml"," 文件",[58,16819,16821],{"className":1557,"code":16820,"language":1559,"meta":63,"style":63},"apiVersion: cert-manager.io\u002Fv1alpha2\nkind: Certificate\nmetadata:\n  name: test-monkeyrun-net-cert\n  namespace: test\nspec:\n  secretName: tls-test-monkeyrun-net # 证书保存的 secret 名\n  duration: 2160h # 90d\n  renewBefore: 720h # 30d\n  dnsNames:\n    - test.monkeyrun.net\n  issuerRef:\n    name: letsencrypt-prod\n    kind: ClusterIssuer\n    group: cert-manager.io\n",[65,16822,16823,16831,16840,16846,16855,16865,16871,16884,16897,16910,16917,16924,16931,16939,16948],{"__ignoreMap":63},[68,16824,16825,16827,16829],{"class":70,"line":71},[68,16826,16602],{"class":730},[68,16828,92],{"class":74},[68,16830,16607],{"class":116},[68,16832,16833,16835,16837],{"class":70,"line":78},[68,16834,16612],{"class":730},[68,16836,92],{"class":74},[68,16838,16839],{"class":116}," Certificate\n",[68,16841,16842,16844],{"class":70,"line":98},[68,16843,16622],{"class":730},[68,16845,1569],{"class":74},[68,16847,16848,16850,16852],{"class":70,"line":123},[68,16849,16645],{"class":730},[68,16851,92],{"class":74},[68,16853,16854],{"class":116}," test-monkeyrun-net-cert\n",[68,16856,16857,16860,16862],{"class":70,"line":129},[68,16858,16859],{"class":730},"  namespace",[68,16861,92],{"class":74},[68,16863,16864],{"class":116}," test\n",[68,16866,16867,16869],{"class":70,"line":212},[68,16868,16658],{"class":730},[68,16870,1569],{"class":74},[68,16872,16873,16876,16878,16881],{"class":70,"line":233},[68,16874,16875],{"class":730},"  secretName",[68,16877,92],{"class":74},[68,16879,16880],{"class":116}," tls-test-monkeyrun-net",[68,16882,16883],{"class":2403}," # 证书保存的 secret 名\n",[68,16885,16886,16889,16891,16894],{"class":70,"line":268},[68,16887,16888],{"class":730},"  duration",[68,16890,92],{"class":74},[68,16892,16893],{"class":116}," 2160h",[68,16895,16896],{"class":2403}," # 90d\n",[68,16898,16899,16902,16904,16907],{"class":70,"line":289},[68,16900,16901],{"class":730},"  renewBefore",[68,16903,92],{"class":74},[68,16905,16906],{"class":116}," 720h",[68,16908,16909],{"class":2403}," # 30d\n",[68,16911,16912,16915],{"class":70,"line":308},[68,16913,16914],{"class":730},"  dnsNames",[68,16916,1569],{"class":74},[68,16918,16919,16921],{"class":70,"line":314},[68,16920,6008],{"class":74},[68,16922,16923],{"class":116}," test.monkeyrun.net\n",[68,16925,16926,16929],{"class":70,"line":320},[68,16927,16928],{"class":730},"  issuerRef",[68,16930,1569],{"class":74},[68,16932,16933,16935,16937],{"class":70,"line":889},[68,16934,10174],{"class":730},[68,16936,92],{"class":74},[68,16938,16640],{"class":116},[68,16940,16941,16944,16946],{"class":70,"line":909},[68,16942,16943],{"class":730},"    kind",[68,16945,92],{"class":74},[68,16947,16617],{"class":116},[68,16949,16950,16953,16955],{"class":70,"line":929},[68,16951,16952],{"class":730},"    group",[68,16954,92],{"class":74},[68,16956,16957],{"class":116}," cert-manager.io\n",[28,16959,16748,16960],{},[65,16961,1559],{},[58,16963,16965],{"className":1502,"code":16964,"language":1504,"meta":63,"style":63},"$ kubectl apply -f test-monkeyrun-net-cert.yaml\n",[65,16966,16967],{"__ignoreMap":63},[68,16968,16969,16971,16973,16975,16977],{"class":70,"line":71},[68,16970,5147],{"class":1511},[68,16972,16263],{"class":116},[68,16974,16305],{"class":116},[68,16976,16311],{"class":1518},[68,16978,16979],{"class":116}," test-monkeyrun-net-cert.yaml\n",[28,16981,16982],{},"检查是否生成证书文件",[58,16984,16986],{"className":1502,"code":16985,"language":1504,"meta":63,"style":63},"$ kubectl get certificate -n test\nNAME                      READY   SECRET                   AGE\ntest-monkeyrun-net-cert   True    test-monkeyrun-net-tls   99m\n",[65,16987,16988,17003,17016],{"__ignoreMap":63},[68,16989,16990,16992,16994,16996,16999,17001],{"class":70,"line":71},[68,16991,5147],{"class":1511},[68,16993,16263],{"class":116},[68,16995,16406],{"class":116},[68,16997,16998],{"class":116}," certificate",[68,17000,16497],{"class":1518},[68,17002,16864],{"class":116},[68,17004,17005,17007,17010,17013],{"class":70,"line":78},[68,17006,16419],{"class":1511},[68,17008,17009],{"class":116},"                      READY",[68,17011,17012],{"class":116},"   SECRET",[68,17014,17015],{"class":116},"                   AGE\n",[68,17017,17018,17021,17023,17026],{"class":70,"line":98},[68,17019,17020],{"class":1511},"test-monkeyrun-net-cert",[68,17022,16804],{"class":116},[68,17024,17025],{"class":116},"    test-monkeyrun-net-tls",[68,17027,17028],{"class":116},"   99m\n",[28,17030,17031,17032,9810,17035,17038,17039,17041],{},"将该证书配置到 ",[65,17033,17034],{},"test.monkeyrun.net",[65,17036,17037],{},"ingress"," 上，测试 ",[65,17040,16186],{}," 访问，成功。",[8514,17043,17045],{"id":17044},"创建deployment时自动签发证书",[4907,17046,17047],{},"创建Deployment时自动签发证书",[28,17049,17050],{},[4907,17051,17052,17053],{},"创建 ",[65,17054,17055],{},"test-nginx.yaml",[58,17057,17059],{"className":1557,"code":17058,"language":1559,"meta":63,"style":63},"apiVersion: extensions\u002Fv1beta1\nkind: Deployment\nmetadata:\n  name: test-nginx\n  namespace: test\nspec:\n  replicas: 1\n  template:\n    metadata:\n      labels:\n        run: test-nginx\n    spec:\n      containers:\n        - name: test-nginx\n          image: nginx\n          ports:\n            - containerPort: 80\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: test-nginx\n  namespace: test\n  labels:\n    app: test-nginx\nspec:\n  ports:\n    - port: 80\n      protocol: TCP\n      name: http\n  selector:\n    run: test-nginx\n---\napiVersion: extensions\u002Fv1beta1\nkind: Ingress\nmetadata:\n  name: test-nginx\n  namespace: test\n  annotations:\n    kubernetes.io\u002Fingress.class: nginx\n    kubernetes.io\u002Ftls-acme: 'true'\n    certmanager.io\u002Fcluster-issuer: letsencrypt-prod\nspec:\n  rules:\n    - host: test.monkeyrun.net\n      http:\n        paths:\n          - backend:\n              serviceName: test-nginx\n              servicePort: 80\n            path: \u002F\n  tls:\n    - secretName: tls-test-monkeyrun-net\n      hosts:\n        - test.monkeyrun.net\n",[65,17060,17061,17070,17079,17085,17094,17102,17108,17117,17124,17131,17138,17147,17154,17161,17173,17182,17189,17202,17207,17216,17225,17231,17239,17247,17253,17262,17268,17275,17286,17296,17305,17312,17321,17325,17333,17342,17348,17356,17364,17371,17380,17393,17402,17408,17415,17426,17433,17440,17450,17459,17468,17478,17485,17497,17504],{"__ignoreMap":63},[68,17062,17063,17065,17067],{"class":70,"line":71},[68,17064,16602],{"class":730},[68,17066,92],{"class":74},[68,17068,17069],{"class":116}," extensions\u002Fv1beta1\n",[68,17071,17072,17074,17076],{"class":70,"line":78},[68,17073,16612],{"class":730},[68,17075,92],{"class":74},[68,17077,17078],{"class":116}," Deployment\n",[68,17080,17081,17083],{"class":70,"line":98},[68,17082,16622],{"class":730},[68,17084,1569],{"class":74},[68,17086,17087,17089,17091],{"class":70,"line":123},[68,17088,16645],{"class":730},[68,17090,92],{"class":74},[68,17092,17093],{"class":116}," test-nginx\n",[68,17095,17096,17098,17100],{"class":70,"line":129},[68,17097,16859],{"class":730},[68,17099,92],{"class":74},[68,17101,16864],{"class":116},[68,17103,17104,17106],{"class":70,"line":212},[68,17105,16658],{"class":730},[68,17107,1569],{"class":74},[68,17109,17110,17113,17115],{"class":70,"line":233},[68,17111,17112],{"class":730},"  replicas",[68,17114,92],{"class":74},[68,17116,2960],{"class":2397},[68,17118,17119,17122],{"class":70,"line":268},[68,17120,17121],{"class":730},"  template",[68,17123,1569],{"class":74},[68,17125,17126,17129],{"class":70,"line":289},[68,17127,17128],{"class":730},"    metadata",[68,17130,1569],{"class":74},[68,17132,17133,17136],{"class":70,"line":308},[68,17134,17135],{"class":730},"      labels",[68,17137,1569],{"class":74},[68,17139,17140,17143,17145],{"class":70,"line":314},[68,17141,17142],{"class":730},"        run",[68,17144,92],{"class":74},[68,17146,17093],{"class":116},[68,17148,17149,17152],{"class":70,"line":320},[68,17150,17151],{"class":730},"    spec",[68,17153,1569],{"class":74},[68,17155,17156,17159],{"class":70,"line":889},[68,17157,17158],{"class":730},"      containers",[68,17160,1569],{"class":74},[68,17162,17163,17166,17169,17171],{"class":70,"line":909},[68,17164,17165],{"class":74},"        -",[68,17167,17168],{"class":730}," name",[68,17170,92],{"class":74},[68,17172,17093],{"class":116},[68,17174,17175,17178,17180],{"class":70,"line":929},[68,17176,17177],{"class":730},"          image",[68,17179,92],{"class":74},[68,17181,16713],{"class":116},[68,17183,17184,17187],{"class":70,"line":949},[68,17185,17186],{"class":730},"          ports",[68,17188,1569],{"class":74},[68,17190,17191,17194,17197,17199],{"class":70,"line":969},[68,17192,17193],{"class":74},"            -",[68,17195,17196],{"class":730}," containerPort",[68,17198,92],{"class":74},[68,17200,17201],{"class":2397}," 80\n",[68,17203,17204],{"class":70,"line":989},[68,17205,17206],{"class":1511},"---\n",[68,17208,17209,17211,17213],{"class":70,"line":1009},[68,17210,16602],{"class":730},[68,17212,92],{"class":74},[68,17214,17215],{"class":116}," v1\n",[68,17217,17218,17220,17222],{"class":70,"line":1029},[68,17219,16612],{"class":730},[68,17221,92],{"class":74},[68,17223,17224],{"class":116}," Service\n",[68,17226,17227,17229],{"class":70,"line":1049},[68,17228,16622],{"class":730},[68,17230,1569],{"class":74},[68,17232,17233,17235,17237],{"class":70,"line":1069},[68,17234,16645],{"class":730},[68,17236,92],{"class":74},[68,17238,17093],{"class":116},[68,17240,17241,17243,17245],{"class":70,"line":1088},[68,17242,16859],{"class":730},[68,17244,92],{"class":74},[68,17246,16864],{"class":116},[68,17248,17249,17251],{"class":70,"line":1108},[68,17250,16629],{"class":730},[68,17252,1569],{"class":74},[68,17254,17255,17258,17260],{"class":70,"line":1128},[68,17256,17257],{"class":730},"    app",[68,17259,92],{"class":74},[68,17261,17093],{"class":116},[68,17263,17264,17266],{"class":70,"line":1148},[68,17265,16658],{"class":730},[68,17267,1569],{"class":74},[68,17269,17270,17273],{"class":70,"line":1168},[68,17271,17272],{"class":730},"  ports",[68,17274,1569],{"class":74},[68,17276,17277,17279,17282,17284],{"class":70,"line":1187},[68,17278,6008],{"class":74},[68,17280,17281],{"class":730}," port",[68,17283,92],{"class":74},[68,17285,17201],{"class":2397},[68,17287,17288,17291,17293],{"class":70,"line":2039},[68,17289,17290],{"class":730},"      protocol",[68,17292,92],{"class":74},[68,17294,17295],{"class":116}," TCP\n",[68,17297,17298,17300,17302],{"class":70,"line":2055},[68,17299,16725],{"class":730},[68,17301,92],{"class":74},[68,17303,17304],{"class":116}," http\n",[68,17306,17307,17310],{"class":70,"line":2071},[68,17308,17309],{"class":730},"  selector",[68,17311,1569],{"class":74},[68,17313,17314,17317,17319],{"class":70,"line":2087},[68,17315,17316],{"class":730},"    run",[68,17318,92],{"class":74},[68,17320,17093],{"class":116},[68,17322,17323],{"class":70,"line":2092},[68,17324,17206],{"class":1511},[68,17326,17327,17329,17331],{"class":70,"line":2097},[68,17328,16602],{"class":730},[68,17330,92],{"class":74},[68,17332,17069],{"class":116},[68,17334,17335,17337,17339],{"class":70,"line":2114},[68,17336,16612],{"class":730},[68,17338,92],{"class":74},[68,17340,17341],{"class":116}," Ingress\n",[68,17343,17344,17346],{"class":70,"line":2139},[68,17345,16622],{"class":730},[68,17347,1569],{"class":74},[68,17349,17350,17352,17354],{"class":70,"line":2158},[68,17351,16645],{"class":730},[68,17353,92],{"class":74},[68,17355,17093],{"class":116},[68,17357,17358,17360,17362],{"class":70,"line":2173},[68,17359,16859],{"class":730},[68,17361,92],{"class":74},[68,17363,16864],{"class":116},[68,17365,17366,17369],{"class":70,"line":2178},[68,17367,17368],{"class":730},"  annotations",[68,17370,1569],{"class":74},[68,17372,17373,17376,17378],{"class":70,"line":2193},[68,17374,17375],{"class":730},"    kubernetes.io\u002Fingress.class",[68,17377,92],{"class":74},[68,17379,16713],{"class":116},[68,17381,17382,17385,17387,17389,17391],{"class":70,"line":2201},[68,17383,17384],{"class":730},"    kubernetes.io\u002Ftls-acme",[68,17386,92],{"class":74},[68,17388,1620],{"class":112},[68,17390,849],{"class":116},[68,17392,1626],{"class":112},[68,17394,17395,17398,17400],{"class":70,"line":2207},[68,17396,17397],{"class":730},"    certmanager.io\u002Fcluster-issuer",[68,17399,92],{"class":74},[68,17401,16640],{"class":116},[68,17403,17404,17406],{"class":70,"line":2234},[68,17405,16658],{"class":730},[68,17407,1569],{"class":74},[68,17409,17410,17413],{"class":70,"line":2258},[68,17411,17412],{"class":730},"  rules",[68,17414,1569],{"class":74},[68,17416,17417,17419,17422,17424],{"class":70,"line":2264},[68,17418,6008],{"class":74},[68,17420,17421],{"class":730}," host",[68,17423,92],{"class":74},[68,17425,16923],{"class":116},[68,17427,17428,17431],{"class":70,"line":2270},[68,17429,17430],{"class":730},"      http",[68,17432,1569],{"class":74},[68,17434,17435,17438],{"class":70,"line":2275},[68,17436,17437],{"class":730},"        paths",[68,17439,1569],{"class":74},[68,17441,17442,17445,17448],{"class":70,"line":2289},[68,17443,17444],{"class":74},"          -",[68,17446,17447],{"class":730}," backend",[68,17449,1569],{"class":74},[68,17451,17452,17455,17457],{"class":70,"line":2339},[68,17453,17454],{"class":730},"              serviceName",[68,17456,92],{"class":74},[68,17458,17093],{"class":116},[68,17460,17461,17464,17466],{"class":70,"line":2363},[68,17462,17463],{"class":730},"              servicePort",[68,17465,92],{"class":74},[68,17467,17201],{"class":2397},[68,17469,17470,17473,17475],{"class":70,"line":2374},[68,17471,17472],{"class":730},"            path",[68,17474,92],{"class":74},[68,17476,17477],{"class":116}," \u002F\n",[68,17479,17480,17483],{"class":70,"line":2407},[68,17481,17482],{"class":730},"  tls",[68,17484,1569],{"class":74},[68,17486,17487,17489,17492,17494],{"class":70,"line":2421},[68,17488,6008],{"class":74},[68,17490,17491],{"class":730}," secretName",[68,17493,92],{"class":74},[68,17495,17496],{"class":116}," tls-test-monkeyrun-net\n",[68,17498,17499,17502],{"class":70,"line":2426},[68,17500,17501],{"class":730},"      hosts",[68,17503,1569],{"class":74},[68,17505,17506,17508],{"class":70,"line":2432},[68,17507,17165],{"class":74},[68,17509,16923],{"class":116},[28,17511,17512],{},[4907,17513,17514,17515,345,17518,17521,17522,5225,17525,17528,17529,17531],{},"删除之前手动创建的 ",[65,17516,17517],{},"Deployment",[65,17519,17520],{},"Service"," 、 ",[65,17523,17524],{},"Ingress",[65,17526,17527],{},"Secret"," 后， 应用 ",[65,17530,1559],{}," 来自动创建",[58,17533,17535],{"className":1502,"code":17534,"language":1504,"meta":63,"style":63},"$ kubectl apply -f test-nginx.yaml\n",[65,17536,17537],{"__ignoreMap":63},[68,17538,17539,17541,17543,17545,17547],{"class":70,"line":71},[68,17540,5147],{"class":1511},[68,17542,16263],{"class":116},[68,17544,16305],{"class":116},[68,17546,16311],{"class":1518},[68,17548,17549],{"class":116}," test-nginx.yaml\n",[28,17551,17552],{},[4907,17553,17554,17555,17558],{},"打开 ",[65,17556,17557],{},"https:\u002F\u002Ftest.monkeyrun.net"," 测试，成功！",[28,17560,17561],{},"不知为何再次使用自动签发证书的时候会报错：",[58,17563,17566],{"className":17564,"code":17565,"language":516},[514],"E0330 07:46:30.070412       1 sync.go:57] cert-manager\u002Fcontroller\u002Fingress-shim \"msg\"=\"failed to determine issuer to be used for ingress resource\" \"error\"=\"failed to determine issuer name to be used for ingress resource\" \"resource_kind\"=\"Ingress\" \"resource_name\"=\"xxx\" \"resource_namespace\"=\"xxx\"\n",[65,17567,17565],{"__ignoreMap":63},[28,17569,17570],{},"解决了半天都没能找到问题，所以还是用手动签发吧，反正也是一次性的操作。",[8514,17572,17574],{"id":17573},"通过-dns-验证域名","通过 DNS 验证域名",[28,17576,17577],{},"刚才通过 http01 的方式验证域名会有个问题，对于已经部署上线的项目，没办法去验证，所以可以通过 dns 的方式来验证。",[28,17579,17580],{},[4907,17581,17582,17583,17588,17589,11056,17594,17597,17598,17600,17601,17606,17607,17612],{},"经过搜寻，找到了几篇文章，都是利用 ",[38,17584,17587],{"href":17585,"rel":17586},"https:\u002F\u002Fgithub.com\u002Fkevinniu666",[42],"kevinniu666"," 这位仁兄基于  ",[38,17590,17593],{"href":17591,"rel":17592},"https:\u002F\u002Fgithub.com\u002Fjetstack\u002Fcert-manager-webhook-example",[42],"jetstack\u002Fcert-manager-webhook-example",[65,17595,17596],{},"alidns"," 的版本来搞的，不过尝试了下，他这里面 ",[65,17599,16357],{}," 版本太老已经跑不起来了，从 GitHub 的 forks 树里面找到了最新的一个 fork，",[38,17602,17605],{"href":17603,"rel":17604},"https:\u002F\u002Fgithub.com\u002Fcolprog\u002Fcert-manager-webhook-alidns",[42],"colprog\u002Fcert0manager-webhooks-alidns","，尝试了下，也不行，他应该是改了镜像，但是不可用了。重新尝试了下上一代 fork ",[38,17608,17611],{"href":17609,"rel":17610},"https:\u002F\u002Fgithub.com\u002Fpangzineng\u002Fcert-manager-webhook-alidns",[42],"pangzineng\u002Fcert-manager-webhook-alidns","，可用。",[58,17614,17616],{"className":1502,"code":17615,"language":1504,"meta":63,"style":63},"$ git clone https:\u002F\u002Fgithub.com\u002Fpangzineng\u002Fcert-manager-webhook-alidns.git\n$ cd cert-manager-webhook-alidns\n$ helm install cert-manager-webhook-alidns --namespace=cert-manager .\u002Fdeploy\u002Fwebhook-alidns\n",[65,17617,17618,17631,17641],{"__ignoreMap":63},[68,17619,17620,17622,17625,17628],{"class":70,"line":71},[68,17621,5147],{"class":1511},[68,17623,17624],{"class":116}," git",[68,17626,17627],{"class":116}," clone",[68,17629,17630],{"class":116}," https:\u002F\u002Fgithub.com\u002Fpangzineng\u002Fcert-manager-webhook-alidns.git\n",[68,17632,17633,17635,17638],{"class":70,"line":78},[68,17634,5147],{"class":1511},[68,17636,17637],{"class":116}," cd",[68,17639,17640],{"class":116}," cert-manager-webhook-alidns\n",[68,17642,17643,17645,17647,17649,17652,17655],{"class":70,"line":98},[68,17644,5147],{"class":1511},[68,17646,16324],{"class":116},[68,17648,16245],{"class":116},[68,17650,17651],{"class":116}," cert-manager-webhook-alidns",[68,17653,17654],{"class":1518}," --namespace=cert-manager",[68,17656,17657],{"class":116}," .\u002Fdeploy\u002Fwebhook-alidns\n",[28,17659,17660],{},[4907,17661,17662],{},"创建 alidns AccessKey Id 和 Secret",[58,17664,17666],{"className":1502,"code":17665,"language":1504,"meta":63,"style":63},"$ kubectl -n cert-manager create secret generic alidns-access-key-id --from-literal=accessKeyId='xxxxxxx'\n$ kubectl -n cert-manager create secret generic alidns-access-key-secret --from-literal=accessKeySecret='xxxxxxx'\n",[65,17667,17668,17699],{"__ignoreMap":63},[68,17669,17670,17672,17674,17676,17678,17680,17683,17686,17689,17692,17694,17697],{"class":70,"line":71},[68,17671,5147],{"class":1511},[68,17673,16263],{"class":116},[68,17675,16497],{"class":1518},[68,17677,16272],{"class":116},[68,17679,16266],{"class":116},[68,17681,17682],{"class":116}," secret",[68,17684,17685],{"class":116}," generic",[68,17687,17688],{"class":116}," alidns-access-key-id",[68,17690,17691],{"class":1518}," --from-literal=accessKeyId=",[68,17693,8129],{"class":112},[68,17695,17696],{"class":116},"xxxxxxx",[68,17698,1626],{"class":112},[68,17700,17701,17703,17705,17707,17709,17711,17713,17715,17718,17721,17723,17725],{"class":70,"line":78},[68,17702,5147],{"class":1511},[68,17704,16263],{"class":116},[68,17706,16497],{"class":1518},[68,17708,16272],{"class":116},[68,17710,16266],{"class":116},[68,17712,17682],{"class":116},[68,17714,17685],{"class":116},[68,17716,17717],{"class":116}," alidns-access-key-secret",[68,17719,17720],{"class":1518}," --from-literal=accessKeySecret=",[68,17722,8129],{"class":112},[68,17724,17696],{"class":116},[68,17726,1626],{"class":112},[28,17728,17729,17730],{},"更新：使用 ",[38,17731,17734],{"href":17732,"rel":17733},"https:\u002F\u002Fgithub.com\u002Fpragkent\u002Falidns-webhook\u002Ftree\u002Fmaster",[42],"pragkent\u002Falidns-webhook",[28,17736,17737,17738],{},"修改我们之前创建的 ",[65,17739,16592],{},[58,17741,17743],{"className":1557,"code":17742,"language":1559,"meta":63,"style":63},"apiVersion: cert-manager.io\u002Fv1\nkind: ClusterIssuer\nmetadata:\n  labels:\n    name: letsencrypt-prod\n  name: letsencrypt-prod # 自定义的签发机构名称，后面会引用\nspec:\n  acme:\n    email: yourname@youremail.com # 你的邮箱，证书快过期的时候会邮件提醒，不过我们可以设置自动续期\n    solvers:\n      - dns01:\n          webhook:\n            groupName: yourgroup.com\n            solverName: alidns\n            config:\n              region: ''\n              accessKeySecretRef:\n                name: alidns-secret\n                key: access-key\n              secretKeySecretRef:\n                name: alidns-secret\n                key: secret-key\n    privateKeySecretRef:\n      name: letsencrypt-prod-account-key # 指示此签发机构的私钥将要存储到哪个 Secret 对象中\n    server: https:\u002F\u002Facme-v02.api.letsencrypt.org\u002Fdirectory # acme 协议的服务端，我们用 Let's Encrypt\n",[65,17744,17745,17754,17762,17768,17774,17782,17792,17798,17804,17814,17820,17829,17836,17846,17856,17863,17872,17879,17889,17899,17906,17914,17923,17929,17940],{"__ignoreMap":63},[68,17746,17747,17749,17751],{"class":70,"line":71},[68,17748,16602],{"class":730},[68,17750,92],{"class":74},[68,17752,17753],{"class":116}," cert-manager.io\u002Fv1\n",[68,17755,17756,17758,17760],{"class":70,"line":78},[68,17757,16612],{"class":730},[68,17759,92],{"class":74},[68,17761,16617],{"class":116},[68,17763,17764,17766],{"class":70,"line":98},[68,17765,16622],{"class":730},[68,17767,1569],{"class":74},[68,17769,17770,17772],{"class":70,"line":123},[68,17771,16629],{"class":730},[68,17773,1569],{"class":74},[68,17775,17776,17778,17780],{"class":70,"line":129},[68,17777,10174],{"class":730},[68,17779,92],{"class":74},[68,17781,16640],{"class":116},[68,17783,17784,17786,17788,17790],{"class":70,"line":212},[68,17785,16645],{"class":730},[68,17787,92],{"class":74},[68,17789,16650],{"class":116},[68,17791,16653],{"class":2403},[68,17793,17794,17796],{"class":70,"line":233},[68,17795,16658],{"class":730},[68,17797,1569],{"class":74},[68,17799,17800,17802],{"class":70,"line":268},[68,17801,16665],{"class":730},[68,17803,1569],{"class":74},[68,17805,17806,17808,17810,17812],{"class":70,"line":289},[68,17807,16672],{"class":730},[68,17809,92],{"class":74},[68,17811,16677],{"class":116},[68,17813,16680],{"class":2403},[68,17815,17816,17818],{"class":70,"line":308},[68,17817,16685],{"class":730},[68,17819,1569],{"class":74},[68,17821,17822,17824,17827],{"class":70,"line":314},[68,17823,1617],{"class":74},[68,17825,17826],{"class":730}," dns01",[68,17828,1569],{"class":74},[68,17830,17831,17834],{"class":70,"line":320},[68,17832,17833],{"class":730},"          webhook",[68,17835,1569],{"class":74},[68,17837,17838,17841,17843],{"class":70,"line":889},[68,17839,17840],{"class":730},"            groupName",[68,17842,92],{"class":74},[68,17844,17845],{"class":116}," yourgroup.com\n",[68,17847,17848,17851,17853],{"class":70,"line":909},[68,17849,17850],{"class":730},"            solverName",[68,17852,92],{"class":74},[68,17854,17855],{"class":116}," alidns\n",[68,17857,17858,17861],{"class":70,"line":929},[68,17859,17860],{"class":730},"            config",[68,17862,1569],{"class":74},[68,17864,17865,17868,17870],{"class":70,"line":949},[68,17866,17867],{"class":730},"              region",[68,17869,92],{"class":74},[68,17871,11746],{"class":112},[68,17873,17874,17877],{"class":70,"line":969},[68,17875,17876],{"class":730},"              accessKeySecretRef",[68,17878,1569],{"class":74},[68,17880,17881,17884,17886],{"class":70,"line":989},[68,17882,17883],{"class":730},"                name",[68,17885,92],{"class":74},[68,17887,17888],{"class":116}," alidns-secret\n",[68,17890,17891,17894,17896],{"class":70,"line":1009},[68,17892,17893],{"class":730},"                key",[68,17895,92],{"class":74},[68,17897,17898],{"class":116}," access-key\n",[68,17900,17901,17904],{"class":70,"line":1029},[68,17902,17903],{"class":730},"              secretKeySecretRef",[68,17905,1569],{"class":74},[68,17907,17908,17910,17912],{"class":70,"line":1049},[68,17909,17883],{"class":730},[68,17911,92],{"class":74},[68,17913,17888],{"class":116},[68,17915,17916,17918,17920],{"class":70,"line":1069},[68,17917,17893],{"class":730},[68,17919,92],{"class":74},[68,17921,17922],{"class":116}," secret-key\n",[68,17924,17925,17927],{"class":70,"line":1088},[68,17926,16718],{"class":730},[68,17928,1569],{"class":74},[68,17930,17931,17933,17935,17938],{"class":70,"line":1108},[68,17932,16725],{"class":730},[68,17934,92],{"class":74},[68,17936,17937],{"class":116}," letsencrypt-prod-account-key",[68,17939,16732],{"class":2403},[68,17941,17942,17944,17946,17948],{"class":70,"line":1128},[68,17943,16737],{"class":730},[68,17945,92],{"class":74},[68,17947,16742],{"class":116},[68,17949,16745],{"class":2403},[28,17951,16748,17952],{},[65,17953,1559],{},[58,17955,17956],{"className":1502,"code":16753,"language":1504,"meta":63,"style":63},[65,17957,17958],{"__ignoreMap":63},[68,17959,17960,17962,17964,17966,17968],{"class":70,"line":71},[68,17961,5147],{"class":1511},[68,17963,16263],{"class":116},[68,17965,16266],{"class":116},[68,17967,16311],{"class":1518},[68,17969,16768],{"class":116},[28,17971,16771],{},[58,17973,17974],{"className":1502,"code":16774,"language":1504,"meta":63,"style":63},[65,17975,17976,17986,17994],{"__ignoreMap":63},[68,17977,17978,17980,17982,17984],{"class":70,"line":71},[68,17979,5147],{"class":1511},[68,17981,16263],{"class":116},[68,17983,16406],{"class":116},[68,17985,16787],{"class":116},[68,17987,17988,17990,17992],{"class":70,"line":78},[68,17989,16419],{"class":1511},[68,17991,16794],{"class":116},[68,17993,16431],{"class":116},[68,17995,17996,17998,18000],{"class":70,"line":98},[68,17997,16801],{"class":1511},[68,17999,16804],{"class":116},[68,18001,16807],{"class":116},[28,18003,18004],{},"重新手动签发证书，验证，成功！",[28,18006,18007],{},"PS：需要注意的是，从 http01 认证修改到 dns01 认证后，有个坑，会一直失败，查看 cert-manager 的 Pod 日志，会发现如下错误：",[58,18009,18011],{"className":361,"code":18010,"language":363,"meta":63,"style":63},"cert-manager\u002Fcontroller\u002Forders \"msg\"=\"Failed to determine the list of Challenge resources needed for the Order\" \"error\"=\"no configured challenge solvers can be used for this challenge\" \"resource_kind\"=\"Order\" \"resource_name\"=\"xxx\"\n",[65,18012,18013],{"__ignoreMap":63},[68,18014,18015,18018,18021,18023,18026,18029,18031,18034,18037,18039,18042,18045,18047],{"class":70,"line":71},[68,18016,18017],{"class":373},"cert-manager\u002Fcontroller\u002Forders ",[68,18019,18020],{"class":116},"\"msg\"",[68,18022,877],{"class":373},[68,18024,18025],{"class":116},"\"Failed to determine the list of Challenge resources needed for the Order\"",[68,18027,18028],{"class":116}," \"error\"",[68,18030,877],{"class":373},[68,18032,18033],{"class":116},"\"no configured challenge solvers can be used for this challenge\"",[68,18035,18036],{"class":116}," \"resource_kind\"",[68,18038,877],{"class":373},[68,18040,18041],{"class":116},"\"Order\"",[68,18043,18044],{"class":116}," \"resource_name\"",[68,18046,877],{"class":373},[68,18048,18049],{"class":116},"\"xxx\"\n",[28,18051,18052,18053,18058,18059,18064,18065,18067],{},"研究了半天都没成功，后来在 GitHub 上找到了这个 ",[38,18054,18057],{"href":18055,"rel":18056},"https:\u002F\u002Fgithub.com\u002Fjetstack\u002Fcert-manager\u002Fissues\u002F2494#issuecomment-585391545",[42],"Issue","，按照 ",[38,18060,18063],{"href":18061,"rel":18062},"https:\u002F\u002Fgithub.com\u002Fdemisx",[42],"demisx"," 这位仁兄的建议，把所有和 ",[65,18066,16357],{}," 相关的东西全部删除重新用 dns01 的方式部署一遍就 OK 了。",[28,18069,18070,18071,18074,18075,18078],{},"另外，cert-manager 的 API group 从 ",[65,18072,18073],{},"certmanager.k8s.io"," 改到 ",[65,18076,18077],{},"certmanager.io"," 了，不少老教程里面仍然是前者，需要改为后者才能正常执行。",[7538,18080,18081,18084],{},[28,18082,18083],{},"参考链接",[1236,18085,18086,18093,18100,18107],{},[1239,18087,18088],{},[38,18089,18092],{"href":18090,"rel":18091},"https:\u002F\u002Fdocs.bitnami.com\u002Fkubernetes\u002Fhow-to\u002Fsecure-kubernetes-services-with-ingress-tls-letsencrypt\u002F",[42],"Secure Kubernetes Services With Ingress, TLS And Let's Encrypt",[1239,18094,18095],{},[38,18096,18099],{"href":18097,"rel":18098},"https:\u002F\u002Fxuchao918.github.io\u002F2019\u002F03\u002F14\u002F%E2%95%A9%E2%95%A3%E2%95%99%E2%94%9Ccert-manager%E2%95%A9%E2%95%A1%E2%95%A7%E2%95%93Ingress-https\u002F",[42],"使用 cert-manager 实现 Ingress https",[1239,18101,18102],{},[38,18103,18106],{"href":18104,"rel":18105},"https:\u002F\u002Fyq.aliyun.com\u002Farticles\u002F718711",[42],"使用 cert-manager 给阿里云的 DNS 域名授权 SSL 证书",[1239,18108,18109],{},[38,18110,18113],{"href":18111,"rel":18112},"https:\u002F\u002Fcert-manager.io\u002Fdocs\u002F",[42],"cert-manager docs",[523,18115,18116],{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":63,"searchDepth":78,"depth":78,"links":18118},[],"很多博主的 https 证书经常容易忘记更新，虽说证书过期前都会有邮件提醒，但是万一确实忙得没时间去处理，忘记了，就会出现证书过期的情况了。",{},"\u002Fposts\u002F2020\u002Fk8s-cert-manager-tls",{"text":1450,"minutes":18123,"time":18124,"words":18125},7.465,447900,1493,{"title":16178,"description":18119},{"loc":18121},"posts\u002F2020\u002F20200227.k8s-cert-manager-tls",[543,18130,14791,7153,7154],"阿里云","天气晴","2aJ6T7QGEjJQr4Yy8PkK08lqxa4n-rxsCy0mGJw2oBY",{"id":18134,"title":18135,"body":18136,"class":528,"cover":1212,"coverSize":528,"date":18267,"description":18268,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":18269,"navigation":415,"path":18270,"readingTime":18271,"seo":18274,"sitemap":18275,"stem":18276,"tags":18277,"time":528,"weather":528,"__hash__":18278},"posts\u002Fposts\u002F2020\u002F20200215.batch-edit-acl-for-oss.md","批量修改阿里云 OSS 的 ACL 权限",{"type":25,"value":18137,"toc":18265},[18138,18144,18151,18155,18204,18207,18225,18229,18259,18262],[28,18139,18140,18143],{},[65,18141,18142],{},"oss-browser"," 是个好工具，但是在修改 ACL 权限上比较蛋疼，只能单个文件设置，不支持批量设置，这在某些默认 ACL 权限为私有的 bucket 上，需要批量设置某个目录为公共读时，会比较不便。",[28,18145,18146,18147,18150],{},"经过搜索，阿里云官方的 ",[65,18148,18149],{},"ossutil"," 工具可以用来解决这个问题。",[13801,18152,18154],{"id":18153},"下载以-mac-系统为例","下载（以 Mac 系统为例）",[58,18156,18158],{"className":1502,"code":18157,"language":1504,"meta":63,"style":63},"curl -o ossutilmac64 http:\u002F\u002Fgosspublic.alicdn.com\u002Fossutil\u002F1.6.10\u002Fossutilmac64\n\nchmod 755 ossutilmac64\n\n.\u002Fossutilmac64 config # 按照提示填写相关配置，参考https:\u002F\u002Fhelp.aliyun.com\u002Fdocument_detail\u002F120075.html\n",[65,18159,18160,18174,18178,18189,18193],{"__ignoreMap":63},[68,18161,18162,18165,18168,18171],{"class":70,"line":71},[68,18163,18164],{"class":1511},"curl",[68,18166,18167],{"class":1518}," -o",[68,18169,18170],{"class":116}," ossutilmac64",[68,18172,18173],{"class":116}," http:\u002F\u002Fgosspublic.alicdn.com\u002Fossutil\u002F1.6.10\u002Fossutilmac64\n",[68,18175,18176],{"class":70,"line":78},[68,18177,416],{"emptyLinePlaceholder":415},[68,18179,18180,18183,18186],{"class":70,"line":98},[68,18181,18182],{"class":1511},"chmod",[68,18184,18185],{"class":2397}," 755",[68,18187,18188],{"class":116}," ossutilmac64\n",[68,18190,18191],{"class":70,"line":123},[68,18192,416],{"emptyLinePlaceholder":415},[68,18194,18195,18198,18201],{"class":70,"line":129},[68,18196,18197],{"class":1511},".\u002Fossutilmac64",[68,18199,18200],{"class":116}," config",[68,18202,18203],{"class":2403}," # 按照提示填写相关配置，参考https:\u002F\u002Fhelp.aliyun.com\u002Fdocument_detail\u002F120075.html\n",[13801,18205,18206],{"id":18206},"测试配置是否正确",[58,18208,18210],{"className":1502,"code":18209,"language":1504,"meta":63,"style":63},".\u002Fossutilmac64 ls oss:\u002F\u002Fyour-bucket-name\u002F # 看看能否列出文件列表\n",[65,18211,18212],{"__ignoreMap":63},[68,18213,18214,18216,18219,18222],{"class":70,"line":71},[68,18215,18197],{"class":1511},[68,18217,18218],{"class":116}," ls",[68,18220,18221],{"class":116}," oss:\u002F\u002Fyour-bucket-name\u002F",[68,18223,18224],{"class":2403}," # 看看能否列出文件列表\n",[13801,18226,18228],{"id":18227},"批量设置-acl-权限","批量设置 ACL 权限",[58,18230,18232],{"className":1502,"code":18231,"language":1504,"meta":63,"style":63},".\u002Fossutilmac64 set-acl oss:\u002F\u002Fyour-bucket-name\u002Fyour-folder\u002F public-read --include \"*\" -r\n",[65,18233,18234],{"__ignoreMap":63},[68,18235,18236,18238,18241,18244,18247,18250,18252,18254,18256],{"class":70,"line":71},[68,18237,18197],{"class":1511},[68,18239,18240],{"class":116}," set-acl",[68,18242,18243],{"class":116}," oss:\u002F\u002Fyour-bucket-name\u002Fyour-folder\u002F",[68,18245,18246],{"class":116}," public-read",[68,18248,18249],{"class":1518}," --include",[68,18251,113],{"class":112},[68,18253,7661],{"class":116},[68,18255,89],{"class":112},[68,18257,18258],{"class":1518}," -r\n",[28,18260,18261],{},"Done.",[523,18263,18264],{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":63,"searchDepth":78,"depth":78,"links":18266},[],"2020-02-15","oss-browser 是个好工具，但是在修改 ACL 权限上比较蛋疼，只能单个文件设置，不支持批量设置，这在某些默认 ACL 权限为私有的 bucket 上，需要批量设置某个目录为公共读时，会比较不便。",{},"\u002Fposts\u002F2020\u002Fbatch-edit-acl-for-oss",{"text":7523,"minutes":18272,"time":18273,"words":4043},0.785,47100,{"title":18135,"description":18268},{"loc":18270},"posts\u002F2020\u002F20200215.batch-edit-acl-for-oss",[543,18130],"635H8eIJZi3F8cl7cZncH1GLQUxLhxnQIIUdkcjX3g8",{"id":18280,"title":18281,"body":18282,"class":528,"cover":528,"coverSize":528,"date":18539,"description":63,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":18540,"navigation":415,"path":18541,"readingTime":18542,"seo":18546,"sitemap":18547,"stem":18548,"tags":18549,"time":528,"weather":528,"__hash__":18550},"posts\u002Fposts\u002F2019\u002F20191229.aliyun-k8s-setup.md","阿里云 k8s 集群搭建",{"type":25,"value":18283,"toc":18535},[18284,18288,18293,18298,18303,18369,18378,18383,18387,18390,18460,18463,18466,18520,18532],[15817,18285,18287],{"id":18286},"为-vpc-配置-snat","为 VPC 配置 SNAT",[28,18289,18290],{},[1242,18291,18292],{},"注：SNAT 已关闭，看起来两个 ECS 节点都有公网 IP，不需要了。（2024-06-04）",[28,18294,18295],{},[4907,18296,18297],{},"阿里云的 NAT 网关太贵，考虑自行搭建 SNAT。",[28,18299,18300],{},[4907,18301,18302],{},"购买最廉价 ECS，配置如下设置",[58,18304,18306],{"className":1502,"code":18305,"language":1504,"meta":63,"style":63},"sysctl net.ipv4.ip_forward # 查看当前 IP 转发配置，0 为关闭，1 为打开\nsysctl -w net.ipv4.ip_forward=1 # 打开 IP 转发\niptables -t nat -I POSTROUTING -s 172.16.0.0\u002F16 -j SNAT --to-source 172.16.117.66\n",[65,18307,18308,18319,18334],{"__ignoreMap":63},[68,18309,18310,18313,18316],{"class":70,"line":71},[68,18311,18312],{"class":1511},"sysctl",[68,18314,18315],{"class":116}," net.ipv4.ip_forward",[68,18317,18318],{"class":2403}," # 查看当前 IP 转发配置，0 为关闭，1 为打开\n",[68,18320,18321,18323,18326,18329,18331],{"class":70,"line":78},[68,18322,18312],{"class":1511},[68,18324,18325],{"class":1518}," -w",[68,18327,18328],{"class":116}," net.ipv4.ip_forward=",[68,18330,401],{"class":2397},[68,18332,18333],{"class":2403}," # 打开 IP 转发\n",[68,18335,18336,18339,18342,18345,18348,18351,18354,18357,18360,18363,18366],{"class":70,"line":98},[68,18337,18338],{"class":1511},"iptables",[68,18340,18341],{"class":1518}," -t",[68,18343,18344],{"class":116}," nat",[68,18346,18347],{"class":1518}," -I",[68,18349,18350],{"class":116}," POSTROUTING",[68,18352,18353],{"class":1518}," -s",[68,18355,18356],{"class":116}," 172.16.0.0\u002F16",[68,18358,18359],{"class":1518}," -j",[68,18361,18362],{"class":116}," SNAT",[68,18364,18365],{"class":1518}," --to-source",[68,18367,18368],{"class":2397}," 172.16.117.66\n",[28,18370,18371],{},[4907,18372,18373,18374,18377],{},"去 VPC 路由表中添加 ",[65,18375,18376],{},"0.0.0.0\u002F0"," 下一跳为上述 ECS",[28,18379,18380],{},[4907,18381,18382],{},"设置 iptasbles 开机启动：",[15817,18384,18386],{"id":18385},"dnat","DNAT",[28,18388,18389],{},"通过 公网 IP 访问集群管理 API",[58,18391,18393],{"className":1502,"code":18392,"language":1504,"meta":63,"style":63},"iptables -t nat -I PREROUTING -p tcp --dport 6443 -j DNAT --to 172.16.117.67:6443\niptables -t nat -I POSTROUTING -d 172.16.117.67\u002F32 -p tcp --dport 6443 -j MASQUERADE\n",[65,18394,18395,18430],{"__ignoreMap":63},[68,18396,18397,18399,18401,18403,18405,18408,18410,18413,18416,18419,18421,18424,18427],{"class":70,"line":71},[68,18398,18338],{"class":1511},[68,18400,18341],{"class":1518},[68,18402,18344],{"class":116},[68,18404,18347],{"class":1518},[68,18406,18407],{"class":116}," PREROUTING",[68,18409,1522],{"class":1518},[68,18411,18412],{"class":116}," tcp",[68,18414,18415],{"class":1518}," --dport",[68,18417,18418],{"class":2397}," 6443",[68,18420,18359],{"class":1518},[68,18422,18423],{"class":116}," DNAT",[68,18425,18426],{"class":1518}," --to",[68,18428,18429],{"class":116}," 172.16.117.67:6443\n",[68,18431,18432,18434,18436,18438,18440,18442,18444,18447,18449,18451,18453,18455,18457],{"class":70,"line":78},[68,18433,18338],{"class":1511},[68,18435,18341],{"class":1518},[68,18437,18344],{"class":116},[68,18439,18347],{"class":1518},[68,18441,18350],{"class":116},[68,18443,1519],{"class":1518},[68,18445,18446],{"class":116}," 172.16.117.67\u002F32",[68,18448,1522],{"class":1518},[68,18450,18412],{"class":116},[68,18452,18415],{"class":1518},[68,18454,18418],{"class":2397},[68,18456,18359],{"class":1518},[68,18458,18459],{"class":116}," MASQUERADE\n",[28,18461,18462],{},"记得开启安全组规则允许 6443 端口",[28,18464,18465],{},"在 k8s 集群信息中设置 自定义证书 SAN 为 47.111.247.217 配置证书，解决以下证书问题：",[58,18467,18469],{"className":1502,"code":18468,"language":1504,"meta":63,"style":63},"Unable to connect to the server: x509: certificate is valid for 172.21.0.1, 127.0.0.1, 7.20.49.48, 172.16.117.67, not 47.111.247.217\n",[65,18470,18471],{"__ignoreMap":63},[68,18472,18473,18476,18479,18482,18484,18487,18490,18493,18495,18498,18501,18503,18506,18509,18512,18515,18517],{"class":70,"line":71},[68,18474,18475],{"class":1511},"Unable",[68,18477,18478],{"class":116}," to",[68,18480,18481],{"class":116}," connect",[68,18483,18478],{"class":116},[68,18485,18486],{"class":116}," the",[68,18488,18489],{"class":116}," server:",[68,18491,18492],{"class":116}," x509:",[68,18494,16998],{"class":116},[68,18496,18497],{"class":116}," is",[68,18499,18500],{"class":116}," valid",[68,18502,3231],{"class":116},[68,18504,18505],{"class":116}," 172.21.0.1,",[68,18507,18508],{"class":116}," 127.0.0.1,",[68,18510,18511],{"class":116}," 7.20.49.48,",[68,18513,18514],{"class":116}," 172.16.117.67,",[68,18516,2438],{"class":116},[68,18518,18519],{"class":2397}," 47.111.247.217\n",[7538,18521,18522,18525],{},[28,18523,18524],{},"参考链接：",[28,18526,18527],{},[38,18528,18531],{"href":18529,"rel":18530},"https:\u002F\u002Fyq.aliyun.com\u002Farticles\u002F112497",[42],"如何通过 EIP 实现 VPC 下的 SNAT 以及 DNAT",[523,18533,18534],{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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":63,"searchDepth":78,"depth":78,"links":18536},[18537,18538],{"id":18286,"depth":98,"text":18287},{"id":18385,"depth":98,"text":18386},"2019-12-29",{},"\u002Fposts\u002F2019\u002Faliyun-k8s-setup",{"text":1217,"minutes":18543,"time":18544,"words":18545},1.17,70200,234,{"title":18281,"description":63},{"loc":18541},"posts\u002F2019\u002F20191229.aliyun-k8s-setup",[543,18130,14791,7153],"nx96pApv8oyqcXahwKsOs9Y-8UfgHxl4bCTvPo9djqQ",{"id":18552,"title":18553,"body":18554,"class":528,"cover":1212,"coverSize":528,"date":18695,"description":18558,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":18696,"navigation":415,"path":18697,"readingTime":18698,"seo":18702,"sitemap":18703,"stem":18704,"tags":18705,"time":528,"weather":18706,"__hash__":18707},"posts\u002Fposts\u002F2019\u002F20191201.docker-registry-auth-with-same-domain.md","Docker 同一域名下多个 Registry 保存凭证的方式",{"type":25,"value":18555,"toc":18693},[18556,18559,18586,18593,18616,18627,18684,18690],[28,18557,18558],{},"阿里云的容器镜像服务是个好东西，配合在阿里云上容器服务，速度非常快。",[28,18560,18561,18562,18565,18566,18568,18569,5551,18572,18575,18576,18578,18579,18581,18582,18585],{},"但是阿里云的容器服务不支持自定义域名，都是在同一个域名下，通过不同的 ",[65,18563,18564],{},"namespace"," 来实现的。当需要管理多个账户下的不同 ",[65,18567,18564],{}," 的时候，Docker 默认的认证存储方式就不太适用了。默认的 ",[65,18570,18571],{},"~\u002F.docker\u002Fconfig.json",[65,18573,18574],{},"auths"," 是根据域名来区分的，会出现登录了这个 ",[65,18577,18564],{}," 之后，另一个 ",[65,18580,18564],{}," 认证会失效的情况。经过一番搜索，发现可以通过 ",[65,18583,18584],{},"docker --config"," 来实现。",[28,18587,18588,18589,18592],{},"通过如下方式来创建一个名为 ",[65,18590,18591],{},"config-a"," 的配置",[58,18594,18596],{"className":1502,"code":18595,"language":1504,"meta":63,"style":63},"docker --config ~\u002F.docker\u002Fconfig-a login --username=config-a-username registry.cn-hangzhou.aliyuncs.com\n",[65,18597,18598],{"__ignoreMap":63},[68,18599,18600,18602,18605,18608,18611,18614],{"class":70,"line":71},[68,18601,1512],{"class":1511},[68,18603,18604],{"class":1518}," --config",[68,18606,18607],{"class":116}," ~\u002F.docker\u002Fconfig-a",[68,18609,18610],{"class":116}," login",[68,18612,18613],{"class":1518}," --username=config-a-username",[68,18615,14281],{"class":116},[28,18617,18618,18619,18622,18623,18626],{},"之后 ",[65,18620,18621],{},"push"," 之类的命令，在前面加个 ",[65,18624,18625],{},"--config ~\u002F.docker\u002Fconfig-a"," 即可，例如：",[58,18628,18630],{"className":1502,"code":18629,"language":1504,"meta":63,"style":63},"docker build -t registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002Fhblb-web:$npm_package_version -t registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002Fhblb-web:latest . && docker --config ~\u002F.docker\u002Fconfig-a push registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002Fhblb-web:$npm_package_version && docker --config ~\u002F.docker\u002Fconfig-a push registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002Fhblb-web:latest\n",[65,18631,18632],{"__ignoreMap":63},[68,18633,18634,18636,18638,18640,18643,18646,18649,18652,18655,18657,18660,18662,18664,18667,18669,18671,18673,18675,18677,18679,18681],{"class":70,"line":71},[68,18635,1512],{"class":1511},[68,18637,12901],{"class":116},[68,18639,18341],{"class":1518},[68,18641,18642],{"class":116}," registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002Fhblb-web:",[68,18644,18645],{"class":373},"$npm_package_version ",[68,18647,18648],{"class":1518},"-t",[68,18650,18651],{"class":116}," registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002Fhblb-web:latest",[68,18653,18654],{"class":116}," .",[68,18656,7832],{"class":74},[68,18658,18659],{"class":1511}," docker",[68,18661,18604],{"class":1518},[68,18663,18607],{"class":116},[68,18665,18666],{"class":116}," push",[68,18668,18642],{"class":116},[68,18670,18645],{"class":373},[68,18672,15215],{"class":74},[68,18674,18659],{"class":1511},[68,18676,18604],{"class":1518},[68,18678,18607],{"class":116},[68,18680,18666],{"class":116},[68,18682,18683],{"class":116}," registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002Fhblb-web:latest\n",[28,18685,18686,18687,18689],{},"同理，可以增加其他的 ",[65,18688,13276],{}," 来完成同一域名下多个账号的认证存储。",[523,18691,18692],{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":63,"searchDepth":78,"depth":78,"links":18694},[],"2019-12-01",{},"\u002Fposts\u002F2019\u002Fdocker-registry-auth-with-same-domain",{"text":1217,"minutes":18699,"time":18700,"words":18701},1.16,69600,232,{"title":18553,"description":18558},{"loc":18697},"posts\u002F2019\u002F20191201.docker-registry-auth-with-same-domain",[543,7154],"天气小雨","wt0A2lhDXspLwF_D24FDYIYXkEH3UndrqDQcKEdEBjs",{"id":18709,"title":18710,"body":18711,"class":528,"cover":1212,"coverSize":528,"date":18736,"description":18715,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":18737,"navigation":415,"path":18738,"readingTime":18739,"seo":18742,"sitemap":18743,"stem":18744,"tags":18745,"time":528,"weather":528,"__hash__":18746},"posts\u002Fposts\u002F2019\u002F20190730.qiniu-ssl-certificate-expire-problem.md","七牛 SSL 证书过期不刷新的坑",{"type":25,"value":18712,"toc":18734},[18713,18716,18719,18722,18725,18728,18731],[28,18714,18715],{},"最近一个七牛上的 SSL 证书到期了，导致 CDN 上的图片访问的时候提示证书失效。",[28,18717,18718],{},"但其实早在一个多月前，我已经针对那个域名重新签发了新的证书。",[28,18720,18721],{},"在发现提示证书失效后，我查看了 CDN 上的 HTTPS 证书，发现已经显示为了最新的证书，并且有效期都是正常的。",[28,18723,18724],{},"初步猜测是主域的 SSL 证书虽然更新了，但各个节点上的 CDN 证书没更新。",[28,18726,18727],{},"于是在 CDN 上的 HTTPS 配置中，重新强制更新下证书，提示 8~15 分钟生效。",[28,18729,18730],{},"果然，更新完之后，图片访问正常了。",[28,18732,18733],{},"七牛竟然没有在证书过期后自动去强制更新所有节点的证书，有点坑。",{"title":63,"searchDepth":78,"depth":78,"links":18735},[],"2019-07-30",{},"\u002Fposts\u002F2019\u002Fqiniu-ssl-certificate-expire-problem",{"text":7523,"minutes":18740,"time":18741,"words":4771},1.005,60300,{"title":18710,"description":18715},{"loc":18738},"posts\u002F2019\u002F20190730.qiniu-ssl-certificate-expire-problem",[543,7153],"6OMV-zbYVn1ddXCucrl6oWSYC9NJLYllpY8DUrii1fA",{"id":18748,"title":18749,"body":18750,"class":528,"cover":1212,"coverSize":528,"date":19471,"description":18754,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":19472,"navigation":415,"path":19473,"readingTime":19474,"seo":19478,"sitemap":19479,"stem":19480,"tags":19481,"time":528,"weather":528,"__hash__":19482},"posts\u002Fposts\u002F2019\u002F20190719.solve-pycharm-adding-pipenv-error.md","解决 PyCharm 设置 pipenv 报错的问题",{"type":25,"value":18751,"toc":19469},[18752,18755,18971,18973,18976,18979,19429,19440,19446,19463,19466],[28,18753,18754],{},"使用 PyCharm 添加 pipenv 会报如下错误：",[58,18756,18758],{"className":361,"code":18757,"language":363,"meta":63,"style":63},"Executed command:\n\u002Fusr\u002Flocal\u002Fbin\u002Fpipenv --python \u002Fusr\u002Flocal\u002Fbin\u002Fpython3.6 install --dev\n\nError occurred:\nError Running Pipenv\n\nCommand output:\nTraceback (most recent call last):\n  File \"\u002Fusr\u002Flocal\u002FCellar\u002Fpipenv\u002F2018.11.26_2\u002Flibexec\u002Fbin\u002Fpipenv\", line 6, in \u003Cmodule>\n    from pkg_resources import load_entry_point\n  File \"\u002Fusr\u002Flocal\u002FCellar\u002Fpython\u002F3.7.3\u002FFrameworks\u002FPython.framework\u002FVersions\u002F3.7\u002Flib\u002Fpython3.7\u002Fsite-packages\u002Fpkg_resources\u002F__init__.py\", line 3241, in \u003Cmodule>\n    @_call_aside\n  File \"\u002Fusr\u002Flocal\u002FCellar\u002Fpython\u002F3.7.3\u002FFrameworks\u002FPython.framework\u002FVersions\u002F3.7\u002Flib\u002Fpython3.7\u002Fsite-packages\u002Fpkg_resources\u002F__init__.py\", line 3225, in _call_aside\n    f(*args, **kwargs)\n  File \"\u002Fusr\u002Flocal\u002FCellar\u002Fpython\u002F3.7.3\u002FFrameworks\u002FPython.framework\u002FVersions\u002F3.7\u002Flib\u002Fpython3.7\u002Fsite-packages\u002Fpkg_resources\u002F__init__.py\", line 3254, in _initialize_master_working_set\n    working_set = WorkingSet._build_master()\n  File \"\u002Fusr\u002Flocal\u002FCellar\u002Fpython\u002F3.7.3\u002FFrameworks\u002FPython.framework\u002FVersions\u002F3.7\u002Flib\u002Fpython3.7\u002Fsite-packages\u002Fpkg_resources\u002F__init__.py\", line 583, in _build_master\n    ws.require(__requires__)\n  File \"\u002Fusr\u002Flocal\u002FCellar\u002Fpython\u002F3.7.3\u002FFrameworks\u002FPython.framework\u002FVersions\u002F3.7\u002Flib\u002Fpython3.7\u002Fsite-packages\u002Fpkg_resources\u002F__init__.py\", line 900, in require\n    needed = self.resolve(parse_requirements(requirements))\n  File \"\u002Fusr\u002Flocal\u002FCellar\u002Fpython\u002F3.7.3\u002FFrameworks\u002FPython.framework\u002FVersions\u002F3.7\u002Flib\u002Fpython3.7\u002Fsite-packages\u002Fpkg_resources\u002F__init__.py\", line 786, in resolve\n    raise DistributionNotFound(req, requirers)\npkg_resources.DistributionNotFound: The 'pipenv==2018.11.26' distribution was not found and is required by the application\n\n",[65,18759,18760,18765,18776,18780,18787,18794,18798,18803,18808,18824,18829,18843,18848,18862,18867,18881,18891,18905,18913,18927,18938,18952,18957],{"__ignoreMap":63},[68,18761,18762],{"class":70,"line":71},[68,18763,18764],{"class":373},"Executed command:\n",[68,18766,18767,18770,18773],{"class":70,"line":78},[68,18768,18769],{"class":373},"\u002Fusr\u002Flocal\u002Fbin\u002Fpipenv --python \u002Fusr\u002Flocal\u002Fbin\u002Fpython3.",[68,18771,18772],{"class":81},"6",[68,18774,18775],{"class":373}," install --dev\n",[68,18777,18778],{"class":70,"line":98},[68,18779,416],{"emptyLinePlaceholder":415},[68,18781,18782,18784],{"class":70,"line":123},[68,18783,370],{"class":116},[68,18785,18786],{"class":373}," occurred:\n",[68,18788,18789,18791],{"class":70,"line":129},[68,18790,370],{"class":116},[68,18792,18793],{"class":373}," Running Pipenv\n",[68,18795,18796],{"class":70,"line":212},[68,18797,416],{"emptyLinePlaceholder":415},[68,18799,18800],{"class":70,"line":233},[68,18801,18802],{"class":373},"Command output:\n",[68,18804,18805],{"class":70,"line":268},[68,18806,18807],{"class":373},"Traceback (most recent call last):\n",[68,18809,18810,18813,18816,18819,18821],{"class":70,"line":289},[68,18811,18812],{"class":373},"  File ",[68,18814,18815],{"class":116},"\"\u002Fusr\u002Flocal\u002FCellar\u002Fpipenv\u002F2018.11.26_2\u002Flibexec\u002Fbin\u002Fpipenv\"",[68,18817,18818],{"class":373},", line ",[68,18820,18772],{"class":81},[68,18822,18823],{"class":373},", in \u003Cmodule>\n",[68,18825,18826],{"class":70,"line":308},[68,18827,18828],{"class":373},"    from pkg_resources import load_entry_point\n",[68,18830,18831,18833,18836,18838,18841],{"class":70,"line":314},[68,18832,18812],{"class":373},[68,18834,18835],{"class":116},"\"\u002Fusr\u002Flocal\u002FCellar\u002Fpython\u002F3.7.3\u002FFrameworks\u002FPython.framework\u002FVersions\u002F3.7\u002Flib\u002Fpython3.7\u002Fsite-packages\u002Fpkg_resources\u002F__init__.py\"",[68,18837,18818],{"class":373},[68,18839,18840],{"class":81},"3241",[68,18842,18823],{"class":373},[68,18844,18845],{"class":70,"line":320},[68,18846,18847],{"class":373},"    @_call_aside\n",[68,18849,18850,18852,18854,18856,18859],{"class":70,"line":889},[68,18851,18812],{"class":373},[68,18853,18835],{"class":116},[68,18855,18818],{"class":373},[68,18857,18858],{"class":81},"3225",[68,18860,18861],{"class":373},", in _call_aside\n",[68,18863,18864],{"class":70,"line":909},[68,18865,18866],{"class":373},"    f(*args, **kwargs)\n",[68,18868,18869,18871,18873,18875,18878],{"class":70,"line":929},[68,18870,18812],{"class":373},[68,18872,18835],{"class":116},[68,18874,18818],{"class":373},[68,18876,18877],{"class":81},"3254",[68,18879,18880],{"class":373},", in _initialize_master_working_set\n",[68,18882,18883,18886,18889],{"class":70,"line":949},[68,18884,18885],{"class":373},"    working_set = ",[68,18887,18888],{"class":81},"WorkingSet._build_master",[68,18890,2136],{"class":373},[68,18892,18893,18895,18897,18899,18902],{"class":70,"line":969},[68,18894,18812],{"class":373},[68,18896,18835],{"class":116},[68,18898,18818],{"class":373},[68,18900,18901],{"class":81},"583",[68,18903,18904],{"class":373},", in _build_master\n",[68,18906,18907,18910],{"class":70,"line":989},[68,18908,18909],{"class":81},"    ws.require",[68,18911,18912],{"class":373},"(__requires__)\n",[68,18914,18915,18917,18919,18921,18924],{"class":70,"line":1009},[68,18916,18812],{"class":373},[68,18918,18835],{"class":116},[68,18920,18818],{"class":373},[68,18922,18923],{"class":81},"900",[68,18925,18926],{"class":373},", in require\n",[68,18928,18929,18932,18935],{"class":70,"line":1029},[68,18930,18931],{"class":373},"    needed = ",[68,18933,18934],{"class":81},"self.resolve",[68,18936,18937],{"class":373},"(parse_requirements(requirements))\n",[68,18939,18940,18942,18944,18946,18949],{"class":70,"line":1049},[68,18941,18812],{"class":373},[68,18943,18835],{"class":116},[68,18945,18818],{"class":373},[68,18947,18948],{"class":81},"786",[68,18950,18951],{"class":373},", in resolve\n",[68,18953,18954],{"class":70,"line":1069},[68,18955,18956],{"class":373},"    raise DistributionNotFound(req, requirers)\n",[68,18958,18959,18962,18965,18968],{"class":70,"line":1088},[68,18960,18961],{"class":81},"pkg_resources.DistributionNotFound",[68,18963,18964],{"class":373},": The ",[68,18966,18967],{"class":116},"'pipenv==2018.11.26'",[68,18969,18970],{"class":373}," distribution was not found and is required by the application\n",[554,18972],{"filename":556},[28,18974,18975],{},"这个问题是因为系统同时存在 Python 3.6 和 Python 3.7 导致的，查看了下，Homebrew 安装的 Python 3.7，而通过官网的 dmg 安装的 Python 3.6。我觉得这个与 PyCharm 内部的运行环境有关，即使我默认的已经是 Python 3.6 了，它不知为何依然调用了 Python 3.7 来执行。尝试卸掉了一个，发现这个问题解决了。",[28,18977,18978],{},"再次尝试，报了另外的错：",[58,18980,18982],{"className":361,"code":18981,"language":363,"meta":63,"style":63},"Executed command:\n\u002FUsers\u002Fbean\u002FLibrary\u002FPython\u002F3.6\u002Fbin\u002Fpipenv --python \u002Fusr\u002Flocal\u002Fbin\u002Fpython3.6 install --dev\n\nError occurred:\nRuntimeError: Click will abort further execution because Python 3 was configured to use ASCII as encoding for the environment. Consult https:\u002F\u002Fclick.palletsprojects.com\u002Fen\u002F7.x\u002Fpython3\u002F for mitigation steps.\n\nTraceback (most recent call last):\n  File \"\u002FUsers\u002Fbean\u002FLibrary\u002FPython\u002F3.6\u002Fbin\u002Fpipenv\", line 10, in \u003Cmodule>\n    sys.exit(cli())\n  File \"\u002FUsers\u002Fbean\u002FLibrary\u002FPython\u002F3.6\u002Flib\u002Fpython\u002Fsite-packages\u002Fpipenv\u002Fvendor\u002Fclick\u002Fcore.py\", line 764, in __call__\n    return self.main(*args, **kwargs)\n  File \"\u002FUsers\u002Fbean\u002FLibrary\u002FPython\u002F3.6\u002Flib\u002Fpython\u002Fsite-packages\u002Fpipenv\u002Fvendor\u002Fclick\u002Fcore.py\", line 696, in main\n    _verify_python3_env()\n  File \"\u002FUsers\u002Fbean\u002FLibrary\u002FPython\u002F3.6\u002Flib\u002Fpython\u002Fsite-packages\u002Fpipenv\u002Fvendor\u002Fclick\u002F_unicodefun.py\", line 124, in _verify_python3_env\n    ' mitigation steps.' + extra\nRuntimeError: Click will abort further execution because Python 3 was configured to use ASCII as encoding for the environment. Consult https:\u002F\u002Fclick.palletsprojects.com\u002Fen\u002F7.x\u002Fpython3\u002F for mitigation steps.\n\nThis system lists a couple of UTF-8 supporting locales that\nyou can pick from.  The following suitable locales were\ndiscovered: af_ZA.UTF-8, am_ET.UTF-8, be_BY.UTF-8, bg_BG.UTF-8, ca_ES.UTF-8, cs_CZ.UTF-8, da_DK.UTF-8, de_AT.UTF-8, de_CH.UTF-8, de_DE.UTF-8, el_GR.UTF-8, en_AU.UTF-8, en_CA.UTF-8, en_GB.UTF-8, en_IE.UTF-8, en_NZ.UTF-8, en_US.UTF-8, es_ES.UTF-8, et_EE.UTF-8, eu_ES.UTF-8, fi_FI.UTF-8, fr_BE.UTF-8, fr_CA.UTF-8, fr_CH.UTF-8, fr_FR.UTF-8, he_IL.UTF-8, hr_HR.UTF-8, hu_HU.UTF-8, hy_AM.UTF-8, is_IS.UTF-8, it_CH.UTF-8, it_IT.UTF-8, ja_JP.UTF-8, kk_KZ.UTF-8, ko_KR.UTF-8, lt_LT.UTF-8, nl_BE.UTF-8, nl_NL.UTF-8, no_NO.UTF-8, pl_PL.UTF-8, pt_BR.UTF-8, pt_PT.UTF-8, ro_RO.UTF-8, ru_RU.UTF-8, sk_SK.UTF-8, sl_SI.UTF-8, sr_YU.UTF-8, sv_SE.UTF-8, tr_TR.UTF-8, uk_UA.UTF-8, zh_CN.UTF-8, zh_HK.UTF-8, zh_TW.UTF-8\n\n",[65,18983,18984,18988,19006,19010,19016,19032,19036,19040,19054,19062,19077,19088,19102,19107,19122,19130,19142,19146,19156,19161],{"__ignoreMap":63},[68,18985,18986],{"class":70,"line":71},[68,18987,18764],{"class":373},[68,18989,18990,18993,18995,18997,18999,19002,19004],{"class":70,"line":78},[68,18991,18992],{"class":373},"\u002FUsers\u002Fbean\u002FLibrary\u002FPython\u002F",[68,18994,5057],{"class":81},[68,18996,404],{"class":373},[68,18998,18772],{"class":81},[68,19000,19001],{"class":373},"\u002Fbin\u002Fpipenv --python \u002Fusr\u002Flocal\u002Fbin\u002Fpython3.",[68,19003,18772],{"class":81},[68,19005,18775],{"class":373},[68,19007,19008],{"class":70,"line":98},[68,19009,416],{"emptyLinePlaceholder":415},[68,19011,19012,19014],{"class":70,"line":123},[68,19013,370],{"class":116},[68,19015,18786],{"class":373},[68,19017,19018,19021,19023,19026,19029],{"class":70,"line":129},[68,19019,19020],{"class":373},"RuntimeError: Click will abort further execution because Python ",[68,19022,5057],{"class":81},[68,19024,19025],{"class":373}," was configured to use ASCII as encoding for the environment. Consult ",[68,19027,19028],{"class":81},"https:\u002F\u002Fclick.palletsprojects.com\u002Fen\u002F7.x\u002Fpython3\u002F",[68,19030,19031],{"class":373}," for mitigation steps.\n",[68,19033,19034],{"class":70,"line":212},[68,19035,416],{"emptyLinePlaceholder":415},[68,19037,19038],{"class":70,"line":233},[68,19039,18807],{"class":373},[68,19041,19042,19044,19047,19049,19052],{"class":70,"line":268},[68,19043,18812],{"class":373},[68,19045,19046],{"class":116},"\"\u002FUsers\u002Fbean\u002FLibrary\u002FPython\u002F3.6\u002Fbin\u002Fpipenv\"",[68,19048,18818],{"class":373},[68,19050,19051],{"class":81},"10",[68,19053,18823],{"class":373},[68,19055,19056,19059],{"class":70,"line":289},[68,19057,19058],{"class":81},"    sys.exit",[68,19060,19061],{"class":373},"(cli())\n",[68,19063,19064,19066,19069,19071,19074],{"class":70,"line":308},[68,19065,18812],{"class":373},[68,19067,19068],{"class":116},"\"\u002FUsers\u002Fbean\u002FLibrary\u002FPython\u002F3.6\u002Flib\u002Fpython\u002Fsite-packages\u002Fpipenv\u002Fvendor\u002Fclick\u002Fcore.py\"",[68,19070,18818],{"class":373},[68,19072,19073],{"class":81},"764",[68,19075,19076],{"class":373},", in __call__\n",[68,19078,19079,19082,19085],{"class":70,"line":314},[68,19080,19081],{"class":373},"    return ",[68,19083,19084],{"class":81},"self.main",[68,19086,19087],{"class":373},"(*args, **kwargs)\n",[68,19089,19090,19092,19094,19096,19099],{"class":70,"line":320},[68,19091,18812],{"class":373},[68,19093,19068],{"class":116},[68,19095,18818],{"class":373},[68,19097,19098],{"class":81},"696",[68,19100,19101],{"class":373},", in main\n",[68,19103,19104],{"class":70,"line":889},[68,19105,19106],{"class":373},"    _verify_python3_env()\n",[68,19108,19109,19111,19114,19116,19119],{"class":70,"line":909},[68,19110,18812],{"class":373},[68,19112,19113],{"class":116},"\"\u002FUsers\u002Fbean\u002FLibrary\u002FPython\u002F3.6\u002Flib\u002Fpython\u002Fsite-packages\u002Fpipenv\u002Fvendor\u002Fclick\u002F_unicodefun.py\"",[68,19115,18818],{"class":373},[68,19117,19118],{"class":81},"124",[68,19120,19121],{"class":373},", in _verify_python3_env\n",[68,19123,19124,19127],{"class":70,"line":929},[68,19125,19126],{"class":116},"    ' mitigation steps.'",[68,19128,19129],{"class":373}," + extra\n",[68,19131,19132,19134,19136,19138,19140],{"class":70,"line":949},[68,19133,19020],{"class":373},[68,19135,5057],{"class":81},[68,19137,19025],{"class":373},[68,19139,19028],{"class":81},[68,19141,19031],{"class":373},[68,19143,19144],{"class":70,"line":969},[68,19145,416],{"emptyLinePlaceholder":415},[68,19147,19148,19151,19153],{"class":70,"line":989},[68,19149,19150],{"class":373},"This system lists a couple of UTF-",[68,19152,407],{"class":81},[68,19154,19155],{"class":373}," supporting locales that\n",[68,19157,19158],{"class":70,"line":1009},[68,19159,19160],{"class":373},"you can pick from.  The following suitable locales were\n",[68,19162,19163,19166,19169,19171,19174,19176,19179,19181,19184,19186,19189,19191,19194,19196,19199,19201,19204,19206,19209,19211,19214,19216,19219,19221,19224,19226,19229,19231,19234,19236,19239,19241,19244,19246,19249,19251,19254,19256,19259,19261,19264,19266,19269,19271,19274,19276,19279,19281,19284,19286,19289,19291,19294,19296,19299,19301,19304,19306,19309,19311,19314,19316,19319,19321,19324,19326,19329,19331,19334,19336,19339,19341,19344,19346,19349,19351,19354,19356,19359,19361,19364,19366,19369,19371,19374,19376,19379,19381,19384,19386,19389,19391,19394,19396,19399,19401,19404,19406,19409,19411,19414,19416,19419,19421,19424,19426],{"class":70,"line":1029},[68,19164,19165],{"class":373},"discovered: ",[68,19167,19168],{"class":81},"af_ZA.UTF-8",[68,19170,597],{"class":373},[68,19172,19173],{"class":81},"am_ET.UTF-8",[68,19175,597],{"class":373},[68,19177,19178],{"class":81},"be_BY.UTF-8",[68,19180,597],{"class":373},[68,19182,19183],{"class":81},"bg_BG.UTF-8",[68,19185,597],{"class":373},[68,19187,19188],{"class":81},"ca_ES.UTF-8",[68,19190,597],{"class":373},[68,19192,19193],{"class":81},"cs_CZ.UTF-8",[68,19195,597],{"class":373},[68,19197,19198],{"class":81},"da_DK.UTF-8",[68,19200,597],{"class":373},[68,19202,19203],{"class":81},"de_AT.UTF-8",[68,19205,597],{"class":373},[68,19207,19208],{"class":81},"de_CH.UTF-8",[68,19210,597],{"class":373},[68,19212,19213],{"class":81},"de_DE.UTF-8",[68,19215,597],{"class":373},[68,19217,19218],{"class":81},"el_GR.UTF-8",[68,19220,597],{"class":373},[68,19222,19223],{"class":81},"en_AU.UTF-8",[68,19225,597],{"class":373},[68,19227,19228],{"class":81},"en_CA.UTF-8",[68,19230,597],{"class":373},[68,19232,19233],{"class":81},"en_GB.UTF-8",[68,19235,597],{"class":373},[68,19237,19238],{"class":81},"en_IE.UTF-8",[68,19240,597],{"class":373},[68,19242,19243],{"class":81},"en_NZ.UTF-8",[68,19245,597],{"class":373},[68,19247,19248],{"class":81},"en_US.UTF-8",[68,19250,597],{"class":373},[68,19252,19253],{"class":81},"es_ES.UTF-8",[68,19255,597],{"class":373},[68,19257,19258],{"class":81},"et_EE.UTF-8",[68,19260,597],{"class":373},[68,19262,19263],{"class":81},"eu_ES.UTF-8",[68,19265,597],{"class":373},[68,19267,19268],{"class":81},"fi_FI.UTF-8",[68,19270,597],{"class":373},[68,19272,19273],{"class":81},"fr_BE.UTF-8",[68,19275,597],{"class":373},[68,19277,19278],{"class":81},"fr_CA.UTF-8",[68,19280,597],{"class":373},[68,19282,19283],{"class":81},"fr_CH.UTF-8",[68,19285,597],{"class":373},[68,19287,19288],{"class":81},"fr_FR.UTF-8",[68,19290,597],{"class":373},[68,19292,19293],{"class":81},"he_IL.UTF-8",[68,19295,597],{"class":373},[68,19297,19298],{"class":81},"hr_HR.UTF-8",[68,19300,597],{"class":373},[68,19302,19303],{"class":81},"hu_HU.UTF-8",[68,19305,597],{"class":373},[68,19307,19308],{"class":81},"hy_AM.UTF-8",[68,19310,597],{"class":373},[68,19312,19313],{"class":81},"is_IS.UTF-8",[68,19315,597],{"class":373},[68,19317,19318],{"class":81},"it_CH.UTF-8",[68,19320,597],{"class":373},[68,19322,19323],{"class":81},"it_IT.UTF-8",[68,19325,597],{"class":373},[68,19327,19328],{"class":81},"ja_JP.UTF-8",[68,19330,597],{"class":373},[68,19332,19333],{"class":81},"kk_KZ.UTF-8",[68,19335,597],{"class":373},[68,19337,19338],{"class":81},"ko_KR.UTF-8",[68,19340,597],{"class":373},[68,19342,19343],{"class":81},"lt_LT.UTF-8",[68,19345,597],{"class":373},[68,19347,19348],{"class":81},"nl_BE.UTF-8",[68,19350,597],{"class":373},[68,19352,19353],{"class":81},"nl_NL.UTF-8",[68,19355,597],{"class":373},[68,19357,19358],{"class":81},"no_NO.UTF-8",[68,19360,597],{"class":373},[68,19362,19363],{"class":81},"pl_PL.UTF-8",[68,19365,597],{"class":373},[68,19367,19368],{"class":81},"pt_BR.UTF-8",[68,19370,597],{"class":373},[68,19372,19373],{"class":81},"pt_PT.UTF-8",[68,19375,597],{"class":373},[68,19377,19378],{"class":81},"ro_RO.UTF-8",[68,19380,597],{"class":373},[68,19382,19383],{"class":81},"ru_RU.UTF-8",[68,19385,597],{"class":373},[68,19387,19388],{"class":81},"sk_SK.UTF-8",[68,19390,597],{"class":373},[68,19392,19393],{"class":81},"sl_SI.UTF-8",[68,19395,597],{"class":373},[68,19397,19398],{"class":81},"sr_YU.UTF-8",[68,19400,597],{"class":373},[68,19402,19403],{"class":81},"sv_SE.UTF-8",[68,19405,597],{"class":373},[68,19407,19408],{"class":81},"tr_TR.UTF-8",[68,19410,597],{"class":373},[68,19412,19413],{"class":81},"uk_UA.UTF-8",[68,19415,597],{"class":373},[68,19417,19418],{"class":81},"zh_CN.UTF-8",[68,19420,597],{"class":373},[68,19422,19423],{"class":81},"zh_HK.UTF-8",[68,19425,597],{"class":373},[68,19427,19428],{"class":81},"zh_TW.UTF-8\n",[28,19430,19431,19432,19435,19436,19439],{},"Google 了半天，没找到有效的答案，大多数是让重新安装之类的，没意义。后来在 JetBrains 的 Issues 平台上提交了问题，最终发现这是一个已知问题，并且与 mac 上使用了 ",[65,19433,19434],{},"zsh"," 终端相关，可能 ",[65,19437,19438],{},"oh-my-zsh"," 重置了编码，导致 PyCharm 的运行环境中存在问题，最终解决方法如下：",[28,19441,1659,19442,19445],{},[65,19443,19444],{},".zshrc"," 文件最后指定编码：",[58,19447,19449],{"className":1502,"code":19448,"language":1504,"meta":63,"style":63},"export LANG=zh_CN.UTF-8\n",[65,19450,19451],{"__ignoreMap":63},[68,19452,19453,19455,19458,19460],{"class":70,"line":71},[68,19454,13293],{"class":1864},[68,19456,19457],{"class":373}," LANG",[68,19459,877],{"class":1899},[68,19461,19462],{"class":373},"zh_CN.UTF-8\n",[28,19464,19465],{},"重启 PyCharm，问题解决！",[523,19467,19468],{},"html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":63,"searchDepth":78,"depth":78,"links":19470},[],"2019-07-19",{},"\u002Fposts\u002F2019\u002Fsolve-pycharm-adding-pipenv-error",{"text":535,"minutes":19475,"time":19476,"words":19477},2.475,148500,495,{"title":18749,"description":18754},{"loc":19473},"posts\u002F2019\u002F20190719.solve-pycharm-adding-pipenv-error",[543,10651],"evfrZW5UIS6uc56-A3l7RyWC1PkguQKJQ3MsPghGai8",{"id":19484,"title":19485,"body":19486,"class":528,"cover":1212,"coverSize":528,"date":19859,"description":19490,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":19860,"navigation":415,"path":19861,"readingTime":19862,"seo":19866,"sitemap":19867,"stem":19868,"tags":19869,"time":528,"weather":528,"__hash__":19870},"posts\u002Fposts\u002F2019\u002F20190710.centos-wire-ieee8021x-config.md","CentOS 有线网卡配置 IEEE 802.1X 上网",{"type":25,"value":19487,"toc":19857},[19488,19491,19494,19837,19854],[28,19489,19490],{},"公司网络使用 IEEE 802.1X 认证网络接入，手机端和 Windows 端都很方便，Linux 上稍微麻烦一些。最近有个测试服务器（CentOS 7.6）需要接入办公网络测试，折腾了一番。",[28,19492,19493],{},"中间走的弯路就不讲了，直接讲最终的解决方案吧。",[5010,19495,19496,19511,19525,19729,19815,19825,19831],{},[1239,19497,19498,19499,19502,19503,19506,19507,19510],{},"取消 ",[65,19500,19501],{},"\u002Fetc\u002Fsysconfig\u002Fnetwork-scripts\u002F"," 中活动网卡（本例中是 ",[65,19504,19505],{},"ifcfg-em1","）的任何配置，例如 ",[65,19508,19509],{},"ONBOOT"," 等。",[1239,19512,19513,19516,19517,19520,19521,19524],{},[65,19514,19515],{},"chkconfig --list"," ，查看是否有 ",[65,19518,19519],{},"network"," 的服务，如果有，执行 ",[65,19522,19523],{},"chkconfig --del network"," 删除",[1239,19526,19527,19528,19531,19532,19631,19634,19635,19637,19638,19641,19642],{},"修改 ",[65,19529,19530],{},"\u002Fetc\u002Fwpa_supplicant\u002Fwpa_supplicant.conf","，写入如下内容：",[58,19533,19535],{"className":1502,"code":19534,"language":1504,"meta":63,"style":63},"ctrl_interface=\u002Fvar\u002Frun\u002Fwpa_supplicant\nap_scan=0\nnetwork={\n    key_mgmt=IEEE8021X\n    eap=PEAP\n    identity=\"YOUR_USER_NAME\"\n    password=\"YOUR_PASSWORD\"\n    phase2=\"autheap=MSCHAPV2\"\n}\n",[65,19536,19537,19547,19557,19565,19575,19585,19599,19613,19627],{"__ignoreMap":63},[68,19538,19539,19542,19544],{"class":70,"line":71},[68,19540,19541],{"class":373},"ctrl_interface",[68,19543,877],{"class":1899},[68,19545,19546],{"class":116},"\u002Fvar\u002Frun\u002Fwpa_supplicant\n",[68,19548,19549,19552,19554],{"class":70,"line":78},[68,19550,19551],{"class":373},"ap_scan",[68,19553,877],{"class":1899},[68,19555,19556],{"class":116},"0\n",[68,19558,19559,19561,19563],{"class":70,"line":98},[68,19560,19519],{"class":373},[68,19562,877],{"class":1899},[68,19564,75],{"class":373},[68,19566,19567,19570,19572],{"class":70,"line":123},[68,19568,19569],{"class":373},"    key_mgmt",[68,19571,877],{"class":1899},[68,19573,19574],{"class":116},"IEEE8021X\n",[68,19576,19577,19580,19582],{"class":70,"line":129},[68,19578,19579],{"class":373},"    eap",[68,19581,877],{"class":1899},[68,19583,19584],{"class":116},"PEAP\n",[68,19586,19587,19590,19592,19594,19597],{"class":70,"line":212},[68,19588,19589],{"class":373},"    identity",[68,19591,877],{"class":1899},[68,19593,89],{"class":112},[68,19595,19596],{"class":116},"YOUR_USER_NAME",[68,19598,120],{"class":112},[68,19600,19601,19604,19606,19608,19611],{"class":70,"line":233},[68,19602,19603],{"class":373},"    password",[68,19605,877],{"class":1899},[68,19607,89],{"class":112},[68,19609,19610],{"class":116},"YOUR_PASSWORD",[68,19612,120],{"class":112},[68,19614,19615,19618,19620,19622,19625],{"class":70,"line":268},[68,19616,19617],{"class":373},"    phase2",[68,19619,877],{"class":1899},[68,19621,89],{"class":112},[68,19623,19624],{"class":116},"autheap=MSCHAPV2",[68,19626,120],{"class":112},[68,19628,19629],{"class":70,"line":289},[68,19630,132],{"class":373},[19632,19633],"br",{},"认证的账号和加密方式需要根据具体需求做更改。",[19632,19636],{},"可通过如下代码来测试是否成功（根据网卡名称 ",[65,19639,19640],{},"em1"," 需要根据实际使用的网卡做调整）：",[58,19643,19645],{"className":1502,"code":19644,"language":1504,"meta":63,"style":63},"$ ifdown em1\n$ wpa_supplicant -B -i em1 -c \u002Fetc\u002Fwpa_supplicant\u002Fwpa_supplicant.conf -D wired\n$ ifup em1\n$ dhclient em1\n$ ip addr # 查看IP地址\n$ ping baidu.com # 检查是否可以 Ping 通百度\n",[65,19646,19647,19657,19685,19694,19703,19716],{"__ignoreMap":63},[68,19648,19649,19651,19654],{"class":70,"line":71},[68,19650,5147],{"class":1511},[68,19652,19653],{"class":116}," ifdown",[68,19655,19656],{"class":116}," em1\n",[68,19658,19659,19661,19664,19667,19670,19673,19676,19679,19682],{"class":70,"line":78},[68,19660,5147],{"class":1511},[68,19662,19663],{"class":116}," wpa_supplicant",[68,19665,19666],{"class":1518}," -B",[68,19668,19669],{"class":1518}," -i",[68,19671,19672],{"class":116}," em1",[68,19674,19675],{"class":1518}," -c",[68,19677,19678],{"class":116}," \u002Fetc\u002Fwpa_supplicant\u002Fwpa_supplicant.conf",[68,19680,19681],{"class":1518}," -D",[68,19683,19684],{"class":116}," wired\n",[68,19686,19687,19689,19692],{"class":70,"line":98},[68,19688,5147],{"class":1511},[68,19690,19691],{"class":116}," ifup",[68,19693,19656],{"class":116},[68,19695,19696,19698,19701],{"class":70,"line":123},[68,19697,5147],{"class":1511},[68,19699,19700],{"class":116}," dhclient",[68,19702,19656],{"class":116},[68,19704,19705,19707,19710,19713],{"class":70,"line":129},[68,19706,5147],{"class":1511},[68,19708,19709],{"class":116}," ip",[68,19711,19712],{"class":116}," addr",[68,19714,19715],{"class":2403}," # 查看IP地址\n",[68,19717,19718,19720,19723,19726],{"class":70,"line":212},[68,19719,5147],{"class":1511},[68,19721,19722],{"class":116}," ping",[68,19724,19725],{"class":116}," baidu.com",[68,19727,19728],{"class":2403}," # 检查是否可以 Ping 通百度\n",[1239,19730,19731,19732,19735,19736,19531,19739],{},"下面设置开机启动，在 ",[65,19733,19734],{},"\u002Fetc\u002Finit.d\u002F"," 中创建 ",[65,19737,19738],{},"wpa_network",[58,19740,19742],{"className":1502,"code":19741,"language":1504,"meta":63,"style":63},"#!\u002Fbin\u002Fbash\n# chkconfig: 2345 10 90\n# description: wpa network\n\nifdown em1\n\nwpa_supplicant -B -i em1 -c \u002Fetc\u002Fwpa_supplicant\u002Fwpa_supplicant.conf -D wired\n\nifup em1\n\ndhclient em1\n",[65,19743,19744,19749,19754,19759,19763,19770,19774,19793,19797,19804,19808],{"__ignoreMap":63},[68,19745,19746],{"class":70,"line":71},[68,19747,19748],{"class":2403},"#!\u002Fbin\u002Fbash\n",[68,19750,19751],{"class":70,"line":78},[68,19752,19753],{"class":2403},"# chkconfig: 2345 10 90\n",[68,19755,19756],{"class":70,"line":98},[68,19757,19758],{"class":2403},"# description: wpa network\n",[68,19760,19761],{"class":70,"line":123},[68,19762,416],{"emptyLinePlaceholder":415},[68,19764,19765,19768],{"class":70,"line":129},[68,19766,19767],{"class":1511},"ifdown",[68,19769,19656],{"class":116},[68,19771,19772],{"class":70,"line":212},[68,19773,416],{"emptyLinePlaceholder":415},[68,19775,19776,19779,19781,19783,19785,19787,19789,19791],{"class":70,"line":233},[68,19777,19778],{"class":1511},"wpa_supplicant",[68,19780,19666],{"class":1518},[68,19782,19669],{"class":1518},[68,19784,19672],{"class":116},[68,19786,19675],{"class":1518},[68,19788,19678],{"class":116},[68,19790,19681],{"class":1518},[68,19792,19684],{"class":116},[68,19794,19795],{"class":70,"line":268},[68,19796,416],{"emptyLinePlaceholder":415},[68,19798,19799,19802],{"class":70,"line":289},[68,19800,19801],{"class":1511},"ifup",[68,19803,19656],{"class":116},[68,19805,19806],{"class":70,"line":308},[68,19807,416],{"emptyLinePlaceholder":415},[68,19809,19810,19813],{"class":70,"line":314},[68,19811,19812],{"class":1511},"dhclient",[68,19814,19656],{"class":116},[1239,19816,19817,19820,19821,19824],{},[65,19818,19819],{},"chkconfig --add wpa-network","，加入到 ",[65,19822,19823],{},"chkconfig"," 中",[1239,19826,19827,19830],{},[65,19828,19829],{},"chkconfig wpa-network on","，开启",[1239,19832,19833,19836],{},[65,19834,19835],{},"reboot"," 重启检测是否成功自动联网",[7538,19838,19839],{},[28,19840,19841,19842,19844,19845,5551,19847,19849,19850,19853],{},"后记：后来发现运维可以直接通过 MAC 地址配置上网，于是又取消了 wpa 自动联网，直接在 ",[65,19843,19501],{}," 中把 ",[65,19846,19505],{},[65,19848,19509],{}," 设为 ",[65,19851,19852],{},"yes"," 即可。",[523,19855,19856],{},"html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}",{"title":63,"searchDepth":78,"depth":78,"links":19858},[],"2019-07-10",{},"\u002Fposts\u002F2019\u002Fcentos-wire-ieee8021x-config",{"text":1217,"minutes":19863,"time":19864,"words":19865},1.635,98100,327,{"title":19485,"description":19490},{"loc":19861},"posts\u002F2019\u002F20190710.centos-wire-ieee8021x-config",[543,7153],"sWdr68ZZC2gy6xqIzSOLUKcTSd2AzXUEPvVwCE_6gEY",{"id":19872,"title":19873,"body":19874,"class":528,"cover":1212,"coverSize":528,"date":21225,"description":19878,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":21226,"navigation":415,"path":21227,"readingTime":21228,"seo":21231,"sitemap":21232,"stem":21233,"tags":21234,"time":528,"weather":18131,"__hash__":21235},"posts\u002Fposts\u002F2019\u002F20190324.audiowave-animation.md","声波动画教程",{"type":25,"value":19875,"toc":21223},[19876,19879,19882,19885,19889,19895,19898,19959,20198,21142,21145,21158,21164,21176,21189,21192,21206,21220],[28,19877,19878],{},"最近项目中有一个声波动画的效果的需求，网上没找到合适的，于是手撸了一个。",[8514,19880,19881],{"id":19881},"效果",[554,19883],{"filename":19884},"01.gif",[8514,19886,19888],{"id":19887},"demo","Demo",[28,19890,19891],{},[38,19892,19893],{"href":19893,"rel":19894},"https:\u002F\u002Fjsfiddle.net\u002FHADB\u002Fx8vdkmqh\u002F",[42],[8514,19896,19897],{"id":19897},"示例代码",[58,19899,19903],{"className":19900,"code":19901,"language":19902,"meta":63,"style":63},"language-html shiki shiki-themes material-theme-lighter github-light github-dark","\u003Cdiv class=\"background\">\n  \u003Cdiv class=\"audiowave\">\u003C\u002Fdiv>\n\u003C\u002Fdiv>\n","html",[65,19904,19905,19926,19951],{"__ignoreMap":63},[68,19906,19907,19909,19912,19915,19917,19919,19922,19924],{"class":70,"line":71},[68,19908,727],{"class":74},[68,19910,19911],{"class":730},"div",[68,19913,19914],{"class":873}," class",[68,19916,877],{"class":74},[68,19918,89],{"class":112},[68,19920,19921],{"class":116},"background",[68,19923,89],{"class":112},[68,19925,734],{"class":74},[68,19927,19928,19931,19933,19935,19937,19939,19942,19944,19947,19949],{"class":70,"line":78},[68,19929,19930],{"class":74},"  \u003C",[68,19932,19911],{"class":730},[68,19934,19914],{"class":873},[68,19936,877],{"class":74},[68,19938,89],{"class":112},[68,19940,19941],{"class":116},"audiowave",[68,19943,89],{"class":112},[68,19945,19946],{"class":74},">\u003C\u002F",[68,19948,19911],{"class":730},[68,19950,734],{"class":74},[68,19952,19953,19955,19957],{"class":70,"line":98},[68,19954,771],{"class":74},[68,19956,19911],{"class":730},[68,19958,734],{"class":74},[58,19960,19962],{"className":11308,"code":19961,"language":11310,"meta":63,"style":63},"\u002F*\n\nAuthor: HADB\nDate: 2019-03-24\nMy Blog: https:\u002F\u002Fhadb.me\nMy GitHub: https:\u002F\u002Fgithub.com\u002FHADB\n\n*\u002F\n\nconst wavePillarCount = 100 \u002F\u002F 柱子总数（用来调整密度）\nconst waveCount = 5 \u002F\u002F 波形总数（用来调整波形数量）\nconst waveAnimationDuration = 3 \u002F\u002F 单个动画秒数（与 animation-duration 一致）\nconst randomRate = 1 \u002F\u002F 随机倍率，越大越随机\n\nfor (i = 0; i \u003C wavePillarCount; i++) {\n  const offset = ((i \u002F wavePillarCount - 1) * waveCount * waveAnimationDuration) - Math.random() * randomRate\n  document.querySelector('.audiowave').innerHTML += `\u003Cdiv style=\"-webkit-animation-delay:${offset}s;\">\u003C\u002Fdiv>`\n}\n",[65,19963,19964,19969,19973,19978,19983,19988,19993,19997,20002,20006,20021,20036,20050,20064,20068,20100,20149,20194],{"__ignoreMap":63},[68,19965,19966],{"class":70,"line":71},[68,19967,19968],{"class":2403},"\u002F*\n",[68,19970,19971],{"class":70,"line":78},[68,19972,416],{"emptyLinePlaceholder":415},[68,19974,19975],{"class":70,"line":98},[68,19976,19977],{"class":2403},"Author: HADB\n",[68,19979,19980],{"class":70,"line":123},[68,19981,19982],{"class":2403},"Date: 2019-03-24\n",[68,19984,19985],{"class":70,"line":129},[68,19986,19987],{"class":2403},"My Blog: https:\u002F\u002Fhadb.me\n",[68,19989,19990],{"class":70,"line":212},[68,19991,19992],{"class":2403},"My GitHub: https:\u002F\u002Fgithub.com\u002FHADB\n",[68,19994,19995],{"class":70,"line":233},[68,19996,416],{"emptyLinePlaceholder":415},[68,19998,19999],{"class":70,"line":268},[68,20000,20001],{"class":2403},"*\u002F\n",[68,20003,20004],{"class":70,"line":289},[68,20005,416],{"emptyLinePlaceholder":415},[68,20007,20008,20010,20013,20015,20018],{"class":70,"line":308},[68,20009,11317],{"class":1864},[68,20011,20012],{"class":665}," wavePillarCount",[68,20014,1900],{"class":1899},[68,20016,20017],{"class":2397}," 100",[68,20019,20020],{"class":2403}," \u002F\u002F 柱子总数（用来调整密度）\n",[68,20022,20023,20025,20028,20030,20033],{"class":70,"line":314},[68,20024,11317],{"class":1864},[68,20026,20027],{"class":665}," waveCount",[68,20029,1900],{"class":1899},[68,20031,20032],{"class":2397}," 5",[68,20034,20035],{"class":2403}," \u002F\u002F 波形总数（用来调整波形数量）\n",[68,20037,20038,20040,20043,20045,20047],{"class":70,"line":320},[68,20039,11317],{"class":1864},[68,20041,20042],{"class":665}," waveAnimationDuration",[68,20044,1900],{"class":1899},[68,20046,13668],{"class":2397},[68,20048,20049],{"class":2403}," \u002F\u002F 单个动画秒数（与 animation-duration 一致）\n",[68,20051,20052,20054,20057,20059,20061],{"class":70,"line":889},[68,20053,11317],{"class":1864},[68,20055,20056],{"class":665}," randomRate",[68,20058,1900],{"class":1899},[68,20060,2646],{"class":2397},[68,20062,20063],{"class":2403}," \u002F\u002F 随机倍率，越大越随机\n",[68,20065,20066],{"class":70,"line":909},[68,20067,416],{"emptyLinePlaceholder":415},[68,20069,20070,20072,20075,20077,20080,20082,20084,20086,20088,20090,20093,20096,20098],{"class":70,"line":929},[68,20071,13327],{"class":1790},[68,20073,20074],{"class":373}," (i ",[68,20076,877],{"class":1899},[68,20078,20079],{"class":2397}," 0",[68,20081,16105],{"class":74},[68,20083,2741],{"class":373},[68,20085,727],{"class":1899},[68,20087,20012],{"class":373},[68,20089,16105],{"class":74},[68,20091,20092],{"class":373}," i",[68,20094,20095],{"class":1899},"++",[68,20097,588],{"class":373},[68,20099,75],{"class":74},[68,20101,20102,20104,20106,20108,20111,20113,20115,20117,20119,20121,20123,20125,20127,20129,20131,20133,20135,20137,20139,20142,20144,20146],{"class":70,"line":949},[68,20103,12448],{"class":1864},[68,20105,8192],{"class":665},[68,20107,1900],{"class":1899},[68,20109,20110],{"class":2122}," ((",[68,20112,2773],{"class":373},[68,20114,7670],{"class":1899},[68,20116,20012],{"class":373},[68,20118,2394],{"class":1899},[68,20120,2646],{"class":2397},[68,20122,588],{"class":2122},[68,20124,7661],{"class":1899},[68,20126,20027],{"class":373},[68,20128,8798],{"class":1899},[68,20130,20042],{"class":373},[68,20132,588],{"class":2122},[68,20134,2652],{"class":1899},[68,20136,7673],{"class":373},[68,20138,404],{"class":74},[68,20140,20141],{"class":2183},"random",[68,20143,15402],{"class":2122},[68,20145,7661],{"class":1899},[68,20147,20148],{"class":373}," randomRate\n",[68,20150,20151,20154,20156,20159,20161,20163,20166,20168,20170,20172,20175,20177,20179,20182,20184,20187,20189,20192],{"class":70,"line":969},[68,20152,20153],{"class":373},"  document",[68,20155,404],{"class":74},[68,20157,20158],{"class":2183},"querySelector",[68,20160,648],{"class":2122},[68,20162,8129],{"class":112},[68,20164,20165],{"class":116},".audiowave",[68,20167,8129],{"class":112},[68,20169,2753],{"class":2122},[68,20171,404],{"class":74},[68,20173,20174],{"class":373},"innerHTML",[68,20176,8319],{"class":1899},[68,20178,12662],{"class":112},[68,20180,20181],{"class":116},"\u003Cdiv style=\"-webkit-animation-delay:",[68,20183,11788],{"class":112},[68,20185,20186],{"class":373},"offset",[68,20188,2400],{"class":112},[68,20190,20191],{"class":116},"s;\">\u003C\u002Fdiv>",[68,20193,11715],{"class":112},[68,20195,20196],{"class":70,"line":989},[68,20197,132],{"class":74},[58,20199,20203],{"className":20200,"code":20201,"language":20202,"meta":63,"style":63},"language-css shiki shiki-themes material-theme-lighter github-light github-dark","body {\n  padding: 0;\n  margin: 0;\n}\n\n.background {\n  width: 100vw;\n  height: 100vh;\n  background-color: #193082;\n}\n\n.audiowave {\n  position: absolute;\n  left: 0;\n  top: 0;\n  width: 100%;\n  height: 200px;\n  display: flex;\n  flex-direction: row;\n  align-items: flex-start;\n  justify-content: space-between;\n}\n\n.audiowave div {\n  top: 0;\n  background: rgba(44, 99, 255, 0.25);\n  z-index: 9;\n  width: 3px;\n  height: 0;\n  animation: audiowave 3s infinite linear;\n}\n\n@keyframes audiowave {\n  0% {\n    height: 28.75%;\n  }\n\n  5% {\n    height: 37.5%;\n  }\n\n  10% {\n    height: 56.25%;\n  }\n\n  15% {\n    height: 48.75%;\n  }\n\n  20% {\n    height: 68.75%;\n  }\n\n  25% {\n    height: 82.5%;\n  }\n\n  30% {\n    height: 71.25%;\n  }\n\n  35% {\n    height: 78.125%;\n  }\n\n  40% {\n    height: 68.75%;\n  }\n\n  45% {\n    height: 80.875%;\n  }\n\n  50% {\n    height: 90%;\n  }\n\n  55% {\n    height: 91.875%;\n  }\n\n  60% {\n    height: 87%;\n  }\n\n  65% {\n    height: 70%;\n  }\n\n  70% {\n    height: 60%;\n  }\n\n  75% {\n    height: 55%;\n  }\n\n  80% {\n    height: 45%;\n  }\n\n  85% {\n    height: 40%;\n  }\n\n  90% {\n    height: 35%;\n  }\n\n  95% {\n    height: 30%;\n  }\n\n  100% {\n    height: 28.75%;\n  }\n}\n","css",[65,20204,20205,20212,20224,20235,20239,20243,20252,20266,20280,20295,20299,20303,20311,20323,20334,20345,20358,20371,20383,20395,20407,20419,20423,20427,20438,20448,20481,20493,20505,20515,20538,20542,20546,20556,20563,20577,20581,20585,20592,20605,20609,20613,20620,20633,20637,20641,20648,20661,20665,20669,20676,20689,20693,20697,20704,20717,20721,20725,20732,20745,20749,20753,20760,20773,20777,20781,20788,20800,20804,20808,20815,20828,20832,20836,20843,20856,20860,20864,20871,20884,20888,20892,20899,20912,20916,20920,20927,20940,20944,20948,20955,20967,20971,20975,20982,20995,20999,21003,21010,21023,21027,21031,21038,21051,21055,21059,21066,21079,21083,21087,21094,21107,21111,21115,21122,21134,21138],{"__ignoreMap":63},[68,20206,20207,20210],{"class":70,"line":71},[68,20208,2673],{"class":20209},"s83vy",[68,20211,95],{"class":74},[68,20213,20214,20218,20220,20222],{"class":70,"line":78},[68,20215,20217],{"class":20216},"soE4H","  padding",[68,20219,92],{"class":74},[68,20221,20079],{"class":2397},[68,20223,5136],{"class":74},[68,20225,20226,20229,20231,20233],{"class":70,"line":98},[68,20227,20228],{"class":20216},"  margin",[68,20230,92],{"class":74},[68,20232,20079],{"class":2397},[68,20234,5136],{"class":74},[68,20236,20237],{"class":70,"line":123},[68,20238,132],{"class":74},[68,20240,20241],{"class":70,"line":129},[68,20242,416],{"emptyLinePlaceholder":415},[68,20244,20245,20248,20250],{"class":70,"line":212},[68,20246,404],{"class":20247},"stp6e",[68,20249,19921],{"class":1511},[68,20251,95],{"class":74},[68,20253,20254,20257,20259,20261,20264],{"class":70,"line":233},[68,20255,20256],{"class":20216},"  width",[68,20258,92],{"class":74},[68,20260,20017],{"class":2397},[68,20262,20263],{"class":575},"vw",[68,20265,5136],{"class":74},[68,20267,20268,20271,20273,20275,20278],{"class":70,"line":268},[68,20269,20270],{"class":20216},"  height",[68,20272,92],{"class":74},[68,20274,20017],{"class":2397},[68,20276,20277],{"class":575},"vh",[68,20279,5136],{"class":74},[68,20281,20282,20285,20287,20290,20293],{"class":70,"line":289},[68,20283,20284],{"class":20216},"  background-color",[68,20286,92],{"class":74},[68,20288,20289],{"class":81}," #",[68,20291,20292],{"class":665},"193082",[68,20294,5136],{"class":74},[68,20296,20297],{"class":70,"line":308},[68,20298,132],{"class":74},[68,20300,20301],{"class":70,"line":314},[68,20302,416],{"emptyLinePlaceholder":415},[68,20304,20305,20307,20309],{"class":70,"line":320},[68,20306,404],{"class":20247},[68,20308,19941],{"class":1511},[68,20310,95],{"class":74},[68,20312,20313,20316,20318,20321],{"class":70,"line":889},[68,20314,20315],{"class":20216},"  position",[68,20317,92],{"class":74},[68,20319,20320],{"class":665}," absolute",[68,20322,5136],{"class":74},[68,20324,20325,20328,20330,20332],{"class":70,"line":909},[68,20326,20327],{"class":20216},"  left",[68,20329,92],{"class":74},[68,20331,20079],{"class":2397},[68,20333,5136],{"class":74},[68,20335,20336,20339,20341,20343],{"class":70,"line":929},[68,20337,20338],{"class":20216},"  top",[68,20340,92],{"class":74},[68,20342,20079],{"class":2397},[68,20344,5136],{"class":74},[68,20346,20347,20349,20351,20353,20356],{"class":70,"line":949},[68,20348,20256],{"class":20216},[68,20350,92],{"class":74},[68,20352,20017],{"class":2397},[68,20354,20355],{"class":575},"%",[68,20357,5136],{"class":74},[68,20359,20360,20362,20364,20366,20369],{"class":70,"line":969},[68,20361,20270],{"class":20216},[68,20363,92],{"class":74},[68,20365,3154],{"class":2397},[68,20367,20368],{"class":575},"px",[68,20370,5136],{"class":74},[68,20372,20373,20376,20378,20381],{"class":70,"line":989},[68,20374,20375],{"class":20216},"  display",[68,20377,92],{"class":74},[68,20379,20380],{"class":665}," flex",[68,20382,5136],{"class":74},[68,20384,20385,20388,20390,20393],{"class":70,"line":1009},[68,20386,20387],{"class":20216},"  flex-direction",[68,20389,92],{"class":74},[68,20391,20392],{"class":665}," row",[68,20394,5136],{"class":74},[68,20396,20397,20400,20402,20405],{"class":70,"line":1029},[68,20398,20399],{"class":20216},"  align-items",[68,20401,92],{"class":74},[68,20403,20404],{"class":665}," flex-start",[68,20406,5136],{"class":74},[68,20408,20409,20412,20414,20417],{"class":70,"line":1049},[68,20410,20411],{"class":20216},"  justify-content",[68,20413,92],{"class":74},[68,20415,20416],{"class":665}," space-between",[68,20418,5136],{"class":74},[68,20420,20421],{"class":70,"line":1069},[68,20422,132],{"class":74},[68,20424,20425],{"class":70,"line":1088},[68,20426,416],{"emptyLinePlaceholder":415},[68,20428,20429,20431,20433,20436],{"class":70,"line":1108},[68,20430,404],{"class":20247},[68,20432,19941],{"class":1511},[68,20434,20435],{"class":20209}," div",[68,20437,95],{"class":74},[68,20439,20440,20442,20444,20446],{"class":70,"line":1128},[68,20441,20338],{"class":20216},[68,20443,92],{"class":74},[68,20445,20079],{"class":2397},[68,20447,5136],{"class":74},[68,20449,20450,20453,20455,20458,20460,20463,20465,20468,20470,20473,20475,20478],{"class":70,"line":1148},[68,20451,20452],{"class":20216},"  background",[68,20454,92],{"class":74},[68,20456,20457],{"class":630}," rgba",[68,20459,648],{"class":74},[68,20461,20462],{"class":2397},"44",[68,20464,255],{"class":74},[68,20466,20467],{"class":2397}," 99",[68,20469,255],{"class":74},[68,20471,20472],{"class":2397}," 255",[68,20474,255],{"class":74},[68,20476,20477],{"class":2397}," 0.25",[68,20479,20480],{"class":74},");\n",[68,20482,20483,20486,20488,20491],{"class":70,"line":1168},[68,20484,20485],{"class":20216},"  z-index",[68,20487,92],{"class":74},[68,20489,20490],{"class":2397}," 9",[68,20492,5136],{"class":74},[68,20494,20495,20497,20499,20501,20503],{"class":70,"line":1187},[68,20496,20256],{"class":20216},[68,20498,92],{"class":74},[68,20500,13668],{"class":2397},[68,20502,20368],{"class":575},[68,20504,5136],{"class":74},[68,20506,20507,20509,20511,20513],{"class":70,"line":2039},[68,20508,20270],{"class":20216},[68,20510,92],{"class":74},[68,20512,20079],{"class":2397},[68,20514,5136],{"class":74},[68,20516,20517,20520,20522,20525,20527,20530,20533,20536],{"class":70,"line":2055},[68,20518,20519],{"class":20216},"  animation",[68,20521,92],{"class":74},[68,20523,20524],{"class":373}," audiowave ",[68,20526,5057],{"class":2397},[68,20528,20529],{"class":575},"s",[68,20531,20532],{"class":665}," infinite",[68,20534,20535],{"class":665}," linear",[68,20537,5136],{"class":74},[68,20539,20540],{"class":70,"line":2071},[68,20541,132],{"class":74},[68,20543,20544],{"class":70,"line":2087},[68,20545,416],{"emptyLinePlaceholder":415},[68,20547,20548,20551,20554],{"class":70,"line":2092},[68,20549,20550],{"class":1790},"@keyframes",[68,20552,20553],{"class":1912}," audiowave",[68,20555,95],{"class":74},[68,20557,20558,20561],{"class":70,"line":2097},[68,20559,20560],{"class":1511},"  0%",[68,20562,95],{"class":74},[68,20564,20565,20568,20570,20573,20575],{"class":70,"line":2114},[68,20566,20567],{"class":20216},"    height",[68,20569,92],{"class":74},[68,20571,20572],{"class":2397}," 28.75",[68,20574,20355],{"class":575},[68,20576,5136],{"class":74},[68,20578,20579],{"class":70,"line":2139},[68,20580,126],{"class":74},[68,20582,20583],{"class":70,"line":2158},[68,20584,416],{"emptyLinePlaceholder":415},[68,20586,20587,20590],{"class":70,"line":2173},[68,20588,20589],{"class":1511},"  5%",[68,20591,95],{"class":74},[68,20593,20594,20596,20598,20601,20603],{"class":70,"line":2178},[68,20595,20567],{"class":20216},[68,20597,92],{"class":74},[68,20599,20600],{"class":2397}," 37.5",[68,20602,20355],{"class":575},[68,20604,5136],{"class":74},[68,20606,20607],{"class":70,"line":2193},[68,20608,126],{"class":74},[68,20610,20611],{"class":70,"line":2201},[68,20612,416],{"emptyLinePlaceholder":415},[68,20614,20615,20618],{"class":70,"line":2207},[68,20616,20617],{"class":1511},"  10%",[68,20619,95],{"class":74},[68,20621,20622,20624,20626,20629,20631],{"class":70,"line":2234},[68,20623,20567],{"class":20216},[68,20625,92],{"class":74},[68,20627,20628],{"class":2397}," 56.25",[68,20630,20355],{"class":575},[68,20632,5136],{"class":74},[68,20634,20635],{"class":70,"line":2258},[68,20636,126],{"class":74},[68,20638,20639],{"class":70,"line":2264},[68,20640,416],{"emptyLinePlaceholder":415},[68,20642,20643,20646],{"class":70,"line":2270},[68,20644,20645],{"class":1511},"  15%",[68,20647,95],{"class":74},[68,20649,20650,20652,20654,20657,20659],{"class":70,"line":2275},[68,20651,20567],{"class":20216},[68,20653,92],{"class":74},[68,20655,20656],{"class":2397}," 48.75",[68,20658,20355],{"class":575},[68,20660,5136],{"class":74},[68,20662,20663],{"class":70,"line":2289},[68,20664,126],{"class":74},[68,20666,20667],{"class":70,"line":2339},[68,20668,416],{"emptyLinePlaceholder":415},[68,20670,20671,20674],{"class":70,"line":2363},[68,20672,20673],{"class":1511},"  20%",[68,20675,95],{"class":74},[68,20677,20678,20680,20682,20685,20687],{"class":70,"line":2374},[68,20679,20567],{"class":20216},[68,20681,92],{"class":74},[68,20683,20684],{"class":2397}," 68.75",[68,20686,20355],{"class":575},[68,20688,5136],{"class":74},[68,20690,20691],{"class":70,"line":2407},[68,20692,126],{"class":74},[68,20694,20695],{"class":70,"line":2421},[68,20696,416],{"emptyLinePlaceholder":415},[68,20698,20699,20702],{"class":70,"line":2426},[68,20700,20701],{"class":1511},"  25%",[68,20703,95],{"class":74},[68,20705,20706,20708,20710,20713,20715],{"class":70,"line":2432},[68,20707,20567],{"class":20216},[68,20709,92],{"class":74},[68,20711,20712],{"class":2397}," 82.5",[68,20714,20355],{"class":575},[68,20716,5136],{"class":74},[68,20718,20719],{"class":70,"line":2454},[68,20720,126],{"class":74},[68,20722,20723],{"class":70,"line":2500},[68,20724,416],{"emptyLinePlaceholder":415},[68,20726,20727,20730],{"class":70,"line":2506},[68,20728,20729],{"class":1511},"  30%",[68,20731,95],{"class":74},[68,20733,20734,20736,20738,20741,20743],{"class":70,"line":2511},[68,20735,20567],{"class":20216},[68,20737,92],{"class":74},[68,20739,20740],{"class":2397}," 71.25",[68,20742,20355],{"class":575},[68,20744,5136],{"class":74},[68,20746,20747],{"class":70,"line":2517},[68,20748,126],{"class":74},[68,20750,20751],{"class":70,"line":2527},[68,20752,416],{"emptyLinePlaceholder":415},[68,20754,20755,20758],{"class":70,"line":2565},[68,20756,20757],{"class":1511},"  35%",[68,20759,95],{"class":74},[68,20761,20762,20764,20766,20769,20771],{"class":70,"line":2586},[68,20763,20567],{"class":20216},[68,20765,92],{"class":74},[68,20767,20768],{"class":2397}," 78.125",[68,20770,20355],{"class":575},[68,20772,5136],{"class":74},[68,20774,20775],{"class":70,"line":2592},[68,20776,126],{"class":74},[68,20778,20779],{"class":70,"line":2597},[68,20780,416],{"emptyLinePlaceholder":415},[68,20782,20783,20786],{"class":70,"line":2605},[68,20784,20785],{"class":1511},"  40%",[68,20787,95],{"class":74},[68,20789,20790,20792,20794,20796,20798],{"class":70,"line":2611},[68,20791,20567],{"class":20216},[68,20793,92],{"class":74},[68,20795,20684],{"class":2397},[68,20797,20355],{"class":575},[68,20799,5136],{"class":74},[68,20801,20802],{"class":70,"line":2660},[68,20803,126],{"class":74},[68,20805,20806],{"class":70,"line":2691},[68,20807,416],{"emptyLinePlaceholder":415},[68,20809,20810,20813],{"class":70,"line":2696},[68,20811,20812],{"class":1511},"  45%",[68,20814,95],{"class":74},[68,20816,20817,20819,20821,20824,20826],{"class":70,"line":2702},[68,20818,20567],{"class":20216},[68,20820,92],{"class":74},[68,20822,20823],{"class":2397}," 80.875",[68,20825,20355],{"class":575},[68,20827,5136],{"class":74},[68,20829,20830],{"class":70,"line":2724},[68,20831,126],{"class":74},[68,20833,20834],{"class":70,"line":2735},[68,20835,416],{"emptyLinePlaceholder":415},[68,20837,20838,20841],{"class":70,"line":2762},[68,20839,20840],{"class":1511},"  50%",[68,20842,95],{"class":74},[68,20844,20845,20847,20849,20852,20854],{"class":70,"line":2813},[68,20846,20567],{"class":20216},[68,20848,92],{"class":74},[68,20850,20851],{"class":2397}," 90",[68,20853,20355],{"class":575},[68,20855,5136],{"class":74},[68,20857,20858],{"class":70,"line":2819},[68,20859,126],{"class":74},[68,20861,20862],{"class":70,"line":2829},[68,20863,416],{"emptyLinePlaceholder":415},[68,20865,20866,20869],{"class":70,"line":2877},[68,20867,20868],{"class":1511},"  55%",[68,20870,95],{"class":74},[68,20872,20873,20875,20877,20880,20882],{"class":70,"line":2883},[68,20874,20567],{"class":20216},[68,20876,92],{"class":74},[68,20878,20879],{"class":2397}," 91.875",[68,20881,20355],{"class":575},[68,20883,5136],{"class":74},[68,20885,20886],{"class":70,"line":2896},[68,20887,126],{"class":74},[68,20889,20890],{"class":70,"line":2908},[68,20891,416],{"emptyLinePlaceholder":415},[68,20893,20894,20897],{"class":70,"line":2946},[68,20895,20896],{"class":1511},"  60%",[68,20898,95],{"class":74},[68,20900,20901,20903,20905,20908,20910],{"class":70,"line":2951},[68,20902,20567],{"class":20216},[68,20904,92],{"class":74},[68,20906,20907],{"class":2397}," 87",[68,20909,20355],{"class":575},[68,20911,5136],{"class":74},[68,20913,20914],{"class":70,"line":2963},[68,20915,126],{"class":74},[68,20917,20918],{"class":70,"line":2968},[68,20919,416],{"emptyLinePlaceholder":415},[68,20921,20922,20925],{"class":70,"line":2974},[68,20923,20924],{"class":1511},"  65%",[68,20926,95],{"class":74},[68,20928,20929,20931,20933,20936,20938],{"class":70,"line":2979},[68,20930,20567],{"class":20216},[68,20932,92],{"class":74},[68,20934,20935],{"class":2397}," 70",[68,20937,20355],{"class":575},[68,20939,5136],{"class":74},[68,20941,20942],{"class":70,"line":2985},[68,20943,126],{"class":74},[68,20945,20946],{"class":70,"line":3022},[68,20947,416],{"emptyLinePlaceholder":415},[68,20949,20950,20953],{"class":70,"line":3039},[68,20951,20952],{"class":1511},"  70%",[68,20954,95],{"class":74},[68,20956,20957,20959,20961,20963,20965],{"class":70,"line":3052},[68,20958,20567],{"class":20216},[68,20960,92],{"class":74},[68,20962,10846],{"class":2397},[68,20964,20355],{"class":575},[68,20966,5136],{"class":74},[68,20968,20969],{"class":70,"line":3080},[68,20970,126],{"class":74},[68,20972,20973],{"class":70,"line":3093},[68,20974,416],{"emptyLinePlaceholder":415},[68,20976,20977,20980],{"class":70,"line":3106},[68,20978,20979],{"class":1511},"  75%",[68,20981,95],{"class":74},[68,20983,20984,20986,20988,20991,20993],{"class":70,"line":3119},[68,20985,20567],{"class":20216},[68,20987,92],{"class":74},[68,20989,20990],{"class":2397}," 55",[68,20992,20355],{"class":575},[68,20994,5136],{"class":74},[68,20996,20997],{"class":70,"line":3132},[68,20998,126],{"class":74},[68,21000,21001],{"class":70,"line":3138},[68,21002,416],{"emptyLinePlaceholder":415},[68,21004,21005,21008],{"class":70,"line":3159},[68,21006,21007],{"class":1511},"  80%",[68,21009,95],{"class":74},[68,21011,21012,21014,21016,21019,21021],{"class":70,"line":3179},[68,21013,20567],{"class":20216},[68,21015,92],{"class":74},[68,21017,21018],{"class":2397}," 45",[68,21020,20355],{"class":575},[68,21022,5136],{"class":74},[68,21024,21025],{"class":70,"line":3208},[68,21026,126],{"class":74},[68,21028,21029],{"class":70,"line":3214},[68,21030,416],{"emptyLinePlaceholder":415},[68,21032,21033,21036],{"class":70,"line":3219},[68,21034,21035],{"class":1511},"  85%",[68,21037,95],{"class":74},[68,21039,21040,21042,21044,21047,21049],{"class":70,"line":3225},[68,21041,20567],{"class":20216},[68,21043,92],{"class":74},[68,21045,21046],{"class":2397}," 40",[68,21048,20355],{"class":575},[68,21050,5136],{"class":74},[68,21052,21053],{"class":70,"line":3250},[68,21054,126],{"class":74},[68,21056,21057],{"class":70,"line":3276},[68,21058,416],{"emptyLinePlaceholder":415},[68,21060,21061,21064],{"class":70,"line":3282},[68,21062,21063],{"class":1511},"  90%",[68,21065,95],{"class":74},[68,21067,21068,21070,21072,21075,21077],{"class":70,"line":3287},[68,21069,20567],{"class":20216},[68,21071,92],{"class":74},[68,21073,21074],{"class":2397}," 35",[68,21076,20355],{"class":575},[68,21078,5136],{"class":74},[68,21080,21081],{"class":70,"line":3293},[68,21082,126],{"class":74},[68,21084,21085],{"class":70,"line":3321},[68,21086,416],{"emptyLinePlaceholder":415},[68,21088,21089,21092],{"class":70,"line":3326},[68,21090,21091],{"class":1511},"  95%",[68,21093,95],{"class":74},[68,21095,21096,21098,21100,21103,21105],{"class":70,"line":3332},[68,21097,20567],{"class":20216},[68,21099,92],{"class":74},[68,21101,21102],{"class":2397}," 30",[68,21104,20355],{"class":575},[68,21106,5136],{"class":74},[68,21108,21109],{"class":70,"line":3359},[68,21110,126],{"class":74},[68,21112,21113],{"class":70,"line":3365},[68,21114,416],{"emptyLinePlaceholder":415},[68,21116,21117,21120],{"class":70,"line":3370},[68,21118,21119],{"class":1511},"  100%",[68,21121,95],{"class":74},[68,21123,21124,21126,21128,21130,21132],{"class":70,"line":3378},[68,21125,20567],{"class":20216},[68,21127,92],{"class":74},[68,21129,20572],{"class":2397},[68,21131,20355],{"class":575},[68,21133,5136],{"class":74},[68,21135,21136],{"class":70,"line":3400},[68,21137,126],{"class":74},[68,21139,21140],{"class":70,"line":3420},[68,21141,132],{"class":74},[8514,21143,21144],{"id":21144},"代码解析",[28,21146,21147,21148,21151,21152,9810,21154,21157],{},"核心还是通过 ",[65,21149,21150],{},"animation-delay"," 动画延迟来实现，",[65,21153,19941],{},[65,21155,21156],{},"keyframes"," 是根据设计图中一个完整波形的大致高度来调整的，不需要太精确，因为我们会加一些随机数进去，这样可以使每个波形之间不完全一致，不然就太死板了。",[28,21159,21160,21163],{},[65,21161,21162],{},"wavePillarCount"," 是指整个声波中柱子的数量，可以根据总宽度和每个柱子的宽度按需调整。",[28,21165,21166,21169,21170,19849,21173,21175],{},[65,21167,21168],{},"waveCount"," 是波的数量，可以尝试把下面的 ",[65,21171,21172],{},"randomRate",[65,21174,768],{}," 就可以清楚地看出来了。",[28,21177,21178,21180,21181,21183,21184,19849,21186,21188],{},[65,21179,21172],{}," 如上所述，是添加的随机延迟倍率，以便增加波形的杂音，不至于太死板。值越大杂音越大，值为 ",[65,21182,768],{}," 的时候没有杂音。下面是把 ",[65,21185,21172],{},[65,21187,768],{}," 的效果。",[554,21190],{"filename":21191},"02.gif",[28,21193,325,21194,21196,21197,21199,21200,21202,21203,21205],{},[65,21195,20186],{}," 计算的时候之所以要减去 ",[65,21198,401],{},"，是为了保证 ",[65,21201,20186],{}," 的值为负数，这样可以保证动画在一开始就是完整的，不然部分波形一开始将不完整，等到了那个 ",[65,21204,20186],{}," 时才会开始动。",[28,21207,21208,21209,21211,21212,21214,21215,21214,21217,21219],{},"基本上就是这样，通过通过 ",[65,21210,21156],{}," 来设置初始波形，通过微调 ",[65,21213,21162],{},"、 ",[65,21216,21168],{},[65,21218,21172],{}," 来获得更加的效果。",[523,21221,21222],{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s9AJx, html code.shiki .s9AJx{--shiki-light:#9C3EDA;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s83vy, html code.shiki .s83vy{--shiki-light:#E2931D;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .soE4H, html code.shiki .soE4H{--shiki-light:#8796B0;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .stp6e, html code.shiki .stp6e{--shiki-light:#39ADB5;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sw1J6, html code.shiki .sw1J6{--shiki-light:#F76D47;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}",{"title":63,"searchDepth":78,"depth":78,"links":21224},[],"2019-03-24",{},"\u002Fposts\u002F2019\u002Faudiowave-animation",{"text":535,"minutes":98,"time":21229,"words":21230},180000,600,{"title":19873,"description":19878},{"loc":21227},"posts\u002F2019\u002F20190324.audiowave-animation",[543,8427],"sgdV0_o07zLLyyGhpqqjl1ZVLmOMtBlps06AjCbHWu0",{"id":21237,"title":21238,"body":21239,"class":528,"cover":1212,"coverSize":528,"date":21258,"description":21243,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":21259,"navigation":415,"path":21260,"readingTime":21261,"seo":21264,"sitemap":21265,"stem":21266,"tags":21267,"time":528,"weather":21268,"__hash__":21269},"posts\u002Fposts\u002F2019\u002F20190109.ghost-docker-mail-config.md","Ghost Docker 部署方式配置邮箱",{"type":25,"value":21240,"toc":21256},[21241,21244,21247,21253],[28,21242,21243],{},"很久没登录博客了，今天登录时，发现忘记密码了，之前都是自动登录的，估计是自动登录过期了，没办法自动登录了，试了几次，账号被锁定了。",[28,21245,21246],{},"尝试找回密码，发现好像没有配置 SMTP 邮箱。于是找了下配置项，用 Docker 部署的话，在编排模板的 environment 中添加如下配置：",[58,21248,21251],{"className":21249,"code":21250,"language":516},[514],"- 'mail__transport=SMTP'\n- 'mail__from=Ghost \u003Cxx@xxx.com>'\n- 'mail__options__host=smtp.qiye.aliyun.com'\n- 'mail__options__secureConnection=true'\n- 'mail__options__port=465'\n- 'mail__options__auth__user=xx@xxx.com'\n- 'mail__options__auth__pass=YOUR_PASSWORD'\n",[65,21252,21250],{"__ignoreMap":63},[28,21254,21255],{},"重新部署一下即可。",{"title":63,"searchDepth":78,"depth":78,"links":21257},[],"2019-01-09",{},"\u002Fposts\u002F2019\u002Fghost-docker-mail-config",{"text":7523,"minutes":21262,"time":21263,"words":3533},0.62,37200,{"title":21238,"description":21243},{"loc":21260},"posts\u002F2019\u002F20190109.ghost-docker-mail-config",[543,10057,11282,7154],"天气阴","yJjcNS7QIvqk0sWEB0_f7Ds-et57mbeIjVP1lrbEJOE",{"id":21271,"title":21272,"body":21273,"class":528,"cover":528,"coverSize":528,"date":21711,"description":21277,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":21712,"navigation":415,"path":21713,"readingTime":21714,"seo":21718,"sitemap":21719,"stem":21720,"tags":21721,"time":528,"weather":21723,"__hash__":21724},"posts\u002Fposts\u002F2018\u002F20180427.windows-storage-spaces-raid1-degraded.md","Windows 存储池 RAID1 虚拟磁盘降级的问题",{"type":25,"value":21274,"toc":21701},[21275,21278,21280,21282,21289,21292,21315,21317,21319,21322,21324,21333,21341,21344,21347,21362,21365,21372,21401,21408,21411,21414,21418,21421,21425,21432,21484,21501,21504,21527,21530,21570,21574,21577,21580,21585,21599,21602,21613,21617,21620,21624,21635,21639,21642,21645,21651,21654,21658,21661,21698],[28,21276,21277],{},"今天早上看到监控邮件，git 和 nexus 服务都挂了，赶紧登录服务器看了下，果然，又是磁盘出问题了。存储池的一个 Mirror 的虚拟磁盘出现警告，提示已降级，其中一块物理磁盘显示“不正常：介质故障，IO 错误”，如下图：",[554,21279],{"filename":556},[554,21281],{"filename":1203},[28,21283,21284,21285,21288],{},"3 月份的时候，也出现过同样的问题，当时刚开始误以为是物理硬盘出了问题，但是检测下来磁盘 ",[65,21286,21287],{},"S.M.A.R.T."," 数据是正常的，检查了坏道，也全部正常。后来把失败的物理磁盘拔下来准备寄回检修的时候，发现存储池的虚拟磁盘提示数据不完整，虽然是 RAID1，但是单靠剩下那一个硬盘还不足以恢复数据，尴尬。当时的处理办法是把两块磁盘都插上，虽然提示降级，但是数据是完整的，数据可以拷贝出来。拷贝出来后把整个存储池删了重建，再把数据拷进去，就正常了。当时也没多想。",[28,21290,21291],{},"今天又出现了同样的问题，出错的磁盘是另一块物理磁盘，这更加确定了该问题不是物理磁盘的锅，可能是存储池的问题。但是不想再通过上次的方法暴力解决。不过还是先把数据都拷贝出去了，以防万一。",[28,21293,21294,21295,21298,21299,21302,21303,21306,21307,21310,21311,21314],{},"尝试修复虚拟磁盘，没用，通过 ",[65,21296,21297],{},"Get-StorageJob"," 查看一直是 ",[65,21300,21301],{},"Suspended"," 的状态，直接用 ",[65,21304,21305],{},"PowerShell"," 执行 ",[65,21308,21309],{},"Repair-VirtualDisk","，提示 ",[65,21312,21313],{},"Repair-VirtualDisk : Not enough available capacity","（没有足够的可用容量）。",[554,21316],{"filename":1710},[554,21318],{"filename":1718},[28,21320,21321],{},"删除和重置物理磁盘都无法成功，提示需要先修复虚拟磁盘才行。",[554,21323],{"filename":1729},[28,21325,21326,21327,21332],{},"后来发现一个系统更新，",[38,21328,21331],{"href":21329,"rel":21330},"https:\u002F\u002Fsupport.microsoft.com\u002Fen-us\u002Fhelp\u002F4093120\u002Fwindows-10-update-kb4093120",[42],"KB4093120","，挺大的，去找了下更新内容，发现如下一条：",[7538,21334,21335,21338],{},[28,21336,21337],{},"Addresses an issue that may generate a capacity reserve fault warning during cluster validation or while running the Debug-StorageSubSystem cmdlet even though enough capacity is actually reserved. The warning is \"The storage pool does not have the minimum recommended reserve capacity. This may limit your ability to restore data resiliency in the event of drive failure(s).\"",[28,21339,21340],{},"修复了在群集验证期间或运行 Debug-StorageSubSystem cmdlet 时可能生成容量保留错误警告（即使实际上保留了足够的容量）的问题。 警告内容为“存储池没有建议的最低保留容量。 这可能会限制在驱动器发生故障时恢复数据弹性的能力。”",[28,21342,21343],{},"可能是引起这个问题的原因，等待更新之后再尝试下。",[28,21345,21346],{},"更新之后发现问题依旧存在。沮丧。",[28,21348,21349,21350,21353,21354,21357,21358,21361],{},"突然想到没有足够的空间可能并不是指存储池的剩余空间，而是可能需要添加一个硬盘，作为临时备份的磁盘。于是找来一个移动硬盘，插上去，添加到存储池中，设置为 ",[65,21351,21352],{},"AutoSelect","。果然，虚拟磁盘的修复开始了。等待修复结束，把提示“介质故障，IO 错误”的硬盘删除掉重新添加进来，然后通过 ",[65,21355,21356],{},"Get-PhysicalDisk -SerialNumber 42H1YCTHF | Set-PhysicalDisk -Usage Retired"," 把移动硬盘手动设置为 ",[65,21359,21360],{},"Retired","，然后在物理磁盘的页面把移动硬盘从存储池删除掉，等待同步完成，就恢复成原样了。",[28,21363,21364],{},"以上便是该问题的解决办法，至于原因，暂时还不得而知。",[28,21366,21367,21368,21371],{},"出现这个错误之前，在系统事件里面，会有不少来源于 ",[65,21369,21370],{},"Disk"," 的警告和错误：",[5010,21373,21374,21384,21393],{},[1239,21375,21376,21377,21380,21381],{},"事件 ID 为 ",[65,21378,21379],{},"154"," 的错误：",[65,21382,21383],{},"由于硬件错误，磁盘 0 (PDO 名称: \\Device\\00000055)的逻辑块地址 0xb5ae5d0 处的 IO 操作失败",[1239,21385,21376,21386,21389,21390],{},[65,21387,21388],{},"153"," 的警告：",[65,21391,21392],{},"已在磁盘 0 (PDO 名称: \\Device\\000000a5)的逻辑块地址 0x0 处重试 IO 操作。",[1239,21394,21376,21395,21389,21398],{},[65,21396,21397],{},"51",[65,21399,21400],{},"传呼期间在设备 \\Device\\Harddisk4\\DR4 上检测到一个错误。",[28,21402,21403,21404,21407],{},"从注册表中 ",[65,21405,21406],{},"HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Disk\\Enum"," 这个目录里面可以看到磁盘 ID 对应的磁盘。",[28,21409,21410],{},"问题是硬盘检测下来是好的，没有坏道，并且上次出问题的硬盘，这次一次错误都没有。",[28,21412,21413],{},"有可能是 SATA 接口的问题，接触不良之类的。上次出问题之后两个硬盘都拆下来重新安装过，可能 SATA 接口换过了。这个具体后面再研究下。",[15817,21415,21417],{"id":21416},"_2018-05-03-更新","2018-05-03 更新：",[28,21419,21420],{},"买了两根绿联的 SATA 线换了下，目前为止没有再次发现这个错误。感觉果然是 SATA 线的问题。",[1232,21422,21424],{"id":21423},"_1-存储池开机自动连接虚拟磁盘","1. 存储池开机自动连接虚拟磁盘",[28,21426,21427,21428,21431],{},"最近修复存储池的问题，不知道为啥，有个虚拟磁盘可能是之前分离过，现在每次重启之后都需要手动连接，很麻烦，可通过 ",[65,21429,21430],{},"Get-VirtualDisk"," 命令查看虚拟磁盘状态，结果如下：",[58,21433,21437],{"className":21434,"code":21435,"language":21436,"meta":63,"style":63},"language-powershell shiki shiki-themes material-theme-lighter github-light github-dark","FriendlyName ResiliencySettingName OperationalStatus HealthStatus IsManualAttach Size\n------------ --------------------- ----------------- ------------ -------------- ----\nSimple       Simple                OK                Healthy      False          1 TB\nMirror       Mirror                OK                Healthy      True           5 TB\n","powershell",[65,21438,21439,21444,21464,21474],{"__ignoreMap":63},[68,21440,21441],{"class":70,"line":71},[68,21442,21443],{"class":373},"FriendlyName ResiliencySettingName OperationalStatus HealthStatus IsManualAttach Size\n",[68,21445,21446,21449,21452,21455,21458,21461],{"class":70,"line":78},[68,21447,21448],{"class":1899},"------------",[68,21450,21451],{"class":1899}," ---------------------",[68,21453,21454],{"class":1899}," -----------------",[68,21456,21457],{"class":1899}," ------------",[68,21459,21460],{"class":1899}," --------------",[68,21462,21463],{"class":1899}," ----\n",[68,21465,21466,21469,21471],{"class":70,"line":98},[68,21467,21468],{"class":373},"Simple       Simple                OK                Healthy      False          ",[68,21470,401],{"class":2397},[68,21472,21473],{"class":373}," TB\n",[68,21475,21476,21479,21482],{"class":70,"line":123},[68,21477,21478],{"class":373},"Mirror       Mirror                OK                Healthy      True           ",[68,21480,21481],{"class":2397},"5",[68,21483,21473],{"class":373},[28,21485,21486,21487,21490,21491,21494,21495,21497,21498,21500],{},"其中的 ",[65,21488,21489],{},"IsManualAttach"," 便是手动连接的属性，我们的这块名为 ",[65,21492,21493],{},"Mirror"," 的虚拟磁盘的 ",[65,21496,21489],{}," 属性值为 ",[65,21499,3009],{},"，所以不会自动连接了。",[28,21502,21503],{},"通过如下命令可将其设为自动挂载：",[58,21505,21507],{"className":21434,"code":21506,"language":21436,"meta":63,"style":63},"Set-VirtualDisk -FriendlyName Mirror -IsManualAttach $False\n",[65,21508,21509],{"__ignoreMap":63},[68,21510,21511,21514,21516,21519,21521,21524],{"class":70,"line":71},[68,21512,21513],{"class":630},"Set-VirtualDisk",[68,21515,2394],{"class":1899},[68,21517,21518],{"class":373},"FriendlyName Mirror ",[68,21520,2652],{"class":1899},[68,21522,21523],{"class":373},"IsManualAttach ",[68,21525,21526],{"class":81},"$False\n",[28,21528,21529],{},"或者通过如下命令将所有手动挂载的磁盘设为自动挂载：",[58,21531,21533],{"className":21434,"code":21532,"language":21436,"meta":63,"style":63},"Get-VirtualDisk | Where-Object {$_.IsManualAttach –eq $True} | Set-VirtualDisk –IsManualAttach $False\n",[65,21534,21535],{"__ignoreMap":63},[68,21536,21537,21539,21542,21545,21547,21549,21552,21555,21558,21560,21562,21565,21568],{"class":70,"line":71},[68,21538,21430],{"class":630},[68,21540,21541],{"class":1899}," |",[68,21543,21544],{"class":630}," Where-Object",[68,21546,2382],{"class":74},[68,21548,5147],{"class":81},[68,21550,21551],{"class":665},"_",[68,21553,21554],{"class":373},".IsManualAttach –eq ",[68,21556,21557],{"class":81},"$True",[68,21559,2400],{"class":74},[68,21561,21541],{"class":1899},[68,21563,21564],{"class":630}," Set-VirtualDisk",[68,21566,21567],{"class":373}," –IsManualAttach ",[68,21569,21526],{"class":81},[1232,21571,21573],{"id":21572},"_2-发现了服务器的一个大问题","2. 发现了服务器的一个大问题",[28,21575,21576],{},"今天拆机发现硬盘很烫，想想不应该啊，摸了下 CPU 风扇，发现往里吹热气，往外的是冷风。😳！我的 CPU 风扇是两边都是风扇的，安装的时候没注意，竟然装反了！导致一直外往里吸气。难怪上面全是灰！！！尴尬了。赶紧淘宝买了一支 7783 的导热硅脂，重新安装一下，顺便清个灰。",[28,21578,21579],{},"安装前后的数据对比：",[28,21581,21582],{},[1242,21583,21584],{},"风扇装反的情况下",[5010,21586,21587,21590,21593,21596],{},[1239,21588,21589],{},"正常开机 6 小时，CPU 温度 38℃，硬盘温度 40℃。",[1239,21591,21592],{},"挖矿软件设置 60%的 CPU 使用率，挖矿 10 分钟左右，CPU 温度 62℃ 左右，硬盘温度 42℃。",[1239,21594,21595],{},"挖矿软件设置 60%的 CPU 使用率，挖矿 60 分钟左右，CPU 温度 64℃ 左右，硬盘温度 45℃。",[1239,21597,21598],{},"挖矿软件设置 60%的 CPU 使用率，挖矿 03 小时左右，CPU 温度 65℃ 左右，硬盘温度 48℃。",[28,21600,21601],{},"重新换过导热硅脂，风扇装正的情况下：",[5010,21603,21604,21607,21610],{},[1239,21605,21606],{},"正常开机 10 分钟，CPU 温度 34℃，硬盘温度 31℃。",[1239,21608,21609],{},"挖矿软件设置 60%的 CPU 使用率，挖矿 10 分钟左右，CPU 温度 55℃ 左右，硬盘温度 36℃。",[1239,21611,21612],{},"挖矿软件设置 60%的 CPU 使用率，挖矿 60 分钟左右，CPU 温度 58℃ 左右，硬盘温度 42℃。",[15817,21614,21616],{"id":21615},"_2018-05-04-更新","2018-05-04 更新：",[28,21618,21619],{},"解决完事件查看器里面其他几个错误，然后重启之后，发现再次出现 Disk 51 和 Disk 154 的错误。不过频率很低，可能还是由于接触的问题。",[15817,21621,21623],{"id":21622},"_2018-05-23-更新","2018-05-23 更新：",[28,21625,21626,21627,21630,21631,21634],{},"今天发现服务器上的服务挂了很久，登上去看，发现 D 盘不见了，尝试重启，等待了很长时间重启完成，可能在更新系统。重启进入后发现 D 盘出现了，但是硬盘一直出现不断重启的声音，事件里面每隔 5-6 秒钟就会出现一个 ",[65,21628,21629],{},"已在磁盘 4 (PDO 名称: \\Device\\Space2)的逻辑块地址 0x1f52d368 处重试 IO 操作。"," 的警告，并且伴随着错误 ",[65,21632,21633],{},"由于硬件错误，磁盘 2 (PDO 名称: \\Device\\0000003e)的逻辑块地址 0xaa1dde8 处的 IO 操作失败。","，磁盘 1、2、4 都有警告，磁盘 1、2 有错误。之后手动拔了磁盘 ID 为 2 的物理磁盘（W5033A6K）的 SATA 线，然后重新插上，发现没有重启的声音了，错误警告也停止了，但虚拟磁盘出现“不正常，未知”的状态。感觉不像是接触不好的问题，因为我动了线没效果，拔了重插才有效果。看了下之前的日志，磁盘 0 是固态硬盘，莫非是系统盘的锅？固态硬盘检测也没有坏道。",[15817,21636,21638],{"id":21637},"_2018-06-08-更新","2018-06-08 更新：",[28,21640,21641],{},"前几天重装了系统，仍然出现了问题。6 号 18:03:06、6 号 20:01:13、7 号 6:16:47、7 号 6:17:11、7 号 6:20:22 都出现了 Disk 154 的错误。根据之前多次出错的事件来看，凌晨 6 点多是报这个错误的高发期，得先搞明白为啥，这个时间段在做什么。",[28,21643,21644],{},"网站报警显示 8 号 2 点 06 分挂了，Hyper-V 虚拟机已经关机了，虚拟机返回的错误如下：",[58,21646,21649],{"className":21647,"code":21648,"language":516},[514],"“ubuntu-01”: 虚拟硬盘“D:\\虚拟机\\Virtual Hard Disks\\ubuntu-nas_E27174DE-7800-4F8C-918C-097516949C09.avhdx”检测到可恢复的错误。当前状态: 磁盘已满。(虚拟机 ID 3695BB9F-3BAA-4D94-938F-0C197B9AA34D)\n",[65,21650,21648],{"__ignoreMap":63},[28,21652,21653],{},"应该是存储空间无法写入导致的。",[15817,21655,21657],{"id":21656},"_2018-06-26-更新","2018-06-26 更新：",[28,21659,21660],{},"前几天重新买了个主板，安装时发现硬盘的电源线插口有点鼓起来变形了，感觉是温度高热胀冷缩的原因。当时手动压扁了安装了。稳定了几天没问题，今天又出问题了。现在有点怀疑是这个电源线的问题。绿联重新买了几根线，等到了再试试。",[7538,21662,21663,21666],{},[28,21664,21665],{},"参考文章：",[1236,21667,21668,21674,21680,21686,21692],{},[1239,21669,21670],{},[38,21671,21672],{"href":21672,"rel":21673},"https:\u002F\u002Fanswers.microsoft.com\u002Fzh-hans\u002Fwindows\u002Fforum\u002Fwindows8_1-hardware\u002F%E4%BA%8B%E4%BB%B6id129%E5%92%8C153\u002F3d4be0c1-d67a-4c81-8367-acea9873f46d",[42],[1239,21675,21676],{},[38,21677,21678],{"href":21678,"rel":21679},"http:\u002F\u002Fwww.pwrusr.com\u002Fsystem-administration\u002Fsolved-warning-the-io-operation-at-logical-block-address-for-disk-was-retried",[42],[1239,21681,21682],{},[38,21683,21684],{"href":21684,"rel":21685},"https:\u002F\u002Fblogs.msdn.microsoft.com\u002Fntdebugging\u002F2013\u002F04\u002F30\u002Finterpreting-event-153-errors\u002F",[42],[1239,21687,21688],{},[38,21689,21690],{"href":21690,"rel":21691},"https:\u002F\u002Fwww.wintips.org\u002Fhow-to-fix-disk-event-51-an-error-detected-on-device-during-paging-operation\u002F",[42],[1239,21693,21694],{},[38,21695,21696],{"href":21696,"rel":21697},"https:\u002F\u002Fsupport.microsoft.com\u002Fzh-cn\u002Fhelp\u002F2806730\u002Fdisk-event-id-154-the-io-operation-failed-due-to-a-hardware-error",[42],[523,21699,21700],{},"html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":63,"searchDepth":78,"depth":78,"links":21702},[21703,21704,21705],{"id":21416,"depth":98,"text":21417},{"id":21423,"depth":78,"text":21424},{"id":21572,"depth":78,"text":21573,"children":21706},[21707,21708,21709,21710],{"id":21615,"depth":98,"text":21616},{"id":21622,"depth":98,"text":21623},{"id":21637,"depth":98,"text":21638},{"id":21656,"depth":98,"text":21657},"2018-04-27",{},"\u002Fposts\u002F2018\u002Fwindows-storage-spaces-raid1-degraded",{"text":7145,"minutes":21715,"time":21716,"words":21717},10.545,632700,2109,{"title":21272,"description":21277},{"loc":21713},"posts\u002F2018\u002F20180427.windows-storage-spaces-raid1-degraded",[543,21722],"Windows","天气 ☀️","_A3Hi1tql1_yWufDPtQJwcr6H9Imp351Wb1RJX3frmE",{"id":21726,"title":21727,"body":21728,"class":528,"cover":528,"coverSize":528,"date":22292,"description":21732,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":22293,"navigation":415,"path":22294,"readingTime":22295,"seo":22300,"sitemap":22301,"stem":22302,"tags":22303,"time":22304,"weather":22305,"__hash__":22306},"posts\u002Fposts\u002F2018\u002F20180414.frontend-components-and-docker-deploy.md","前端跨项目组件化及基于 Docker 的快速部署方案",{"type":25,"value":21729,"toc":22288},[21730,21733,21736,21739,21762,21765,21813,21838,21857,21867,21915,21922,21928,21975,21981,21990,21993,22033,22087,22104,22108,22111,22114,22210,22220,22229,22275,22282,22285],[28,21731,21732],{},"最近静下心来写了几个项目，花了些时间重新整理了整套组件化方案和部署方案，记录一下。",[15817,21734,21735],{"id":21735},"跨项目组件化",[28,21737,21738],{},"前端的组件化不用多说了，发展到现在，无论是 React 的还是 Vue，都提供了相当方便的组件化实现。在日常项目中，有些组件其实是可以跨多个项目使用的，将这些组件抽离出来作为单独项目，并复用到其他项目中去，一来可以避免重复造轮子，加快开发速度，二来维护效率也高，一些 bugfix 或者新特性直接在组件中更新，项目中只需要更新引用版本号即可，方便快捷。",[28,21740,21741,21742,21745,21746,21749,21750,21752,21753,21755,21756,21758,21759,21761],{},"跨项目的组件化方式也很多，开发阶段可以用 ",[65,21743,21744],{},"npm link","，相当于在主项目的 ",[65,21747,21748],{},"node_modules"," 目录中创建了一个链向组件项目的软链，方便是挺方便，但是有几个问题。一是 Eslint 的目录递归检查是基于最终实际目录的，也就是说虽然 Eslint 默认排除 ",[65,21751,21748],{}," 目录，但它依然会对该目录中的软链项目进行检查，一旦组件项目的 Eslint 规则和主项目的 Eslint 不一致的话，主项目 Eslint 就没法通过，这个比较蛋疼，就得临时禁用 Eslint 或者修改组件项目的规则。作为组件项目应该保证少依赖，而且要服务多个项目，没办法保证匹配各个项目的 Eslint 规则。第二个问题是，通过 ",[65,21754,21744],{}," 实现的依赖，不会体现在 ",[65,21757,11591],{}," 中，如果通过 Docker 去部署，在 Docker 上是不知道你这个软链的，即便能够把软链写进去，在 Docker 中构建的时候，由于目录问题，也不能保证可以把组件项目文件拷过去。因此，在生产中，",[65,21760,21744],{}," 这个方案是没办法用的。",[28,21763,21764],{},"之前在国资的时候，采用的是通过组件项目的 git 地址来定位包，使用起来也很方便。如下：",[58,21766,21768],{"className":60,"code":21767,"language":62,"meta":63,"style":63},"{\n  \"dependencies\": {\n    \"example-component\": \"git+ssh:\u002F\u002Fgit@xxx.com\u002Fexample-component.git#v1.0.0\"\n  }\n}\n",[65,21769,21770,21774,21787,21805,21809],{"__ignoreMap":63},[68,21771,21772],{"class":70,"line":71},[68,21773,75],{"class":74},[68,21775,21776,21778,21781,21783,21785],{"class":70,"line":78},[68,21777,82],{"class":81},[68,21779,21780],{"class":85},"dependencies",[68,21782,89],{"class":81},[68,21784,92],{"class":74},[68,21786,95],{"class":74},[68,21788,21789,21791,21794,21796,21798,21800,21803],{"class":70,"line":98},[68,21790,101],{"class":81},[68,21792,21793],{"class":104},"example-component",[68,21795,89],{"class":81},[68,21797,92],{"class":74},[68,21799,113],{"class":112},[68,21801,21802],{"class":116},"git+ssh:\u002F\u002Fgit@xxx.com\u002Fexample-component.git#v1.0.0",[68,21804,120],{"class":112},[68,21806,21807],{"class":70,"line":123},[68,21808,126],{"class":74},[68,21810,21811],{"class":70,"line":129},[68,21812,132],{"class":74},[28,21814,21815,21816,21819,21820,21823,21824,21826,21827,21830,21831,21833,21834,21837],{},"npm 支持通过 tag 来定位版本，组件项目发布的时候打一个 tag，对应的在项目中更新最后的版本号重新安装就可以完成升级了。由于之前没有用 Docker 部署，所以也没发现有什么问题。用了 Docker 的话，就会有些小问题。一般为了精简，都会采用基于 ",[65,21817,21818],{},"Alpine"," 的基础镜像，我目前的前端项目都是基于 ",[65,21821,21822],{},"node:8-alpine"," 来构建的。要知道，",[65,21825,21818],{}," 镜像本身只有 4.8M，",[65,21828,21829],{},"node:8-apline"," 也只有 20 几兆，非常精简。但是通过上面的这种方式，需要依赖 git，而 ",[65,21832,21818],{}," 显然是没有安装 ",[65,21835,21836],{},"git"," 的，也没有必要为了部署专门去安装一个 git。",[28,21839,21840,21841,21844,21845,21848,21849,21852,21853,21856],{},"于是便有了第三种方案，基于 Nexus 的 npm repository 方案。把包发布到 npm 上不太现实，大部分公司项目还是希望私有，和 maven 一样，Nexus 也支持 npm。创建一个 ",[65,21842,21843],{},"hosted"," 类型的 npm 仓库，例如 ",[65,21846,21847],{},"npm-hosted","，具体教程自行谷歌。但是我们又不希望给该仓库增加过多的压力，不想把所有 npm 或者 yarn 默认的 registry 改为 Nexus，没必要，因为即便改为 Nexus，在国内的网络环境，还是 proxy 到 ",[65,21850,21851],{},"https:\u002F\u002Fregistry.npm.taobao.org"," 上去了，而且会在 Nexus 上留下大量缓存，也经过了两层的下载，我尝试过，很蛋疼，在 Nexus 没有缓存第一次去下载的时候，还会有很多失败。后来发现 npm 也支持直接通过 ",[65,21854,21855],{},"tgz"," 文件的方式来引用，这样就好办了。",[28,21858,21859,21860,21863,21864,21866],{},"首先执行 ",[65,21861,21862],{},"npm adduser --registry=https:\u002F\u002Fmyregistry.example.com","，输入 Nexus 上具有上传 npm 包权限的用户名和密码，会在本地记录该用户的登录认证。然后在组件项目的 ",[65,21865,11591],{}," 中加入：",[58,21868,21870],{"className":60,"code":21869,"language":62,"meta":63,"style":63},"{\n  \"publishConfig\": {\n    \"registry\": \"https:\u002F\u002Fmyregistry.example.com\u002Frepository\u002Fnpm-hosted\u002F\"\n  }\n}\n",[65,21871,21872,21876,21889,21907,21911],{"__ignoreMap":63},[68,21873,21874],{"class":70,"line":71},[68,21875,75],{"class":74},[68,21877,21878,21880,21883,21885,21887],{"class":70,"line":78},[68,21879,82],{"class":81},[68,21881,21882],{"class":85},"publishConfig",[68,21884,89],{"class":81},[68,21886,92],{"class":74},[68,21888,95],{"class":74},[68,21890,21891,21893,21896,21898,21900,21902,21905],{"class":70,"line":98},[68,21892,101],{"class":81},[68,21894,21895],{"class":104},"registry",[68,21897,89],{"class":81},[68,21899,92],{"class":74},[68,21901,113],{"class":112},[68,21903,21904],{"class":116},"https:\u002F\u002Fmyregistry.example.com\u002Frepository\u002Fnpm-hosted\u002F",[68,21906,120],{"class":112},[68,21908,21909],{"class":70,"line":123},[68,21910,126],{"class":74},[68,21912,21913],{"class":70,"line":129},[68,21914,132],{"class":74},[28,21916,21917,21918,21921],{},"然后执行 ",[65,21919,21920],{},"npm publish","，组件项目就会被上传到 Nexus 了。",[28,21923,21924,21925,21927],{},"在具体项目中，需要用到改组件的时候，在 ",[65,21926,11591],{}," 中这样引用：",[58,21929,21931],{"className":60,"code":21930,"language":62,"meta":63,"style":63},"{\n  \"dependencies\": {\n    \"vue-footer\": \"https:\u002F\u002Fmyregistry.example.com\u002Frepository\u002Fnpm-hosted\u002Fexample-component\u002F-\u002Fexample-component-1.0.0.tgz\"\n  }\n}\n",[65,21932,21933,21937,21949,21967,21971],{"__ignoreMap":63},[68,21934,21935],{"class":70,"line":71},[68,21936,75],{"class":74},[68,21938,21939,21941,21943,21945,21947],{"class":70,"line":78},[68,21940,82],{"class":81},[68,21942,21780],{"class":85},[68,21944,89],{"class":81},[68,21946,92],{"class":74},[68,21948,95],{"class":74},[68,21950,21951,21953,21956,21958,21960,21962,21965],{"class":70,"line":98},[68,21952,101],{"class":81},[68,21954,21955],{"class":104},"vue-footer",[68,21957,89],{"class":81},[68,21959,92],{"class":74},[68,21961,113],{"class":112},[68,21963,21964],{"class":116},"https:\u002F\u002Fmyregistry.example.com\u002Frepository\u002Fnpm-hosted\u002Fexample-component\u002F-\u002Fexample-component-1.0.0.tgz",[68,21966,120],{"class":112},[68,21968,21969],{"class":70,"line":123},[68,21970,126],{"class":74},[68,21972,21973],{"class":70,"line":129},[68,21974,132],{"class":74},[28,21976,21977,21978,1686],{},"或者直接 ",[65,21979,21980],{},"npm install https:\u002F\u002Fmyregistry.example.com\u002Frepository\u002Fnpm-hosted\u002Fexample-component\u002F-\u002Fexample-component-1.0.0.tgz --save",[28,21982,21983,21984,21986,21987,21989],{},"组件更新通过修改 ",[65,21985,11591],{}," 里的版本号，然后 ",[65,21988,21920],{},"，然后具体项目中修改最后的版本号，重新安装即可。",[28,21991,21992],{},"另外由于这次都是用的最新的组件，也遇到了一个很大的坑。",[28,21994,21995,21996,21999,22000,22003,22004,19849,22007,22010,22011,9810,22014,22017,22018,22021,22022,22026,22027,22032],{},"我的项目都是基于 Nuxt.js 来搞的，Nuxt.js 的框架用起来更爽，ssr 性能也比 vue 原生的要高。有一个问题，组件项目在 webpack 打包的时候，默认是不支持 Nuxt 的 SSR 的，用了 ",[65,21997,21998],{},"vue-style-loader"," 之后，里面会有多个地方用到了 ",[65,22001,22002],{},"document","。如果需要同时支持 Browser 和 SSR，需要再建一个 SSR 的 webpack config，将 ",[65,22005,22006],{},"target",[65,22008,22009],{},"node","，并且 ",[65,22012,22013],{},"vue-loader",[65,22015,22016],{},"options"," 中需要加入 ",[65,22019,22020],{},"optimizeSSR: false","，这个是因为尤大在最近的某个版本中针对 SSR 做了一些优化，但是在 Nuxt 的 SSR 中会有些问题，具体可以参见 ",[38,22023,22024],{"href":22024,"rel":22025},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt.js\u002Fissues\u002F2565",[42]," ，找了很久找到了这个 issue，追踪了下，尤大貌似在最近的 ",[38,22028,22031],{"href":22029,"rel":22030},"https:\u002F\u002Fgithub.com\u002Fvuejs\u002Fvue\u002Fcommit\u002F9b22d86ab315a3c6061a6a4776eab1964304f92e",[42],"v2.5.17-beta.0"," 中已经修复了这个问题，具体等 release 版发布之后再试下。在 Nuxt 中，创建一个 plugin，直接引用生成的 SSR 版本的文件即可。",[58,22034,22036],{"className":11308,"code":22035,"language":11310,"meta":63,"style":63},"import ExampleComponent from 'example-component\u002Fdist\u002Fssr.js'\nimport Vue from 'vue'\n\nVue.use(ExampleComponent)\n",[65,22037,22038,22054,22070,22074],{"__ignoreMap":63},[68,22039,22040,22042,22045,22047,22049,22052],{"class":70,"line":71},[68,22041,1791],{"class":1790},[68,22043,22044],{"class":373}," ExampleComponent ",[68,22046,1813],{"class":1790},[68,22048,1620],{"class":112},[68,22050,22051],{"class":116},"example-component\u002Fdist\u002Fssr.js",[68,22053,1626],{"class":112},[68,22055,22056,22058,22061,22063,22065,22068],{"class":70,"line":78},[68,22057,1791],{"class":1790},[68,22059,22060],{"class":373}," Vue ",[68,22062,1813],{"class":1790},[68,22064,1620],{"class":112},[68,22066,22067],{"class":116},"vue",[68,22069,1626],{"class":112},[68,22071,22072],{"class":70,"line":98},[68,22073,416],{"emptyLinePlaceholder":415},[68,22075,22076,22079,22081,22084],{"class":70,"line":123},[68,22077,22078],{"class":373},"Vue",[68,22080,404],{"class":74},[68,22082,22083],{"class":2183},"use",[68,22085,22086],{"class":373},"(ExampleComponent)\n",[28,22088,1659,22089,9810,22092,22095,22096,22099,22100,22103],{},[65,22090,22091],{},"nuxt.config.js",[65,22093,22094],{},"plugins"," 中直接加入 ",[65,22097,22098],{},"'~plugins\u002Fcomponents-plugin.js'"," 即可。网上大部分解决方案是引用的时候将组件项目设置为 ",[65,22101,22102],{},"ssr: false","，其实是治标不治本，放弃了该组件在服务端的渲染，不可取。",[15817,22105,22107],{"id":22106},"基于-docker-的快速部署","基于 Docker 的快速部署",[28,22109,22110],{},"使用 Docker 也快 1 年了，基本上从开始用上 Docker 之后，就爱不释手了，大大缩短了发布时间，减少了运维成本。",[28,22112,22113],{},"目前我的项目都是部署在阿里云上，基于阿里云的容器集群方案，前面通过 SLB，后面横向部署多台机器。不多说，贴下 Dockerfile：",[58,22115,22117],{"className":14143,"code":22116,"language":14139,"meta":63,"style":63},"FROM node:8-alpine\n\nWORKDIR \u002Fapp\n\nCOPY package.json \u002Fapp\nCOPY yarn.lock \u002Fapp\nRUN npm config set registry https:\u002F\u002Fregistry.npm.taobao.org && yarn config set registry https:\u002F\u002Fregistry.npm.taobao.org && yarn install\nCOPY . \u002Fapp\nRUN npm run build\n\nEXPOSE 6002\nENV SERVER_ENV $SERVER_ENV\nCMD [\"npm\", \"run\", \"start\"]\n",[65,22118,22119,22126,22130,22136,22140,22146,22152,22159,22165,22172,22176,22183,22190],{"__ignoreMap":63},[68,22120,22121,22123],{"class":70,"line":71},[68,22122,14151],{"class":575},[68,22124,22125],{"class":373}," node:8-alpine\n",[68,22127,22128],{"class":70,"line":78},[68,22129,416],{"emptyLinePlaceholder":415},[68,22131,22132,22134],{"class":70,"line":98},[68,22133,14163],{"class":575},[68,22135,14166],{"class":373},[68,22137,22138],{"class":70,"line":123},[68,22139,416],{"emptyLinePlaceholder":415},[68,22141,22142,22144],{"class":70,"line":129},[68,22143,14175],{"class":575},[68,22145,14178],{"class":373},[68,22147,22148,22150],{"class":70,"line":212},[68,22149,14175],{"class":575},[68,22151,14185],{"class":373},[68,22153,22154,22156],{"class":70,"line":233},[68,22155,14190],{"class":575},[68,22157,22158],{"class":373}," npm config set registry https:\u002F\u002Fregistry.npm.taobao.org && yarn config set registry https:\u002F\u002Fregistry.npm.taobao.org && yarn install\n",[68,22160,22161,22163],{"class":70,"line":268},[68,22162,14175],{"class":575},[68,22164,14199],{"class":373},[68,22166,22167,22169],{"class":70,"line":289},[68,22168,14190],{"class":575},[68,22170,22171],{"class":373}," npm run build\n",[68,22173,22174],{"class":70,"line":308},[68,22175,416],{"emptyLinePlaceholder":415},[68,22177,22178,22180],{"class":70,"line":314},[68,22179,14215],{"class":575},[68,22181,22182],{"class":373}," 6002\n",[68,22184,22185,22187],{"class":70,"line":320},[68,22186,14223],{"class":575},[68,22188,22189],{"class":373}," SERVER_ENV $SERVER_ENV\n",[68,22191,22192,22194,22196,22199,22201,22204,22206,22208],{"class":70,"line":889},[68,22193,14231],{"class":575},[68,22195,245],{"class":373},[68,22197,22198],{"class":116},"\"npm\"",[68,22200,597],{"class":373},[68,22202,22203],{"class":116},"\"run\"",[68,22205,597],{"class":373},[68,22207,14241],{"class":116},[68,22209,2657],{"class":373},[28,22211,22212,22213,22216,22217,22219],{},"记得在 ",[65,22214,22215],{},".dockerignore"," 文件中把 ",[65,22218,21748],{}," 目录加上，在 COPY 的时候不进行复制，而是在 docker 环境中重新获取。",[28,22221,22222,22223,22225,22226,22228],{},"在项目的 ",[65,22224,11591],{}," 中配置好 ",[65,22227,6034],{}," 命令：",[58,22230,22232],{"className":60,"code":22231,"language":62,"meta":63,"style":63},"{\n  \"scripts\": {\n    \"deploy\": \"docker build -t registry.cn-hangzhou.aliyuncs.com\u002Fyour-name\u002Fexample-project:$npm_package_version -t registry.cn-hangzhou.aliyuncs.com\u002Fyour-name\u002Fexample-project:latest . && docker push registry.cn-hangzhou.aliyuncs.com\u002Fyour-name\u002Fexample-project:$npm_package_version && docker push registry.cn-hangzhou.aliyuncs.com\u002Fyour-name\u002Fexample-project:latest\"\n  }\n}\n",[65,22233,22234,22238,22250,22267,22271],{"__ignoreMap":63},[68,22235,22236],{"class":70,"line":71},[68,22237,75],{"class":74},[68,22239,22240,22242,22244,22246,22248],{"class":70,"line":78},[68,22241,82],{"class":81},[68,22243,5554],{"class":85},[68,22245,89],{"class":81},[68,22247,92],{"class":74},[68,22249,95],{"class":74},[68,22251,22252,22254,22256,22258,22260,22262,22265],{"class":70,"line":98},[68,22253,101],{"class":81},[68,22255,6034],{"class":104},[68,22257,89],{"class":81},[68,22259,92],{"class":74},[68,22261,113],{"class":112},[68,22263,22264],{"class":116},"docker build -t registry.cn-hangzhou.aliyuncs.com\u002Fyour-name\u002Fexample-project:$npm_package_version -t registry.cn-hangzhou.aliyuncs.com\u002Fyour-name\u002Fexample-project:latest . && docker push registry.cn-hangzhou.aliyuncs.com\u002Fyour-name\u002Fexample-project:$npm_package_version && docker push registry.cn-hangzhou.aliyuncs.com\u002Fyour-name\u002Fexample-project:latest",[68,22266,120],{"class":112},[68,22268,22269],{"class":70,"line":123},[68,22270,126],{"class":74},[68,22272,22273],{"class":70,"line":129},[68,22274,132],{"class":74},[28,22276,22277,22278,22281],{},"在阿里云的容器镜像服务中建好镜像，便可以部署上去了。之前我是直接通过绑定 gitlab，在代码更新后，触发阿里云镜像服务在线构建，但是我们采用了私有的 npm 仓库的话，就比较麻烦了，还得配置 Nexus 账号，索性直接在本地构建，上传的网速也不是问题。在阿里云的容器服务中创建好应用，设置好触发器，在镜像更新后触发重新部署，就大功告成了。如果有多台机器，在调度配置中配置好“平滑升级”，会在同一服务的多个容器升级的时候，保证当前一批或者一个容器升级或者更新成功（健康检查成功）之后，再来更新或者升级下一批容器，也就是“滚动发布”。之后只需要 ",[65,22279,22280],{},"npm run deploy","，喝杯咖啡 ☕️，就完成了所有的部署工作了。方便、快捷、不易出错。",[28,22283,22284],{},"最近看了篇文章，里面有句话：“这世界上肯定存在让人上瘾的代码”，提出了“技术多巴胺”这个说法。而此刻的我，就仿佛打了几针“技术多巴胺”一样，很兴奋，一点睡意都没有。",[523,22286,22287],{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sw1J6, html code.shiki .sw1J6{--shiki-light:#F76D47;--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":63,"searchDepth":78,"depth":78,"links":22289},[22290,22291],{"id":21735,"depth":98,"text":21735},{"id":22106,"depth":98,"text":22107},"2018-04-14",{},"\u002Fposts\u002F2018\u002Ffrontend-components-and-docker-deploy",{"text":22296,"minutes":22297,"time":22298,"words":22299},"10 min read",9.455,567300,1891,{"title":21727,"description":21732},{"loc":22294},"posts\u002F2018\u002F20180414.frontend-components-and-docker-deploy",[543,8427,7154,7153],"凌晨","天气🌧","9_mH8lcdetq97RzpovxCwJ60jWmSz7r17jZvBEuf42s",{"id":22308,"title":22309,"body":22310,"class":528,"cover":528,"coverSize":528,"date":23104,"description":23105,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":23106,"navigation":415,"path":23107,"readingTime":23108,"seo":23112,"sitemap":23113,"stem":23114,"tags":23115,"time":528,"weather":23116,"__hash__":23117},"posts\u002Fposts\u002F2018\u002F20180412.dockerfile-maven.md","Maven 项目 Docker 一键发布配置",{"type":25,"value":22311,"toc":23102},[22312,22327,22330,22432,22438,22455,22996,23003,23093,23099],[28,22313,22314,22315,22320,22321,22326],{},"Docker 用了很久了，之前 Maven 项目一直用的",[38,22316,22319],{"href":22317,"rel":22318},"https:\u002F\u002Fgithub.com\u002Fspotify\u002Fdocker-maven-plugin",[42],"docker-maven-plugin","，但是作者目前已经不推荐使用这种方式了，该项目已经不再更新功能，只提供 bugfix。他们的新项目叫做",[38,22322,22325],{"href":22323,"rel":22324},"https:\u002F\u002Fgithub.com\u002Fspotify\u002Fdockerfile-maven",[42],"dockerfile-maven","，配置上有些不同，之前一直没时间去更新，最近的一个项目中，采用了最新的插件，中间也踩过不少坑，刚刚终于都搞定了，记录一下。",[28,22328,22329],{},"Dockerfile 无需多说，整理了一个通用的，可以用在任意 Spring Boot 项目中，如下：",[58,22331,22333],{"className":14143,"code":22332,"language":14139,"meta":63,"style":63},"FROM frolvlad\u002Falpine-oraclejdk8:slim\n\nRUN ln -sf \u002Fusr\u002Fshare\u002Fzoneinfo\u002FAsia\u002FShanghai \u002Fetc\u002Flocaltime\n\nVOLUME \u002Ftmp\n\nARG JAR_FILE\nADD ${JAR_FILE} app.jar\nRUN sh -c 'touch \u002Fapp.jar'\nENV JAVA_OPTS=\"\"\nENV ENV=\"\"\nENTRYPOINT [ \"sh\", \"-c\", \"java $JAVA_OPTS -Djava.security.egd=file:\u002Fdev\u002F.\u002Furandom -jar \u002Fapp.jar --spring.profiles.active=$ENV\" ]\n",[65,22334,22335,22342,22346,22352,22356,22362,22366,22374,22382,22392,22401,22410],{"__ignoreMap":63},[68,22336,22337,22339],{"class":70,"line":71},[68,22338,14151],{"class":575},[68,22340,22341],{"class":373}," frolvlad\u002Falpine-oraclejdk8:slim\n",[68,22343,22344],{"class":70,"line":78},[68,22345,416],{"emptyLinePlaceholder":415},[68,22347,22348,22350],{"class":70,"line":98},[68,22349,14190],{"class":575},[68,22351,14441],{"class":373},[68,22353,22354],{"class":70,"line":123},[68,22355,416],{"emptyLinePlaceholder":415},[68,22357,22358,22360],{"class":70,"line":129},[68,22359,14450],{"class":575},[68,22361,14453],{"class":373},[68,22363,22364],{"class":70,"line":212},[68,22365,416],{"emptyLinePlaceholder":415},[68,22367,22368,22371],{"class":70,"line":233},[68,22369,22370],{"class":575},"ARG",[68,22372,22373],{"class":373}," JAR_FILE\n",[68,22375,22376,22379],{"class":70,"line":268},[68,22377,22378],{"class":575},"ADD",[68,22380,22381],{"class":373}," ${JAR_FILE} app.jar\n",[68,22383,22384,22386,22389],{"class":70,"line":289},[68,22385,14190],{"class":575},[68,22387,22388],{"class":373}," sh -c ",[68,22390,22391],{"class":116},"'touch \u002Fapp.jar'\n",[68,22393,22394,22396,22398],{"class":70,"line":308},[68,22395,14223],{"class":575},[68,22397,14481],{"class":373},[68,22399,22400],{"class":116},"\"\"\n",[68,22402,22403,22405,22408],{"class":70,"line":314},[68,22404,14223],{"class":575},[68,22406,22407],{"class":373}," ENV=",[68,22409,22400],{"class":116},[68,22411,22412,22414,22416,22419,22421,22424,22426,22429],{"class":70,"line":320},[68,22413,14489],{"class":575},[68,22415,14492],{"class":373},[68,22417,22418],{"class":116},"\"sh\"",[68,22420,597],{"class":373},[68,22422,22423],{"class":116},"\"-c\"",[68,22425,597],{"class":373},[68,22427,22428],{"class":116},"\"java $JAVA_OPTS -Djava.security.egd=file:\u002Fdev\u002F.\u002Furandom -jar \u002Fapp.jar --spring.profiles.active=$ENV\"",[68,22430,22431],{"class":373}," ]\n",[28,22433,22434,22435,22437],{},"在用阿里云容器服务的时候，这里的",[65,22436,14223],{},"可以直接显示到配置项中进行配置，根据不同的配置选择不同的 profiles 文件。",[28,22439,22440,22443,22444,22447,22448,22451,22452,22454],{},[65,22441,22442],{},"pom.xml","文件中，加入",[65,22445,22446],{},"plugin","，会自动添加版本号和",[65,22449,22450],{},"latest","两个",[65,22453,13200],{},"，并推送。",[58,22456,22458],{"className":718,"code":22457,"language":720,"meta":63,"style":63},"\u003Cplugin>\n    \u003CgroupId>com.spotify\u003C\u002FgroupId>\n    \u003CartifactId>dockerfile-maven-plugin\u003C\u002FartifactId>\n    \u003Cversion>1.4.0\u003C\u002Fversion>\n    \u003Cexecutions>\n        \u003Cexecution>\n            \u003Cid>build-image\u003C\u002Fid>\n            \u003Cphase>package\u003C\u002Fphase>\n            \u003Cgoals>\n                \u003Cgoal>build\u003C\u002Fgoal>\n            \u003C\u002Fgoals>\n        \u003C\u002Fexecution>\n        \u003Cexecution>\n            \u003Cid>tag-image-version\u003C\u002Fid>\n            \u003Cphase>deploy\u003C\u002Fphase>\n            \u003Cgoals>\n                \u003Cgoal>tag\u003C\u002Fgoal>\n                \u003Cgoal>push\u003C\u002Fgoal>\n            \u003C\u002Fgoals>\n            \u003Cconfiguration>\n                \u003Ctag>${project.version}\u003C\u002Ftag>\n            \u003C\u002Fconfiguration>\n        \u003C\u002Fexecution>\n        \u003Cexecution>\n            \u003Cid>tag-image-latest\u003C\u002Fid>\n            \u003Cphase>deploy\u003C\u002Fphase>\n            \u003Cgoals>\n                \u003Cgoal>tag\u003C\u002Fgoal>\n                \u003Cgoal>push\u003C\u002Fgoal>\n            \u003C\u002Fgoals>\n            \u003Cconfiguration>\n                \u003Ctag>latest\u003C\u002Ftag>\n            \u003C\u002Fconfiguration>\n        \u003C\u002Fexecution>\n    \u003C\u002Fexecutions>\n    \u003Cconfiguration>\n        \u003Crepository>registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002F${project.artifactId}\u003C\u002Frepository>\n        \u003Ctag>${project.version}\u003C\u002Ftag>\n        \u003CuseMavenSettingsForAuth>true\u003C\u002FuseMavenSettingsForAuth>\n        \u003CbuildArgs>\n            \u003CJAR_FILE>target\u002F${project.build.finalName}.jar\u003C\u002FJAR_FILE>\n        \u003C\u002FbuildArgs>\n    \u003C\u002Fconfiguration>\n\u003C\u002Fplugin>\n\n",[65,22459,22460,22468,22486,22504,22521,22530,22539,22556,22574,22583,22601,22610,22618,22626,22643,22659,22667,22683,22699,22707,22716,22733,22741,22749,22757,22774,22790,22798,22814,22830,22838,22846,22862,22870,22878,22886,22894,22912,22928,22945,22954,22972,22980,22988],{"__ignoreMap":63},[68,22461,22462,22464,22466],{"class":70,"line":71},[68,22463,727],{"class":74},[68,22465,22446],{"class":730},[68,22467,734],{"class":74},[68,22469,22470,22472,22475,22477,22480,22482,22484],{"class":70,"line":78},[68,22471,739],{"class":74},[68,22473,22474],{"class":730},"groupId",[68,22476,765],{"class":74},[68,22478,22479],{"class":373},"com.spotify",[68,22481,771],{"class":74},[68,22483,22474],{"class":730},[68,22485,734],{"class":74},[68,22487,22488,22490,22493,22495,22498,22500,22502],{"class":70,"line":98},[68,22489,739],{"class":74},[68,22491,22492],{"class":730},"artifactId",[68,22494,765],{"class":74},[68,22496,22497],{"class":373},"dockerfile-maven-plugin",[68,22499,771],{"class":74},[68,22501,22492],{"class":730},[68,22503,734],{"class":74},[68,22505,22506,22508,22510,22512,22515,22517,22519],{"class":70,"line":123},[68,22507,739],{"class":74},[68,22509,155],{"class":730},[68,22511,765],{"class":74},[68,22513,22514],{"class":373},"1.4.0",[68,22516,771],{"class":74},[68,22518,155],{"class":730},[68,22520,734],{"class":74},[68,22522,22523,22525,22528],{"class":70,"line":129},[68,22524,739],{"class":74},[68,22526,22527],{"class":730},"executions",[68,22529,734],{"class":74},[68,22531,22532,22534,22537],{"class":70,"line":212},[68,22533,749],{"class":74},[68,22535,22536],{"class":730},"execution",[68,22538,734],{"class":74},[68,22540,22541,22543,22545,22547,22550,22552,22554],{"class":70,"line":233},[68,22542,759],{"class":74},[68,22544,2213],{"class":730},[68,22546,765],{"class":74},[68,22548,22549],{"class":373},"build-image",[68,22551,771],{"class":74},[68,22553,2213],{"class":730},[68,22555,734],{"class":74},[68,22557,22558,22560,22563,22565,22568,22570,22572],{"class":70,"line":268},[68,22559,759],{"class":74},[68,22561,22562],{"class":730},"phase",[68,22564,765],{"class":74},[68,22566,22567],{"class":373},"package",[68,22569,771],{"class":74},[68,22571,22562],{"class":730},[68,22573,734],{"class":74},[68,22575,22576,22578,22581],{"class":70,"line":289},[68,22577,759],{"class":74},[68,22579,22580],{"class":730},"goals",[68,22582,734],{"class":74},[68,22584,22585,22588,22591,22593,22595,22597,22599],{"class":70,"line":308},[68,22586,22587],{"class":74},"                \u003C",[68,22589,22590],{"class":730},"goal",[68,22592,765],{"class":74},[68,22594,13012],{"class":373},[68,22596,771],{"class":74},[68,22598,22590],{"class":730},[68,22600,734],{"class":74},[68,22602,22603,22606,22608],{"class":70,"line":314},[68,22604,22605],{"class":74},"            \u003C\u002F",[68,22607,22580],{"class":730},[68,22609,734],{"class":74},[68,22611,22612,22614,22616],{"class":70,"line":320},[68,22613,797],{"class":74},[68,22615,22536],{"class":730},[68,22617,734],{"class":74},[68,22619,22620,22622,22624],{"class":70,"line":889},[68,22621,749],{"class":74},[68,22623,22536],{"class":730},[68,22625,734],{"class":74},[68,22627,22628,22630,22632,22634,22637,22639,22641],{"class":70,"line":909},[68,22629,759],{"class":74},[68,22631,2213],{"class":730},[68,22633,765],{"class":74},[68,22635,22636],{"class":373},"tag-image-version",[68,22638,771],{"class":74},[68,22640,2213],{"class":730},[68,22642,734],{"class":74},[68,22644,22645,22647,22649,22651,22653,22655,22657],{"class":70,"line":929},[68,22646,759],{"class":74},[68,22648,22562],{"class":730},[68,22650,765],{"class":74},[68,22652,6034],{"class":373},[68,22654,771],{"class":74},[68,22656,22562],{"class":730},[68,22658,734],{"class":74},[68,22660,22661,22663,22665],{"class":70,"line":949},[68,22662,759],{"class":74},[68,22664,22580],{"class":730},[68,22666,734],{"class":74},[68,22668,22669,22671,22673,22675,22677,22679,22681],{"class":70,"line":969},[68,22670,22587],{"class":74},[68,22672,22590],{"class":730},[68,22674,765],{"class":74},[68,22676,13200],{"class":373},[68,22678,771],{"class":74},[68,22680,22590],{"class":730},[68,22682,734],{"class":74},[68,22684,22685,22687,22689,22691,22693,22695,22697],{"class":70,"line":989},[68,22686,22587],{"class":74},[68,22688,22590],{"class":730},[68,22690,765],{"class":74},[68,22692,18621],{"class":373},[68,22694,771],{"class":74},[68,22696,22590],{"class":730},[68,22698,734],{"class":74},[68,22700,22701,22703,22705],{"class":70,"line":1009},[68,22702,22605],{"class":74},[68,22704,22580],{"class":730},[68,22706,734],{"class":74},[68,22708,22709,22711,22714],{"class":70,"line":1029},[68,22710,759],{"class":74},[68,22712,22713],{"class":730},"configuration",[68,22715,734],{"class":74},[68,22717,22718,22720,22722,22724,22727,22729,22731],{"class":70,"line":1049},[68,22719,22587],{"class":74},[68,22721,13200],{"class":730},[68,22723,765],{"class":74},[68,22725,22726],{"class":373},"${project.version}",[68,22728,771],{"class":74},[68,22730,13200],{"class":730},[68,22732,734],{"class":74},[68,22734,22735,22737,22739],{"class":70,"line":1069},[68,22736,22605],{"class":74},[68,22738,22713],{"class":730},[68,22740,734],{"class":74},[68,22742,22743,22745,22747],{"class":70,"line":1088},[68,22744,797],{"class":74},[68,22746,22536],{"class":730},[68,22748,734],{"class":74},[68,22750,22751,22753,22755],{"class":70,"line":1108},[68,22752,749],{"class":74},[68,22754,22536],{"class":730},[68,22756,734],{"class":74},[68,22758,22759,22761,22763,22765,22768,22770,22772],{"class":70,"line":1128},[68,22760,759],{"class":74},[68,22762,2213],{"class":730},[68,22764,765],{"class":74},[68,22766,22767],{"class":373},"tag-image-latest",[68,22769,771],{"class":74},[68,22771,2213],{"class":730},[68,22773,734],{"class":74},[68,22775,22776,22778,22780,22782,22784,22786,22788],{"class":70,"line":1148},[68,22777,759],{"class":74},[68,22779,22562],{"class":730},[68,22781,765],{"class":74},[68,22783,6034],{"class":373},[68,22785,771],{"class":74},[68,22787,22562],{"class":730},[68,22789,734],{"class":74},[68,22791,22792,22794,22796],{"class":70,"line":1168},[68,22793,759],{"class":74},[68,22795,22580],{"class":730},[68,22797,734],{"class":74},[68,22799,22800,22802,22804,22806,22808,22810,22812],{"class":70,"line":1187},[68,22801,22587],{"class":74},[68,22803,22590],{"class":730},[68,22805,765],{"class":74},[68,22807,13200],{"class":373},[68,22809,771],{"class":74},[68,22811,22590],{"class":730},[68,22813,734],{"class":74},[68,22815,22816,22818,22820,22822,22824,22826,22828],{"class":70,"line":2039},[68,22817,22587],{"class":74},[68,22819,22590],{"class":730},[68,22821,765],{"class":74},[68,22823,18621],{"class":373},[68,22825,771],{"class":74},[68,22827,22590],{"class":730},[68,22829,734],{"class":74},[68,22831,22832,22834,22836],{"class":70,"line":2055},[68,22833,22605],{"class":74},[68,22835,22580],{"class":730},[68,22837,734],{"class":74},[68,22839,22840,22842,22844],{"class":70,"line":2071},[68,22841,759],{"class":74},[68,22843,22713],{"class":730},[68,22845,734],{"class":74},[68,22847,22848,22850,22852,22854,22856,22858,22860],{"class":70,"line":2087},[68,22849,22587],{"class":74},[68,22851,13200],{"class":730},[68,22853,765],{"class":74},[68,22855,22450],{"class":373},[68,22857,771],{"class":74},[68,22859,13200],{"class":730},[68,22861,734],{"class":74},[68,22863,22864,22866,22868],{"class":70,"line":2092},[68,22865,22605],{"class":74},[68,22867,22713],{"class":730},[68,22869,734],{"class":74},[68,22871,22872,22874,22876],{"class":70,"line":2097},[68,22873,797],{"class":74},[68,22875,22536],{"class":730},[68,22877,734],{"class":74},[68,22879,22880,22882,22884],{"class":70,"line":2114},[68,22881,806],{"class":74},[68,22883,22527],{"class":730},[68,22885,734],{"class":74},[68,22887,22888,22890,22892],{"class":70,"line":2139},[68,22889,739],{"class":74},[68,22891,22713],{"class":730},[68,22893,734],{"class":74},[68,22895,22896,22898,22901,22903,22906,22908,22910],{"class":70,"line":2158},[68,22897,749],{"class":74},[68,22899,22900],{"class":730},"repository",[68,22902,765],{"class":74},[68,22904,22905],{"class":373},"registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002F${project.artifactId}",[68,22907,771],{"class":74},[68,22909,22900],{"class":730},[68,22911,734],{"class":74},[68,22913,22914,22916,22918,22920,22922,22924,22926],{"class":70,"line":2173},[68,22915,749],{"class":74},[68,22917,13200],{"class":730},[68,22919,765],{"class":74},[68,22921,22726],{"class":373},[68,22923,771],{"class":74},[68,22925,13200],{"class":730},[68,22927,734],{"class":74},[68,22929,22930,22932,22935,22937,22939,22941,22943],{"class":70,"line":2178},[68,22931,749],{"class":74},[68,22933,22934],{"class":730},"useMavenSettingsForAuth",[68,22936,765],{"class":74},[68,22938,849],{"class":373},[68,22940,771],{"class":74},[68,22942,22934],{"class":730},[68,22944,734],{"class":74},[68,22946,22947,22949,22952],{"class":70,"line":2193},[68,22948,749],{"class":74},[68,22950,22951],{"class":730},"buildArgs",[68,22953,734],{"class":74},[68,22955,22956,22958,22961,22963,22966,22968,22970],{"class":70,"line":2201},[68,22957,759],{"class":74},[68,22959,22960],{"class":730},"JAR_FILE",[68,22962,765],{"class":74},[68,22964,22965],{"class":373},"target\u002F${project.build.finalName}.jar",[68,22967,771],{"class":74},[68,22969,22960],{"class":730},[68,22971,734],{"class":74},[68,22973,22974,22976,22978],{"class":70,"line":2207},[68,22975,797],{"class":74},[68,22977,22951],{"class":730},[68,22979,734],{"class":74},[68,22981,22982,22984,22986],{"class":70,"line":2234},[68,22983,806],{"class":74},[68,22985,22713],{"class":730},[68,22987,734],{"class":74},[68,22989,22990,22992,22994],{"class":70,"line":2258},[68,22991,771],{"class":74},[68,22993,22446],{"class":730},[68,22995,734],{"class":74},[28,22997,22998,22999,23002],{},"想要直接用",[65,23000,23001],{},"mvn deploy","完成整个部署的话，还需要加一下 Nexus 的发布配置",[58,23004,23006],{"className":718,"code":23005,"language":720,"meta":63,"style":63},"\u003CdistributionManagement>\n    \u003Crepository>\n        \u003Cid>monkey-run-maven-release\u003C\u002Fid>\n        \u003Cname>MonkeyRun Maven Release Repository\u003C\u002Fname>\n        \u003Curl>你的nexus仓库地址\u003C\u002Furl>\n    \u003C\u002Frepository>\n\u003C\u002FdistributionManagement>\n",[65,23007,23008,23017,23025,23042,23059,23077,23085],{"__ignoreMap":63},[68,23009,23010,23012,23015],{"class":70,"line":71},[68,23011,727],{"class":74},[68,23013,23014],{"class":730},"distributionManagement",[68,23016,734],{"class":74},[68,23018,23019,23021,23023],{"class":70,"line":78},[68,23020,739],{"class":74},[68,23022,22900],{"class":730},[68,23024,734],{"class":74},[68,23026,23027,23029,23031,23033,23036,23038,23040],{"class":70,"line":98},[68,23028,749],{"class":74},[68,23030,2213],{"class":730},[68,23032,765],{"class":74},[68,23034,23035],{"class":373},"monkey-run-maven-release",[68,23037,771],{"class":74},[68,23039,2213],{"class":730},[68,23041,734],{"class":74},[68,23043,23044,23046,23048,23050,23053,23055,23057],{"class":70,"line":123},[68,23045,749],{"class":74},[68,23047,217],{"class":730},[68,23049,765],{"class":74},[68,23051,23052],{"class":373},"MonkeyRun Maven Release Repository",[68,23054,771],{"class":74},[68,23056,217],{"class":730},[68,23058,734],{"class":74},[68,23060,23061,23063,23066,23068,23071,23073,23075],{"class":70,"line":129},[68,23062,749],{"class":74},[68,23064,23065],{"class":730},"url",[68,23067,765],{"class":74},[68,23069,23070],{"class":373},"你的nexus仓库地址",[68,23072,771],{"class":74},[68,23074,23065],{"class":730},[68,23076,734],{"class":74},[68,23078,23079,23081,23083],{"class":70,"line":212},[68,23080,806],{"class":74},[68,23082,22900],{"class":730},[68,23084,734],{"class":74},[68,23086,23087,23089,23091],{"class":70,"line":233},[68,23088,771],{"class":74},[68,23090,23014],{"class":730},[68,23092,734],{"class":74},[28,23094,23095,23096,23098],{},"阿里云容器上配置好镜像更新重新部署的触发器，之后就是直接",[65,23097,23001],{},"等编译好上传完就发布完成啦！",[523,23100,23101],{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sw1J6, html code.shiki .sw1J6{--shiki-light:#F76D47;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":63,"searchDepth":78,"depth":78,"links":23103},[],"2018-04-12","Docker 用了很久了，之前 Maven 项目一直用的docker-maven-plugin，但是作者目前已经不推荐使用这种方式了，该项目已经不再更新功能，只提供 bugfix。他们的新项目叫做dockerfile-maven，配置上有些不同，之前一直没时间去更新，最近的一个项目中，采用了最新的插件，中间也踩过不少坑，刚刚终于都搞定了，记录一下。",{},"\u002Fposts\u002F2018\u002Fdockerfile-maven",{"text":1217,"minutes":23109,"time":23110,"words":23111},1.81,108600,362,{"title":22309,"description":23105},{"loc":23107},"posts\u002F2018\u002F20180412.dockerfile-maven",[543,7154,7153],"天气小雨🌦","cvFl_OQs7LBBHpi45-gE5m-7kKeSxsJyCWuH8fPZRM8",{"id":23119,"title":23120,"body":23121,"class":528,"cover":528,"coverSize":528,"date":23228,"description":23125,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":23229,"navigation":415,"path":23230,"readingTime":23231,"seo":23235,"sitemap":23236,"stem":23237,"tags":23238,"time":528,"weather":528,"__hash__":23239},"posts\u002Fposts\u002F2017\u002F20171116.wxapp-ssl-error.md","微信小程序在安卓上 SSL 报错的问题",{"type":25,"value":23122,"toc":23226},[23123,23126,23135,23142,23145,23153,23162,23173,23176,23223],[28,23124,23125],{},"开发工具上和 iOS 真机上访问 api 都是正常的，在安卓上提示如下错误：",[58,23127,23129],{"className":361,"code":23128,"language":363,"meta":63,"style":63},"request:fail ssl hand shake error\n",[65,23130,23131],{"__ignoreMap":63},[68,23132,23133],{"class":70,"line":71},[68,23134,23128],{"class":373},[28,23136,23137,23138,23141],{},"尝试在安卓的浏览器中访问 api 地址，提示“",[65,23139,23140],{},"该证书并非来自可信的授权中心","”，于是感觉应该是 SSL 证书的问题。",[28,23143,23144],{},"SSL 证书是通过 Let's Encrypt 申请的，部署在阿里云 SLB 上。",[28,23146,23147,23148,23152],{},"通过",[38,23149,23150],{"href":23150,"rel":23151},"https:\u002F\u002Fwww.ssllabs.com\u002Fssltest\u002Findex.html",[42]," 测试，TLS1.0、TLS1.1、TLS1.2 都是支持的，但有如下提示",[58,23154,23156],{"className":361,"code":23155,"language":363,"meta":63,"style":63},"This server's certificate chain is incomplete. Grade capped to B.\n",[65,23157,23158],{"__ignoreMap":63},[68,23159,23160],{"class":70,"line":71},[68,23161,23155],{"class":373},[28,23163,23164,23165,23168,23169,23172],{},"于是重新查看了下 Let's Encrypt 生成的证书文件，想起来在阿里云 SLB 的证书填写的是",[65,23166,23167],{},"cert.pem","的内容，没有包含中间证书。于是重新填写",[65,23170,23171],{},"fullchain.pem","里的内容，问题解决。",[28,23174,23175],{},"下面是 Let's Encrypt 生成的证书文件及其内容：",[23177,23178,23179],"scrollable-table",{},[9859,23180,23181,23191],{},[9862,23182,23183],{},[9865,23184,23185,23188],{},[9868,23186,23187],{},"文件名",[9868,23189,23190],{},"内容",[9880,23192,23193,23200,23208,23215],{},[9865,23194,23195,23197],{},[9885,23196,23167],{},[9885,23198,23199],{},"服务端证书",[9865,23201,23202,23205],{},[9885,23203,23204],{},"chain.pem",[9885,23206,23207],{},"浏览器需要的所有证书但不包括服务端证书，比如根证书和中间证书",[9865,23209,23210,23212],{},[9885,23211,23171],{},[9885,23213,23214],{},"包括了 cert.pem 和 chain.pem 的内容",[9865,23216,23217,23220],{},[9885,23218,23219],{},"privkey.pem",[9885,23221,23222],{},"证书的私钥",[523,23224,23225],{},"html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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":63,"searchDepth":78,"depth":78,"links":23227},[],"2017-11-16",{},"\u002Fposts\u002F2017\u002Fwxapp-ssl-error",{"text":1217,"minutes":23232,"time":23233,"words":23234},1.2,72000,240,{"title":23120,"description":23125},{"loc":23230},"posts\u002F2017\u002F20171116.wxapp-ssl-error",[543,8427],"OJ4ttgQOk0mUu8qLcQvEH9tsqn4vc1dma_uHLfP5yM4",{"id":23241,"title":23242,"body":23243,"class":528,"cover":1212,"coverSize":528,"date":23356,"description":23247,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":23357,"navigation":415,"path":23358,"readingTime":23359,"seo":23364,"sitemap":23365,"stem":23366,"tags":23367,"time":528,"weather":528,"__hash__":23368},"posts\u002Fposts\u002F2017\u002F20171102.transfer-blog-to-aliyun-docker.md","Ghost 博客迁移至阿里云 Docker",{"type":25,"value":23244,"toc":23354},[23245,23248,23251,23254,23257,23280,23283,23291,23294,23297,23300,23303,23306,23351],[28,23246,23247],{},"刚刚，将 Ghost 博客迁移到了阿里云 Docker 上。",[28,23249,23250],{},"由于近期网络问题，导致家里的 NAS 已经无法提供 443 端口的服务了，之前的临时解决方案是将 hadb.me 的域名解析到 DigitalOcean 的一台机器上，然后用 nginx 转发到 NAS 的 20443 端口，通过海外的服务器做了中转，访问速度可想而知。并且近期海外网络极不稳定，最终决定还是老老实实备案，迁移到阿里云上来。",[28,23252,23253],{},"近年来，Docker 容器化越来越火，我最近的几个项目也都是通过 Docker 来部署的，非常方便。",[28,23255,23256],{},"域名备案经历了几波周折，提交备案后，阿里云初检未通过，有如下问题：",[5010,23258,23259,23268,23271,23274,23277],{},[1239,23260,23261,23262,23267],{},"根据要求已经取得备案号的网站最下方必须显示您的备案号，并能链接到工信部网站",[38,23263,23266],{"href":23264,"rel":23265},"http:\u002F\u002Fwww.miitbeian.gov.cn\u002F",[42],"www.miitbeian.gov.cn","，目前您网站“monkeyrun.net”最下方备案号无法链接工信部网站，请您修改",[1239,23269,23270],{},"根据要求网站名称必须与主办单位名称有一定的关联性。您备案的网站名称“HADB 的博客”与主办单位名称“上海猿奋网络科技有限公司”没有关联性，请修改",[1239,23272,23273],{},"根据要求域名持有者必须与主办单位名称一致，经查询您的域名“hadb.me”持有者与您备案信息中“邓斌 ”单位名称\u002F法人姓名不一致，请您先办理域名过户",[1239,23275,23276],{},"根据管局要求域名有效期需要大于 6 个月，您的域名“hadb.me”有效期不足 6 个月，请您修改",[1239,23278,23279],{},"“邓斌”证件号码在多个单位\u002F个人备案中重复出现多次，根据要求，一个证件号码只能出现在一个单位\u002F个人备案下，请您更换其它证件",[28,23281,23282],{},"问题 1、2 改起来都还好，很快改完了。",[28,23284,23285,23286,23290],{},"问题 3 操作的过程中遇到了一个很蛋疼的问题。域名原先在 Godaddy 上购买的，在过户前，手贱把域名里的持有者信息修改了下，从英文名改成了中文品拼音，然后就尴尬了，Godaddy 禁止域名转出了，锁定期貌似 60 天，后来给 Godaddy 打中文客服，一个妹子客服跟我说可以给",[38,23287,23289],{"href":23288},"mailto:review60@goaddy.com","review60@goaddy.com","发邮件申请解锁，发了邮件一天没回复。又打电话过去，这次是个男客服接的，他跟我说，这个锁定期是没办法解锁的，巴拉巴拉，口径竟然不一样。后来 Godaddy 的 review60 团队回复我邮件了，说已经解锁了 60 天的锁定期。如果有遇到同样问题的朋友，可以尝试给 review60 团队发邮件就可以解锁了。但是在万网进行域名过户的时候，一直提示“该域名产品暂时不允许转入，无法进行转入操作”，查了下，万网目前不支持.me 域名的新注册和转入。后来就尝试了下直接提交，没有做过户操作，也通过了初审。阿里云这里要求的过户其实是非必要的，只需要把持有者信息修改就可以了。",[28,23292,23293],{},"问题 4 续费了下就可以了。",[28,23295,23296],{},"问题 5，也费了些功夫。几年前上大学时，一个外包项目中用的我自己的身份证作为网站负责人备案的。之前一直没有要求说一个证件号码只能出现在一个备案下，不过既然现在提示了这个问题，那就去处理下。由于这个外包项目已经停止了，并且甲方的网站也已经不做了。所以处理起来很简单，直接登录原备案账号，把备案号注销掉就可以了。",[28,23298,23299],{},"几经周折，备案号终于下来了，接下来开始处理部署的问题。",[28,23301,23302],{},"在阿里云上部署与在自己的机器上部署 Docker 有些区别。",[28,23304,23305],{},"具体流程如下：",[5010,23307,23308,23311,23318,23328,23345,23348],{},[1239,23309,23310],{},"购买阿里云文件存储 NAS 服务，用来存放 Docker 数据卷",[1239,23312,23313,23314,23317],{},"在 ECS 上挂在 NAS，将以前的博客数据复制到 NAS 中的",[65,23315,23316],{},"\u002Fghost-hadb-data","目录下",[1239,23319,23320,23321,23324,23325,23327],{},"容器服务中创建 NAS 类型的数据卷",[65,23322,23323],{},"ghost-hadb-data","，指向",[65,23326,23316],{},"目录",[1239,23329,23330,23331,23333,23334,23337,23338,23341,23342],{},"创建应用，简单路由配置，将",[65,23332,10039],{},"指向容器端口",[65,23335,23336],{},"2368","，选择刚刚创建的数据卷，容器路径为",[65,23339,23340],{},"\u002Fvar\u002Flib\u002Fghost\u002Fcontent","，在环境变量中配置 url 为",[65,23343,23344],{},"https:\u002F\u002Fhadb.me\u002F",[1239,23346,23347],{},"配置负载均衡，添加 https 协议 443 端口监听，导入证书",[1239,23349,23350],{},"将域名解析切换到负载均衡 ip 地址",[28,23352,23353],{},"Done！以后可以愉快的写博客啦！",{"title":63,"searchDepth":78,"depth":78,"links":23355},[],"2017-11-02",{},"\u002Fposts\u002F2017\u002Ftransfer-blog-to-aliyun-docker",{"text":23360,"minutes":23361,"time":23362,"words":23363},"6 min read",5.875,352500,1175,{"title":23242,"description":23247},{"loc":23358},"posts\u002F2017\u002F20171102.transfer-blog-to-aliyun-docker",[543,10057,11282,7154],"Q9OzmZ88ueumMvvOGN6bGncIsjTRbiAg-qgAHmieR4k",{"id":23370,"title":23371,"body":23372,"class":528,"cover":528,"coverSize":528,"date":23428,"description":23429,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":23430,"navigation":415,"path":23431,"readingTime":23432,"seo":23436,"sitemap":23437,"stem":23438,"tags":23439,"time":528,"weather":528,"__hash__":23441},"posts\u002Fposts\u002F2016\u002F20161208.asp-net-core-ef-sqlite-add-foreign-key-to-exist-table.md","Entity Framework Core SQLite provider 向已存在的表中添加外键",{"type":25,"value":23373,"toc":23426},[23374,23382,23385,23388,23391,23394,23423],[28,23375,23376,23377,1686],{},"SQLite 本身不支持向已创建的表中添加外键，类似的限制还有很多，比较蛋疼，具体可以参见",[38,23378,23381],{"href":23379,"rel":23380},"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fef\u002Fcore\u002Fproviders\u002Fsqlite\u002Flimitations",[42],"SQLite Limitations",[28,23383,23384],{},"项目中，如果是测试的时候，数据不是很重要的话，最方便的方法就是把已经创建的 Migrations 包括 ModelSnapshot 都删掉，重新 Add-Migration 重建数据库。",[28,23386,23387],{},"对于已经发布的应用，数据库不能删了创建的话，可以“曲线救国”。",[28,23389,23390],{},"假设需要给 TableA 添加一个需要建立外键的字段 ColumnA，为了增加难度，假设 TableB 中的 Column",[28,23392,23393],{},"B 是 TableA 的外键。具体操作方法如下：",[5010,23395,23396,23399,23402,23405,23408,23411,23414,23417,23420],{},[1239,23397,23398],{},"先在代码中 TableA 里添加 ColumnA（不设置外键），Add-Migration，更新到线上数据库",[1239,23400,23401],{},"将本地的数据库改名为 database-backup，删除项目中所有 Migrations 和 ModelSnapshot，创建一个 RebuildDatabase 的 Migration，创建全新的数据库，从新数据库中复制 TableA 的 Create Statement SQL 语句并将改 SQL 语句中的表名改为 TableA-New",[1239,23403,23404],{},"在线上数据库中执行步骤 2 中的 SQL 语句，将创建 TableA-New（已经含有外键约束了）",[1239,23406,23407],{},"导出线上数据库中 TableA 中的数据到 SQL 文件中，并将该 SQL 文件中的表名改为 TableA-New",[1239,23409,23410],{},"将步骤 4 中的 SQL 文件的数据导入到线上数据库中",[1239,23412,23413],{},"将线上数据库的 TableA 改名为 TableA-Old，将 TableA-New 改名为 TableA",[1239,23415,23416],{},"因为重命名的关系，这时候 TableB 中的 ColumnB 是 TableA-Old 的外键，通过如下方法，将 TableB 中的外键约束改到 TableA 中：复制 TableB 的 Create Statement，创建一个 TableB-New，其中的 ColumnB 是 TableA 的外键，然后将 TableB 改名为 TableB-Old，并将 TableB-New 改名为 TableB（如果还有 TableC 中有 TableB-Old 的外键，通过同样的方法操作，以此类推），删除 TableB-Old。（如果没有这样的 TableB，则此步骤省略）",[1239,23418,23419],{},"删除 TableAOld",[1239,23421,23422],{},"清空线上数据库__EFMigrationHistory 表中的数据，并手动添加一条数据，以 RebuildDatabase 的 Migration 的文件名作为 MigrationId，并输入当前的 ProductVersion",[28,23424,23425],{},"收工！",{"title":63,"searchDepth":78,"depth":78,"links":23427},[],"2016-12-08","SQLite 本身不支持向已创建的表中添加外键，类似的限制还有很多，比较蛋疼，具体可以参见SQLite Limitations。",{},"\u002Fposts\u002F2016\u002Fasp-net-core-ef-sqlite-add-foreign-key-to-exist-table",{"text":535,"minutes":23433,"time":23434,"words":23435},2.535,152100,507,{"title":23371,"description":23429},{"loc":23431},"posts\u002F2016\u002F20161208.asp-net-core-ef-sqlite-add-foreign-key-to-exist-table",[543,23440],".NET","_hu7m8FKjqaeMscDcXPApg4_4iD34-7HKcNatqV5v3o",{"id":23443,"title":23444,"body":23445,"class":528,"cover":528,"coverSize":528,"date":23494,"description":23495,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":23496,"navigation":415,"path":23497,"readingTime":23498,"seo":23502,"sitemap":23503,"stem":23504,"tags":23505,"time":528,"weather":528,"__hash__":23506},"posts\u002Fposts\u002F2016\u002F20161109.aliyun-cdn-not-support-sni.md","解决阿里云 CDN 回源 https 返回 503 错误的问题",{"type":25,"value":23446,"toc":23492},[23447,23462,23469,23472,23475,23478,23486,23489],[28,23448,23449,23450,23455,23456,23461],{},"最近打算把",[38,23451,23454],{"href":23452,"rel":23453},"https:\u002F\u002Fwww.monkeyrun.net",[42],"www.monkeyrun.net","改成全站 https，使用的",[38,23457,23460],{"href":23458,"rel":23459},"https:\u002F\u002Fletsencrypt.org\u002F",[42],"Let’s Encrypt","的证书。然而在设置阿里云 CDN 的时候，阿里云 CDN 回源一直返回 503 错误，发工单，来来回回经过整整两天，终于把问题解决。容我娓娓道来。",[28,23463,23464,23465,23468],{},"最一开始，我先开启了阿里云的 CDN，源站设置为",[38,23466,23454],{"href":23452,"rel":23467},[42],"，通过 80 端口回源，没有任何问题。",[28,23470,23471],{},"后来当时配置好证书，站点也开启了 https 之后，将回源端口改为 443，开始出问题了，CDN 资源全部返回 503。而直接通过浏览器访问 https 的源站内容，都是没有问题的。",[28,23473,23474],{},"发工单，经过漫长的等待和提供链接等更详细的信息之后，阿里云的工作人员首先认为这个问题可能是由于我开启了防火墙或者一些安全软件导致，拦截或阻止了 CDN 节点的回源请求。我关闭了防火墙，问题依旧存在。",[28,23476,23477],{},"又经过漫长的等待以及转交专项处理人员处理之后，给我发了个抓的包，说是 CDN 回源请求被源站给 RST 了，让我检查我的服务器在网络层面是不是做了什么限制。看了半天抓包的数据，也不大看得懂，各种谷歌，最后感觉可能是协议不同，握手的时候有一个是 TLS 1.0，有一个是 TLS 1.2，谷歌了一通，被带入了另一个未知领域，尝试了各种 cipher suites，随后还是无果。",[28,23479,23480,23481,23485],{},"后来找到一个网站，测试 SSL 兼容性的，",[38,23482,23483],{"href":23483,"rel":23484},"https:\u002F\u002Fwww.ssllabs.com\u002Fssltest\u002F",[42],"，测试了一下网站 SSL 兼容性，发现不支持 SNI 的请求会直接 close connection。于是又问阿里工作人员，得知他们 CDN 回源时，SSL 握手不支持发送 SNI。",[28,23487,23488],{},"定位到问题了，在 IIS 站点里面，编辑网站绑定，取消勾选“需要服务器名称指示”，问题解决！",[28,23490,23491],{},"可以愉快的开启全站 https 了！",{"title":63,"searchDepth":78,"depth":78,"links":23493},[],"2016-11-09","最近打算把www.monkeyrun.net改成全站 https，使用的Let’s Encrypt的证书。然而在设置阿里云 CDN 的时候，阿里云 CDN 回源一直返回 503 错误，发工单，来来回回经过整整两天，终于把问题解决。容我娓娓道来。",{},"\u002Fposts\u002F2016\u002Faliyun-cdn-not-support-sni",{"text":535,"minutes":23499,"time":23500,"words":23501},2.565,153900,513,{"title":23444,"description":23495},{"loc":23497},"posts\u002F2016\u002F20161109.aliyun-cdn-not-support-sni",[543,7153,18130],"nHBSr5cQSJtMEotBzDat8oOGoidlnu2hPDoy8mW_Blo",{"id":23508,"title":23509,"body":23510,"class":528,"cover":528,"coverSize":528,"date":23793,"description":23514,"draft":530,"extension":531,"hideComments":530,"location":23794,"meta":23795,"navigation":415,"path":23796,"readingTime":23797,"seo":23801,"sitemap":23802,"stem":23803,"tags":23804,"time":22304,"weather":528,"__hash__":23805},"posts\u002Fposts\u002F2016\u002F20161023.redis-windows-sentinel.md","Windoows 下 Redis Sentinel 的部署",{"type":25,"value":23511,"toc":23791},[23512,23515,23519,23526,23529,23535,23538,23559,23566,23570,23573,23579,23582,23589,23593,23596,23599,23621,23624,23640,23643,23659,23662,23688,23691,23713,23716,23732,23735,23750,23753,23778,23782,23785,23788],[28,23513,23514],{},"虽然很久之前就了解了 Redis 的哨兵机制，今天第一次尝试在多个服务器上部署多个 Redis 实例，并且设置了哨兵用来进行自动的主从切换。",[8514,23516,23518],{"id":23517},"一部署-redis","一、部署 Redis",[28,23520,23521,23522,1686],{},"在 3 台服务器上分别安装了 Redis，Redis on Windows 下载地址：",[38,23523,23524],{"href":23524,"rel":23525},"https:\u002F\u002Fgithub.com\u002FMSOpenTech\u002Fredis\u002Freleases",[42],[28,23527,23528],{},"配置文件添加密码：",[58,23530,23533],{"className":23531,"code":23532,"language":516},[514],"requirepass \u003C密码>\nmasterauth \u003C密码>\n",[65,23534,23532],{"__ignoreMap":63},[28,23536,23537],{},"除了设置本实例的密码外，还需要输入 master 的密码（需要和本实例密码相同），所有实例需要设置相同的密码，以便进行主从切换。",[28,23539,23540,23541,23544,23545,23547,23548,23551,23552,23555,23556,23558],{},"需要注意的是，Redis 从某个版本起，加入了一个",[65,23542,23543],{},"protected-mode","的保护模式。启动保护模式的条件是",[65,23546,23543],{},"开启，且没有设置",[65,23549,23550],{},"bind","，且没有设置密码。我的 Redis 的实例部署在多个公网服务器下，所以加密码是必须的，另外需要注释掉默认的",[65,23553,23554],{},"bind 127.0.0.1","，以使用公网 IP。因为设置了密码，所以",[65,23557,23543],{},"就无需进行改动，直接使用默认的就可以了。但是在后面哨兵的配置中的保护模式会有一个坑。",[28,23560,23561,23562,23565],{},"在 3 个 Redis 实例中挑选一个作为初始的 master。在另外两个实例的配置文件中，加入",[65,23563,23564],{},"slaveof","的配置。",[8514,23567,23569],{"id":23568},"二配置-sentinel","二、配置 Sentinel",[28,23571,23572],{},"创建哨兵的配置文件，内容如下：",[58,23574,23577],{"className":23575,"code":23576,"language":516},[514],"port \u003C端口号>\n\nsentinel monitor redis-master \u003CMaster IP> \u003CMaster端口号> 2\n\nsentinel down-after-milliseconds redis-master 5000\n\nsentinel failover-timeout redis-master 900000\n\nsentinel parallel-syncs redis-master 2\n\nsentinel auth-pass redis-master \u003C密码>\n\nlogfile \"LogFiles\u002Fmonkeyrun-sentinel.log\"\n\nprotected-mode no\n",[65,23578,23576],{"__ignoreMap":63},[28,23580,23581],{},"具体参数的含义不再赘述，可以 Google。",[28,23583,23584,23585,23588],{},"这里需要加上最后一行",[65,23586,23587],{},"protected-mode no","，把哨兵的保护模式关掉。因为哨兵目前不支持设置密码，如果不加这行就会启动保护模式了，外网无法访问，会出现哨兵与哨兵之间互相认为对方不可用的情况。",[8514,23590,23592],{"id":23591},"三一些命令","三、一些命令",[28,23594,23595],{},"因为是在 Windows 下，有些命令可以通过批处理文件方便处理。将如下代码分别保存为.bat 文件，可以直接双击执行。默认安装完会新建一个名叫 Redis 的服务，我不喜欢这个名字，可以先卸载这个默认的 Redis 服务，然后重新安装自己命名的服务。这样的好处是以后可以在一台服务器上安装多个不同用途的 Redis 实例，以便区分。",[28,23597,23598],{},"1、安装 Redis 服务",[58,23600,23602],{"className":1502,"code":23601,"language":1504,"meta":63,"style":63},"redis-server --service-install redis-service-monkey-run.conf --service-name redis-service-monkey-run\n",[65,23603,23604],{"__ignoreMap":63},[68,23605,23606,23609,23612,23615,23618],{"class":70,"line":71},[68,23607,23608],{"class":1511},"redis-server",[68,23610,23611],{"class":1518}," --service-install",[68,23613,23614],{"class":116}," redis-service-monkey-run.conf",[68,23616,23617],{"class":1518}," --service-name",[68,23619,23620],{"class":116}," redis-service-monkey-run\n",[28,23622,23623],{},"2、启动 Redis 服务",[58,23625,23627],{"className":1502,"code":23626,"language":1504,"meta":63,"style":63},"redis-server --service-start --service-name redis-service-monkey-run\n",[65,23628,23629],{"__ignoreMap":63},[68,23630,23631,23633,23636,23638],{"class":70,"line":71},[68,23632,23608],{"class":1511},[68,23634,23635],{"class":1518}," --service-start",[68,23637,23617],{"class":1518},[68,23639,23620],{"class":116},[28,23641,23642],{},"3、停止 Redis 服务",[58,23644,23646],{"className":1502,"code":23645,"language":1504,"meta":63,"style":63},"redis-server --service-stop --service-name redis-service-monkey-run\n",[65,23647,23648],{"__ignoreMap":63},[68,23649,23650,23652,23655,23657],{"class":70,"line":71},[68,23651,23608],{"class":1511},[68,23653,23654],{"class":1518}," --service-stop",[68,23656,23617],{"class":1518},[68,23658,23620],{"class":116},[28,23660,23661],{},"4、卸载 Redis 服务",[58,23663,23665],{"className":1502,"code":23664,"language":1504,"meta":63,"style":63},"redis-server --service-stop --service-name redis-service-monkey-run\nredis-server --service-uninstall --service-name redis-service-monkey-run\n",[65,23666,23667,23677],{"__ignoreMap":63},[68,23668,23669,23671,23673,23675],{"class":70,"line":71},[68,23670,23608],{"class":1511},[68,23672,23654],{"class":1518},[68,23674,23617],{"class":1518},[68,23676,23620],{"class":116},[68,23678,23679,23681,23684,23686],{"class":70,"line":78},[68,23680,23608],{"class":1511},[68,23682,23683],{"class":1518}," --service-uninstall",[68,23685,23617],{"class":1518},[68,23687,23620],{"class":116},[28,23689,23690],{},"5、安装 Sentinel 服务",[58,23692,23694],{"className":1502,"code":23693,"language":1504,"meta":63,"style":63},"redis-server --service-install sentinel-monkey-run.conf --service-name redis-sentinel-service-monkey-run --sentinel\n",[65,23695,23696],{"__ignoreMap":63},[68,23697,23698,23700,23702,23705,23707,23710],{"class":70,"line":71},[68,23699,23608],{"class":1511},[68,23701,23611],{"class":1518},[68,23703,23704],{"class":116}," sentinel-monkey-run.conf",[68,23706,23617],{"class":1518},[68,23708,23709],{"class":116}," redis-sentinel-service-monkey-run",[68,23711,23712],{"class":1518}," --sentinel\n",[28,23714,23715],{},"6、启动 Sentinel 服务",[58,23717,23719],{"className":1502,"code":23718,"language":1504,"meta":63,"style":63},"redis-server --service-start --service-name redis-sentinel-service-monkey-run\n",[65,23720,23721],{"__ignoreMap":63},[68,23722,23723,23725,23727,23729],{"class":70,"line":71},[68,23724,23608],{"class":1511},[68,23726,23635],{"class":1518},[68,23728,23617],{"class":1518},[68,23730,23731],{"class":116}," redis-sentinel-service-monkey-run\n",[28,23733,23734],{},"7、停止 Sentinel 服务",[58,23736,23738],{"className":1502,"code":23737,"language":1504,"meta":63,"style":63},"redis-server --service-stop --service-name redis-sentinel-service-monkey-run\n",[65,23739,23740],{"__ignoreMap":63},[68,23741,23742,23744,23746,23748],{"class":70,"line":71},[68,23743,23608],{"class":1511},[68,23745,23654],{"class":1518},[68,23747,23617],{"class":1518},[68,23749,23731],{"class":116},[28,23751,23752],{},"8、卸载 Sentinel 服务",[58,23754,23756],{"className":1502,"code":23755,"language":1504,"meta":63,"style":63},"redis-server --service-stop --service-name redis-sentinel-service-monkey-run\nredis-server --service-uninstall --service-name redis-sentinel-service-monkey-run\n",[65,23757,23758,23768],{"__ignoreMap":63},[68,23759,23760,23762,23764,23766],{"class":70,"line":71},[68,23761,23608],{"class":1511},[68,23763,23654],{"class":1518},[68,23765,23617],{"class":1518},[68,23767,23731],{"class":116},[68,23769,23770,23772,23774,23776],{"class":70,"line":78},[68,23771,23608],{"class":1511},[68,23773,23683],{"class":1518},[68,23775,23617],{"class":1518},[68,23777,23731],{"class":116},[8514,23779,23781],{"id":23780},"四其他","四、其他",[28,23783,23784],{},"验证了一下哨兵的主从切换，很爽！",[28,23786,23787],{},"睡觉！",[523,23789,23790],{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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":63,"searchDepth":78,"depth":78,"links":23792},[],"2016-10-23","老家",{},"\u002Fposts\u002F2016\u002Fredis-windows-sentinel",{"text":8419,"minutes":23798,"time":23799,"words":23800},3.485,209100,697,{"title":23509,"description":23514},{"loc":23796},"posts\u002F2016\u002F20161023.redis-windows-sentinel",[543],"-ccA1alUlUH92vmCjZ2FbJ80LqhCHW4CcyhYVqLQdqg",{"id":23807,"title":23808,"body":23809,"class":528,"cover":528,"coverSize":528,"date":23793,"description":23813,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":23954,"navigation":415,"path":23955,"readingTime":23956,"seo":23957,"sitemap":23958,"stem":23959,"tags":23960,"time":528,"weather":528,"__hash__":23961},"posts\u002Fposts\u002F2016\u002F20161023.upgrade-to-net-core-app-1-0-1-problem.md","更新 Microsoft.NETCore.App 到 1.0.1 出现 502.5 错误的问题",{"type":25,"value":23810,"toc":23952},[23811,23814,23817,23820,23823,23889,23896,23926,23934,23949],[28,23812,23813],{},"今天白天遇到一个问题，花了很长时间才解决。记录下。",[28,23815,23816],{},"问题是这样的，我是个强迫症，如果发现有可以更新的包，我肯定会去更新。",[28,23818,23819],{},"新建了一个 ASP.NET Core 的 Api 项目，发现有包可以更新，于是通过 Nuget 自动更新。更新完之后，出现第一个坑。",[28,23821,23822],{},"原先的",[58,23824,23826],{"className":60,"code":23825,"language":62,"meta":63,"style":63},"{\n  \"Microsoft.NETCore.App\": {\n    \"version\": \"1.0.0\",\n    \"type\": \"platform\"\n  }\n}\n",[65,23827,23828,23832,23845,23864,23881,23885],{"__ignoreMap":63},[68,23829,23830],{"class":70,"line":71},[68,23831,75],{"class":74},[68,23833,23834,23836,23839,23841,23843],{"class":70,"line":78},[68,23835,82],{"class":81},[68,23837,23838],{"class":85},"Microsoft.NETCore.App",[68,23840,89],{"class":81},[68,23842,92],{"class":74},[68,23844,95],{"class":74},[68,23846,23847,23849,23851,23853,23855,23857,23860,23862],{"class":70,"line":98},[68,23848,101],{"class":81},[68,23850,155],{"class":104},[68,23852,89],{"class":81},[68,23854,92],{"class":74},[68,23856,113],{"class":112},[68,23858,23859],{"class":116},"1.0.0",[68,23861,89],{"class":112},[68,23863,169],{"class":74},[68,23865,23866,23868,23870,23872,23874,23876,23879],{"class":70,"line":123},[68,23867,101],{"class":81},[68,23869,196],{"class":104},[68,23871,89],{"class":81},[68,23873,92],{"class":74},[68,23875,113],{"class":112},[68,23877,23878],{"class":116},"platform",[68,23880,120],{"class":112},[68,23882,23883],{"class":70,"line":129},[68,23884,126],{"class":74},[68,23886,23887],{"class":70,"line":212},[68,23888,132],{"class":74},[28,23890,23891,23892,23895],{},"更新之后会丢失 ",[65,23893,23894],{},"\"type\": \"platform\"","，变成",[58,23897,23899],{"className":60,"code":23898,"language":62,"meta":63,"style":63},"{\n  \"Microsoft.NETCore.App\": \"1.0.1\"\n}\n",[65,23900,23901,23905,23922],{"__ignoreMap":63},[68,23902,23903],{"class":70,"line":71},[68,23904,75],{"class":74},[68,23906,23907,23909,23911,23913,23915,23917,23920],{"class":70,"line":78},[68,23908,82],{"class":81},[68,23910,23838],{"class":85},[68,23912,89],{"class":81},[68,23914,92],{"class":74},[68,23916,113],{"class":112},[68,23918,23919],{"class":116},"1.0.1",[68,23921,120],{"class":112},[68,23923,23924],{"class":70,"line":98},[68,23925,132],{"class":74},[28,23927,23928,23929,5225,23931,23933],{},"直接编译都会报错。这个好解决，自己手动改下。把 ",[65,23930,155],{},[65,23932,196],{}," 加上。",[28,23935,23936,23937,23942,23943,23948],{},"改好之后，编译不报错了，但是在 iis express 上运行的时候，会出现 502.5 的错误，百思不得其解。Google 了很长时间也没找到解决方案。后来猛然在 ",[38,23938,23941],{"href":23939,"rel":23940},"https:\u002F\u002Fwww.microsoft.com\u002Fnet\u002Fcore#windows",[42],".NET Core 首页"," 的 Install .NET Core SDK 中看到一个 ",[38,23944,23947],{"href":23945,"rel":23946},"https:\u002F\u002Fgo.microsoft.com\u002Ffwlink\u002F?LinkID=827546",[42],".NET Core 1.0.1 - VS 2015 Tooling Preview 2","，突然感觉是不是还得安装下这个更新才能用.NET Core 1.0.1，于是下载更新，问题解决。",[523,23950,23951],{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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":63,"searchDepth":78,"depth":78,"links":23953},[],{},"\u002Fposts\u002F2016\u002Fupgrade-to-net-core-app-1-0-1-problem",{"text":1217,"minutes":8463,"time":8464,"words":8465},{"title":23808,"description":23813},{"loc":23955},"posts\u002F2016\u002F20161023.upgrade-to-net-core-app-1-0-1-problem",[543,23440],"3ymDoxvUSQNkTj9e34yl6hn703dFuShJVtfnlufSXxg",{"id":23963,"title":23964,"body":23965,"class":528,"cover":528,"coverSize":528,"date":24478,"description":63,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":24479,"navigation":415,"path":24480,"readingTime":24481,"seo":24485,"sitemap":24486,"stem":24487,"tags":24488,"time":528,"weather":528,"__hash__":24489},"posts\u002Fposts\u002F2016\u002F20161017.publishing-asp-net-core-to-iis-with-web-deploy-using-visual-studio.md","使用 Visual Studio Web Deploy 发布 ASP.NET Core 至 IIS",{"type":25,"value":23966,"toc":24476},[23967,23970,23978,23982,23985,23989,24010,24018,24021,24036,24123,24129,24133,24136,24177,24217,24221,24224,24227,24310,24314,24317,24328,24331,24347,24362,24366,24377,24380,24388,24397,24400,24409,24413,24416,24419,24458,24460,24473],[8514,23968,23969],{"id":23969},"操作系统要求",[1236,23971,23972,23975],{},[1239,23973,23974],{},"Windows 7 及以上",[1239,23976,23977],{},"Windows Server 2008 R2 及以上",[8514,23979,23981],{"id":23980},"iis-配置","IIS 配置",[28,23983,23984],{},"在服务器管理器中，通过添加角色和功能的向导，在服务器角色中勾选 Web 服务器（IIS），并安装。",[8514,23986,23988],{"id":23987},"安装net-core-windows-server-hosting-包","安装.NET Core Windows Server Hosting 包",[5010,23990,23991,24000],{},[1239,23992,23993,23994,23999],{},"安装",[38,23995,23998],{"href":23996,"rel":23997},"https:\u002F\u002Fgo.microsoft.com\u002Ffwlink\u002F?LinkID=827547",[42],".NET Core Windows Server Hosting","，这个包会安装.NET Core 运行时、.NET Core 库和 ASP.NET Core 模块，这个模块会在 IIS 和 Kestrel 服务器之间创建反向代理。",[1239,24001,24002,24003,24006,24007,1686],{},"重启服务器，或者从命令行执行",[65,24004,24005],{},"net stop was \u002Fy","，接着执行",[65,24008,24009],{},"net start w3svc",[28,24011,24012,24013,1686],{},"更多关于 ASP.NET Core 模块以及针对该模块的配置、web.config 中系统变量的设置、app_ofline.htm 的使用、模块日志的激活等，请参阅",[38,24014,24017],{"href":24015,"rel":24016},"https:\u002F\u002Fdocs.asp.net\u002Fen\u002Flatest\u002Fhosting\u002Faspnet-core-module.html",[42],"ASP.NET Core Module Configuration Reference",[8514,24019,24020],{"id":24020},"应用程序配置",[28,24022,24023,24024,24027,24028,24031,24032,24035],{},"添加对",[65,24025,24026],{},"Microsoft.AspNetCore.Server.IISIntegration","包的依赖，添加",[65,24029,24030],{},".UseIISIntegration()","到",[65,24033,24034],{},"WebHostBuilder()","中以引入 IIS 集成中间件。",[58,24037,24041],{"className":24038,"code":24039,"language":24040,"meta":63,"style":63},"language-csharp shiki shiki-themes material-theme-lighter github-light github-dark","var host = new WebHostBuilder()\n  .UseKestrel()\n  .UseContentRoot(Directory.GetCurrentDirectory())\n  .UseIISIntegration()\n  .UseStartup\u003CStartup>()\n  .Build();\n","csharp",[65,24042,24043,24060,24070,24089,24098,24113],{"__ignoreMap":63},[68,24044,24045,24049,24051,24053,24055,24058],{"class":70,"line":71},[68,24046,24048],{"class":24047},"sG8yY","var",[68,24050,17421],{"class":1511},[68,24052,1900],{"class":1899},[68,24054,7746],{"class":1899},[68,24056,24057],{"class":1511}," WebHostBuilder",[68,24059,2136],{"class":74},[68,24061,24062,24065,24068],{"class":70,"line":78},[68,24063,24064],{"class":74},"  .",[68,24066,24067],{"class":2183},"UseKestrel",[68,24069,2136],{"class":74},[68,24071,24072,24074,24077,24079,24082,24084,24087],{"class":70,"line":98},[68,24073,24064],{"class":74},[68,24075,24076],{"class":2183},"UseContentRoot",[68,24078,648],{"class":74},[68,24080,24081],{"class":373},"Directory",[68,24083,404],{"class":74},[68,24085,24086],{"class":2183},"GetCurrentDirectory",[68,24088,12009],{"class":74},[68,24090,24091,24093,24096],{"class":70,"line":123},[68,24092,24064],{"class":74},[68,24094,24095],{"class":2183},"UseIISIntegration",[68,24097,2136],{"class":74},[68,24099,24100,24102,24105,24107,24110],{"class":70,"line":129},[68,24101,24064],{"class":74},[68,24103,24104],{"class":2183},"UseStartup",[68,24106,727],{"class":74},[68,24108,24109],{"class":1511},"Startup",[68,24111,24112],{"class":74},">()\n",[68,24114,24115,24117,24120],{"class":70,"line":212},[68,24116,24064],{"class":74},[68,24118,24119],{"class":2183},"Build",[68,24121,24122],{"class":74},"();\n",[28,24124,24125,24126,24128],{},"需要指出的是，添加",[65,24127,24030],{},"并不会影响代码的可移植性。",[8514,24130,24132],{"id":24131},"为-iisintegration-服务设置-iisoptions","为 IISIntegration 服务设置 IISOptions",[28,24134,24135],{},"为了配置 IISIntegration 服务，需要在 ConfigureServices 中为 IISOptions 添加服务器配置。",[58,24137,24139],{"className":24038,"code":24138,"language":24040,"meta":63,"style":63},"services.Configure\u003CIISOptions>(options => {\n  ...\n});\n",[65,24140,24141,24164,24172],{"__ignoreMap":63},[68,24142,24143,24145,24147,24150,24152,24155,24158,24160,24162],{"class":70,"line":71},[68,24144,1566],{"class":373},[68,24146,404],{"class":74},[68,24148,24149],{"class":2183},"Configure",[68,24151,727],{"class":74},[68,24153,24154],{"class":1511},"IISOptions",[68,24156,24157],{"class":74},">(",[68,24159,22016],{"class":1511},[68,24161,7765],{"class":1899},[68,24163,95],{"class":74},[68,24165,24166,24169],{"class":70,"line":78},[68,24167,24168],{"class":1899},"  ..",[68,24170,24171],{"class":373},".\n",[68,24173,24174],{"class":70,"line":98},[68,24175,24176],{"class":74},"});\n",[23177,24178,24179],{},[9859,24180,24181,24191],{},[9862,24182,24183],{},[9865,24184,24185,24188],{},[9868,24186,24187],{},"Option",[9868,24189,24190],{},"Setting",[9880,24192,24193,24201,24209],{},[9865,24194,24195,24198],{},[9885,24196,24197],{},"AutomaticAuthentication",[9885,24199,24200],{},"If true, the authentication middleware will alter the request user arriving and respond to generic challenges. If false, the authentication middleware will only provide identity and respond to challenges when explicitly indicated by the AuthenticationScheme.",[9865,24202,24203,24206],{},[9885,24204,24205],{},"ForwardClientCertificate",[9885,24207,24208],{},"If true and the MS-ASPNETCORE-CLIENTCERT request header is present, the ITLSConnectionFeature will be populated.",[9865,24210,24211,24214],{},[9885,24212,24213],{},"ForwardWindowsAuthentication",[9885,24215,24216],{},"If true, authentication middleware will attempt to authenticate using platform handler windows authentication. If false, authentication middleware won’t be added.",[8514,24218,24220],{"id":24219},"publish-iis-工具","publish-iis 工具",[28,24222,24223],{},"The publish-iis tool can be added to any .NET Core application and will configure the ASP.NET Core Module by creating or modifying the web.config file. The tool runs after publishing with the dotnet publish command or publishing with Visual Studio and will configure the processPath and arguments for you. If you’re publishing a web.config file by including the file in your project and listing the file in the publishOptions section of project.json, the tool will not modify other IIS settings you have included in the file.",[28,24225,24226],{},"To include the publish-iis tool in your application, add entries to the tools and scripts sections of project.json.",[58,24228,24230],{"className":60,"code":24229,"language":62,"meta":63,"style":63},"{\n  \"tools\": {\n    \"Microsoft.AspNetCore.Server.IISIntegration.Tools\": \"1.0.0-preview2-final\"\n  },\n  \"scripts\": {\n    \"postpublish\": \"dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%\"\n  }\n}\n",[65,24231,24232,24236,24249,24267,24272,24284,24302,24306],{"__ignoreMap":63},[68,24233,24234],{"class":70,"line":71},[68,24235,75],{"class":74},[68,24237,24238,24240,24243,24245,24247],{"class":70,"line":78},[68,24239,82],{"class":81},[68,24241,24242],{"class":85},"tools",[68,24244,89],{"class":81},[68,24246,92],{"class":74},[68,24248,95],{"class":74},[68,24250,24251,24253,24256,24258,24260,24262,24265],{"class":70,"line":98},[68,24252,101],{"class":81},[68,24254,24255],{"class":104},"Microsoft.AspNetCore.Server.IISIntegration.Tools",[68,24257,89],{"class":81},[68,24259,92],{"class":74},[68,24261,113],{"class":112},[68,24263,24264],{"class":116},"1.0.0-preview2-final",[68,24266,120],{"class":112},[68,24268,24269],{"class":70,"line":123},[68,24270,24271],{"class":74},"  },\n",[68,24273,24274,24276,24278,24280,24282],{"class":70,"line":129},[68,24275,82],{"class":81},[68,24277,5554],{"class":85},[68,24279,89],{"class":81},[68,24281,92],{"class":74},[68,24283,95],{"class":74},[68,24285,24286,24288,24291,24293,24295,24297,24300],{"class":70,"line":212},[68,24287,101],{"class":81},[68,24289,24290],{"class":104},"postpublish",[68,24292,89],{"class":81},[68,24294,92],{"class":74},[68,24296,113],{"class":112},[68,24298,24299],{"class":116},"dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%",[68,24301,120],{"class":112},[68,24303,24304],{"class":70,"line":233},[68,24305,126],{"class":74},[68,24307,24308],{"class":70,"line":268},[68,24309,132],{"class":74},[8514,24311,24313],{"id":24312},"web-deploy-配置","Web Deploy 配置",[28,24315,24316],{},"第一步需要确保你的服务器支持 ASP.NET Core，必要的条件是：",[5010,24318,24319,24322,24325],{},[1239,24320,24321],{},"安装了 IIS 7.5+",[1239,24323,24324],{},"安装了 HttpPlatformHandler",[1239,24326,24327],{},"安装了 Web Deploy v3.6",[28,24329,24330],{},"HttpPlatformHandler 是一个新的组件，用来连接 IIS 和 ASP.NET Core 应用程序，下载链接如下：",[1236,24332,24333,24340],{},[1239,24334,24335],{},[38,24336,24339],{"href":24337,"rel":24338},"http:\u002F\u002Fgo.microsoft.com\u002Ffwlink\u002F?LinkID=690721",[42],"64 位下载地址",[1239,24341,24342],{},[38,24343,24346],{"href":24344,"rel":24345},"http:\u002F\u002Fgo.microsoft.com\u002Ffwlink\u002F?LinkId=690722",[42],"32 位下载地址",[28,24348,24349,24350,24355,24356,24361],{},"安装 HttpPlatformHandler 之前，需要先安装 Web Deploy v3.6，可以通过",[38,24351,24354],{"href":24352,"rel":24353},"https:\u002F\u002Fwww.microsoft.com\u002Fweb\u002Fdownloads\u002Fplatform.aspx",[42],"Web Platform Installer","（WebPI），或者直接从",[38,24357,24360],{"href":24358,"rel":24359},"https:\u002F\u002Fwww.microsoft.com\u002Fen-us\u002Fdownload\u002Fdetails.aspx?id=43717",[42],"下载中心","下载，不过推荐通过 WebPI 的方式下载，它提供了独立的安装并且包含了一些必要的配置。",[8514,24363,24365],{"id":24364},"在-iis-中配置站点","在 IIS 中配置站点",[5010,24367,24368,24371,24374],{},[1239,24369,24370],{},"在 IIS 管理器中新建一个网站，输入网站名、物理地址以及域名绑定的配置。",[1239,24372,24373],{},"设置应用程序池的.NET CLR 版本为无托管代码。",[1239,24375,24376],{},"右键网站->部署->启用 Web Deploy 发布...，会在桌面生成一个.PublishSettings 后缀的配置文件，复制出来，后续操作中需要导入到 Visual Studio 中。",[8514,24378,24379],{"id":24379},"配置数据保护",[28,24381,24382,24383,1686],{},"为了持久化数据保护的密钥，你必须为每个应用程序池创建注册表存储单元来存储这些密钥。需要为每个 ASP.NET Core 应用程序池执行这个 PowerShell 脚本",[38,24384,24387],{"href":24385,"rel":24386},"https:\u002F\u002Fgithub.com\u002Faspnet\u002FDataProtection\u002Fblob\u002Fdev\u002FProvision-AutoGenKeys.ps1",[42],"Provisioning PowerShell",[28,24389,24390,24391,24396],{},"For web farm scenarios developers can configure their applications to use a UNC path to store the data protection key ring. By default this does not encrypt the key ring. You can deploy an x509 certificate to each machine and use that to encrypt the keyring. See the ",[38,24392,24395],{"href":24393,"rel":24394},"https:\u002F\u002Fdocs.asp.net\u002Fen\u002Flatest\u002Fsecurity\u002Fdata-protection\u002Fconfiguration\u002Foverview.html#data-protection-configuring",[42],"configuration APIs"," for more details.",[28,24398,24399],{},"Warning: Data Protection is used by various ASP.NET middlewares, including those used in authentication. Even if you do not specifically call any Data Protection APIs from your own code you should configure Data Protection with the deployment script or in your own code. If you do not configure data protection when using IIS by default the keys will be held in memory and discarded when your application closes or restarts. This will then, for example, invalidate any cookies written by the cookie authentication and users will have to login again.",[28,24401,24402,24403,24408],{},"关于配置 IIS，可以前往",[38,24404,24407],{"href":24405,"rel":24406},"https:\u002F\u002Fdocs.asp.net\u002Fen\u002Flatest\u002Fpublishing\u002Fiis.html",[42],"Publishing to IIS","查看更多详情。下面我们来看看 Visual Studio 中的步骤。",[8514,24410,24412],{"id":24411},"通过-visual-studio-发布","通过 Visual Studio 发布",[28,24414,24415],{},"在配置好服务器之后，下一步就是在 Visual Studio 中创建一个发布配置文件。将 ASP.NET Core 应用程序发布到标准的 IIS 服务器上最简单的方法就是使用发布配置文件。如果你的服务器支持创建发布配置文件，可以下载过来，然后在 Visual Studio 发布对话框中导入进来。",[28,24417,24418],{},"如果发现使用 Web Deploy 无法部署，可能是由于数据保护没有配置好，也可以不配置，在 pubxml 中添加如下两行：",[58,24420,24422],{"className":718,"code":24421,"language":720,"meta":63,"style":63},"\u003CAllowUntrustedCertificate>True\u003C\u002FAllowUntrustedCertificate>\n\u003CUsePowerShell>False\u003C\u002FUsePowerShell>\n",[65,24423,24424,24441],{"__ignoreMap":63},[68,24425,24426,24428,24431,24433,24435,24437,24439],{"class":70,"line":71},[68,24427,727],{"class":74},[68,24429,24430],{"class":730},"AllowUntrustedCertificate",[68,24432,765],{"class":74},[68,24434,3009],{"class":373},[68,24436,771],{"class":74},[68,24438,24430],{"class":730},[68,24440,734],{"class":74},[68,24442,24443,24445,24448,24450,24452,24454,24456],{"class":70,"line":78},[68,24444,727],{"class":74},[68,24446,24447],{"class":730},"UsePowerShell",[68,24449,765],{"class":74},[68,24451,2495],{"class":373},[68,24453,771],{"class":74},[68,24455,24447],{"class":730},[68,24457,734],{"class":74},[8514,24459,18083],{"id":18083},[1236,24461,24462,24467],{},[1239,24463,24464],{},[38,24465,24405],{"href":24405,"rel":24466},[42],[1239,24468,24469],{},[38,24470,24471],{"href":24471,"rel":24472},"https:\u002F\u002Fdocs.asp.net\u002Fen\u002Flatest\u002Fpublishing\u002Fiis-with-msdeploy.html",[42],[523,24474,24475],{},"html pre.shiki code .sG8yY, html code.shiki .sG8yY{--shiki-light:#E2931D;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":63,"searchDepth":78,"depth":78,"links":24477},[],"2016-10-17",{},"\u002Fposts\u002F2016\u002Fpublishing-asp-net-core-to-iis-with-web-deploy-using-visual-studio",{"text":23360,"minutes":24482,"time":24483,"words":24484},5.425,325500,1085,{"title":23964,"description":63},{"loc":24480},"posts\u002F2016\u002F20161017.publishing-asp-net-core-to-iis-with-web-deploy-using-visual-studio",[543,23440],"3ExP9FCefv35aQIisudcgyfLdozLzz3J85Pq1SD6g4U",{"id":24491,"title":24492,"body":24493,"class":528,"cover":528,"coverSize":528,"date":24791,"description":24497,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":24792,"navigation":415,"path":24793,"readingTime":24794,"seo":24798,"sitemap":24799,"stem":24800,"tags":24801,"time":528,"weather":528,"__hash__":24802},"posts\u002Fposts\u002F2016\u002F20160731.bad-request-invalid-hostname.md","“Bad Request - Invalid Hostname” 的解决办法",{"type":25,"value":24494,"toc":24789},[24495,24498,24508,24515,24518,24546,24557,24754,24767,24780,24783,24786],[28,24496,24497],{},"最近在做一个微信端的应用，除了在本地测试之外，有时候还需要在手机上进行测试。",[28,24499,24500,24501,24504,24505,1686],{},"假设我的手机和 PC 在同一内网内，PC 的 IP 是",[65,24502,24503],{},"192.168.1.2","，Website 的端口是",[65,24506,24507],{},"12345",[28,24509,24510,24511,24514],{},"我的第一反应是，我应该在手机上通过",[65,24512,24513],{},"http:\u002F\u002F192.168.1.2:12345","来访问我的站点。",[28,24516,24517],{},"然而，我得到了这样一个错误：",[58,24519,24521],{"className":361,"code":24520,"language":363,"meta":63,"style":63},"Bad Request - Invalid Hostname\n------------------------------------------------\nHTTP Error 400. The request hostname is invalid.\n",[65,24522,24523,24528,24533],{"__ignoreMap":63},[68,24524,24525],{"class":70,"line":71},[68,24526,24527],{"class":373},"Bad Request - Invalid Hostname\n",[68,24529,24530],{"class":70,"line":78},[68,24531,24532],{"class":373},"------------------------------------------------\n",[68,24534,24535,24538,24540,24543],{"class":70,"line":98},[68,24536,24537],{"class":373},"HTTP ",[68,24539,370],{"class":116},[68,24541,24542],{"class":81}," 400",[68,24544,24545],{"class":373},". The request hostname is invalid.\n",[28,24547,24548,24549,24552,24553,24556],{},"方法很简单，Visual Studio 2015 的项目目录中会有一个",[65,24550,24551],{},".vs","的文件夹，打开",[65,24554,24555],{},".vs\\config\\applicationhost.config","，找到目标站点的配置节点，例如：",[58,24558,24560],{"className":718,"code":24559,"language":720,"meta":63,"style":63},"\u003Csite name=\"Demo.Website\" id=\"2\">\n    \u003Capplication path=\"\u002F\" applicationPool=\"Clr4IntegratedAppPool\">\n        \u003CvirtualDirectory path=\"\u002F\" physicalPath=\"D:\\Projects\\Demo\\Demo.Website\" \u002F>\n    \u003C\u002Fapplication>\n    \u003Cbindings>\n        \u003Cbinding protocol=\"http\" bindingInformation=\"*:52945:localhost\" \u002F>\n        \u003Cbinding protocol=\"http\" bindingInformation=\"*:52945:*\" \u002F> \u003C!-- 加上这一行 -->\n    \u003C\u002Fbindings>\n\u003C\u002Fsite>\n",[65,24561,24562,24593,24624,24655,24663,24672,24705,24738,24746],{"__ignoreMap":63},[68,24563,24564,24566,24569,24571,24573,24575,24578,24580,24583,24585,24587,24589,24591],{"class":70,"line":71},[68,24565,727],{"class":74},[68,24567,24568],{"class":730},"site",[68,24570,17168],{"class":873},[68,24572,877],{"class":74},[68,24574,89],{"class":112},[68,24576,24577],{"class":116},"Demo.Website",[68,24579,89],{"class":112},[68,24581,24582],{"class":873}," id",[68,24584,877],{"class":74},[68,24586,89],{"class":112},[68,24588,9366],{"class":116},[68,24590,89],{"class":112},[68,24592,734],{"class":74},[68,24594,24595,24597,24600,24602,24604,24606,24608,24610,24613,24615,24617,24620,24622],{"class":70,"line":78},[68,24596,739],{"class":74},[68,24598,24599],{"class":730},"application",[68,24601,11320],{"class":873},[68,24603,877],{"class":74},[68,24605,89],{"class":112},[68,24607,6],{"class":116},[68,24609,89],{"class":112},[68,24611,24612],{"class":873}," applicationPool",[68,24614,877],{"class":74},[68,24616,89],{"class":112},[68,24618,24619],{"class":116},"Clr4IntegratedAppPool",[68,24621,89],{"class":112},[68,24623,734],{"class":74},[68,24625,24626,24628,24631,24633,24635,24637,24639,24641,24644,24646,24648,24651,24653],{"class":70,"line":98},[68,24627,749],{"class":74},[68,24629,24630],{"class":730},"virtualDirectory",[68,24632,11320],{"class":873},[68,24634,877],{"class":74},[68,24636,89],{"class":112},[68,24638,6],{"class":116},[68,24640,89],{"class":112},[68,24642,24643],{"class":873}," physicalPath",[68,24645,877],{"class":74},[68,24647,89],{"class":112},[68,24649,24650],{"class":116},"D:\\Projects\\Demo\\Demo.Website",[68,24652,89],{"class":112},[68,24654,886],{"class":74},[68,24656,24657,24659,24661],{"class":70,"line":123},[68,24658,806],{"class":74},[68,24660,24599],{"class":730},[68,24662,734],{"class":74},[68,24664,24665,24667,24670],{"class":70,"line":129},[68,24666,739],{"class":74},[68,24668,24669],{"class":730},"bindings",[68,24671,734],{"class":74},[68,24673,24674,24676,24679,24682,24684,24686,24689,24691,24694,24696,24698,24701,24703],{"class":70,"line":212},[68,24675,749],{"class":74},[68,24677,24678],{"class":730},"binding",[68,24680,24681],{"class":873}," protocol",[68,24683,877],{"class":74},[68,24685,89],{"class":112},[68,24687,24688],{"class":116},"http",[68,24690,89],{"class":112},[68,24692,24693],{"class":873}," bindingInformation",[68,24695,877],{"class":74},[68,24697,89],{"class":112},[68,24699,24700],{"class":116},"*:52945:localhost",[68,24702,89],{"class":112},[68,24704,886],{"class":74},[68,24706,24707,24709,24711,24713,24715,24717,24719,24721,24723,24725,24727,24730,24732,24735],{"class":70,"line":233},[68,24708,749],{"class":74},[68,24710,24678],{"class":730},[68,24712,24681],{"class":873},[68,24714,877],{"class":74},[68,24716,89],{"class":112},[68,24718,24688],{"class":116},[68,24720,89],{"class":112},[68,24722,24693],{"class":873},[68,24724,877],{"class":74},[68,24726,89],{"class":112},[68,24728,24729],{"class":116},"*:52945:*",[68,24731,89],{"class":112},[68,24733,24734],{"class":74}," \u002F>",[68,24736,24737],{"class":2403}," \u003C!-- 加上这一行 -->\n",[68,24739,24740,24742,24744],{"class":70,"line":268},[68,24741,806],{"class":74},[68,24743,24669],{"class":730},[68,24745,734],{"class":74},[68,24747,24748,24750,24752],{"class":70,"line":289},[68,24749,771],{"class":74},[68,24751,24568],{"class":730},[68,24753,734],{"class":74},[28,24755,24756,24757,24760,24761,24763,24764,24766],{},"网上大部分教程基本就说了这么多，然而我在这样配置了之后依然有问题。甚至有些教程是直接把",[65,24758,24759],{},"localhost","改成了",[65,24762,7661],{},"，也可以理解。然而，我每次这么做之后，重新启动站点的时候，总是会自动重新生成一个节点，那个里面配置的是",[65,24765,24759],{},"。纠结了一下午。",[28,24768,24769,24770,24772,24773,24776,24777,24779],{},"最后发现是权限的问题，如果想配置非",[65,24771,24759],{},"的绑定，VS 必须以",[1242,24774,24775],{},"管理员权限","运行才行。这样如果只配置了非",[65,24778,24759],{},"的绑定，就不会新建了，或者配置多条绑定也可以生效了。",[28,24781,24782],{},"遇到这个问题一直解决不了的朋友可以参考下。",[28,24784,24785],{},"另外如果站点甚至无法访问的话，可以新建一条防火墙入站规则，把端口号配置进去。",[523,24787,24788],{},"html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s9AJx, html code.shiki .s9AJx{--shiki-light:#9C3EDA;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}",{"title":63,"searchDepth":78,"depth":78,"links":24790},[],"2016-07-31",{},"\u002Fposts\u002F2016\u002Fbad-request-invalid-hostname",{"text":1217,"minutes":24795,"time":24796,"words":24797},1.93,115800,386,{"title":24492,"description":24497},{"loc":24793},"posts\u002F2016\u002F20160731.bad-request-invalid-hostname",[543,23440],"5Oytl4Q6T2ls3lm_J5MjkIk3EFeaQ6vcO3BRUtaNHHA",{"id":24804,"title":24805,"body":24806,"class":528,"cover":528,"coverSize":528,"date":24960,"description":24810,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":24961,"navigation":415,"path":24962,"readingTime":24963,"seo":24967,"sitemap":24968,"stem":24969,"tags":24970,"time":528,"weather":528,"__hash__":24971},"posts\u002Fposts\u002F2016\u002F20160721.asp-net-core-first-experience.md","ASP.NET Core 初体验",{"type":25,"value":24807,"toc":24958},[24808,24811,24830,24845,24848,24866,24869,24906,24942,24955],[28,24809,24810],{},"前两天试了下 ASP.NET Core MVC，很好用。微软整合了大量前端工具，npm、Bower 都可以很方便地使用了，甚至对 Grunt、Gulp 这类的工具都有集成一些任务管理器，这对前端来说，是一件鼓舞人心的事。",[28,24812,24813,24814,24817,24818,24820,24821,24823,24824,24826,24827,24829],{},"ASP.NET Core MVC 的推荐目录结构也进行了调整，新增了",[65,24815,24816],{},"wwwroot","这样一个静态目录，js、css、图片都可以放这里面，而 Bower 管理的第三方前端库则会自动下载到",[65,24819,24816],{},"里面的 lib 目录下。作为强迫症的我，",[65,24822,24816],{},"这个目录必须全部是自动生成的。通过 Gulp，可以很轻松的实现这一点。继承原先的目录结构习惯，在解决方案下建立 Scripts、Styles、Images 文件夹，里面用来放原始的 js、less 和图片，然后通过 Gulp 进行合并、压缩、复制到",[65,24825,24816],{},"目录下，这样 wwwroot 这个目录就可以在 git 里面排除掉了。完美。而在 ASP.NET Core 的项目目录下默认的",[65,24828,10633],{},"文件里，微软其实是已经有这样的想法：",[58,24831,24833],{"className":361,"code":24832,"language":363,"meta":63,"style":63},"# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot\u002F\n",[65,24834,24835,24840],{"__ignoreMap":63},[68,24836,24837],{"class":70,"line":71},[68,24838,24839],{"class":373},"# Uncomment if you have tasks that create the project's static files in wwwroot\n",[68,24841,24842],{"class":70,"line":78},[68,24843,24844],{"class":373},"#wwwroot\u002F\n",[28,24846,24847],{},"在代码层面，和之前差别不大，基本上 M、V、C 的代码都可以直接拿过来用。在 bundle 上有一些变化，然而我是直接删掉了默认的 bundle 配置，既然可以方便地使用 Gulp 了，为啥不用呢？",[28,24849,24850,24851,24854,24855,24857,24858,24861,24862,24865],{},"在 View 中，新增了",[65,24852,24853],{},"environment","的语法，可以通过",[65,24856,24853],{},"标签来控制开发环境和生产环境的不同输出，主要是用来控制 css、js 这些文件的引用，在开发环境下使用未压缩的文件，在生产环境下使用压缩过的文件。还提供了 cdn 的方式，可以配置多个链接，优先使用 CDN 的链接，通过",[65,24859,24860],{},"asp-fallback-test","来检查 CDN 的链接是否可用，不可用的话再切换为本地的链接。和之前 Bundle 里面的方式差不多，只是使用起来更简单了。现在 css、js 这些文件的缓存控制也比以前更简单，只需要加上",[65,24863,24864],{},"asp-append-version=\"true\"","即可在文件名后面自动加上版本号后缀。",[28,24867,24868],{},"项目部署上面，确实遇到了一个坑。我是通过 Web Deploy 来自动部署的，花了半天时间才终于搞定。",[5010,24870,24871,24878,24898,24903],{},[1239,24872,24873,24874,24877],{},"IIS 里应用程序池中的",[65,24875,24876],{},".NET CLR","版本要选择“无托管代码”",[1239,24879,23993,24880,24882,24883,24886,24887,24890,24891,24894,24895,24897],{},[65,24881,23998],{},"，这里有个大坑，安装完之后要执行一下",[65,24884,24885],{},"iisreset","，我没有执行这一步，导致出现了",[65,24888,24889],{},"HTTP Error 502.5 - Process Failure","的问题，从事件查看器里面看到的错误日志是：",[65,24892,24893],{},"Failed to start process with commandline '\"dotnet\" .\\****.dll', ErrorCode = '0x80070002'.","。遇到同样问题的朋友可以试试",[65,24896,24885],{},"或者重启机器。",[1239,24899,23993,24900,1686],{},[65,24901,24902],{},"HttpPlatformHandler",[1239,24904,24905],{},"还有个就是我当时用 Web Delpoy 往服务器部署的时候，文件总是推不上去，后来 Google 了一下，在 pubxml 里面加上了以下两行：",[58,24907,24908],{"className":718,"code":24421,"language":720,"meta":63,"style":63},[65,24909,24910,24926],{"__ignoreMap":63},[68,24911,24912,24914,24916,24918,24920,24922,24924],{"class":70,"line":71},[68,24913,727],{"class":74},[68,24915,24430],{"class":730},[68,24917,765],{"class":74},[68,24919,3009],{"class":373},[68,24921,771],{"class":74},[68,24923,24430],{"class":730},[68,24925,734],{"class":74},[68,24927,24928,24930,24932,24934,24936,24938,24940],{"class":70,"line":78},[68,24929,727],{"class":74},[68,24931,24447],{"class":730},[68,24933,765],{"class":74},[68,24935,2495],{"class":373},[68,24937,771],{"class":74},[68,24939,24447],{"class":730},[68,24941,734],{"class":74},[5010,24943,24944],{"start":129},[1239,24945,24946,24947,24951,24952,24954],{},"下载",[38,24948,24950],{"href":24385,"rel":24949,"title":24950},[42],"Provisioning PowerShell script","，在服务器上使用",[65,24953,21305],{},"运行，输入应用程序池的名称即可。",[523,24956,24957],{},"html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":63,"searchDepth":78,"depth":78,"links":24959},[],"2016-07-21",{},"\u002Fposts\u002F2016\u002Fasp-net-core-first-experience",{"text":8419,"minutes":24964,"time":24965,"words":24966},3.695,221700,739,{"title":24805,"description":24810},{"loc":24962},"posts\u002F2016\u002F20160721.asp-net-core-first-experience",[543,23440],"R33Y8oAf52Ae4S4ZF29eR7LlxZpXApb2LypHiBQIWSw",{"id":24973,"title":24974,"body":24975,"class":528,"cover":528,"coverSize":528,"date":24982,"description":24979,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":24983,"navigation":415,"path":24984,"readingTime":24985,"seo":24988,"sitemap":24989,"stem":24990,"tags":24991,"time":528,"weather":528,"__hash__":24992},"posts\u002Fposts\u002F2016\u002F20160719.hexo-with-travis-ci.md","使用 Travis CI 自动部署 Hexo",{"type":25,"value":24976,"toc":24980},[24977],[28,24978,24979],{},"先占个坑，有空写教程。",{"title":63,"searchDepth":78,"depth":78,"links":24981},[],"2016-07-19",{},"\u002Fposts\u002F2016\u002Fhexo-with-travis-ci",{"text":7523,"minutes":24986,"time":24987,"words":289},0.045,2700,{"title":24974,"description":24979},{"loc":24984},"posts\u002F2016\u002F20160719.hexo-with-travis-ci",[543,10057],"G9idRED7gYRHZtG067jMPWNpFNP49qC-5XxGnVQhGwg",{"id":24994,"title":24995,"body":24996,"class":528,"cover":528,"coverSize":528,"date":25058,"description":63,"draft":530,"extension":531,"hideComments":530,"location":25059,"meta":25060,"navigation":415,"path":25061,"readingTime":25062,"seo":25066,"sitemap":25067,"stem":25068,"tags":25069,"time":25070,"weather":528,"__hash__":25071},"posts\u002Fposts\u002F2015\u002F20150820.change-domain-in-weixin.md","微信公众号中更换域名",{"type":25,"value":24997,"toc":25056},[24998,25001,25009,25011,25014,25017,25020,25023,25030,25032,25054],[8514,24999,25000],{"id":25000},"更新",[28,25002,25003,25004],{},"如果需要实现微信授权支持多个回调域名，可以参考我这个开源项目：",[38,25005,25008],{"href":25006,"rel":25007},"https:\u002F\u002Fgithub.com\u002FHADB\u002FGetWeixinCode",[42],"GetWeixinCode",[4947,25010],{},[8514,25012,25013],{"id":25013},"问题描述",[28,25015,25016],{},"项目刚做的时候，并没有找到好的域名，所以用了一个比较长的域名。后来公司花钱买了一个心仪的域名，理所当然，我们需要启用新域名了。",[28,25018,25019],{},"我们的 H5 站点是基于微信的，由于微信的各种坑，这里有很多值得注意的地方。",[28,25021,25022],{},"首先，需要在公众号设置中，将新域名加入到业务域名以及 JS 接口安全域名中，在微信支付的开发配置中，也要将新域名加入支付授权目录中。这几个比较容易，因为他们都支持配置多个域名。",[28,25024,25025,25026,25029],{},"我们的页面加载之后会立即通过静默授权跳转去拿用户的 code 以换取 openid，来实现自动登录，为了减少跳转，我们在微信公众号的自定义菜单中配置的链接就是微信的授权链接\n",[65,25027,25028],{},"https:\u002F\u002Fopen.weixin.qq.com\u002Fconnect\u002Foauth2\u002Fauthorize?XXXXX&redirect_uri=h.xxx-old.com","\n这里比较坑的是，授权回调页面域名只能配置一个，而且自定义菜单的修改最慢要 24 小时才能生效，而且你没法确定是什么时候生效，有的用户会生效，有的用户仍是旧的链接。所以这里的链接不能冒然改成新链接。不过我们可以这么实现。",[8514,25031,1483],{"id":1483},[5010,25033,25034,25041,25048],{},[1239,25035,25036,25037,25040],{},"修改公众号自定义菜单中配置的链接，直接改为原域名",[65,25038,25039],{},"http:\u002F\u002Fh.xxx-old.com","，在页面代码中做判断，如果没有拿到 code 参数，就主动跳转到微信的授权页面去拿 code（这个原来就做了，为了让用户直接访问域名的时候也能拿到 code）。这个生效可能需要 24 小时，稳妥的做法就是等 24 小时之后再进行后面的操作。",[1239,25042,25043,25044,25047],{},"将代码中跳转到微信授权页面的 redirect_uri 改为新域名：\n",[65,25045,25046],{},"https:\u002F\u002Fopen.weixin.qq.com\u002Fconnect\u002Foauth2\u002Fauthorize?XXXXX&redirect_uri=h.xxx-new.com","\n同时将微信公众号中的授权回调页面域名改为新域名（这个是立即生效的）。这时，无论是从旧域名访问还是从新域名访问，授权回调的时候，都会成功跳转到新域名，并且带上 code 了。",[1239,25049,25050,25051,25053],{},"修改公众号中的链接，改为微信授权链接，并且 redirect_uri 写成新域名：\n",[65,25052,25046],{},"\n这个时候，无论是更新后的链接还是尚未更新的链接，都能成功授权，只是直接用域名会多跳转一下而已。",[28,25055,18261],{},{"title":63,"searchDepth":78,"depth":78,"links":25057},[],"2015-08-20","莘庄",{},"\u002Fposts\u002F2015\u002Fchange-domain-in-weixin",{"text":8419,"minutes":25063,"time":25064,"words":25065},3.27,196200,654,{"title":24995,"description":63},{"loc":25061},"posts\u002F2015\u002F20150820.change-domain-in-weixin",[543,8427],"上午","r8Hm16aVzTZvxFywiwcPdbBnSlvUwb2dxVuKq1sK1x0",{"id":25073,"title":25074,"body":25075,"class":528,"cover":528,"coverSize":528,"date":25118,"description":63,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":25119,"navigation":415,"path":25120,"readingTime":25121,"seo":25125,"sitemap":25126,"stem":25127,"tags":25128,"time":528,"weather":528,"__hash__":25129},"posts\u002Fposts\u002F2015\u002F20150730.vs2015-installer-not-work-in-windows-xp.md","VS2015 打包程序无法在 XP 下安装的问题",{"type":25,"value":25076,"toc":25116},[25077,25079,25088,25091,25105,25108],[8514,25078,25013],{"id":25013},[28,25080,25081,25082,25087],{},"最近有个需求，需要做一个 WinForm 程序，目标机器基本都是比较旧的 XP 机器。需要安装.net Framework 环境以及添加快捷方式等，所以决定做一个安装程序。VS 默认的是推荐使用 InstallShield Limited Edition，经过尝试，发现实在不好用，而且 Limited 版还有不少限制。于是想用以前 VS 版本中的 Installer Project。寻找了一下，发现有 2015 版的插件（",[38,25083,25086],{"href":25084,"rel":25085,"title":25086},"https:\u002F\u002Fvisualstudiogallery.msdn.microsoft.com\u002F003f3135-bbca-4eb2-951d-88820065a124",[42],"Microsoft Visual Studio 2015 Installer Projects","）。其他都很顺利，在 Win7、Win8.1、Win10 中安装都没有问题。唯独当我不远万里来到目标机器的时候，发现在 XP 系统上安装失败！安装程序莫名退出。纠结了几天，最终在网上搜到了解决方案。原因是这样，在 VS2010 之后的 VS 中，dpca.dll 这个文件中最低的 Windows 版本已经不支持 XP 了，导致在用 2010 以上的 VS 版本打包的安装包在 XP 上总是失败的。",[8514,25089,25090],{"id":25090},"解决方法",[5010,25092,25093,25096,25099,25102],{},[1239,25094,25095],{},"关闭 Visual Studio",[1239,25097,25098],{},"从 C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\Tools\\Deployment 中复制 dpca.dll 文件到 C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\Common7\\IDE\\CommonExtensions\\Microsoft\\VSI\\bin 中替换。（需要找仍在使用 VS2010 的小伙伴）",[1239,25100,25101],{},"打开项目",[1239,25103,25104],{},"重新编译",[8514,25106,25107],{"id":25107},"相关文章",[1236,25109,25110],{},[1239,25111,25112],{},[38,25113,25114],{"href":25114,"rel":25115},"http:\u002F\u002Fstackoverflow.com\u002Fquestions\u002F23978677\u002Fdirca-checkfx-return-value-3-vs-2013-deployment-project\u002F26039835#26039835",[42],{"title":63,"searchDepth":78,"depth":78,"links":25117},[],"2015-07-30",{},"\u002Fposts\u002F2015\u002Fvs2015-installer-not-work-in-windows-xp",{"text":1217,"minutes":25122,"time":25123,"words":25124},1.535,92100,307,{"title":25074,"description":63},{"loc":25120},"posts\u002F2015\u002F20150730.vs2015-installer-not-work-in-windows-xp",[543],"Ob4Rr7jHjaNgJr3C-lx-SBaU2vyctrv4qRoVaWyKZf4",{"id":25131,"title":25132,"body":25133,"class":528,"cover":528,"coverSize":528,"date":25377,"description":25137,"draft":530,"extension":531,"hideComments":530,"location":25378,"meta":25379,"navigation":415,"path":25380,"readingTime":25381,"seo":25385,"sitemap":25386,"stem":25387,"tags":25388,"time":528,"weather":528,"__hash__":25389},"posts\u002Fposts\u002F2015\u002F20150516.web-deploy.md","Web Deploy 进阶",{"type":25,"value":25134,"toc":25375},[25135,25138,25142,25145,25148,25334,25338,25341,25344,25347,25369,25372],[28,25136,25137],{},"之前想写个 Web Deploy 的教程，结果一直耽搁了。最近又遇到一个比较高级的用法，打算暂时就不写基础教程了，把几个不常用的但是很有用的用法列一下。",[8514,25139,25141],{"id":25140},"_1部署的时候排除某些文件或文件夹","1、部署的时候排除某些文件或文件夹",[28,25143,25144],{},"这个功能其实很有用，比如一些自定义的 config，里面包含了一些 key 或者很重要的信息，而你的代码是开源的，你希望分享源代码，但是这些服务器相关的 key 还是不能暴露的，这时候，你本地的 config 里可能只是一些测试的 key，或者压根就可以是空的。在服务器上的 config 里，你可以放心大胆地配置这些 key。每次部署的时候，我们就希望跳过这些 config 文件，而不至于用本地的 config 去替换掉线上的。",[28,25146,25147],{},"我们需要修改项目的配置文件，也就是.csproj 文件，注意修改 Release 的，如果你发布选项中配置的是 Release 的话。",[58,25149,25151],{"className":718,"code":25150,"language":720,"meta":63,"style":63},"\u003CPropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    \u003CDebugType>pdbonly\u003C\u002FDebugType>\n    \u003COptimize>true\u003C\u002FOptimize>\n    \u003COutputPath>bin\\\u003C\u002FOutputPath>\n    \u003CDefineConstants>TRACE\u003C\u002FDefineConstants>\n    \u003CErrorReport>prompt\u003C\u002FErrorReport>\n    \u003CWarningLevel>4\u003C\u002FWarningLevel>\n    \u003C!-- 下面这一行用来排除指定文件夹，分号分割多个文件夹 -->\n    \u003CExcludeFoldersFromDeployment>Configurations\u003C\u002FExcludeFoldersFromDeployment>\n    \u003C!-- 下面这一行用来排除指定文件，分号来分隔多个文件 -->\n    \u003CExcludeFilesFromDeployment>XXX.config;YYY.config\u003C\u002FExcludeFilesFromDeployment>\n\u003C\u002FPropertyGroup>\n",[65,25152,25153,25174,25192,25209,25227,25245,25263,25280,25285,25303,25308,25326],{"__ignoreMap":63},[68,25154,25155,25157,25160,25163,25165,25167,25170,25172],{"class":70,"line":71},[68,25156,727],{"class":74},[68,25158,25159],{"class":730},"PropertyGroup",[68,25161,25162],{"class":873}," Condition",[68,25164,877],{"class":74},[68,25166,89],{"class":112},[68,25168,25169],{"class":116}," '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ",[68,25171,89],{"class":112},[68,25173,734],{"class":74},[68,25175,25176,25178,25181,25183,25186,25188,25190],{"class":70,"line":78},[68,25177,739],{"class":74},[68,25179,25180],{"class":730},"DebugType",[68,25182,765],{"class":74},[68,25184,25185],{"class":373},"pdbonly",[68,25187,771],{"class":74},[68,25189,25180],{"class":730},[68,25191,734],{"class":74},[68,25193,25194,25196,25199,25201,25203,25205,25207],{"class":70,"line":98},[68,25195,739],{"class":74},[68,25197,25198],{"class":730},"Optimize",[68,25200,765],{"class":74},[68,25202,849],{"class":373},[68,25204,771],{"class":74},[68,25206,25198],{"class":730},[68,25208,734],{"class":74},[68,25210,25211,25213,25216,25218,25221,25223,25225],{"class":70,"line":123},[68,25212,739],{"class":74},[68,25214,25215],{"class":730},"OutputPath",[68,25217,765],{"class":74},[68,25219,25220],{"class":373},"bin\\",[68,25222,771],{"class":74},[68,25224,25215],{"class":730},[68,25226,734],{"class":74},[68,25228,25229,25231,25234,25236,25239,25241,25243],{"class":70,"line":129},[68,25230,739],{"class":74},[68,25232,25233],{"class":730},"DefineConstants",[68,25235,765],{"class":74},[68,25237,25238],{"class":373},"TRACE",[68,25240,771],{"class":74},[68,25242,25233],{"class":730},[68,25244,734],{"class":74},[68,25246,25247,25249,25252,25254,25257,25259,25261],{"class":70,"line":212},[68,25248,739],{"class":74},[68,25250,25251],{"class":730},"ErrorReport",[68,25253,765],{"class":74},[68,25255,25256],{"class":373},"prompt",[68,25258,771],{"class":74},[68,25260,25251],{"class":730},[68,25262,734],{"class":74},[68,25264,25265,25267,25270,25272,25274,25276,25278],{"class":70,"line":233},[68,25266,739],{"class":74},[68,25268,25269],{"class":730},"WarningLevel",[68,25271,765],{"class":74},[68,25273,13563],{"class":373},[68,25275,771],{"class":74},[68,25277,25269],{"class":730},[68,25279,734],{"class":74},[68,25281,25282],{"class":70,"line":268},[68,25283,25284],{"class":2403},"    \u003C!-- 下面这一行用来排除指定文件夹，分号分割多个文件夹 -->\n",[68,25286,25287,25289,25292,25294,25297,25299,25301],{"class":70,"line":289},[68,25288,739],{"class":74},[68,25290,25291],{"class":730},"ExcludeFoldersFromDeployment",[68,25293,765],{"class":74},[68,25295,25296],{"class":373},"Configurations",[68,25298,771],{"class":74},[68,25300,25291],{"class":730},[68,25302,734],{"class":74},[68,25304,25305],{"class":70,"line":308},[68,25306,25307],{"class":2403},"    \u003C!-- 下面这一行用来排除指定文件，分号来分隔多个文件 -->\n",[68,25309,25310,25312,25315,25317,25320,25322,25324],{"class":70,"line":314},[68,25311,739],{"class":74},[68,25313,25314],{"class":730},"ExcludeFilesFromDeployment",[68,25316,765],{"class":74},[68,25318,25319],{"class":373},"XXX.config;YYY.config",[68,25321,771],{"class":74},[68,25323,25314],{"class":730},[68,25325,734],{"class":74},[68,25327,25328,25330,25332],{"class":70,"line":320},[68,25329,771],{"class":74},[68,25331,25159],{"class":730},[68,25333,734],{"class":74},[8514,25335,25337],{"id":25336},"_2通过文件校验而不是修改时间来决定某个文件是否需要发布","2、通过文件校验而不是修改时间来决定某个文件是否需要发布",[28,25339,25340],{},"这个功能同样很有用，尤其当你通过源代码管理的时候，你在不同的电脑上，虽然代码相同，但其实每个文件的修改时间并不同。这会导致你在这台电脑上部署了之后，在另一台电脑上修改了部分内容，却还是需要部署所有文件。当网络不给力的时候，部署需要很长时间。",[28,25342,25343],{},"我们需要修改部署配置文件，一般是 Properties\\PublishFiles\\XXX.pubxml。",[28,25345,25346],{},"我们在 PropertyGroup 里添加这么一行就可以了",[58,25348,25350],{"className":718,"code":25349,"language":720,"meta":63,"style":63},"\u003CMSDeployUseChecksum>true\u003C\u002FMSDeployUseChecksum>\n",[65,25351,25352],{"__ignoreMap":63},[68,25353,25354,25356,25359,25361,25363,25365,25367],{"class":70,"line":71},[68,25355,727],{"class":74},[68,25357,25358],{"class":730},"MSDeployUseChecksum",[68,25360,765],{"class":74},[68,25362,849],{"class":373},[68,25364,771],{"class":74},[68,25366,25358],{"class":730},[68,25368,734],{"class":74},[28,25370,25371],{},"先写这两个。之后有空再写个详细的教程。一开始配置 Web Deploy 还是有不少注意点的。",[523,25373,25374],{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s9AJx, html code.shiki .s9AJx{--shiki-light:#9C3EDA;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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":63,"searchDepth":78,"depth":78,"links":25376},[],"2015-05-16","Newegg",{},"\u002Fposts\u002F2015\u002Fweb-deploy",{"text":535,"minutes":25382,"time":25383,"words":25384},2.67,160200,534,{"title":25132,"description":25137},{"loc":25380},"posts\u002F2015\u002F20150516.web-deploy",[543],"2jUSREuWsKeGgy1Q_Pm499_KB5FkuLSK5o5h2wYnQvw",{"id":25391,"title":25392,"body":25393,"class":528,"cover":528,"coverSize":528,"date":25683,"description":25397,"draft":530,"extension":531,"hideComments":530,"location":25684,"meta":25685,"navigation":415,"path":25686,"readingTime":25687,"seo":25691,"sitemap":25692,"stem":25693,"tags":25694,"time":22304,"weather":528,"__hash__":25695},"posts\u002Fposts\u002F2015\u002F20150413.fix-fixed-bug-in-ios-when-call-virtual-keyboards.md","修复 position:fixed 在 ios 虚拟键盘弹出时错位的 bug",{"type":25,"value":25394,"toc":25681},[25395,25398,25406,25415,25418,25421,25424,25427,25430,25463,25466,25672,25675,25678],[28,25396,25397],{},"问题描述：在使用 bootstrap 的 navbar-fixed-top 时，发现在 iPhone 上的微信里面，当点击 input 弹出输入法之后，顶部 fixed 的 navbar 消失，在输入法没有关闭的情况下，向上滚动，会发现 navbar 在半空中。",[28,25399,25400,25401,338],{},"Google 了一下，发现这个问题在 iOS 中很常见，Bootstrap 也对此进行了说明（",[38,25402,25405],{"href":25403,"rel":25404},"http:\u002F\u002Fgetbootstrap.com\u002Fgetting-started\u002F#support-fixed-position-keyboards",[42],"戳这里",[7538,25407,25408,25412],{},[8514,25409,25411],{"id":25410},"virtual-keyboards","Virtual keyboards",[28,25413,25414],{},"Also, note that if you're using a fixed navbar or using inputs within a modal, iOS has a rendering bug that doesn't update the position of fixed elements when the virtual keyboard is triggered. A few workarounds for this include transforming your elements to position: absolute or invoking a timer on focus to try to correct the positioning manually. This is not handled by Bootstrap, so it is up to you to decide which solution is best for your application.",[28,25416,25417],{},"不过我在最近刚更新的 iOS8.3 的 Safari 中，没有发现这个问题。在 8.3 的 Safari 中，点击 input 弹出输入法之后，fixed 元素会失效，navbar 回到最顶端，没有浮在半空中。猜测是在弹出虚拟键盘之后为了节省页面空间，而对 fixed 的元素进行了处理，但是在微信中的浏览器上处理出了点问题。",[28,25419,25420],{},"目前发现最好的解决方案是在点击 input 之后，直接把 fixed 的元素变成 absolute 的，不需要浏览器自己去做处理。",[28,25422,25423],{},"有人说在滚动时用 timer 实时调整元素位置，我觉得这个很低端。浏览器去处理 fixed 元素自然有它的道理，确实可以节省屏幕空间。我们其实也没有必要在这个情况下强制显示 navbar，这时用户的重点在于输入。当我们 input 失去焦点之后，输入法关闭，这时我们再显示出 navbar。",[28,25425,25426],{},"下面直接上代码：",[28,25428,25429],{},"添加这样一段 css：",[58,25431,25433],{"className":20200,"code":25432,"language":20202,"meta":63,"style":63},".fixfixed.navbar-fixed-top {\n  position: absolute;\n}\n",[65,25434,25435,25449,25459],{"__ignoreMap":63},[68,25436,25437,25439,25442,25444,25447],{"class":70,"line":71},[68,25438,404],{"class":20247},[68,25440,25441],{"class":1511},"fixfixed",[68,25443,404],{"class":20247},[68,25445,25446],{"class":1511},"navbar-fixed-top",[68,25448,95],{"class":74},[68,25450,25451,25453,25455,25457],{"class":70,"line":78},[68,25452,20315],{"class":20216},[68,25454,92],{"class":74},[68,25456,20320],{"class":665},[68,25458,5136],{"class":74},[68,25460,25461],{"class":70,"line":98},[68,25462,132],{"class":74},[28,25464,25465],{},"添加这样一段 js：",[58,25467,25471],{"className":25468,"code":25469,"language":25470,"meta":63,"style":63},"language-js shiki shiki-themes material-theme-lighter github-light github-dark","$(() => {\n  if (Modernizr.touch) {\n    $(document).on('focus', 'input', () => {\n      $('.navbar-fixed-top').addClass('fixfixed')\n    })\n\n    $(document).on('blur', 'input', () => {\n      $('.navbar-fixed-top').removeClass('fixfixed')\n    })\n  }\n})\n","js",[65,25472,25473,25485,25504,25547,25578,25584,25588,25627,25656,25662,25666],{"__ignoreMap":63},[68,25474,25475,25477,25479,25481,25483],{"class":70,"line":71},[68,25476,5147],{"class":2183},[68,25478,648],{"class":373},[68,25480,3345],{"class":74},[68,25482,7765],{"class":1864},[68,25484,95],{"class":74},[68,25486,25487,25490,25492,25495,25497,25500,25502],{"class":70,"line":78},[68,25488,25489],{"class":1790},"  if",[68,25491,7664],{"class":2122},[68,25493,25494],{"class":373},"Modernizr",[68,25496,404],{"class":74},[68,25498,25499],{"class":373},"touch",[68,25501,588],{"class":2122},[68,25503,75],{"class":74},[68,25505,25506,25509,25511,25513,25515,25517,25520,25522,25524,25527,25529,25531,25533,25536,25538,25540,25543,25545],{"class":70,"line":98},[68,25507,25508],{"class":2183},"    $",[68,25510,648],{"class":2122},[68,25512,22002],{"class":373},[68,25514,2753],{"class":2122},[68,25516,404],{"class":74},[68,25518,25519],{"class":2183},"on",[68,25521,648],{"class":2122},[68,25523,8129],{"class":112},[68,25525,25526],{"class":116},"focus",[68,25528,8129],{"class":112},[68,25530,255],{"class":74},[68,25532,1620],{"class":112},[68,25534,25535],{"class":116},"input",[68,25537,8129],{"class":112},[68,25539,255],{"class":74},[68,25541,25542],{"class":74}," ()",[68,25544,7765],{"class":1864},[68,25546,95],{"class":74},[68,25548,25549,25552,25554,25556,25559,25561,25563,25565,25568,25570,25572,25574,25576],{"class":70,"line":123},[68,25550,25551],{"class":2183},"      $",[68,25553,648],{"class":2122},[68,25555,8129],{"class":112},[68,25557,25558],{"class":116},".navbar-fixed-top",[68,25560,8129],{"class":112},[68,25562,2753],{"class":2122},[68,25564,404],{"class":74},[68,25566,25567],{"class":2183},"addClass",[68,25569,648],{"class":2122},[68,25571,8129],{"class":112},[68,25573,25441],{"class":116},[68,25575,8129],{"class":112},[68,25577,410],{"class":2122},[68,25579,25580,25582],{"class":70,"line":129},[68,25581,8370],{"class":74},[68,25583,410],{"class":2122},[68,25585,25586],{"class":70,"line":212},[68,25587,416],{"emptyLinePlaceholder":415},[68,25589,25590,25592,25594,25596,25598,25600,25602,25604,25606,25609,25611,25613,25615,25617,25619,25621,25623,25625],{"class":70,"line":233},[68,25591,25508],{"class":2183},[68,25593,648],{"class":2122},[68,25595,22002],{"class":373},[68,25597,2753],{"class":2122},[68,25599,404],{"class":74},[68,25601,25519],{"class":2183},[68,25603,648],{"class":2122},[68,25605,8129],{"class":112},[68,25607,25608],{"class":116},"blur",[68,25610,8129],{"class":112},[68,25612,255],{"class":74},[68,25614,1620],{"class":112},[68,25616,25535],{"class":116},[68,25618,8129],{"class":112},[68,25620,255],{"class":74},[68,25622,25542],{"class":74},[68,25624,7765],{"class":1864},[68,25626,95],{"class":74},[68,25628,25629,25631,25633,25635,25637,25639,25641,25643,25646,25648,25650,25652,25654],{"class":70,"line":268},[68,25630,25551],{"class":2183},[68,25632,648],{"class":2122},[68,25634,8129],{"class":112},[68,25636,25558],{"class":116},[68,25638,8129],{"class":112},[68,25640,2753],{"class":2122},[68,25642,404],{"class":74},[68,25644,25645],{"class":2183},"removeClass",[68,25647,648],{"class":2122},[68,25649,8129],{"class":112},[68,25651,25441],{"class":116},[68,25653,8129],{"class":112},[68,25655,410],{"class":2122},[68,25657,25658,25660],{"class":70,"line":289},[68,25659,8370],{"class":74},[68,25661,410],{"class":2122},[68,25663,25664],{"class":70,"line":308},[68,25665,126],{"class":74},[68,25667,25668,25670],{"class":70,"line":314},[68,25669,2400],{"class":74},[68,25671,410],{"class":373},[28,25673,25674],{},"使用了 Modernizr，仅在触屏上进行处理，对桌面浏览器不做处理，这样对于桌面浏览器上的体验更好。",[28,25676,25677],{},"完美解决问题！",[523,25679,25680],{},"html pre.shiki code .stp6e, html code.shiki .stp6e{--shiki-light:#39ADB5;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .soE4H, html code.shiki .soE4H{--shiki-light:#8796B0;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":63,"searchDepth":78,"depth":78,"links":25682},[],"2015-04-13","家中",{},"\u002Fposts\u002F2015\u002Ffix-fixed-bug-in-ios-when-call-virtual-keyboards",{"text":535,"minutes":25688,"time":25689,"words":25690},2.57,154200,514,{"title":25392,"description":25397},{"loc":25686},"posts\u002F2015\u002F20150413.fix-fixed-bug-in-ios-when-call-virtual-keyboards",[543,8427],"IpoJwNty1t4VbyLpxIQGlO4O7-oswPS7EG0WKTuTxds",{"id":25697,"title":25698,"body":25699,"class":528,"cover":528,"coverSize":528,"date":25742,"description":25743,"draft":530,"extension":531,"hideComments":530,"location":25744,"meta":25745,"navigation":415,"path":25746,"readingTime":25747,"seo":25751,"sitemap":25752,"stem":25753,"tags":25754,"time":528,"weather":528,"__hash__":25755},"posts\u002Fposts\u002F2015\u002F20150323.authenticationmanager-signout-not-working.md","AuthenticationManager 无法注销用户的问题",{"type":25,"value":25700,"toc":25740},[25701,25708,25722,25725,25728,25734],[28,25702,25703,25704,25707],{},"最近遇到一个很诡异的问题，在最近的一个新项目中，发现在 MVC5 下，偶尔会出现登陆的用户无法注销的问题，经检查发现",[65,25705,25706],{},"AuthenticationManager.SignOut()","执行之后并没有删除 Cookie，手动删除 Cookie 之后，该功能又正常了，又能正常登陆、注销了。前面几次出现这个问题我都是手动删除 Cookie，发现恢复了之后，我也就没在意。",[28,25709,25710,25711,25714,25715,25717,25718,1686],{},"刚刚又出现这个问题，我怒了，决定 Google 下。Google 了一番之后，发现这个问题还挺普遍，但是都没有什么好的答案。后来看到有人说，用",[65,25712,25713],{},"AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie)","可以解决这个问题，一试，还真解决了。可是官方的例子里并没有传这个参数，而且我以前的几个站点，都是用的官方例子里的",[65,25716,25706],{},"，而且都没有出现这个问题。百思不得其解，好挫折。不弄清楚睡不着觉。于是又继续搜，终于发现了一个帖子，",[38,25719,25720],{"href":25720,"rel":25721},"https:\u002F\u002Faspnetidentity.codeplex.com\u002Fworkitem\u002F2347",[42],[28,25723,25724],{},"当初这个问题应该是发生在 Microsoft.AspNet.Identity 2.0\u002F2.1 rc + Microsoft.Owin.3.0 rc 版中，我以前项目用的 Identity2.2 + Owin 3.0 中，应该是没有这个问题。最近刚更新的 Identity2.3 + Owin 3.0.1 中，又有了这个问题。",[28,25726,25727],{},"哈哈！突然感觉我已经走在 MVC 的前沿了。记下这个问题，为今后遇到这个问题的朋友们排忧解难！",[28,25729,25730,25731,25733],{},"解决方案就是，在 LogOff 方法里，用",[65,25732,25713],{},"就行了。",[28,25735,25736,25737,1686],{},"当然，我这个项目并没有用到第三方登陆，如果用到了第三方登陆，可能还需要添加",[65,25738,25739],{},"AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);",{"title":63,"searchDepth":78,"depth":78,"links":25741},[],"2015-03-23","最近遇到一个很诡异的问题，在最近的一个新项目中，发现在 MVC5 下，偶尔会出现登陆的用户无法注销的问题，经检查发现AuthenticationManager.SignOut()执行之后并没有删除 Cookie，手动删除 Cookie 之后，该功能又正常了，又能正常登陆、注销了。前面几次出现这个问题我都是手动删除 Cookie，发现恢复了之后，我也就没在意。","Home",{},"\u002Fposts\u002F2015\u002Fauthenticationmanager-signout-not-working",{"text":535,"minutes":25748,"time":25749,"words":25750},2.03,121800,406,{"title":25698,"description":25743},{"loc":25746},"posts\u002F2015\u002F20150323.authenticationmanager-signout-not-working",[543,23440],"wtzFl_TJcolfsXAqKAiPNQyzXqyzmEkZ4wHoeuvjm4E",{"id":25757,"title":25758,"body":25759,"class":528,"cover":528,"coverSize":528,"date":26560,"description":25763,"draft":530,"extension":531,"hideComments":530,"location":25378,"meta":26561,"navigation":415,"path":26562,"readingTime":26563,"seo":26567,"sitemap":26568,"stem":26569,"tags":26570,"time":528,"weather":528,"__hash__":26571},"posts\u002Fposts\u002F2015\u002F20150317.entity-framework-code-first-two-foreign-keys-from-same-table.md","Entity Framework Code First 两个字段关联到同一张表",{"type":25,"value":25760,"toc":26558},[25761,25764,25767,25770,25773,26181,26367,26370,26548,26555],[28,25762,25763],{},"之前也遇到过类似的问题，属于 Code First 中稍微复杂点的关系处理，现将解决方法记录下来。",[8514,25765,25766],{"id":25766},"场景",[28,25768,25769],{},"某网上书城欲推出书券功能，书券购买之后，会有一个唯一的 Id，可用来直接兑换某本书。书券可以自己兑换，也可以将 ID 送给朋友来兑换。现在我们需要将书券的购买者和兑换者都记录下来。",[8514,25771,25772],{"id":25772},"类的设计和注意事项",[58,25774,25776],{"className":24038,"code":25775,"language":24040,"meta":63,"style":63},"public class BookCoupon\n{\n    [Key]\n    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]\n    [Index(\"BookCouponIdIndex\")]\n    public Guid BookCouponId { get; set; }\n\n    [Index(\"BookIdIndex\")]\n    public int BookId { get; set; }\n\n    [ForeignKey(\"BookId\")]\n    public virtual Book Book { get; set; }\n\n    [Index(\"BuyUserIdIndex\")]\n    public string BuyUserId { get; set; }\n\n    [ForeignKey(\"BuyUserId\")]\n    public virtual User BuyUser { get; set; }\n\n    [Index(\"RedeemUserIdIndex\")]\n    public string RedeemUserId { get; set; }\n\n    [ForeignKey(\"RedeemUserId\")]\n    public virtual User RedeemUser { get; set; }\n\n    public DateTime BuyTime { get; set; }\n\n    public DateTime RedeemTime { get; set; }\n}\n",[65,25777,25778,25788,25792,25802,25822,25840,25864,25868,25885,25906,25910,25928,25952,25956,25973,25995,25999,26016,26040,26044,26061,26082,26086,26103,26126,26130,26152,26156,26177],{"__ignoreMap":63},[68,25779,25780,25783,25785],{"class":70,"line":71},[68,25781,25782],{"class":1864},"public",[68,25784,19914],{"class":24047},[68,25786,25787],{"class":1511}," BookCoupon\n",[68,25789,25790],{"class":70,"line":78},[68,25791,75],{"class":74},[68,25793,25794,25797,25800],{"class":70,"line":98},[68,25795,25796],{"class":74},"    [",[68,25798,25799],{"class":1511},"Key",[68,25801,2657],{"class":74},[68,25803,25804,25806,25809,25811,25814,25816,25819],{"class":70,"line":123},[68,25805,25796],{"class":74},[68,25807,25808],{"class":1511},"DatabaseGeneratedAttribute",[68,25810,648],{"class":74},[68,25812,25813],{"class":373},"DatabaseGeneratedOption",[68,25815,404],{"class":74},[68,25817,25818],{"class":373},"Identity",[68,25820,25821],{"class":74},")]\n",[68,25823,25824,25826,25829,25831,25833,25836,25838],{"class":70,"line":129},[68,25825,25796],{"class":74},[68,25827,25828],{"class":1511},"Index",[68,25830,648],{"class":74},[68,25832,89],{"class":112},[68,25834,25835],{"class":116},"BookCouponIdIndex",[68,25837,89],{"class":112},[68,25839,25821],{"class":74},[68,25841,25842,25845,25848,25851,25853,25855,25857,25860,25862],{"class":70,"line":212},[68,25843,25844],{"class":1864},"    public",[68,25846,25847],{"class":1511}," Guid",[68,25849,25850],{"class":1511}," BookCouponId",[68,25852,2382],{"class":74},[68,25854,16406],{"class":24047},[68,25856,16105],{"class":74},[68,25858,25859],{"class":24047}," set",[68,25861,16105],{"class":74},[68,25863,13347],{"class":74},[68,25865,25866],{"class":70,"line":233},[68,25867,416],{"emptyLinePlaceholder":415},[68,25869,25870,25872,25874,25876,25878,25881,25883],{"class":70,"line":268},[68,25871,25796],{"class":74},[68,25873,25828],{"class":1511},[68,25875,648],{"class":74},[68,25877,89],{"class":112},[68,25879,25880],{"class":116},"BookIdIndex",[68,25882,89],{"class":112},[68,25884,25821],{"class":74},[68,25886,25887,25889,25891,25894,25896,25898,25900,25902,25904],{"class":70,"line":289},[68,25888,25844],{"class":1864},[68,25890,4484],{"class":1899},[68,25892,25893],{"class":1511}," BookId",[68,25895,2382],{"class":74},[68,25897,16406],{"class":24047},[68,25899,16105],{"class":74},[68,25901,25859],{"class":24047},[68,25903,16105],{"class":74},[68,25905,13347],{"class":74},[68,25907,25908],{"class":70,"line":308},[68,25909,416],{"emptyLinePlaceholder":415},[68,25911,25912,25914,25917,25919,25921,25924,25926],{"class":70,"line":314},[68,25913,25796],{"class":74},[68,25915,25916],{"class":1511},"ForeignKey",[68,25918,648],{"class":74},[68,25920,89],{"class":112},[68,25922,25923],{"class":116},"BookId",[68,25925,89],{"class":112},[68,25927,25821],{"class":74},[68,25929,25930,25932,25935,25938,25940,25942,25944,25946,25948,25950],{"class":70,"line":320},[68,25931,25844],{"class":1864},[68,25933,25934],{"class":1864}," virtual",[68,25936,25937],{"class":1511}," Book",[68,25939,25937],{"class":1511},[68,25941,2382],{"class":74},[68,25943,16406],{"class":24047},[68,25945,16105],{"class":74},[68,25947,25859],{"class":24047},[68,25949,16105],{"class":74},[68,25951,13347],{"class":74},[68,25953,25954],{"class":70,"line":889},[68,25955,416],{"emptyLinePlaceholder":415},[68,25957,25958,25960,25962,25964,25966,25969,25971],{"class":70,"line":909},[68,25959,25796],{"class":74},[68,25961,25828],{"class":1511},[68,25963,648],{"class":74},[68,25965,89],{"class":112},[68,25967,25968],{"class":116},"BuyUserIdIndex",[68,25970,89],{"class":112},[68,25972,25821],{"class":74},[68,25974,25975,25977,25980,25983,25985,25987,25989,25991,25993],{"class":70,"line":929},[68,25976,25844],{"class":1864},[68,25978,25979],{"class":1899}," string",[68,25981,25982],{"class":1511}," BuyUserId",[68,25984,2382],{"class":74},[68,25986,16406],{"class":24047},[68,25988,16105],{"class":74},[68,25990,25859],{"class":24047},[68,25992,16105],{"class":74},[68,25994,13347],{"class":74},[68,25996,25997],{"class":70,"line":949},[68,25998,416],{"emptyLinePlaceholder":415},[68,26000,26001,26003,26005,26007,26009,26012,26014],{"class":70,"line":969},[68,26002,25796],{"class":74},[68,26004,25916],{"class":1511},[68,26006,648],{"class":74},[68,26008,89],{"class":112},[68,26010,26011],{"class":116},"BuyUserId",[68,26013,89],{"class":112},[68,26015,25821],{"class":74},[68,26017,26018,26020,26022,26025,26028,26030,26032,26034,26036,26038],{"class":70,"line":989},[68,26019,25844],{"class":1864},[68,26021,25934],{"class":1864},[68,26023,26024],{"class":1511}," User",[68,26026,26027],{"class":1511}," BuyUser",[68,26029,2382],{"class":74},[68,26031,16406],{"class":24047},[68,26033,16105],{"class":74},[68,26035,25859],{"class":24047},[68,26037,16105],{"class":74},[68,26039,13347],{"class":74},[68,26041,26042],{"class":70,"line":1009},[68,26043,416],{"emptyLinePlaceholder":415},[68,26045,26046,26048,26050,26052,26054,26057,26059],{"class":70,"line":1029},[68,26047,25796],{"class":74},[68,26049,25828],{"class":1511},[68,26051,648],{"class":74},[68,26053,89],{"class":112},[68,26055,26056],{"class":116},"RedeemUserIdIndex",[68,26058,89],{"class":112},[68,26060,25821],{"class":74},[68,26062,26063,26065,26067,26070,26072,26074,26076,26078,26080],{"class":70,"line":1049},[68,26064,25844],{"class":1864},[68,26066,25979],{"class":1899},[68,26068,26069],{"class":1511}," RedeemUserId",[68,26071,2382],{"class":74},[68,26073,16406],{"class":24047},[68,26075,16105],{"class":74},[68,26077,25859],{"class":24047},[68,26079,16105],{"class":74},[68,26081,13347],{"class":74},[68,26083,26084],{"class":70,"line":1069},[68,26085,416],{"emptyLinePlaceholder":415},[68,26087,26088,26090,26092,26094,26096,26099,26101],{"class":70,"line":1088},[68,26089,25796],{"class":74},[68,26091,25916],{"class":1511},[68,26093,648],{"class":74},[68,26095,89],{"class":112},[68,26097,26098],{"class":116},"RedeemUserId",[68,26100,89],{"class":112},[68,26102,25821],{"class":74},[68,26104,26105,26107,26109,26111,26114,26116,26118,26120,26122,26124],{"class":70,"line":1108},[68,26106,25844],{"class":1864},[68,26108,25934],{"class":1864},[68,26110,26024],{"class":1511},[68,26112,26113],{"class":1511}," RedeemUser",[68,26115,2382],{"class":74},[68,26117,16406],{"class":24047},[68,26119,16105],{"class":74},[68,26121,25859],{"class":24047},[68,26123,16105],{"class":74},[68,26125,13347],{"class":74},[68,26127,26128],{"class":70,"line":1128},[68,26129,416],{"emptyLinePlaceholder":415},[68,26131,26132,26134,26137,26140,26142,26144,26146,26148,26150],{"class":70,"line":1148},[68,26133,25844],{"class":1864},[68,26135,26136],{"class":1511}," DateTime",[68,26138,26139],{"class":1511}," BuyTime",[68,26141,2382],{"class":74},[68,26143,16406],{"class":24047},[68,26145,16105],{"class":74},[68,26147,25859],{"class":24047},[68,26149,16105],{"class":74},[68,26151,13347],{"class":74},[68,26153,26154],{"class":70,"line":1168},[68,26155,416],{"emptyLinePlaceholder":415},[68,26157,26158,26160,26162,26165,26167,26169,26171,26173,26175],{"class":70,"line":1187},[68,26159,25844],{"class":1864},[68,26161,26136],{"class":1511},[68,26163,26164],{"class":1511}," RedeemTime",[68,26166,2382],{"class":74},[68,26168,16406],{"class":24047},[68,26170,16105],{"class":74},[68,26172,25859],{"class":24047},[68,26174,16105],{"class":74},[68,26176,13347],{"class":74},[68,26178,26179],{"class":70,"line":2039},[68,26180,132],{"class":74},[58,26182,26184],{"className":24038,"code":26183,"language":24040,"meta":63,"style":63},"public class User\n{\n    [Key]\n    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]\n    [Index(\"UserIdIndex\")]\n    public Guid UserId { get; set; }\n\n    public string UserName { get; set; }\n\n    public virtual ICollection\u003CBookCoupon> BuyCoupons { get; set; }\n\n    public virtual ICollection\u003CBookCoupon> RedeemCoupons { get; set; }\n\n    ......\n}\n",[65,26185,26186,26195,26199,26207,26223,26240,26261,26265,26286,26290,26321,26325,26354,26358,26363],{"__ignoreMap":63},[68,26187,26188,26190,26192],{"class":70,"line":71},[68,26189,25782],{"class":1864},[68,26191,19914],{"class":24047},[68,26193,26194],{"class":1511}," User\n",[68,26196,26197],{"class":70,"line":78},[68,26198,75],{"class":74},[68,26200,26201,26203,26205],{"class":70,"line":98},[68,26202,25796],{"class":74},[68,26204,25799],{"class":1511},[68,26206,2657],{"class":74},[68,26208,26209,26211,26213,26215,26217,26219,26221],{"class":70,"line":123},[68,26210,25796],{"class":74},[68,26212,25808],{"class":1511},[68,26214,648],{"class":74},[68,26216,25813],{"class":373},[68,26218,404],{"class":74},[68,26220,25818],{"class":373},[68,26222,25821],{"class":74},[68,26224,26225,26227,26229,26231,26233,26236,26238],{"class":70,"line":129},[68,26226,25796],{"class":74},[68,26228,25828],{"class":1511},[68,26230,648],{"class":74},[68,26232,89],{"class":112},[68,26234,26235],{"class":116},"UserIdIndex",[68,26237,89],{"class":112},[68,26239,25821],{"class":74},[68,26241,26242,26244,26246,26249,26251,26253,26255,26257,26259],{"class":70,"line":212},[68,26243,25844],{"class":1864},[68,26245,25847],{"class":1511},[68,26247,26248],{"class":1511}," UserId",[68,26250,2382],{"class":74},[68,26252,16406],{"class":24047},[68,26254,16105],{"class":74},[68,26256,25859],{"class":24047},[68,26258,16105],{"class":74},[68,26260,13347],{"class":74},[68,26262,26263],{"class":70,"line":233},[68,26264,416],{"emptyLinePlaceholder":415},[68,26266,26267,26269,26271,26274,26276,26278,26280,26282,26284],{"class":70,"line":268},[68,26268,25844],{"class":1864},[68,26270,25979],{"class":1899},[68,26272,26273],{"class":1511}," UserName",[68,26275,2382],{"class":74},[68,26277,16406],{"class":24047},[68,26279,16105],{"class":74},[68,26281,25859],{"class":24047},[68,26283,16105],{"class":74},[68,26285,13347],{"class":74},[68,26287,26288],{"class":70,"line":289},[68,26289,416],{"emptyLinePlaceholder":415},[68,26291,26292,26294,26296,26299,26301,26304,26306,26309,26311,26313,26315,26317,26319],{"class":70,"line":308},[68,26293,25844],{"class":1864},[68,26295,25934],{"class":1864},[68,26297,26298],{"class":1511}," ICollection",[68,26300,727],{"class":74},[68,26302,26303],{"class":1511},"BookCoupon",[68,26305,765],{"class":74},[68,26307,26308],{"class":1511}," BuyCoupons",[68,26310,2382],{"class":74},[68,26312,16406],{"class":24047},[68,26314,16105],{"class":74},[68,26316,25859],{"class":24047},[68,26318,16105],{"class":74},[68,26320,13347],{"class":74},[68,26322,26323],{"class":70,"line":314},[68,26324,416],{"emptyLinePlaceholder":415},[68,26326,26327,26329,26331,26333,26335,26337,26339,26342,26344,26346,26348,26350,26352],{"class":70,"line":320},[68,26328,25844],{"class":1864},[68,26330,25934],{"class":1864},[68,26332,26298],{"class":1511},[68,26334,727],{"class":74},[68,26336,26303],{"class":1511},[68,26338,765],{"class":74},[68,26340,26341],{"class":1511}," RedeemCoupons",[68,26343,2382],{"class":74},[68,26345,16406],{"class":24047},[68,26347,16105],{"class":74},[68,26349,25859],{"class":24047},[68,26351,16105],{"class":74},[68,26353,13347],{"class":74},[68,26355,26356],{"class":70,"line":889},[68,26357,416],{"emptyLinePlaceholder":415},[68,26359,26360],{"class":70,"line":909},[68,26361,26362],{"class":373},"    ......\n",[68,26364,26365],{"class":70,"line":929},[68,26366,132],{"class":74},[28,26368,26369],{},"另外，在 DbContext 中需要 override OnModelCreating 方法：",[58,26371,26373],{"className":24038,"code":26372,"language":24040,"meta":63,"style":63},"protected override void OnModelCreating(DbModelBuilder modelBuilder)\n{\n    base.OnModelCreating(modelBuilder);\n    modelBuilder.Entity\u003CApplicationUser>().HasMany(u => u.BuyCoupons).WithRequired(n => n.BuyUser).WillCascadeOnDelete(false);\n    modelBuilder.Entity\u003CApplicationUser>().HasMany(u => u.RedeemCoupons).WithRequired(n => n.RedeemUser).WillCascadeOnDelete(false);\n}\n",[65,26374,26375,26399,26403,26420,26488,26544],{"__ignoreMap":63},[68,26376,26377,26380,26383,26386,26389,26391,26394,26397],{"class":70,"line":71},[68,26378,26379],{"class":1864},"protected",[68,26381,26382],{"class":1864}," override",[68,26384,26385],{"class":1899}," void",[68,26387,26388],{"class":2183}," OnModelCreating",[68,26390,648],{"class":74},[68,26392,26393],{"class":1511},"DbModelBuilder",[68,26395,26396],{"class":1511}," modelBuilder",[68,26398,410],{"class":74},[68,26400,26401],{"class":70,"line":78},[68,26402,75],{"class":74},[68,26404,26405,26408,26410,26413,26415,26418],{"class":70,"line":98},[68,26406,26407],{"class":665},"    base",[68,26409,404],{"class":74},[68,26411,26412],{"class":2183},"OnModelCreating",[68,26414,648],{"class":74},[68,26416,26417],{"class":373},"modelBuilder",[68,26419,20480],{"class":74},[68,26421,26422,26425,26427,26430,26432,26435,26438,26441,26443,26446,26448,26451,26453,26456,26458,26461,26463,26466,26468,26471,26473,26476,26478,26481,26483,26486],{"class":70,"line":123},[68,26423,26424],{"class":373},"    modelBuilder",[68,26426,404],{"class":74},[68,26428,26429],{"class":2183},"Entity",[68,26431,727],{"class":74},[68,26433,26434],{"class":1511},"ApplicationUser",[68,26436,26437],{"class":74},">().",[68,26439,26440],{"class":2183},"HasMany",[68,26442,648],{"class":74},[68,26444,26445],{"class":1511},"u",[68,26447,7765],{"class":1899},[68,26449,26450],{"class":373}," u",[68,26452,404],{"class":74},[68,26454,26455],{"class":373},"BuyCoupons",[68,26457,4608],{"class":74},[68,26459,26460],{"class":2183},"WithRequired",[68,26462,648],{"class":74},[68,26464,26465],{"class":1511},"n",[68,26467,7765],{"class":1899},[68,26469,26470],{"class":373}," n",[68,26472,404],{"class":74},[68,26474,26475],{"class":373},"BuyUser",[68,26477,4608],{"class":74},[68,26479,26480],{"class":2183},"WillCascadeOnDelete",[68,26482,648],{"class":74},[68,26484,26485],{"class":12342},"false",[68,26487,20480],{"class":74},[68,26489,26490,26492,26494,26496,26498,26500,26502,26504,26506,26508,26510,26512,26514,26517,26519,26521,26523,26525,26527,26529,26531,26534,26536,26538,26540,26542],{"class":70,"line":129},[68,26491,26424],{"class":373},[68,26493,404],{"class":74},[68,26495,26429],{"class":2183},[68,26497,727],{"class":74},[68,26499,26434],{"class":1511},[68,26501,26437],{"class":74},[68,26503,26440],{"class":2183},[68,26505,648],{"class":74},[68,26507,26445],{"class":1511},[68,26509,7765],{"class":1899},[68,26511,26450],{"class":373},[68,26513,404],{"class":74},[68,26515,26516],{"class":373},"RedeemCoupons",[68,26518,4608],{"class":74},[68,26520,26460],{"class":2183},[68,26522,648],{"class":74},[68,26524,26465],{"class":1511},[68,26526,7765],{"class":1899},[68,26528,26470],{"class":373},[68,26530,404],{"class":74},[68,26532,26533],{"class":373},"RedeemUser",[68,26535,4608],{"class":74},[68,26537,26480],{"class":2183},[68,26539,648],{"class":74},[68,26541,26485],{"class":12342},[68,26543,20480],{"class":74},[68,26545,26546],{"class":70,"line":212},[68,26547,132],{"class":74},[28,26549,26550,26551,26554],{},"注意最后的",[65,26552,26553],{},".WillCascadeOnDelete(false)","，因为在这样多对多的绑定中，使用级联删除会报错。",[523,26556,26557],{},"html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sG8yY, html code.shiki .sG8yY{--shiki-light:#E2931D;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":63,"searchDepth":78,"depth":78,"links":26559},[],"2015-03-17",{},"\u002Fposts\u002F2015\u002Fentity-framework-code-first-two-foreign-keys-from-same-table",{"text":1217,"minutes":26564,"time":26565,"words":26566},1.49,89400,298,{"title":25758,"description":25763},{"loc":26562},"posts\u002F2015\u002F20150317.entity-framework-code-first-two-foreign-keys-from-same-table",[543],"Erw-iLbDHHcgxcCq-ZV9uJ6F7f3-NtOcl_DuUppVbLM",{"id":26573,"title":26574,"body":26575,"class":528,"cover":528,"coverSize":528,"date":26736,"description":26579,"draft":530,"extension":531,"hideComments":530,"location":25378,"meta":26737,"navigation":415,"path":26738,"readingTime":26739,"seo":26740,"sitemap":26741,"stem":26742,"tags":26743,"time":528,"weather":528,"__hash__":26745},"posts\u002Fposts\u002F2015\u002F20150306.windows-close-monitor-tool.md","Windows 关屏小工具",{"type":25,"value":26576,"toc":26734},[26577,26580,26725,26728,26731],[28,26578,26579],{},"有时候下班的时候不想关机，有很多原因，比如有 N 个网页 Tab 开着，有些可能还需要进一步查阅，关了的话从历史里不太好找，又或者，VS 开着调试，没做完，而第二天重新跑一下要很久。于是便有了挂机。可是公然挂机其实并不好，公司有规定下班自觉关机。按显示器按钮太 LOW，高端人士怎么能用这么粗鲁的方法呢！以前我用的是设置 Windows 关屏时间，5 分钟不动鼠标就关闭屏幕。这个坏处是不够及时。有什么办法能立马关闭屏幕呢？于是找到了如下代码：",[58,26581,26585],{"className":26582,"code":26583,"language":26584,"meta":63,"style":63},"language-cpp shiki shiki-themes material-theme-lighter github-light github-dark","#pragma comment( linker, \"\u002Fsubsystem:\\\"windows\\\" \u002Fentry:\\\"mainCRTStartup\\\"\" )\n#include \u003Cwindows.h>\nint main()\n{\n    ::SendMessageA(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)2);\n    ::Sleep(200);\n    LockWorkStation();\n    return 0;\n}\n","cpp",[65,26586,26587,26631,26644,26653,26657,26693,26706,26713,26721],{"__ignoreMap":63},[68,26588,26589,26592,26595,26598,26601,26603,26605,26608,26611,26614,26616,26619,26621,26624,26626,26628],{"class":70,"line":71},[68,26590,26591],{"class":1790},"#pragma",[68,26593,26594],{"class":873}," comment",[68,26596,26597],{"class":373},"( ",[68,26599,26600],{"class":873},"linker",[68,26602,597],{"class":373},[68,26604,89],{"class":112},[68,26606,26607],{"class":116},"\u002Fsubsystem:",[68,26609,26610],{"class":665},"\\\"",[68,26612,26613],{"class":116},"windows",[68,26615,26610],{"class":665},[68,26617,26618],{"class":116}," \u002Fentry:",[68,26620,26610],{"class":665},[68,26622,26623],{"class":116},"mainCRTStartup",[68,26625,26610],{"class":665},[68,26627,89],{"class":112},[68,26629,26630],{"class":373}," )\n",[68,26632,26633,26636,26639,26642],{"class":70,"line":78},[68,26634,26635],{"class":1790},"#include",[68,26637,26638],{"class":112}," \u003C",[68,26640,26641],{"class":116},"windows.h",[68,26643,734],{"class":112},[68,26645,26646,26649,26651],{"class":70,"line":98},[68,26647,26648],{"class":1864},"int",[68,26650,11420],{"class":2183},[68,26652,2136],{"class":74},[68,26654,26655],{"class":70,"line":123},[68,26656,75],{"class":74},[68,26658,26659,26662,26665,26667,26670,26672,26675,26677,26680,26682,26684,26687,26689,26691],{"class":70,"line":129},[68,26660,26661],{"class":373},"    ::",[68,26663,26664],{"class":2183},"SendMessageA",[68,26666,648],{"class":74},[68,26668,26669],{"class":373},"HWND_BROADCAST",[68,26671,255],{"class":74},[68,26673,26674],{"class":373}," WM_SYSCOMMAND",[68,26676,255],{"class":74},[68,26678,26679],{"class":373}," SC_MONITORPOWER",[68,26681,255],{"class":74},[68,26683,7664],{"class":74},[68,26685,26686],{"class":373},"LPARAM",[68,26688,2753],{"class":74},[68,26690,9366],{"class":2397},[68,26692,20480],{"class":74},[68,26694,26695,26697,26700,26702,26704],{"class":70,"line":212},[68,26696,26661],{"class":373},[68,26698,26699],{"class":2183},"Sleep",[68,26701,648],{"class":74},[68,26703,4630],{"class":2397},[68,26705,20480],{"class":74},[68,26707,26708,26711],{"class":70,"line":233},[68,26709,26710],{"class":2183},"    LockWorkStation",[68,26712,24122],{"class":74},[68,26714,26715,26717,26719],{"class":70,"line":268},[68,26716,9544],{"class":1790},[68,26718,20079],{"class":2397},[68,26720,5136],{"class":74},[68,26722,26723],{"class":70,"line":289},[68,26724,132],{"class":74},[28,26726,26727],{},"建一个 C++控制台程序，插入上面的代码，搞定，双击一下自动关屏+锁屏！",[28,26729,26730],{},"Cool！",[523,26732,26733],{},"html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .s9AJx, html code.shiki .s9AJx{--shiki-light:#9C3EDA;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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":63,"searchDepth":78,"depth":78,"links":26735},[],"2015-03-06",{},"\u002Fposts\u002F2015\u002Fwindows-close-monitor-tool",{"text":1217,"minutes":18699,"time":18700,"words":18701},{"title":26574,"description":26579},{"loc":26738},"posts\u002F2015\u002F20150306.windows-close-monitor-tool",[543,26744,21722],"工具","dgiV6L1SMQcSMphs_oQmpDY3C1WffDqXgSpWGeaSKtw",{"id":26747,"title":26748,"body":26749,"class":528,"cover":528,"coverSize":528,"date":26809,"description":26810,"draft":530,"extension":531,"hideComments":530,"location":25378,"meta":26811,"navigation":415,"path":26812,"readingTime":26813,"seo":26814,"sitemap":26815,"stem":26816,"tags":26817,"time":528,"weather":528,"__hash__":26818},"posts\u002Fposts\u002F2015\u002F20150204.move-duoshuo-comments.md","跨站点迁移多说评论",{"type":25,"value":26750,"toc":26807},[26751,26781,26792],[28,26752,26753,26754,26758,26759,26764,26765,26768,26769,26772,26773,26776,26777,26780],{},"近日在捣鼓",[38,26755,26757],{"href":23344,"rel":26756},[42],"HADB.ME","的个人博客，之前所有的博客都在",[38,26760,26763],{"href":26761,"rel":26762},"http:\u002F\u002Fblog.haoest.com\u002F",[42],"blog.haoest.com","里，如今想把一些技术分享以及个人的小结什么的单独抽出来放到",[38,26766,26757],{"href":23344,"rel":26767},[42],"里，而和好易思特有关的博客还放在",[38,26770,26763],{"href":26761,"rel":26771},[42],"里。这涉及到一个问题，那就是要将原先多说里属于",[38,26774,26763],{"href":26761,"rel":26775},[42],"的评论移动到",[38,26778,26757],{"href":23344,"rel":26779},[42],"里。",[28,26782,26783,26784,26787,26788,26791],{},"经过思考，发现可以这么搞。在多说",[38,26785,26763],{"href":26761,"rel":26786},[42],"站点的后台中，将所有评论导出，然后导入到多说",[38,26789,26757],{"href":23344,"rel":26790},[42],"站点的后台中。",[28,26793,26794,26795,26798,26799,26802,26803,26806],{},"不过，多说的管理页面有个很坑爹的 ThreadKey，其实没啥作用，我一开始以为是用 ThreadKey 作为文章的 Id 的，结果发现，其实有个隐藏的 ThreadId 才是关键。于是打开从",[38,26796,26763],{"href":26761,"rel":26797},[42],"中导出的评论，是个 json 格式的文件。注意，只需要评论，而无需导出文章列表，因为导出的文章列表的 id、url 等都是旧数据。我们可以依次打开新站点的一篇文章，以及旧站点中对应的文章，分别通过",[65,26800,26801],{},"$('input[name=\"thread_id\"]').val()","来获取其 ThreadId，然后在 json 文件中进行替换，将旧的 ThreadId 替换成新的 ThreadId，最后在导入到多说",[38,26804,26757],{"href":23344,"rel":26805},[42],"站点的后台中即可。",{"title":63,"searchDepth":78,"depth":78,"links":26808},[],"2015-02-04","近日在捣鼓HADB.ME的个人博客，之前所有的博客都在blog.haoest.com里，如今想把一些技术分享以及个人的小结什么的单独抽出来放到HADB.ME里，而和好易思特有关的博客还放在blog.haoest.com里。这涉及到一个问题，那就是要将原先多说里属于blog.haoest.com的评论移动到HADB.ME里。",{},"\u002Fposts\u002F2015\u002Fmove-duoshuo-comments",{"text":1217,"minutes":13509,"time":13510,"words":13511},{"title":26748,"description":26810},{"loc":26812},"posts\u002F2015\u002F20150204.move-duoshuo-comments",[543],"NxtgHRKGQ3z5ODxvx9aBYKRdgOfvC9iUGbVUpCYx8Xg",{"id":26820,"title":26821,"body":26822,"class":528,"cover":528,"coverSize":528,"date":26990,"description":26826,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":26991,"navigation":415,"path":26992,"readingTime":26993,"seo":26997,"sitemap":26998,"stem":26999,"tags":27000,"time":528,"weather":528,"__hash__":27001},"posts\u002Fposts\u002F2015\u002F20150130.use-custom-domain-for-hexo-on-github.md","在 GitHub 上为 Hexo 配置自定义域名",{"type":25,"value":26823,"toc":26988},[26824,26827,26845,26854,26857,26894,26978,26985],[28,26825,26826],{},"昨天尝试了下 Hexo，感觉很酷。主要有以下几个特点：",[5010,26828,26829,26832,26835,26842],{},[1239,26830,26831],{},"无需数据库，所有文章都可以基于 git 来存储",[1239,26833,26834],{},"纯静态，在编写好文章之后，生成静态文件，对服务器基本没要求",[1239,26836,26837,26838,26841],{},"部署简单，",[65,26839,26840],{},"hexo generate --deploy","，轻松完成生成、部署功能",[1239,26843,26844],{},"主题丰富，界面简洁，相比臃肿的 WordPress，爽多了",[28,26846,26847,26848,26853],{},"安装很简单，按照",[38,26849,26852],{"href":26850,"rel":26851},"http:\u002F\u002Fhexo.io\u002Fdocs\u002F",[42],"这里","的教程，进行就可以了。前提是确保 npm 和 git 功能都能用就行。我主要分享一下在配置自定义域名所遇到的问题。",[28,26855,26856],{},"我是将其部署到 GitHub 上的，没有采用 hadb.github.io 作为 repo 名，因为我是想将网站叫做 HADB.ME，所以我就创建了一个 HADB.ME 的 repo。这和使用 hadb.github.io 有点区别。",[28,26858,26859,26860,26862,26863,26865,26866,26869,26870,26873,26874,26876,26877,26881,26882,26885,26886,26889,26890,26893],{},"使用 hadb.github.io 的话，master 分支是作为页面显示的分支的，而使用 HADB.ME 的话，是使用 ph-pages 分支作为显示的分支的。这时，只需在 ph-pages 分支的根目录放一个 CNAME 文件，内容就是",[65,26861,10039],{},"，然后将域名 cname 到 hadb.github.io，github 会自动判断出 HADB.ME 这个项目中有一个 CNAME，里面就是配的",[65,26864,10039],{},"，于是",[38,26867,10039],{"href":23344,"rel":26868},[42],"就成功地变成了 HADB.ME 项目页面的域名了。这时，我们在_config.yml 中，就可以将 url 配为",[65,26871,26872],{},"http:\u002F\u002Fhadb.me\u002F","，root 为",[65,26875,6],{},"。当然，这样会有一个问题，就是直接访问 ",[38,26878,26879],{"href":26879,"rel":26880},"http:\u002F\u002Fhadb.github.io\u002FHADB.ME\u002F",[42]," 的时候，无法显示样式。这个可以这样解决，当访问路径是 ",[38,26883,26879],{"href":26879,"rel":26884},[42]," 时，通过 js 直接跳转到 ",[38,26887,23344],{"href":23344,"rel":26888},[42]," 上就可以了，在主题的 head.ejs 文件中，",[65,26891,26892],{},"\u003C\u002Fhead>","前加入如下代码：",[58,26895,26897],{"className":19900,"code":26896,"language":19902,"meta":63,"style":63},"\u003Cscript>\n  \u002F\u002F Redirect to hadb.me\n  if (window.location.hostname === 'hadb.github.io') {\n    window.location.href = 'http:\u002F\u002Fhadb.me\u002F'\n  }\n\u003C\u002Fscript>\n",[65,26898,26899,26908,26913,26944,26966,26970],{"__ignoreMap":63},[68,26900,26901,26903,26906],{"class":70,"line":71},[68,26902,727],{"class":74},[68,26904,26905],{"class":730},"script",[68,26907,734],{"class":74},[68,26909,26910],{"class":70,"line":78},[68,26911,26912],{"class":2403},"  \u002F\u002F Redirect to hadb.me\n",[68,26914,26915,26917,26920,26922,26925,26927,26930,26933,26935,26938,26940,26942],{"class":70,"line":98},[68,26916,25489],{"class":1790},[68,26918,26919],{"class":373}," (window",[68,26921,404],{"class":74},[68,26923,26924],{"class":373},"location",[68,26926,404],{"class":74},[68,26928,26929],{"class":373},"hostname ",[68,26931,26932],{"class":1899},"===",[68,26934,1620],{"class":112},[68,26936,26937],{"class":116},"hadb.github.io",[68,26939,8129],{"class":112},[68,26941,588],{"class":373},[68,26943,75],{"class":74},[68,26945,26946,26949,26951,26953,26955,26958,26960,26962,26964],{"class":70,"line":123},[68,26947,26948],{"class":373},"    window",[68,26950,404],{"class":74},[68,26952,26924],{"class":373},[68,26954,404],{"class":74},[68,26956,26957],{"class":373},"href",[68,26959,1900],{"class":1899},[68,26961,1620],{"class":112},[68,26963,26872],{"class":116},[68,26965,1626],{"class":112},[68,26967,26968],{"class":70,"line":129},[68,26969,126],{"class":74},[68,26971,26972,26974,26976],{"class":70,"line":212},[68,26973,771],{"class":74},[68,26975,26905],{"class":730},[68,26977,734],{"class":74},[28,26979,26980,26981,26984],{},"还有一个问题是，每当我在 ph-pages 中创建一个 CNAME，每次 hexo deploy 之后，ph-pages 中的 commit 历史会重建，CNAME 文件就丢失了，为此我很苦恼。后来发现，只需要将 CNAME 文件放到",[65,26982,26983],{},"source","文件夹下，就可以了，每次 deploy 会自动放到根目录。",[523,26986,26987],{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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":63,"searchDepth":78,"depth":78,"links":26989},[],"2015-01-30",{},"\u002Fposts\u002F2015\u002Fuse-custom-domain-for-hexo-on-github",{"text":535,"minutes":26994,"time":26995,"words":26996},2.45,147000,490,{"title":26821,"description":26826},{"loc":26992},"posts\u002F2015\u002F20150130.use-custom-domain-for-hexo-on-github",[543,10057],"PZTpx1YHK4BkGPdXvLtboiqmxK-mvftqlLbF4_jsFT4",{"id":27003,"title":27004,"body":27005,"class":528,"cover":528,"coverSize":528,"date":27025,"description":18083,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":27026,"navigation":415,"path":27027,"readingTime":27028,"seo":27031,"sitemap":27032,"stem":27033,"tags":27034,"time":528,"weather":528,"__hash__":27035},"posts\u002Fposts\u002F2014\u002F20140827.webkit-white-spacenowrap.md","WebKit 浏览器 table 里的 white-space:nowrap 的问题",{"type":25,"value":27006,"toc":27023},[27007,27013],[28,27008,27009],{},[38,27010,18083],{"href":27011,"rel":27012},"http:\u002F\u002Fwww.w3help.org\u002Fzh-cn\u002Fcauses\u002FRT5004",[42],[28,27014,27015,27016,27019,27020,1686],{},"今天遇到一个类似的问题，在 table 里面，设置的 ",[65,27017,27018],{},"white-space:nowrap"," 导致一行被撑大，解决办法在 table 上设置 ",[65,27021,27022],{},"table-layout:fixed",{"title":63,"searchDepth":78,"depth":78,"links":27024},[],"2014-08-27",{},"\u002Fposts\u002F2014\u002Fwebkit-white-spacenowrap",{"text":7523,"minutes":27029,"time":27030,"words":2173},0.19,11400,{"title":27004,"description":18083},{"loc":27027},"posts\u002F2014\u002F20140827.webkit-white-spacenowrap",[543,8427],"MFXuqYsYnUlpDQFgWGipvlfm5SrDJbuvTGRynYYgrrw",{"id":27037,"title":27038,"body":27039,"class":528,"cover":528,"coverSize":528,"date":27668,"description":27043,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":27669,"navigation":415,"path":27670,"readingTime":27671,"seo":27674,"sitemap":27675,"stem":27676,"tags":27677,"time":528,"weather":528,"__hash__":27678},"posts\u002Fposts\u002F2013\u002F20131003.fix-chinese-url-in-wordpress-in-iis.md","IIS 下 WordPress 中文 URL 无法访问的解决方法",{"type":25,"value":27040,"toc":27666},[27041,27044,27047,27556,27559,27653,27660,27663],[28,27042,27043],{},"这个问题是因为 IIS 和 wordpress 对 url 的编码不一致的问题，导致传到 wordpress 里的 URL 中的中文是乱码，所以 wordpress 无法给出正确的页面。解决方法很简单：",[28,27045,27046],{},"在网站根目录下建立一个 php 文件，名字自定，例如 chineseUrl.php，内容如下：",[58,27048,27052],{"className":27049,"code":27050,"language":27051,"meta":63,"style":63},"language-php shiki shiki-themes material-theme-lighter github-light github-dark","\u003C?php\n    if (isset($_SERVER['HTTP_X_ORIGINAL_URL'])) {\n        \u002F\u002F IIS Mod-Rewrite\n        $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL'];\n    }\n    else if (isset($_SERVER['HTTP_X_REWRITE_URL'])) {\n        \u002F\u002F IIS Isapi_Rewrite\n        $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_REWRITE_URL'];\n    }\n    else {\n        \u002F\u002F Use ORIG_PATH_INFO if there is no PATH_INFO\n        (!isset($_SERVER['PATH_INFO']) && isset($_SERVER['ORIG_PATH_INFO'])) && ($_SERVER['PATH_INFO'] = $_SERVER['ORIG_PATH_INFO']);\n        \u002F\u002F Some IIS + PHP configurations puts the script-name in the path-info (No need to append it twice)\n        if (isset($_SERVER['PATH_INFO'])) {\n                ($_SERVER['PATH_INFO'] == $_SERVER['SCRIPT_NAME']) ? ($_SERVER['REQUEST_URI'] = $_SERVER['PATH_INFO']) : ($_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] . $_SERVER['PATH_INFO']);\n        }\n        \u002F\u002F Append the query string if it exists and isn't null\n        (isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) && ($_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING']);\n    }\n    require(\"index.php\");\n?>\n","php",[65,27053,27054,27062,27091,27096,27132,27136,27163,27168,27200,27204,27210,27215,27293,27298,27322,27438,27442,27447,27531,27535,27551],{"__ignoreMap":63},[68,27055,27056,27059],{"class":70,"line":71},[68,27057,27058],{"class":1899},"\u003C?",[68,27060,27061],{"class":665},"php\n",[68,27063,27064,27066,27068,27071,27074,27077,27079,27081,27084,27086,27089],{"class":70,"line":78},[68,27065,9649],{"class":1790},[68,27067,7664],{"class":74},[68,27069,27070],{"class":630},"isset",[68,27072,27073],{"class":74},"($",[68,27075,27076],{"class":373},"_SERVER",[68,27078,2326],{"class":74},[68,27080,8129],{"class":112},[68,27082,27083],{"class":116},"HTTP_X_ORIGINAL_URL",[68,27085,8129],{"class":112},[68,27087,27088],{"class":74},"]))",[68,27090,95],{"class":74},[68,27092,27093],{"class":70,"line":98},[68,27094,27095],{"class":2403},"        \u002F\u002F IIS Mod-Rewrite\n",[68,27097,27098,27101,27103,27105,27107,27110,27112,27114,27116,27119,27121,27123,27125,27127,27129],{"class":70,"line":123},[68,27099,27100],{"class":74},"        $",[68,27102,27076],{"class":373},[68,27104,2326],{"class":74},[68,27106,8129],{"class":112},[68,27108,27109],{"class":116},"REQUEST_URI",[68,27111,8129],{"class":112},[68,27113,2786],{"class":74},[68,27115,1900],{"class":1899},[68,27117,27118],{"class":74}," $",[68,27120,27076],{"class":373},[68,27122,2326],{"class":74},[68,27124,8129],{"class":112},[68,27126,27083],{"class":116},[68,27128,8129],{"class":112},[68,27130,27131],{"class":74},"];\n",[68,27133,27134],{"class":70,"line":129},[68,27135,311],{"class":74},[68,27137,27138,27140,27142,27144,27146,27148,27150,27152,27154,27157,27159,27161],{"class":70,"line":212},[68,27139,9671],{"class":1790},[68,27141,2840],{"class":1790},[68,27143,7664],{"class":74},[68,27145,27070],{"class":630},[68,27147,27073],{"class":74},[68,27149,27076],{"class":373},[68,27151,2326],{"class":74},[68,27153,8129],{"class":112},[68,27155,27156],{"class":116},"HTTP_X_REWRITE_URL",[68,27158,8129],{"class":112},[68,27160,27088],{"class":74},[68,27162,95],{"class":74},[68,27164,27165],{"class":70,"line":233},[68,27166,27167],{"class":2403},"        \u002F\u002F IIS Isapi_Rewrite\n",[68,27169,27170,27172,27174,27176,27178,27180,27182,27184,27186,27188,27190,27192,27194,27196,27198],{"class":70,"line":268},[68,27171,27100],{"class":74},[68,27173,27076],{"class":373},[68,27175,2326],{"class":74},[68,27177,8129],{"class":112},[68,27179,27109],{"class":116},[68,27181,8129],{"class":112},[68,27183,2786],{"class":74},[68,27185,1900],{"class":1899},[68,27187,27118],{"class":74},[68,27189,27076],{"class":373},[68,27191,2326],{"class":74},[68,27193,8129],{"class":112},[68,27195,27156],{"class":116},[68,27197,8129],{"class":112},[68,27199,27131],{"class":74},[68,27201,27202],{"class":70,"line":289},[68,27203,311],{"class":74},[68,27205,27206,27208],{"class":70,"line":308},[68,27207,9671],{"class":1790},[68,27209,95],{"class":74},[68,27211,27212],{"class":70,"line":314},[68,27213,27214],{"class":2403},"        \u002F\u002F Use ORIG_PATH_INFO if there is no PATH_INFO\n",[68,27216,27217,27220,27222,27224,27226,27228,27230,27232,27235,27237,27239,27241,27244,27246,27248,27250,27252,27255,27257,27259,27261,27264,27266,27268,27270,27272,27274,27276,27278,27280,27282,27284,27286,27288,27290],{"class":70,"line":320},[68,27218,27219],{"class":74},"        (",[68,27221,11642],{"class":1899},[68,27223,27070],{"class":630},[68,27225,27073],{"class":74},[68,27227,27076],{"class":373},[68,27229,2326],{"class":74},[68,27231,8129],{"class":112},[68,27233,27234],{"class":116},"PATH_INFO",[68,27236,8129],{"class":112},[68,27238,12570],{"class":74},[68,27240,7832],{"class":1899},[68,27242,27243],{"class":630}," isset",[68,27245,27073],{"class":74},[68,27247,27076],{"class":373},[68,27249,2326],{"class":74},[68,27251,8129],{"class":112},[68,27253,27254],{"class":116},"ORIG_PATH_INFO",[68,27256,8129],{"class":112},[68,27258,27088],{"class":74},[68,27260,7832],{"class":1899},[68,27262,27263],{"class":74}," ($",[68,27265,27076],{"class":373},[68,27267,2326],{"class":74},[68,27269,8129],{"class":112},[68,27271,27234],{"class":116},[68,27273,8129],{"class":112},[68,27275,2786],{"class":74},[68,27277,1900],{"class":1899},[68,27279,27118],{"class":74},[68,27281,27076],{"class":373},[68,27283,2326],{"class":74},[68,27285,8129],{"class":112},[68,27287,27254],{"class":116},[68,27289,8129],{"class":112},[68,27291,27292],{"class":74},"]);\n",[68,27294,27295],{"class":70,"line":889},[68,27296,27297],{"class":2403},"        \u002F\u002F Some IIS + PHP configurations puts the script-name in the path-info (No need to append it twice)\n",[68,27299,27300,27302,27304,27306,27308,27310,27312,27314,27316,27318,27320],{"class":70,"line":909},[68,27301,2435],{"class":1790},[68,27303,7664],{"class":74},[68,27305,27070],{"class":630},[68,27307,27073],{"class":74},[68,27309,27076],{"class":373},[68,27311,2326],{"class":74},[68,27313,8129],{"class":112},[68,27315,27234],{"class":116},[68,27317,8129],{"class":112},[68,27319,27088],{"class":74},[68,27321,95],{"class":74},[68,27323,27324,27327,27329,27331,27333,27335,27337,27339,27341,27343,27345,27347,27349,27352,27354,27356,27359,27361,27363,27365,27367,27369,27371,27373,27375,27377,27379,27381,27383,27385,27387,27389,27392,27394,27396,27398,27400,27402,27404,27406,27408,27410,27412,27414,27416,27418,27420,27422,27424,27426,27428,27430,27432,27434,27436],{"class":70,"line":929},[68,27325,27326],{"class":74},"                ($",[68,27328,27076],{"class":373},[68,27330,2326],{"class":74},[68,27332,8129],{"class":112},[68,27334,27234],{"class":116},[68,27336,8129],{"class":112},[68,27338,2786],{"class":74},[68,27340,2789],{"class":1899},[68,27342,27118],{"class":74},[68,27344,27076],{"class":373},[68,27346,2326],{"class":74},[68,27348,8129],{"class":112},[68,27350,27351],{"class":116},"SCRIPT_NAME",[68,27353,8129],{"class":112},[68,27355,12570],{"class":74},[68,27357,27358],{"class":1899}," ?",[68,27360,27263],{"class":74},[68,27362,27076],{"class":373},[68,27364,2326],{"class":74},[68,27366,8129],{"class":112},[68,27368,27109],{"class":116},[68,27370,8129],{"class":112},[68,27372,2786],{"class":74},[68,27374,1900],{"class":1899},[68,27376,27118],{"class":74},[68,27378,27076],{"class":373},[68,27380,2326],{"class":74},[68,27382,8129],{"class":112},[68,27384,27234],{"class":116},[68,27386,8129],{"class":112},[68,27388,12570],{"class":74},[68,27390,27391],{"class":1899}," :",[68,27393,27263],{"class":74},[68,27395,27076],{"class":373},[68,27397,2326],{"class":74},[68,27399,8129],{"class":112},[68,27401,27109],{"class":116},[68,27403,8129],{"class":112},[68,27405,2786],{"class":74},[68,27407,1900],{"class":1899},[68,27409,27118],{"class":74},[68,27411,27076],{"class":373},[68,27413,2326],{"class":74},[68,27415,8129],{"class":112},[68,27417,27351],{"class":116},[68,27419,8129],{"class":112},[68,27421,2786],{"class":74},[68,27423,18654],{"class":1899},[68,27425,27118],{"class":74},[68,27427,27076],{"class":373},[68,27429,2326],{"class":74},[68,27431,8129],{"class":112},[68,27433,27234],{"class":116},[68,27435,8129],{"class":112},[68,27437,27292],{"class":74},[68,27439,27440],{"class":70,"line":949},[68,27441,2589],{"class":74},[68,27443,27444],{"class":70,"line":969},[68,27445,27446],{"class":2403},"        \u002F\u002F Append the query string if it exists and isn't null\n",[68,27448,27449,27451,27453,27455,27457,27459,27461,27464,27466,27468,27470,27473,27476,27478,27480,27482,27484,27486,27488,27490,27492,27494,27496,27498,27500,27502,27504,27506,27509,27511,27513,27515,27517,27519,27521,27523,27525,27527,27529],{"class":70,"line":989},[68,27450,27219],{"class":74},[68,27452,27070],{"class":630},[68,27454,27073],{"class":74},[68,27456,27076],{"class":373},[68,27458,2326],{"class":74},[68,27460,8129],{"class":112},[68,27462,27463],{"class":116},"QUERY_STRING",[68,27465,8129],{"class":112},[68,27467,12570],{"class":74},[68,27469,7832],{"class":1899},[68,27471,27472],{"class":1899}," !",[68,27474,27475],{"class":630},"empty",[68,27477,27073],{"class":74},[68,27479,27076],{"class":373},[68,27481,2326],{"class":74},[68,27483,8129],{"class":112},[68,27485,27463],{"class":116},[68,27487,8129],{"class":112},[68,27489,27088],{"class":74},[68,27491,7832],{"class":1899},[68,27493,27263],{"class":74},[68,27495,27076],{"class":373},[68,27497,2326],{"class":74},[68,27499,8129],{"class":112},[68,27501,27109],{"class":116},[68,27503,8129],{"class":112},[68,27505,2786],{"class":74},[68,27507,27508],{"class":1899}," .=",[68,27510,1620],{"class":112},[68,27512,10929],{"class":116},[68,27514,8129],{"class":112},[68,27516,18654],{"class":1899},[68,27518,27118],{"class":74},[68,27520,27076],{"class":373},[68,27522,2326],{"class":74},[68,27524,8129],{"class":112},[68,27526,27463],{"class":116},[68,27528,8129],{"class":112},[68,27530,27292],{"class":74},[68,27532,27533],{"class":70,"line":1009},[68,27534,311],{"class":74},[68,27536,27537,27540,27542,27544,27547,27549],{"class":70,"line":1029},[68,27538,27539],{"class":1790},"    require",[68,27541,648],{"class":74},[68,27543,89],{"class":112},[68,27545,27546],{"class":116},"index.php",[68,27548,89],{"class":112},[68,27550,20480],{"class":74},[68,27552,27553],{"class":70,"line":1049},[68,27554,27555],{"class":1899},"?>\n",[28,27557,27558],{},"修改根目录下的 web.config 文件，在 rules 节点里添加如下节点：",[58,27560,27562],{"className":718,"code":27561,"language":720,"meta":63,"style":63},"\u003Crule name=\"chineseUrl\" stopProcessing=\"true\">\n    \u003Cmatch url=\"^(tag|category)\u002F(.*)$\" \u002F>\n    \u003Caction type=\"Rewrite\" url=\"chineseurl.php\" \u002F>\n\u003C\u002Frule>\n",[65,27563,27564,27595,27614,27645],{"__ignoreMap":63},[68,27565,27566,27568,27571,27573,27575,27577,27580,27582,27585,27587,27589,27591,27593],{"class":70,"line":71},[68,27567,727],{"class":74},[68,27569,27570],{"class":730},"rule",[68,27572,17168],{"class":873},[68,27574,877],{"class":74},[68,27576,89],{"class":112},[68,27578,27579],{"class":116},"chineseUrl",[68,27581,89],{"class":112},[68,27583,27584],{"class":873}," stopProcessing",[68,27586,877],{"class":74},[68,27588,89],{"class":112},[68,27590,849],{"class":116},[68,27592,89],{"class":112},[68,27594,734],{"class":74},[68,27596,27597,27599,27601,27603,27605,27607,27610,27612],{"class":70,"line":78},[68,27598,739],{"class":74},[68,27600,3771],{"class":730},[68,27602,11438],{"class":873},[68,27604,877],{"class":74},[68,27606,89],{"class":112},[68,27608,27609],{"class":116},"^(tag|category)\u002F(.*)$",[68,27611,89],{"class":112},[68,27613,886],{"class":74},[68,27615,27616,27618,27621,27623,27625,27627,27630,27632,27634,27636,27638,27641,27643],{"class":70,"line":98},[68,27617,739],{"class":74},[68,27619,27620],{"class":730},"action",[68,27622,4779],{"class":873},[68,27624,877],{"class":74},[68,27626,89],{"class":112},[68,27628,27629],{"class":116},"Rewrite",[68,27631,89],{"class":112},[68,27633,11438],{"class":873},[68,27635,877],{"class":74},[68,27637,89],{"class":112},[68,27639,27640],{"class":116},"chineseurl.php",[68,27642,89],{"class":112},[68,27644,886],{"class":74},[68,27646,27647,27649,27651],{"class":70,"line":123},[68,27648,771],{"class":74},[68,27650,27570],{"class":730},[68,27652,734],{"class":74},[28,27654,27655,27656,27659],{},"这里的",[65,27657,27658],{},"(tag|category)","根据具体情况做相应调整。",[28,27661,27662],{},"搞定！",[523,27664,27665],{},"html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s9AJx, html code.shiki .s9AJx{--shiki-light:#9C3EDA;--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":63,"searchDepth":78,"depth":78,"links":27667},[],"2013-10-03",{},"\u002Fposts\u002F2013\u002Ffix-chinese-url-in-wordpress-in-iis",{"text":1217,"minutes":27672,"time":27673,"words":7072},1.125,67500,{"title":27038,"description":27043},{"loc":27670},"posts\u002F2013\u002F20131003.fix-chinese-url-in-wordpress-in-iis",[543],"2w20CBhbfwFbKYR-D5axJJLPbVIwMXEa8bTBss4t2nY",{"id":27680,"title":27681,"body":27682,"class":528,"cover":1212,"coverSize":528,"date":27705,"description":27686,"draft":530,"extension":531,"hideComments":530,"location":27706,"meta":27707,"navigation":415,"path":27708,"readingTime":27709,"seo":27712,"sitemap":27713,"stem":27714,"tags":27715,"time":25070,"weather":528,"__hash__":27716},"posts\u002Fposts\u002F2013\u002F20130818.set-win8-explorer-to-my-computer.md","设置 Win8 文件资源管理器默认打开我的电脑",{"type":25,"value":27683,"toc":27703},[27684,27687,27690,27693,27700],[28,27685,27686],{},"在 Win8 中，任务栏上的文件资源管理器默认打开的是“库”，而我找文件的习惯是直接从我的电脑开始，库的功能用的并不多，因为我的文件放置都很有条理。怎么做呢？其实很简单。",[28,27688,27689],{},"Shift+右键资源管理器的图标，会弹出此快捷方式的右键菜单，而不是 JumpList 菜单，如下图。至于 JumpList 是什么，不必深究。",[554,27691],{"description":27692,"filename":556},"Shift+右键的菜单",[28,27694,27695,27696,27699],{},"在快捷方式属性里，将目标改为",[65,27697,27698],{},"explorer.exe shell:MyComputerFolder","，确定即可。",[554,27701],{"description":27702,"filename":1203},"设置文件资源管理器的目标位置",{"title":63,"searchDepth":78,"depth":78,"links":27704},[],"2013-08-18","成都",{},"\u002Fposts\u002F2013\u002Fset-win8-explorer-to-my-computer",{"text":7523,"minutes":27710,"time":27711,"words":3640},0.665,39900,{"title":27681,"description":27686},{"loc":27708},"posts\u002F2013\u002F20130818.set-win8-explorer-to-my-computer",[543],"U3pU9JhN7e9sJzpur9OsAFVqquDPUlHboSek8MZulE4",{"id":27718,"title":27719,"body":27720,"class":528,"cover":528,"coverSize":528,"date":27830,"description":63,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":27831,"navigation":415,"path":27832,"readingTime":27833,"seo":27836,"sitemap":27837,"stem":27838,"tags":27839,"time":528,"weather":528,"__hash__":27840},"posts\u002Fposts\u002F2013\u002F20130514.icbc-plugin-cause-blank-line-in-chrome.md","工行插件导致 Chrome 下方有莫名空行的问题",{"type":25,"value":27721,"toc":27828},[27722,27725,27728,27731,27734,27819,27822,27825],[554,27723],{"filename":27724},"cover.png",[28,27726,27727],{},"今天在调试自己的网页的时候，发现浏览器下方总是有一行 20px 高度的空格，见下图：",[554,27729],{"filename":556,"description":27730},"浏览器最下方有一个20px高的空行",[28,27732,27733],{},"找来找去发现我并没有这样的 padding 或者 margin，后来发现，html 最后面竟然多了这么个 DIV：",[58,27735,27737],{"className":19900,"code":27736,"language":19902,"meta":63,"style":63},"\u003Cdiv>\n  \u003Cobject id=\"ClCache\" click=\"sendMsg\" host=\"\" width=\"0\" height=\"0\">\u003C\u002Fobject>\n\u003C\u002Fdiv>\n",[65,27738,27739,27747,27811],{"__ignoreMap":63},[68,27740,27741,27743,27745],{"class":70,"line":71},[68,27742,727],{"class":74},[68,27744,19911],{"class":730},[68,27746,734],{"class":74},[68,27748,27749,27751,27754,27756,27758,27760,27763,27765,27768,27770,27772,27775,27777,27779,27781,27783,27786,27788,27790,27792,27794,27797,27799,27801,27803,27805,27807,27809],{"class":70,"line":78},[68,27750,19930],{"class":74},[68,27752,27753],{"class":730},"object",[68,27755,24582],{"class":873},[68,27757,877],{"class":74},[68,27759,89],{"class":112},[68,27761,27762],{"class":116},"ClCache",[68,27764,89],{"class":112},[68,27766,27767],{"class":873}," click",[68,27769,877],{"class":74},[68,27771,89],{"class":112},[68,27773,27774],{"class":116},"sendMsg",[68,27776,89],{"class":112},[68,27778,17421],{"class":873},[68,27780,877],{"class":74},[68,27782,1969],{"class":112},[68,27784,27785],{"class":873}," width",[68,27787,877],{"class":74},[68,27789,89],{"class":112},[68,27791,768],{"class":116},[68,27793,89],{"class":112},[68,27795,27796],{"class":873}," height",[68,27798,877],{"class":74},[68,27800,89],{"class":112},[68,27802,768],{"class":116},[68,27804,89],{"class":112},[68,27806,19946],{"class":74},[68,27808,27753],{"class":730},[68,27810,734],{"class":74},[68,27812,27813,27815,27817],{"class":70,"line":98},[68,27814,771],{"class":74},[68,27816,19911],{"class":730},[68,27818,734],{"class":74},[28,27820,27821],{},"不服气百度一下，发现这个是由于工行插件 ICBCChromeExtension 导致的，停用插件，问题解决。",[28,27823,27824],{},"我只想说，工行你太坑爹了！",[523,27826,27827],{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s9AJx, html code.shiki .s9AJx{--shiki-light:#9C3EDA;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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":63,"searchDepth":78,"depth":78,"links":27829},[],"2013-05-14",{},"\u002Fposts\u002F2013\u002Ficbc-plugin-cause-blank-line-in-chrome",{"text":7523,"minutes":27834,"time":27835,"words":3378},0.575,34500,{"title":27719,"description":63},{"loc":27832},"posts\u002F2013\u002F20130514.icbc-plugin-cause-blank-line-in-chrome",[543],"XCxwuutABiN9tzmWW59a_AFWme01E2KUXVV4mhjmhPQ",{"id":27842,"title":27843,"body":27844,"class":528,"cover":528,"coverSize":528,"date":27954,"description":27848,"draft":530,"extension":531,"hideComments":530,"location":27955,"meta":27956,"navigation":415,"path":27957,"readingTime":27958,"seo":27961,"sitemap":27962,"stem":27963,"tags":27964,"time":27965,"weather":528,"__hash__":27966},"posts\u002Fposts\u002F2013\u002F20130419.win8-app-develop-study-design-logo.md","Win8 应用开发学习记录 —— Logo 设计",{"type":25,"value":27845,"toc":27952},[27846,27849,27852,27855,27858,27861,27864,27867,27870,27872,27880,27883,27886,27889,27892,27895,27898,27901,27904,27907,27910,27913,27916,27919,27922,27925,27928,27931,27934,27937,27940,27943,27946,27949],[28,27847,27848],{},"Surface Pro 到手也有一段时间了，也把玩了这么些天，今天终于开始来写 Win8 的应用了。",[28,27850,27851],{},"环境搭建什么的就不详述了，Windows 8 Pro+Visual Studio 2012。",[28,27853,27854],{},"我想做的是 Win8 上的二维码应用，应用商店里有不少，但良莠不齐，大多都是英文的，中文的寥寥无几，而且质量都比较差。所以我准备做一款优秀的 QR Code For Win8 的应用。",[28,27856,27857],{},"一个好的应用，很重要的一点是用户体验，而用户体验的第一点就是 Logo，尤其在 Win8 界面下，Logo 是尤为重要的，下面是几个应用商店中我认为很不合格的 Logo：",[28,27859,27860],{},"（以下是在应用商店里搜索 Qr Code 出现的第一列应用）",[554,27862],{"filename":556,"description":27863},"Win8应用商店里搜索Qr Code出现的第一列结果",[28,27865,27866],{},"除了“Magnet QR Code Generator”勉强符合 Win8 风格外，其余均是不合格的。不合格原因是五颜六色或者过于杂乱。",[28,27868,27869],{},"说到二维码，第一感觉自然是以二维码本身作为 Logo 最为直观，我起初也是这样的打算，下面是我的第一个想法：",[554,27871],{"filename":1203},[28,27873,27874,27875],{},"想法是，QR Code For Win8，于是中间一个 Win8 的标志，外面是二维码，二维码本身采用的是“",[38,27876,27879],{"href":27877,"rel":27878},"http:\u002F\u002Fwww.haoest.com%E2%80%9D%E7%9A%84%E9%93%BE%E6%8E%A5%E3%80%82",[42],"http:\u002F\u002Fwww.haoest.com”的链接。",[28,27881,27882],{},"然而，它在 Win8 开始菜单中显示效果格格不入。",[28,27884,27885],{},"下面是 Win8 自带的几个应用的 Logo：",[554,27887],{"filename":1710,"description":27888},"Win8自带的几个应用的Logo",[28,27890,27891],{},"其特点是简洁、清爽，以白色前景色为图案，彩色为背景色。",[28,27893,27894],{},"于是，经过一段时间的构思和设计，我后来重新设计了 Logo 如下：",[554,27896],{"filename":1718,"description":27897},"新设计的二维码Logo",[28,27899,27900],{},"外面一个 Q，里面是个 Win8 的标志。",[28,27902,27903],{},"Logo 原型设计好了，下面开始为 Visual Studio 的需要进行适配了。",[28,27905,27906],{},"在 Visual Studio 中，你需要为 Win8 应用准备多种尺寸的 Logo：",[554,27908],{"filename":1729,"description":27909},"徽标——最基本的Logo，开始屏幕的方格Logo",[554,27911],{"filename":5287,"description":27912},"宽徽标——在开始屏幕中放大后占两个的Logo",[554,27914],{"filename":4862,"description":27915},"小徽标",[554,27917],{"filename":5309,"description":27918},"应用商店徽标",[554,27920],{"filename":5235,"description":27921},"徽章徽标——用于锁屏应用在锁屏时显示，不是必要的",[554,27923],{"filename":5238,"description":27924},"初始屏幕徽标——打开应用时出现的Logo",[28,27926,27927],{},"需要注意的是，你所生成的这些不同尺寸的图片只需要前景色的白色就行了，无需背景色，因为背景色可以在前面进行统一设置，这样的好处是，你可以随时把背景色换成你喜欢的颜色。",[554,27929],{"filename":5241,"description":27930},"可方便更改背景色",[28,27932,27933],{},"在这点上，我吃了大亏，之前的背景色是蓝色的，我也没有注意到这点，导致生成的所有图片都带有蓝色背景。再后来我想改成深灰色的时候，费了好长时间，最后发现了这个问题，又花了同样的时间，把深灰色背景色去掉，换成透明背景色。",[28,27935,27936],{},"上图中的“短名称”是显示在开始屏幕的 Logo 左下角的名称，前景文本指的就是这个名称的颜色，有深浅两种选择，分别为黑白两色。图块中的背景色指的就是上面所有的 Logo 徽标的背景色。而初始屏幕的背景色可以单独设置，如果为空，则使用图块的背景色。这样一来，你就可以方便的更改背景色了。",[28,27938,27939],{},"下面分别是我的 Logo 的两种显示效果：",[554,27941],{"filename":5214,"description":27942},"QR Code Logo 显示效果1",[554,27944],{"filename":4854,"description":27945},"QR Code Logo 显示效果2",[28,27947,27948],{},"怎么样，是不是与 Win8 风格很一致呢？",[28,27950,27951],{},"好的 Logo 是一个应用的开始，嗯，今天的工作就到这里，我也该睡觉了。",{"title":63,"searchDepth":78,"depth":78,"links":27953},[],"2013-04-19","宿舍",{},"\u002Fposts\u002F2013\u002Fwin8-app-develop-study-design-logo",{"text":8419,"minutes":123,"time":27959,"words":27960},240000,800,{"title":27843,"description":27848},{"loc":27957},"posts\u002F2013\u002F20130419.win8-app-develop-study-design-logo",[543],"01:27","VndOsU3KvQ-xAY9ETuF1cckQ2KN52liYm-HBzdpjVa4",{"id":27968,"title":27969,"body":27970,"class":528,"cover":528,"coverSize":528,"date":28062,"description":28063,"draft":530,"extension":531,"hideComments":530,"location":28064,"meta":28065,"navigation":415,"path":28066,"readingTime":28067,"seo":28068,"sitemap":28069,"stem":28070,"tags":28071,"time":25070,"weather":528,"__hash__":28072},"posts\u002Fposts\u002F2013\u002F20130227.forbid-index-of-your-host.md","虚拟主机如何禁止目录访问",{"type":25,"value":27971,"toc":28060},[27972,28000,28007,28010,28016,28031,28040,28045,28048,28054,28057],[28,27973,27974,27975,27979,27980,27984,27985,27989,27990,27994,27995,27999],{},"之前",[38,27976,27977],{"href":27977,"rel":27978},"http:\u002F\u002Fwww.haoest.com\u002F",[42],"上有一个 bug，最早的时候，我有个 Wordpress 的 Page，固定链接是",[38,27981,27982],{"href":27982,"rel":27983},"http:\u002F\u002Fwww.haoest.com\u002Fproducts\u002F",[42],"，然后有一些 Page 是在 products 下的，如",[38,27986,27987],{"href":27987,"rel":27988},"http:\u002F\u002Fwww.haoest.com\u002Fproducts\u002Fcapture-show\u002F",[42],"。原本是没有什么问题的。但是后来，为了减少子域名数量，我不想建立太多诸如",[38,27991,27992],{"href":27992,"rel":27993},"http:\u002F\u002Flistmanager.haoest.com\u002F",[42],"这样的网站，而改为",[38,27996,27997],{"href":27997,"rel":27998},"http:\u002F\u002Fwww.haoest.com\u002Fproducts\u002Flistmanager\u002F",[42],"这样的形式，把这些独立的站点都放到 products 文件夹里。只给一些重要的产品设置单独的子域名，因为我这个虚拟主机的子域名数量是有限的。但自从建立了 products 文件夹之后，问题出现了。",[28,28001,28002,28003,28006],{},"打开",[38,28004,27982],{"href":27982,"rel":28005},[42],"之后并不是显示原来的 products 的那个 page，而是显示一个 Index of 的页面，把我 products 文件夹的列表显示出来了，很不友好，也很不安全。发现这个问题后，我立马想到应该可以通过.htaccess 文件来解决，但后来不知道因为什么事情而耽搁了，就搞忘记了。后来，@梅崇华 提醒我这个问题，我才再次想起来这个事情。前些日子，通过一句简单的代码搞定了这个问题，今天把它记录下来，以便大家学习。",[28,28008,28009],{},"如果是自己的服务器的话，可以修改 httpd.conf 配置，把",[58,28011,28014],{"className":28012,"code":28013,"language":516},[514],"\u003CDirectory \"D:\u002Fxx\u002Fxx\u002Fxx\">\n    Options Indexes FollowSymLinks\n    AllowOverride All\n    Order allow,deny\n    Allow from all\n\u003C\u002FDirectory>\n",[65,28015,28013],{"__ignoreMap":63},[28,28017,28018,28019,28022,28023,28026,28027,28030],{},"中的",[65,28020,28021],{},"Options Indexes FollowSymLinks","改为",[65,28024,28025],{},"Options -Indexes FollowSymLinks","就可以了，也就是在",[65,28028,28029],{},"Indexes","前面加了个减号，减号表示关闭，加号表示开启，不加符号表示默认。",[28,28032,28033,28034,28039],{},"但是，在虚拟主机中没办法修改 httpd.conf 文件，我们可以修改.htaccess 文件，大多数虚拟主机都是可以使用.htaccess 的。 在 ",[38,28035,28038],{"href":28036,"rel":28037},"http:\u002F\u002Fwww.haoest.com",[42],"www.haoest.com"," 的目录下添加.hatccess 文件，如果没有的话，然后添加一句",[28,28041,28042],{},[65,28043,28044],{},"Options -Indexes",[28,28046,28047],{},"就可以了。 至于网上还有的教程是添加",[58,28049,28052],{"className":28050,"code":28051,"language":516},[514],"\u003CFiles *>\n    Options -Indexes\n\u003C\u002FFiles>\n",[65,28053,28051],{"__ignoreMap":63},[28,28055,28056],{},"这个 Files 是什么意思，目前还不是很清楚，不过也是可以的。",[28,28058,28059],{},"给大家推荐一个 Wordpress 的 SEO 插件，叫 Yoast WordPress SEO，超级强大，其设置里有个“编辑文件”，可以直接编辑 robots.txt 文件和.htaccess 文件，很方便。",{"title":63,"searchDepth":78,"depth":78,"links":28061},[],"2013-02-27","之前http:\u002F\u002Fwww.haoest.com\u002F上有一个 bug，最早的时候，我有个 Wordpress 的 Page，固定链接是http:\u002F\u002Fwww.haoest.com\u002Fproducts\u002F，然后有一些 Page 是在 products 下的，如http:\u002F\u002Fwww.haoest.com\u002Fproducts\u002Fcapture-show\u002F。原本是没有什么问题的。但是后来，为了减少子域名数量，我不想建立太多诸如http:\u002F\u002Flistmanager.haoest.com\u002F这样的网站，而改为http:\u002F\u002Fwww.haoest.com\u002Fproducts\u002Flistmanager\u002F这样的形式，把这些独立的站点都放到 products 文件夹里。只给一些重要的产品设置单独的子域名，因为我这个虚拟主机的子域名数量是有限的。但自从建立了 products 文件夹之后，问题出现了。","教室",{},"\u002Fposts\u002F2013\u002Fforbid-index-of-your-host",{"text":535,"minutes":25382,"time":25383,"words":25384},{"title":27969,"description":28063},{"loc":28066},"posts\u002F2013\u002F20130227.forbid-index-of-your-host",[543],"vUXp3_nSdKFZpl8aHkPZJMsaHi0leEY9rzR_ZPuTVD0",{"id":28074,"title":28075,"body":28076,"class":528,"cover":528,"coverSize":528,"date":28100,"description":28080,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":28101,"navigation":415,"path":28102,"readingTime":28103,"seo":28107,"sitemap":28108,"stem":28109,"tags":28110,"time":28111,"weather":528,"__hash__":28112},"posts\u002Fposts\u002F2013\u002F20130216.uninstall-orbit-plugin-for-chrome.md","卸载 Orbit 之后 Chrome 下载仍指向 Orbit 的解决方法",{"type":25,"value":28077,"toc":28098},[28078,28081,28084,28086,28093,28095],[28,28079,28080],{},"之前使用过 Orbit，外国的一款 P2P 下载软件，比较优秀精简，大可替代迅雷。我之前用它是使用他的 Grab++功能，可以捕捉 own3D 上的视频，这个让我用得很爽。说起迅雷，我总是很不爽，迅雷的界面实在是太土了，一味追求绚丽的效果，界面总是卡死，这一点上，Orbit 实在是好多了，简洁的界面，从不会卡死。然而，迅雷在国内的下载速度确实是应该比 Orbit 好一点，具体我也没有去测试，可以确定的是 Orbit 是不会在后台默默上传的，所以相对来说，可能下载速度会稍微差一点点。所以后来我也就不用了，卸载了。",[28,28082,28083],{},"然而，卸载之后却发生了问题，我在 Chrome 下直接点击一些链接，还是会出现“Download transferred to Orbit Downloader. Click ‘Previous’ to go back and keep browsing.”，如下图：",[554,28085],{"filename":556},[28,28087,28088,28089,28092],{},"导致我一直以为我没有卸载 Orbit，后来我找来找去，都找不到 Orbit 的卸载文件，也找不到它的安装目录，我想，应该是卸载了的呀！后来一想，既然是在 Chrome 下出现问题，估计是插件的原因，于是在 Chrome 地址栏输入：",[65,28090,28091],{},"chrome:\u002F\u002Fplugins\u002F","进入 Chrome 的插件管理，果然发现一个 Orbit Downloader 的插件，如下图，停用，问题解决。",[554,28094],{"filename":1203},[28,28096,28097],{},"当然，可以去插件的目录，把 nporbit.dll 删除即可。",{"title":63,"searchDepth":78,"depth":78,"links":28099},[],"2013-02-16",{},"\u002Fposts\u002F2013\u002Funinstall-orbit-plugin-for-chrome",{"text":1217,"minutes":28104,"time":28105,"words":28106},1.83,109800,366,{"title":28075,"description":28080},{"loc":28102},"posts\u002F2013\u002F20130216.uninstall-orbit-plugin-for-chrome",[543],"晚上","pgaLM0X6thwggLKh5wbxBDgQV64Mpa-8Y5FzO1AOqFM",{"id":28114,"title":28115,"body":28116,"class":528,"cover":528,"coverSize":528,"date":28137,"description":28138,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":28139,"navigation":415,"path":28140,"readingTime":28141,"seo":28144,"sitemap":28145,"stem":28146,"tags":28147,"time":28111,"weather":528,"__hash__":28148},"posts\u002Fposts\u002F2012\u002F20121219.snaps-to-device-pixels.md","【WPF 学习日记】SnapsToDevicePixels 属性",{"type":25,"value":28117,"toc":28135},[28118,28125,28128],[28,28119,28120,28121,28124],{},"今天在给 ListBoxItem 添加 Border 的时候，发现 ",[65,28122,28123],{},"BorderThickness = 1"," 时，出现了意料之外的问题。",[28,28126,28127],{},"我给每个 ListBoxItem 的 Border 设置为，上、左、右的厚度为 0，下方的厚度为 1，这样的效果是把 ListBoxItem 分隔开来，但是我发现他们之间的分割线，居然不一样厚，有点模糊，不是点阵的线，而是有那种抗锯齿的感觉，很不爽。",[28,28129,28130,28131,28134],{},"后来发现 Border 有 ",[65,28132,28133],{},"SnapsToDevicePixels"," 这么个属性，看名字感觉或许有效果，把它打勾之后，发现果然问题解决了。它的作用是使像素与显示器对齐，我的理解就是点阵效果。",{"title":63,"searchDepth":78,"depth":78,"links":28136},[],"2012-12-19","今天在给 ListBoxItem 添加 Border 的时候，发现 BorderThickness = 1 时，出现了意料之外的问题。",{},"\u002Fposts\u002F2012\u002Fsnaps-to-device-pixels",{"text":7523,"minutes":28142,"time":28143,"words":4147},0.82,49200,{"title":28115,"description":28138},{"loc":28140},"posts\u002F2012\u002F20121219.snaps-to-device-pixels",[543,23440],"ARRLsels47DUMIQffXFfMFRUzrkl0Xdp9FmcNHjKtbU",{"id":28150,"title":28151,"body":28152,"class":528,"cover":528,"coverSize":528,"date":28165,"description":28156,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":28166,"navigation":415,"path":28167,"readingTime":28168,"seo":28171,"sitemap":28172,"stem":28173,"tags":28174,"time":528,"weather":528,"__hash__":28175},"posts\u002Fposts\u002F2012\u002F20121217.try-mono-for-android.md","Mono For Android 试用感想",{"type":25,"value":28153,"toc":28163},[28154,28157,28160],[28,28155,28156],{},"昨天发现一个很厉害的工具，Mono，可以使用 C#来写 Android 程序，如果可以用 Visual Studio 来写 Android 程序，那简直太爽了！",[28,28158,28159],{},"很兴奋，花了很长时间把搭建环境需要的东西都下载好了。早上花了一早上把环境搭建好了，测试了一个 Demo，很伤心。",[28,28161,28162],{},"以为它生成的 apk 文件是可以直接在机器上运行的，然而发现它还需要在 Android 上安装它所需要的环境，需要安装十几兆的东西，这个不能忍。也不知道是不是免费版的原因，总之不令我满意。Over。还是乖乖用 Eclipse 和 Java 吧。",{"title":63,"searchDepth":78,"depth":78,"links":28164},[],"2012-12-17",{},"\u002Fposts\u002F2012\u002Ftry-mono-for-android",{"text":7523,"minutes":28169,"time":28170,"words":4313},0.87,52200,{"title":28151,"description":28156},{"loc":28167},"posts\u002F2012\u002F20121217.try-mono-for-android",[543,23440],"pQPCJKzAXg8qa5MIcom8VvCRwiiaY54XoemmHjQqvxI",{"id":28177,"title":28178,"body":28179,"class":528,"cover":528,"coverSize":528,"date":28323,"description":28183,"draft":530,"extension":531,"hideComments":530,"location":27955,"meta":28324,"navigation":415,"path":28325,"readingTime":28326,"seo":28330,"sitemap":28331,"stem":28332,"tags":28333,"time":28111,"weather":528,"__hash__":28334},"posts\u002Fposts\u002F2012\u002F20121215.type-initialization-exception.md","静态成员初始化异常的解决办法",{"type":25,"value":28180,"toc":28321},[28181,28184,28187,28190,28311,28318],[28,28182,28183],{},"今天遇到了这样一个问题，在调用某个类的静态方法时，产生了这样一个异常“未处理 TypeInitializationException “XXXXX”的类型初始值设定项引发异常。”然而，这个方法我是做了异常处理的，可就是无法捕获到这个异常的位置。后来发现，是由于这个类里的静态成员初始化产生了异常，这个要如何解决呢？",[28,28185,28186],{},"我们可以利用静态构造函数来解决这个问题，静态构造函数和实例构造函数之间的区别在于静态构造函数是由 CLR 调用执行的，所以静态构造函数只能是一个，同时不能有参数。",[28,28188,28189],{},"使用方法如下：",[58,28191,28193],{"className":24038,"code":28192,"language":24040,"meta":63,"style":63},"public class Command\n{\n    private static SimpleDatabase simpleDatabase;\n    static Command() \u002F\u002F前面不能有修饰符\n    {\n        try\n        {\n            simpleDatabase = new SimpleDatabase(\"settings.db\", \"mima\");\n        }\n        catch\n        {\n            \u002F\u002F处理异常\n        }\n    }\n}\n",[65,28194,28195,28204,28208,28223,28236,28240,28245,28250,28281,28285,28290,28294,28299,28303,28307],{"__ignoreMap":63},[68,28196,28197,28199,28201],{"class":70,"line":71},[68,28198,25782],{"class":1864},[68,28200,19914],{"class":24047},[68,28202,28203],{"class":1511}," Command\n",[68,28205,28206],{"class":70,"line":78},[68,28207,75],{"class":74},[68,28209,28210,28212,28215,28218,28221],{"class":70,"line":98},[68,28211,14941],{"class":1864},[68,28213,28214],{"class":1864}," static",[68,28216,28217],{"class":1511}," SimpleDatabase",[68,28219,28220],{"class":1511}," simpleDatabase",[68,28222,5136],{"class":74},[68,28224,28225,28228,28231,28233],{"class":70,"line":123},[68,28226,28227],{"class":1864},"    static",[68,28229,28230],{"class":2183}," Command",[68,28232,3345],{"class":74},[68,28234,28235],{"class":2403}," \u002F\u002F前面不能有修饰符\n",[68,28237,28238],{"class":70,"line":129},[68,28239,188],{"class":74},[68,28241,28242],{"class":70,"line":212},[68,28243,28244],{"class":1790},"        try\n",[68,28246,28247],{"class":70,"line":233},[68,28248,28249],{"class":74},"        {\n",[68,28251,28252,28255,28257,28259,28261,28263,28265,28268,28270,28272,28274,28277,28279],{"class":70,"line":268},[68,28253,28254],{"class":373},"            simpleDatabase ",[68,28256,877],{"class":1899},[68,28258,7746],{"class":1899},[68,28260,28217],{"class":1511},[68,28262,648],{"class":74},[68,28264,89],{"class":112},[68,28266,28267],{"class":116},"settings.db",[68,28269,89],{"class":112},[68,28271,255],{"class":74},[68,28273,113],{"class":112},[68,28275,28276],{"class":116},"mima",[68,28278,89],{"class":112},[68,28280,20480],{"class":74},[68,28282,28283],{"class":70,"line":289},[68,28284,2589],{"class":74},[68,28286,28287],{"class":70,"line":308},[68,28288,28289],{"class":1790},"        catch\n",[68,28291,28292],{"class":70,"line":314},[68,28293,28249],{"class":74},[68,28295,28296],{"class":70,"line":320},[68,28297,28298],{"class":2403},"            \u002F\u002F处理异常\n",[68,28300,28301],{"class":70,"line":889},[68,28302,2589],{"class":74},[68,28304,28305],{"class":70,"line":909},[68,28306,311],{"class":74},[68,28308,28309],{"class":70,"line":929},[68,28310,132],{"class":74},[28,28312,28313,28314,28317],{},"注意， 在 catch 到异常之后，我刚开始还想 ",[65,28315,28316],{},"throw new Exception(\"SimpleDatabase初始化失败\")","，但是这样是不行的，依旧会在外层报出 “TypeInitializationException” 的异常。所以异常只能在静态构造函数内部处理掉，我的解决办法是，既然我不能打开自己建立的数据库，那么这个数据库文件肯定损坏了，或者并不是我原先生成的数据库，那我就重新建立一个数据库文件好了，因为我这个数据库文件里只是存放的一些简单配置，所以无妨。",[523,28319,28320],{},"html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sG8yY, html code.shiki .sG8yY{--shiki-light:#E2931D;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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":63,"searchDepth":78,"depth":78,"links":28322},[],"2012-12-15",{},"\u002Fposts\u002F2012\u002Ftype-initialization-exception",{"text":1217,"minutes":28327,"time":28328,"words":28329},1.86,111600,372,{"title":28178,"description":28183},{"loc":28325},"posts\u002F2012\u002F20121215.type-initialization-exception",[543,23440],"TZeRlPit3sTNSuXBopyj3_DIJggbee8Zo6i4UTJq9Cs",{"id":28336,"title":28337,"body":28338,"class":528,"cover":528,"coverSize":528,"date":28354,"description":28342,"draft":530,"extension":531,"hideComments":530,"location":27955,"meta":28355,"navigation":415,"path":28356,"readingTime":28357,"seo":28361,"sitemap":28362,"stem":28363,"tags":28364,"time":28365,"weather":528,"__hash__":28366},"posts\u002Fposts\u002F2012\u002F20121130.could-not-load-file-or-assembly.md","未能加载文件或程序集 “XXXXXXX” 或它的某一个依赖项。试图加载格式不正确的程序的解决方法",{"type":25,"value":28339,"toc":28352},[28340,28343,28346,28349],[28,28341,28342],{},"好久没有写博客了。",[28,28344,28345],{},"这个问题之前遇到过一次，但是当时没有记下来，导致今天遇到这个问题的时候，我已经想不起来是什么原因了，又花了好长时间搜索、查找，才终于解决，所以我决定把它记下来。",[28,28347,28348],{},"无法加载文件或程序集，可能有多种原因，网上多数是因为 DLL 没有复制到 bin 文件夹下，或者程序集名称不一致等等，但这些并不是我的项目里所发生的。",[28,28350,28351],{},"我的问题其实是这样的，我的 A 项目引用了我自己写的一个类库 B，B 里面引用了一个 DLL，但是这个 DLL 的目标平台是 x86，而我的 A 项目的目标平台是 Any CPU，导致无法运行，所以只需要把 A 项目的目标平台修改为 x86，就 OK 了。至于类库 B，还是默认的 Any CPU，好像也没有什么问题。",{"title":63,"searchDepth":78,"depth":78,"links":28353},[],"2012-11-30",{},"\u002Fposts\u002F2012\u002Fcould-not-load-file-or-assembly",{"text":1217,"minutes":28358,"time":28359,"words":28360},1.26,75600,252,{"title":28337,"description":28342},{"loc":28356},"posts\u002F2012\u002F20121130.could-not-load-file-or-assembly",[543,23440],"下午","MrVf6xwahX4tUMhv9bQXMqqpee1PV-Yc4MSD3_VGyFI",{"id":28368,"title":28369,"body":28370,"class":528,"cover":528,"coverSize":528,"date":28460,"description":28374,"draft":530,"extension":531,"hideComments":530,"location":27955,"meta":28461,"navigation":415,"path":28462,"readingTime":28463,"seo":28466,"sitemap":28467,"stem":28468,"tags":28469,"time":25070,"weather":528,"__hash__":28470},"posts\u002Fposts\u002F2012\u002F20121105.ubuntu-mp3-messy-code.md","关于 Ubuntu 下音乐列表乱码的解决方法",{"type":25,"value":28371,"toc":28458},[28372,28375,28382,28400,28403,28406,28427,28430,28452,28455],[28,28373,28374],{},"使用 Ubuntu 听音乐的时候，播放列表不少歌曲显示为乱码，非常不爽。",[28,28376,28377,28378,28381],{},"简单的方法就是将 MP3 标签转换为 Unicode 编码，要使用到 ",[65,28379,28380],{},"python-mutagen","，在新立得软件管理中可以直接找到，也可以用以下的命令进行安装：",[58,28383,28385],{"className":1502,"code":28384,"language":1504,"meta":63,"style":63},"sudo apt-get install python-mutagen\n",[65,28386,28387],{"__ignoreMap":63},[68,28388,28389,28392,28395,28397],{"class":70,"line":71},[68,28390,28391],{"class":1511},"sudo",[68,28393,28394],{"class":116}," apt-get",[68,28396,16245],{"class":116},[68,28398,28399],{"class":116}," python-mutagen\n",[28,28401,28402],{},"使用方法：",[28,28404,28405],{},"在终端中进入音乐文件所在的目录，执行：",[58,28407,28409],{"className":1502,"code":28408,"language":1504,"meta":63,"style":63},"mid3iconv -e gbk *.mp3\n",[65,28410,28411],{"__ignoreMap":63},[68,28412,28413,28416,28419,28422,28424],{"class":70,"line":71},[68,28414,28415],{"class":1511},"mid3iconv",[68,28417,28418],{"class":1518}," -e",[68,28420,28421],{"class":116}," gbk",[68,28423,8798],{"class":665},[68,28425,28426],{"class":116},".mp3\n",[28,28428,28429],{},"如果有子目录的话，再执行：",[58,28431,28433],{"className":1502,"code":28432,"language":1504,"meta":63,"style":63},"mid3iconv -e GBK *\u002F*.mp3\n",[65,28434,28435],{"__ignoreMap":63},[68,28436,28437,28439,28441,28444,28446,28448,28450],{"class":70,"line":71},[68,28438,28415],{"class":1511},[68,28440,28418],{"class":1518},[68,28442,28443],{"class":116}," GBK",[68,28445,8798],{"class":665},[68,28447,6],{"class":116},[68,28449,7661],{"class":665},[68,28451,28426],{"class":116},[28,28453,28454],{},"现在再看看，是不是搞定啦？",[523,28456,28457],{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":63,"searchDepth":78,"depth":78,"links":28459},[],"2012-11-05",{},"\u002Fposts\u002F2012\u002Fubuntu-mp3-messy-code",{"text":7523,"minutes":28464,"time":28465,"words":3597},0.65,39000,{"title":28369,"description":28374},{"loc":28462},"posts\u002F2012\u002F20121105.ubuntu-mp3-messy-code",[543,7530],"ANMlw5_GHk2BAWS3F52v4vtu4XjHz9tNopYz2HvQ9vk",{"id":28472,"title":28473,"body":28474,"class":528,"cover":528,"coverSize":528,"date":28608,"description":28478,"draft":530,"extension":531,"hideComments":530,"location":27955,"meta":28609,"navigation":415,"path":28610,"readingTime":28611,"seo":28615,"sitemap":28616,"stem":28617,"tags":28618,"time":28111,"weather":528,"__hash__":28619},"posts\u002Fposts\u002F2012\u002F20120925.sqlite-error-no-such-table.md","关于 sqlite error no such table 错误的解决办法",{"type":25,"value":28475,"toc":28606},[28476,28479,28481,28483,28486,28497,28528,28531,28597,28600,28603],[28,28477,28478],{},"今天开始尝试为“星际 2 客户端切换器”添加 Win7 下的 JumpList 功能，如图：",[554,28480],{"filename":556},[554,28482],{"filename":1203},[28,28484,28485],{},"这样可以在任务栏和开始菜单中快速启动相应的客户端，而不需要启动软件。",[28,28487,28488,28489,28492,28493,28496],{},"在测试过程中遇到一个错误，",[65,28490,28491],{},"sqlite error no such table…","，百思不得其解，一直以为是由于 JumpList 在未启动程序的情况下调用程序内的方法导致数据库没有加载的问题，但后来始终没有弄明白，一直在搜索关于 JumpList 调用的问题。后来干脆直接搜 ",[65,28494,28495],{},"sqlite error no such table"," 的错误原因，才发现问题所在。我的数据库调用使用的是相对地址：",[58,28498,28500],{"className":24038,"code":28499,"language":24040,"meta":63,"style":63},"SQLiteConnection conn = new SQLiteConnection(\"Data Source = settings.db;\");\n",[65,28501,28502],{"__ignoreMap":63},[68,28503,28504,28507,28510,28512,28514,28517,28519,28521,28524,28526],{"class":70,"line":71},[68,28505,28506],{"class":1511},"SQLiteConnection",[68,28508,28509],{"class":1511}," conn",[68,28511,1900],{"class":1899},[68,28513,7746],{"class":1899},[68,28515,28516],{"class":1511}," SQLiteConnection",[68,28518,648],{"class":74},[68,28520,89],{"class":112},[68,28522,28523],{"class":116},"Data Source = settings.db;",[68,28525,89],{"class":112},[68,28527,20480],{"class":74},[28,28529,28530],{},"在直接打开软件是没有任何问题的，但是通过 JumpList 调用的话，我怀疑启动的位置不一样，所以导致找不到数据库，于是换成：",[58,28532,28534],{"className":24038,"code":28533,"language":24040,"meta":63,"style":63},"SQLiteConnection conn = new SQLiteConnection(@\"Data Source =\" + System.Windows.Forms.Application.StartupPath + \"\\\\settings.db;\");\n",[65,28535,28536],{"__ignoreMap":63},[68,28537,28538,28540,28542,28544,28546,28548,28550,28553,28556,28558,28561,28564,28566,28568,28570,28573,28575,28578,28580,28583,28585,28587,28590,28593,28595],{"class":70,"line":71},[68,28539,28506],{"class":1511},[68,28541,28509],{"class":1511},[68,28543,1900],{"class":1899},[68,28545,7746],{"class":1899},[68,28547,28516],{"class":1511},[68,28549,648],{"class":74},[68,28551,28552],{"class":112},"@\"",[68,28554,28555],{"class":116},"Data Source =",[68,28557,89],{"class":112},[68,28559,28560],{"class":1899}," +",[68,28562,28563],{"class":373}," System",[68,28565,404],{"class":74},[68,28567,21722],{"class":373},[68,28569,404],{"class":74},[68,28571,28572],{"class":373},"Forms",[68,28574,404],{"class":74},[68,28576,28577],{"class":373},"Application",[68,28579,404],{"class":74},[68,28581,28582],{"class":373},"StartupPath ",[68,28584,1670],{"class":1899},[68,28586,113],{"class":112},[68,28588,28589],{"class":665},"\\\\",[68,28591,28592],{"class":116},"settings.db;",[68,28594,89],{"class":112},[68,28596,20480],{"class":74},[28,28598,28599],{},"问题搞定！",[28,28601,28602],{},"今后数据库文件还是用绝对位置比较妥当。",[523,28604,28605],{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":63,"searchDepth":78,"depth":78,"links":28607},[],"2012-09-25",{},"\u002Fposts\u002F2012\u002Fsqlite-error-no-such-table",{"text":1217,"minutes":28612,"time":28613,"words":28614},1.315,78900,263,{"title":28473,"description":28478},{"loc":28610},"posts\u002F2012\u002F20120925.sqlite-error-no-such-table",[543,23440],"P6y0J80I5fzcC1tuPSFGnPDkKZlIcPcpa1iAtl6m4KM",{"id":28621,"title":28622,"body":28623,"class":528,"cover":528,"coverSize":528,"date":29177,"description":28627,"draft":530,"extension":531,"hideComments":530,"location":27706,"meta":29178,"navigation":415,"path":29179,"readingTime":29180,"seo":29184,"sitemap":29185,"stem":29186,"tags":29187,"time":29188,"weather":528,"__hash__":29189},"posts\u002Fposts\u002F2012\u002F20120731.android-unit-test.md","Android 单元测试",{"type":25,"value":28624,"toc":29175},[28625,28628,28631,28634,28637,28644,28647,28654,28660,28690,28695,28763,28766,28848,28851,28857,28886,28889,29150,29153,29156,29159,29161,29164,29166,29169,29172],[28,28626,28627],{},"今天终于向让我退缩了很久的单元测试前进了一步。",[28,28629,28630],{},"很早就知道 Android 里可以建立测试项目，但我一直不明白怎么去用，也总觉得我的这些个小项目，需要用到测试这么高端的东西吗？今天突然对之前搁置很久的全能计算器的重构有了一些灵感，写了个计算专用的工具类，然而，之前项目中有不少错误，现在是没法运行的，于是，我只想对这个工具类进行测试，怎么做呢？",[28,28632,28633],{},"如果不用单元测试的话，得先把整个项目的错误改掉，编译成功，然后通过日志输出的方式来测试那个类，但相当繁琐。如果使用单元测试的话，就相当轻松啦。",[28,28635,28636],{},"Android 里的单元测试有两种方式，一种是建立一个新的测试项目（Android Test Project），那个似乎是对整个项目进行测试的，没有仔细去了解，还是比较庞大，跟我们这里的要求不符。",[28,28638,28639,28640,28643],{},"另一种方式，则是在需要测试的项目里新建一个测试类，继承 ",[65,28641,28642],{},"AndroidTestCase","，然后运行时使用 Android JUnit Test 的方式运行就可以了。",[28,28645,28646],{},"下面开始看代码：",[28,28648,28649,28650,28653],{},"首先，需要对项目的 ",[65,28651,28652],{},"AndroidManifest.xml"," 文件进行一些改动",[28,28655,1659,28656,28659],{},[65,28657,28658],{},"\u003Capplication>"," 结点里加入：",[58,28661,28663],{"className":718,"code":28662,"language":720,"meta":63,"style":63},"\u003Cuses-library android:name=\"android.test.runner\" \u002F>\n",[65,28664,28665],{"__ignoreMap":63},[68,28666,28667,28669,28672,28675,28677,28679,28681,28683,28686,28688],{"class":70,"line":71},[68,28668,727],{"class":74},[68,28670,28671],{"class":730},"uses-library",[68,28673,28674],{"class":873}," android",[68,28676,92],{"class":20247},[68,28678,217],{"class":873},[68,28680,877],{"class":74},[68,28682,89],{"class":112},[68,28684,28685],{"class":116},"android.test.runner",[68,28687,89],{"class":112},[68,28689,886],{"class":74},[28,28691,1659,28692,28694],{},[65,28693,28658],{}," 结点外加入：",[58,28696,28698],{"className":718,"code":28697,"language":720,"meta":63,"style":63},"\u003Cinstrumentation\n    android:name=\"android.test.InstrumentationTestRunner\"\n    android:label=\"Test\"\n    android:targetPackage=\"你的包名\" \u002F>\n",[65,28699,28700,28707,28725,28743],{"__ignoreMap":63},[68,28701,28702,28704],{"class":70,"line":71},[68,28703,727],{"class":74},[68,28705,28706],{"class":730},"instrumentation\n",[68,28708,28709,28712,28714,28716,28718,28720,28723],{"class":70,"line":78},[68,28710,28711],{"class":873},"    android",[68,28713,92],{"class":20247},[68,28715,217],{"class":873},[68,28717,877],{"class":74},[68,28719,89],{"class":112},[68,28721,28722],{"class":116},"android.test.InstrumentationTestRunner",[68,28724,120],{"class":112},[68,28726,28727,28729,28731,28734,28736,28738,28741],{"class":70,"line":98},[68,28728,28711],{"class":873},[68,28730,92],{"class":20247},[68,28732,28733],{"class":873},"label",[68,28735,877],{"class":74},[68,28737,89],{"class":112},[68,28739,28740],{"class":116},"Test",[68,28742,120],{"class":112},[68,28744,28745,28747,28749,28752,28754,28756,28759,28761],{"class":70,"line":123},[68,28746,28711],{"class":873},[68,28748,92],{"class":20247},[68,28750,28751],{"class":873},"targetPackage",[68,28753,877],{"class":74},[68,28755,89],{"class":112},[68,28757,28758],{"class":116},"你的包名",[68,28760,89],{"class":112},[68,28762,886],{"class":74},[28,28764,28765],{},"注意，此处的包名一定要与最上方的",[58,28767,28769],{"className":718,"code":28768,"language":720,"meta":63,"style":63},"\u003Cmanifest xmlns:android=\"http:\u002F\u002Fschemas.android.com\u002Fapk\u002Fres\u002Fandroid\"\n    package=\"包名\"\n    android:versionCode=\"版本号\"\n    android:versionName=\"版本名\" >\n",[65,28770,28771,28795,28809,28827],{"__ignoreMap":63},[68,28772,28773,28775,28778,28781,28783,28786,28788,28790,28793],{"class":70,"line":71},[68,28774,727],{"class":74},[68,28776,28777],{"class":730},"manifest",[68,28779,28780],{"class":873}," xmlns",[68,28782,92],{"class":20247},[68,28784,28785],{"class":873},"android",[68,28787,877],{"class":74},[68,28789,89],{"class":112},[68,28791,28792],{"class":116},"http:\u002F\u002Fschemas.android.com\u002Fapk\u002Fres\u002Fandroid",[68,28794,120],{"class":112},[68,28796,28797,28800,28802,28804,28807],{"class":70,"line":78},[68,28798,28799],{"class":873},"    package",[68,28801,877],{"class":74},[68,28803,89],{"class":112},[68,28805,28806],{"class":116},"包名",[68,28808,120],{"class":112},[68,28810,28811,28813,28815,28818,28820,28822,28825],{"class":70,"line":98},[68,28812,28711],{"class":873},[68,28814,92],{"class":20247},[68,28816,28817],{"class":873},"versionCode",[68,28819,877],{"class":74},[68,28821,89],{"class":112},[68,28823,28824],{"class":116},"版本号",[68,28826,120],{"class":112},[68,28828,28829,28831,28833,28836,28838,28840,28843,28845],{"class":70,"line":123},[68,28830,28711],{"class":873},[68,28832,92],{"class":20247},[68,28834,28835],{"class":873},"versionName",[68,28837,877],{"class":74},[68,28839,89],{"class":112},[68,28841,28842],{"class":116},"版本名",[68,28844,89],{"class":112},[68,28846,28847],{"class":74}," >\n",[28,28849,28850],{},"这里的包名一致。",[28,28852,28853,28854,28856],{},"同时，还需要添加权限，也在 ",[65,28855,28658],{}," 外：",[58,28858,28860],{"className":718,"code":28859,"language":720,"meta":63,"style":63},"\u003Cuses-permission android:name=\"android.permission.RUN_INSTRUMENTATION\" \u002F>\n",[65,28861,28862],{"__ignoreMap":63},[68,28863,28864,28866,28869,28871,28873,28875,28877,28879,28882,28884],{"class":70,"line":71},[68,28865,727],{"class":74},[68,28867,28868],{"class":730},"uses-permission",[68,28870,28674],{"class":873},[68,28872,92],{"class":20247},[68,28874,217],{"class":873},[68,28876,877],{"class":74},[68,28878,89],{"class":112},[68,28880,28881],{"class":116},"android.permission.RUN_INSTRUMENTATION",[68,28883,89],{"class":112},[68,28885,886],{"class":74},[28,28887,28888],{},"下面，新建一个类，我是新建的一个 CalcTest 类，用来测试 Calc 类的运行情况：",[58,28890,28893],{"className":28891,"code":28892,"language":205,"meta":63,"style":63},"language-java shiki shiki-themes material-theme-lighter github-light github-dark","public class CalcTest extends AndroidTestCase\n{\n    private static final String TAG = \"CalcTest\";\n\n    public void testPlus()\n    {\n        String answer = Calc.add(\"1.31\", \"4.63\");\n        Log.i(TAG, answer);\n        Assert.assertEquals(\"5.94\", answer);\n    }\n\n    public void testMinus()\n    {\n        String answer = Calc.subtract(\"1.7\", \"1.6\");\n        Log.i(TAG, answer);\n        Assert.assertEquals(\"0.1\", answer);\n    }\n}\n",[65,28894,28895,28910,28914,28941,28945,28956,28960,28998,29019,29044,29048,29052,29063,29067,29102,29120,29142,29146],{"__ignoreMap":63},[68,28896,28897,28899,28901,28904,28907],{"class":70,"line":71},[68,28898,25782],{"class":1864},[68,28900,19914],{"class":1864},[68,28902,28903],{"class":1511}," CalcTest",[68,28905,28906],{"class":1864}," extends",[68,28908,28909],{"class":1511}," AndroidTestCase\n",[68,28911,28912],{"class":70,"line":78},[68,28913,75],{"class":74},[68,28915,28916,28918,28920,28923,28927,28930,28932,28934,28937,28939],{"class":70,"line":98},[68,28917,14941],{"class":1864},[68,28919,28214],{"class":1864},[68,28921,28922],{"class":1864}," final",[68,28924,28926],{"class":28925},"s_bVq"," String",[68,28928,28929],{"class":373}," TAG ",[68,28931,877],{"class":1899},[68,28933,113],{"class":112},[68,28935,28936],{"class":116},"CalcTest",[68,28938,89],{"class":112},[68,28940,5136],{"class":74},[68,28942,28943],{"class":70,"line":123},[68,28944,416],{"emptyLinePlaceholder":415},[68,28946,28947,28949,28951,28954],{"class":70,"line":129},[68,28948,25844],{"class":1864},[68,28950,26385],{"class":1864},[68,28952,28953],{"class":2183}," testPlus",[68,28955,2136],{"class":74},[68,28957,28958],{"class":70,"line":212},[68,28959,188],{"class":74},[68,28961,28962,28965,28968,28970,28973,28975,28978,28980,28982,28985,28987,28989,28991,28994,28996],{"class":70,"line":233},[68,28963,28964],{"class":28925},"        String",[68,28966,28967],{"class":373}," answer ",[68,28969,877],{"class":1899},[68,28971,28972],{"class":373}," Calc",[68,28974,404],{"class":74},[68,28976,28977],{"class":2183},"add",[68,28979,648],{"class":74},[68,28981,89],{"class":112},[68,28983,28984],{"class":116},"1.31",[68,28986,89],{"class":112},[68,28988,255],{"class":74},[68,28990,113],{"class":112},[68,28992,28993],{"class":116},"4.63",[68,28995,89],{"class":112},[68,28997,20480],{"class":74},[68,28999,29000,29003,29005,29007,29009,29012,29014,29017],{"class":70,"line":268},[68,29001,29002],{"class":373},"        Log",[68,29004,404],{"class":74},[68,29006,2773],{"class":2183},[68,29008,648],{"class":74},[68,29010,29011],{"class":373},"TAG",[68,29013,255],{"class":74},[68,29015,29016],{"class":373}," answer",[68,29018,20480],{"class":74},[68,29020,29021,29024,29026,29029,29031,29033,29036,29038,29040,29042],{"class":70,"line":289},[68,29022,29023],{"class":373},"        Assert",[68,29025,404],{"class":74},[68,29027,29028],{"class":2183},"assertEquals",[68,29030,648],{"class":74},[68,29032,89],{"class":112},[68,29034,29035],{"class":116},"5.94",[68,29037,89],{"class":112},[68,29039,255],{"class":74},[68,29041,29016],{"class":373},[68,29043,20480],{"class":74},[68,29045,29046],{"class":70,"line":308},[68,29047,311],{"class":74},[68,29049,29050],{"class":70,"line":314},[68,29051,416],{"emptyLinePlaceholder":415},[68,29053,29054,29056,29058,29061],{"class":70,"line":320},[68,29055,25844],{"class":1864},[68,29057,26385],{"class":1864},[68,29059,29060],{"class":2183}," testMinus",[68,29062,2136],{"class":74},[68,29064,29065],{"class":70,"line":889},[68,29066,188],{"class":74},[68,29068,29069,29071,29073,29075,29077,29079,29082,29084,29086,29089,29091,29093,29095,29098,29100],{"class":70,"line":909},[68,29070,28964],{"class":28925},[68,29072,28967],{"class":373},[68,29074,877],{"class":1899},[68,29076,28972],{"class":373},[68,29078,404],{"class":74},[68,29080,29081],{"class":2183},"subtract",[68,29083,648],{"class":74},[68,29085,89],{"class":112},[68,29087,29088],{"class":116},"1.7",[68,29090,89],{"class":112},[68,29092,255],{"class":74},[68,29094,113],{"class":112},[68,29096,29097],{"class":116},"1.6",[68,29099,89],{"class":112},[68,29101,20480],{"class":74},[68,29103,29104,29106,29108,29110,29112,29114,29116,29118],{"class":70,"line":929},[68,29105,29002],{"class":373},[68,29107,404],{"class":74},[68,29109,2773],{"class":2183},[68,29111,648],{"class":74},[68,29113,29011],{"class":373},[68,29115,255],{"class":74},[68,29117,29016],{"class":373},[68,29119,20480],{"class":74},[68,29121,29122,29124,29126,29128,29130,29132,29134,29136,29138,29140],{"class":70,"line":949},[68,29123,29023],{"class":373},[68,29125,404],{"class":74},[68,29127,29028],{"class":2183},[68,29129,648],{"class":74},[68,29131,89],{"class":112},[68,29133,3872],{"class":116},[68,29135,89],{"class":112},[68,29137,255],{"class":74},[68,29139,29016],{"class":373},[68,29141,20480],{"class":74},[68,29143,29144],{"class":70,"line":969},[68,29145,311],{"class":74},[68,29147,29148],{"class":70,"line":989},[68,29149,132],{"class":74},[28,29151,29152],{},"Assert 是 junit 里的一个类，比如我使用的是 assertEquals 方法，如果两个参数的值相等，则运行时不会报错，如果值不等，则会报错。",[28,29154,29155],{},"编写好测试类之后，右击，运行方式里选择 Android JUnit Test，然后等待即可。",[28,29157,29158],{},"测试成功的截图：",[554,29160],{"filename":556},[28,29162,29163],{},"如果我们把 testMinus()中的 Assert.assertEquals(“0.1″, answer)改成 Assert.assertEquals(“0.2″, answer)，再运行的话，就会出现：",[554,29165],{"filename":1203},[28,29167,29168],{},"可以在故障跟踪里看到到底哪里出了问题。",[28,29170,29171],{},"好了，简单的 Android 单元测试就介绍到这里，以后可以很轻松地对某个类进行测试了，而不需要运行整个项目。",[523,29173,29174],{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s9AJx, html code.shiki .s9AJx{--shiki-light:#9C3EDA;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .stp6e, html code.shiki .stp6e{--shiki-light:#39ADB5;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_bVq, html code.shiki .s_bVq{--shiki-light:#9C3EDA;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":63,"searchDepth":78,"depth":78,"links":29176},[],"2012-07-31",{},"\u002Fposts\u002F2012\u002Fandroid-unit-test",{"text":8419,"minutes":29181,"time":29182,"words":29183},3.25,195000,650,{"title":28622,"description":28627},{"loc":29179},"posts\u002F2012\u002F20120731.android-unit-test",[543,13759],"中午","-EQ96eMLk-j-puq0a9ftLoOxuMvfOt9YYAQkFoYxj1g",{"id":29191,"title":29192,"body":29193,"class":528,"cover":528,"coverSize":528,"date":29435,"description":29197,"draft":530,"extension":531,"hideComments":530,"location":27706,"meta":29436,"navigation":415,"path":29437,"readingTime":29438,"seo":29442,"sitemap":29443,"stem":29444,"tags":29445,"time":29188,"weather":528,"__hash__":29446},"posts\u002Fposts\u002F2012\u002F20120728.csharp-get-ipaddress.md","C# 获取指定网卡的 IP 地址",{"type":25,"value":29194,"toc":29433},[29195,29198,29201,29204,29427,29430],[28,29196,29197],{},"最近几天都在玩游戏，没怎么编程，感觉好空虚啊！主要是之前在 Android 上建立 wifi 热点出现了一些问题，难以进展下去，于是便耽搁了，今天决定先跳过那个问题，Android 手机之间的传输先暂时不考虑，先做一下 Android 与 PC 之间的数据传输。",[28,29199,29200],{},"刚刚主要完成了这么一件事情，根据指定的网卡获取其 ip，以便之后的 socket 使用。",[28,29202,29203],{},"会出现这个问题是因为我是利用 Win7 的 netsh 功能建立的虚拟网卡，它与其它网卡可以同时存在，这就意味着这台主机可能拥有多个 ip 地址，然而我们需要的只是我们建立的虚拟网卡的那个 ip 地址，实现方法如下：",[58,29205,29207],{"className":24038,"code":29206,"language":24040,"meta":63,"style":63},"NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces(); \u002F\u002F获取本机所有网卡对象\nforeach (NetworkInterface adapter in adapters)\n{\n    if (adapter.Description.Contains(\"Virtual\")) \u002F\u002F枚举条件：描述中包含 \"Virtual\"\n    {\n        IPInterfaceProperties ipProperties = adapter.GetIPProperties(); \u002F\u002F获取 IP 配置\n        UnicastIPAddressInformationCollection ipCollection = ipProperties.UnicastAddresses; \u002F\u002F获取单播地址集\n        foreach (UnicastIPAddressInformation ip in ipCollection)\n        {\n            if (ip.Address.AddressFamily == AddressFamily.InterNetwork) \u002F\u002F只要 ipv4 的\n                ipAddress = ip.Address; \u002F\u002F获取 ip\n        }\n    }\n}\n",[65,29208,29209,29236,29255,29259,29293,29297,29319,29341,29359,29363,29397,29415,29419,29423],{"__ignoreMap":63},[68,29210,29211,29214,29217,29220,29222,29225,29227,29230,29233],{"class":70,"line":71},[68,29212,29213],{"class":1511},"NetworkInterface",[68,29215,29216],{"class":74},"[]",[68,29218,29219],{"class":1511}," adapters",[68,29221,1900],{"class":1899},[68,29223,29224],{"class":373}," NetworkInterface",[68,29226,404],{"class":74},[68,29228,29229],{"class":2183},"GetAllNetworkInterfaces",[68,29231,29232],{"class":74},"();",[68,29234,29235],{"class":2403}," \u002F\u002F获取本机所有网卡对象\n",[68,29237,29238,29241,29243,29245,29248,29251,29253],{"class":70,"line":78},[68,29239,29240],{"class":1790},"foreach",[68,29242,7664],{"class":74},[68,29244,29213],{"class":1511},[68,29246,29247],{"class":1511}," adapter",[68,29249,29250],{"class":1790}," in",[68,29252,29219],{"class":373},[68,29254,410],{"class":74},[68,29256,29257],{"class":70,"line":98},[68,29258,75],{"class":74},[68,29260,29261,29263,29265,29268,29270,29273,29275,29278,29280,29282,29285,29287,29290],{"class":70,"line":123},[68,29262,9649],{"class":1790},[68,29264,7664],{"class":74},[68,29266,29267],{"class":373},"adapter",[68,29269,404],{"class":74},[68,29271,29272],{"class":373},"Description",[68,29274,404],{"class":74},[68,29276,29277],{"class":2183},"Contains",[68,29279,648],{"class":74},[68,29281,89],{"class":112},[68,29283,29284],{"class":116},"Virtual",[68,29286,89],{"class":112},[68,29288,29289],{"class":74},"))",[68,29291,29292],{"class":2403}," \u002F\u002F枚举条件：描述中包含 \"Virtual\"\n",[68,29294,29295],{"class":70,"line":129},[68,29296,188],{"class":74},[68,29298,29299,29302,29305,29307,29309,29311,29314,29316],{"class":70,"line":212},[68,29300,29301],{"class":1511},"        IPInterfaceProperties",[68,29303,29304],{"class":1511}," ipProperties",[68,29306,1900],{"class":1899},[68,29308,29247],{"class":373},[68,29310,404],{"class":74},[68,29312,29313],{"class":2183},"GetIPProperties",[68,29315,29232],{"class":74},[68,29317,29318],{"class":2403}," \u002F\u002F获取 IP 配置\n",[68,29320,29321,29324,29327,29329,29331,29333,29336,29338],{"class":70,"line":233},[68,29322,29323],{"class":1511},"        UnicastIPAddressInformationCollection",[68,29325,29326],{"class":1511}," ipCollection",[68,29328,1900],{"class":1899},[68,29330,29304],{"class":373},[68,29332,404],{"class":74},[68,29334,29335],{"class":373},"UnicastAddresses",[68,29337,16105],{"class":74},[68,29339,29340],{"class":2403}," \u002F\u002F获取单播地址集\n",[68,29342,29343,29346,29348,29351,29353,29355,29357],{"class":70,"line":268},[68,29344,29345],{"class":1790},"        foreach",[68,29347,7664],{"class":74},[68,29349,29350],{"class":1511},"UnicastIPAddressInformation",[68,29352,19709],{"class":1511},[68,29354,29250],{"class":1790},[68,29356,29326],{"class":373},[68,29358,410],{"class":74},[68,29360,29361],{"class":70,"line":289},[68,29362,28249],{"class":74},[68,29364,29365,29367,29369,29372,29374,29377,29379,29382,29384,29387,29389,29392,29394],{"class":70,"line":308},[68,29366,15195],{"class":1790},[68,29368,7664],{"class":74},[68,29370,29371],{"class":373},"ip",[68,29373,404],{"class":74},[68,29375,29376],{"class":373},"Address",[68,29378,404],{"class":74},[68,29380,29381],{"class":373},"AddressFamily ",[68,29383,3660],{"class":1899},[68,29385,29386],{"class":373}," AddressFamily",[68,29388,404],{"class":74},[68,29390,29391],{"class":373},"InterNetwork",[68,29393,2753],{"class":74},[68,29395,29396],{"class":2403}," \u002F\u002F只要 ipv4 的\n",[68,29398,29399,29402,29404,29406,29408,29410,29412],{"class":70,"line":314},[68,29400,29401],{"class":373},"                ipAddress ",[68,29403,877],{"class":1899},[68,29405,19709],{"class":373},[68,29407,404],{"class":74},[68,29409,29376],{"class":373},[68,29411,16105],{"class":74},[68,29413,29414],{"class":2403}," \u002F\u002F获取 ip\n",[68,29416,29417],{"class":70,"line":320},[68,29418,2589],{"class":74},[68,29420,29421],{"class":70,"line":889},[68,29422,311],{"class":74},[68,29424,29425],{"class":70,"line":909},[68,29426,132],{"class":74},[28,29428,29429],{},"任务完成！下面开始学习 socket 通信。",[523,29431,29432],{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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":63,"searchDepth":78,"depth":78,"links":29434},[],"2012-07-28",{},"\u002Fposts\u002F2012\u002Fcsharp-get-ipaddress",{"text":1217,"minutes":29439,"time":29440,"words":29441},1.52,91200,304,{"title":29192,"description":29197},{"loc":29437},"posts\u002F2012\u002F20120728.csharp-get-ipaddress",[543,23440],"GwWG8vIatcoF2JywldfLLQRoJzUjhKTelQzSQc6ah-c",{"id":29448,"title":29449,"body":29450,"class":528,"cover":528,"coverSize":528,"date":30113,"description":30114,"draft":530,"extension":531,"hideComments":530,"location":27706,"meta":30115,"navigation":415,"path":30116,"readingTime":30117,"seo":30121,"sitemap":30122,"stem":30123,"tags":30124,"time":28365,"weather":528,"__hash__":30125},"posts\u002Fposts\u002F2012\u002F20120723.android-wifi-connection.md","Android Wifi 的设置、连接操作",{"type":25,"value":29451,"toc":30111},[29452,29459,29462,29465,29688,29691,29981,29984,29987,30086,30102,30105,30108],[28,29453,29454,29455],{},"我项目中这部分的代码是参考的这里的：",[38,29456,29457],{"href":29457,"rel":29458},"http:\u002F\u002Fblog.csdn.net\u002Fcscmaker\u002Farticle\u002Fdetails\u002F7032277",[42],[28,29460,29461],{},"但是，参考了这段代码之后可没少忙活！怎么试都连不上，wifi 的信息是创建了，可就是没法连接上。百思不得其解，后来我想，会不会是设置的地方出了问题。",[28,29463,29464],{},"原来是这样设置的：",[58,29466,29468],{"className":28891,"code":29467,"language":205,"meta":63,"style":63},"config.preSharedKey = \"\\\"\" + Password + \"\\\"\";\nconfig.hiddenSSID = true;\nconfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);\nconfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);\nconfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);\nconfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);\nconfig.allowedProtocols.set(WifiConfiguration.Protocol.WPA);\nconfig.status = WifiConfiguration.Status.ENABLED;\n",[65,29469,29470,29502,29517,29547,29576,29605,29633,29662],{"__ignoreMap":63},[68,29471,29472,29474,29476,29479,29481,29483,29485,29487,29489,29492,29494,29496,29498,29500],{"class":70,"line":71},[68,29473,13276],{"class":373},[68,29475,404],{"class":74},[68,29477,29478],{"class":373},"preSharedKey ",[68,29480,877],{"class":1899},[68,29482,113],{"class":112},[68,29484,26610],{"class":665},[68,29486,89],{"class":112},[68,29488,28560],{"class":1899},[68,29490,29491],{"class":373}," Password ",[68,29493,1670],{"class":1899},[68,29495,113],{"class":112},[68,29497,26610],{"class":665},[68,29499,89],{"class":112},[68,29501,5136],{"class":74},[68,29503,29504,29506,29508,29511,29513,29515],{"class":70,"line":78},[68,29505,13276],{"class":373},[68,29507,404],{"class":74},[68,29509,29510],{"class":373},"hiddenSSID ",[68,29512,877],{"class":1899},[68,29514,5858],{"class":81},[68,29516,5136],{"class":74},[68,29518,29519,29521,29523,29526,29528,29530,29532,29535,29537,29540,29542,29545],{"class":70,"line":98},[68,29520,13276],{"class":373},[68,29522,404],{"class":74},[68,29524,29525],{"class":373},"allowedAuthAlgorithms",[68,29527,404],{"class":74},[68,29529,16042],{"class":2183},[68,29531,648],{"class":74},[68,29533,29534],{"class":373},"WifiConfiguration",[68,29536,404],{"class":74},[68,29538,29539],{"class":373},"AuthAlgorithm",[68,29541,404],{"class":74},[68,29543,29544],{"class":373},"OPEN",[68,29546,20480],{"class":74},[68,29548,29549,29551,29553,29556,29558,29560,29562,29564,29566,29569,29571,29574],{"class":70,"line":123},[68,29550,13276],{"class":373},[68,29552,404],{"class":74},[68,29554,29555],{"class":373},"allowedGroupCiphers",[68,29557,404],{"class":74},[68,29559,16042],{"class":2183},[68,29561,648],{"class":74},[68,29563,29534],{"class":373},[68,29565,404],{"class":74},[68,29567,29568],{"class":373},"GroupCipher",[68,29570,404],{"class":74},[68,29572,29573],{"class":373},"TKIP",[68,29575,20480],{"class":74},[68,29577,29578,29580,29582,29585,29587,29589,29591,29593,29595,29598,29600,29603],{"class":70,"line":129},[68,29579,13276],{"class":373},[68,29581,404],{"class":74},[68,29583,29584],{"class":373},"allowedKeyManagement",[68,29586,404],{"class":74},[68,29588,16042],{"class":2183},[68,29590,648],{"class":74},[68,29592,29534],{"class":373},[68,29594,404],{"class":74},[68,29596,29597],{"class":373},"KeyMgmt",[68,29599,404],{"class":74},[68,29601,29602],{"class":373},"WPA_PSK",[68,29604,20480],{"class":74},[68,29606,29607,29609,29611,29614,29616,29618,29620,29622,29624,29627,29629,29631],{"class":70,"line":212},[68,29608,13276],{"class":373},[68,29610,404],{"class":74},[68,29612,29613],{"class":373},"allowedPairwiseCiphers",[68,29615,404],{"class":74},[68,29617,16042],{"class":2183},[68,29619,648],{"class":74},[68,29621,29534],{"class":373},[68,29623,404],{"class":74},[68,29625,29626],{"class":373},"PairwiseCipher",[68,29628,404],{"class":74},[68,29630,29573],{"class":373},[68,29632,20480],{"class":74},[68,29634,29635,29637,29639,29642,29644,29646,29648,29650,29652,29655,29657,29660],{"class":70,"line":233},[68,29636,13276],{"class":373},[68,29638,404],{"class":74},[68,29640,29641],{"class":373},"allowedProtocols",[68,29643,404],{"class":74},[68,29645,16042],{"class":2183},[68,29647,648],{"class":74},[68,29649,29534],{"class":373},[68,29651,404],{"class":74},[68,29653,29654],{"class":373},"Protocol",[68,29656,404],{"class":74},[68,29658,29659],{"class":373},"WPA",[68,29661,20480],{"class":74},[68,29663,29664,29666,29668,29671,29673,29676,29678,29681,29683,29686],{"class":70,"line":268},[68,29665,13276],{"class":373},[68,29667,404],{"class":74},[68,29669,29670],{"class":373},"status ",[68,29672,877],{"class":1899},[68,29674,29675],{"class":373}," WifiConfiguration",[68,29677,404],{"class":74},[68,29679,29680],{"class":373},"Status",[68,29682,404],{"class":74},[68,29684,29685],{"class":373},"ENABLED",[68,29687,5136],{"class":74},[28,29689,29690],{},"我是这样检测的，我自己手动连接好一个网络，然后获取这个连接，将下面的信息输出：",[58,29692,29694],{"className":28891,"code":29693,"language":205,"meta":63,"style":63},"Log.i(TAG, \"SSID:\" + existingConfig.SSID);\nLog.i(TAG, \"preSharedKey:\" + existingConfig.preSharedKey);\nLog.i(TAG, \"hiddenSSID:\" + existingConfig.hiddenSSID);\nLog.i(TAG, \"allowedAuthAlgorithms:\" + existingConfig.allowedAuthAlgorithms);\nLog.i(TAG, \"allowedGroupCiphers:\" + existingConfig.allowedGroupCiphers);\nLog.i(TAG, \"allowedKeyManagement:\" + existingConfig.allowedKeyManagement);\nLog.i(TAG, \"allowedPairwiseCiphers:\" + existingConfig.allowedPairwiseCiphers);\nLog.i(TAG, \"allowedProtocols:\" + existingConfig.allowedProtocols);\nLog.i(TAG, \"status:\" + existingConfig.status);\n",[65,29695,29696,29730,29762,29794,29825,29856,29887,29918,29949],{"__ignoreMap":63},[68,29697,29698,29701,29703,29705,29707,29709,29711,29713,29716,29718,29720,29723,29725,29728],{"class":70,"line":71},[68,29699,29700],{"class":373},"Log",[68,29702,404],{"class":74},[68,29704,2773],{"class":2183},[68,29706,648],{"class":74},[68,29708,29011],{"class":373},[68,29710,255],{"class":74},[68,29712,113],{"class":112},[68,29714,29715],{"class":116},"SSID:",[68,29717,89],{"class":112},[68,29719,28560],{"class":1899},[68,29721,29722],{"class":373}," existingConfig",[68,29724,404],{"class":74},[68,29726,29727],{"class":373},"SSID",[68,29729,20480],{"class":74},[68,29731,29732,29734,29736,29738,29740,29742,29744,29746,29749,29751,29753,29755,29757,29760],{"class":70,"line":78},[68,29733,29700],{"class":373},[68,29735,404],{"class":74},[68,29737,2773],{"class":2183},[68,29739,648],{"class":74},[68,29741,29011],{"class":373},[68,29743,255],{"class":74},[68,29745,113],{"class":112},[68,29747,29748],{"class":116},"preSharedKey:",[68,29750,89],{"class":112},[68,29752,28560],{"class":1899},[68,29754,29722],{"class":373},[68,29756,404],{"class":74},[68,29758,29759],{"class":373},"preSharedKey",[68,29761,20480],{"class":74},[68,29763,29764,29766,29768,29770,29772,29774,29776,29778,29781,29783,29785,29787,29789,29792],{"class":70,"line":98},[68,29765,29700],{"class":373},[68,29767,404],{"class":74},[68,29769,2773],{"class":2183},[68,29771,648],{"class":74},[68,29773,29011],{"class":373},[68,29775,255],{"class":74},[68,29777,113],{"class":112},[68,29779,29780],{"class":116},"hiddenSSID:",[68,29782,89],{"class":112},[68,29784,28560],{"class":1899},[68,29786,29722],{"class":373},[68,29788,404],{"class":74},[68,29790,29791],{"class":373},"hiddenSSID",[68,29793,20480],{"class":74},[68,29795,29796,29798,29800,29802,29804,29806,29808,29810,29813,29815,29817,29819,29821,29823],{"class":70,"line":123},[68,29797,29700],{"class":373},[68,29799,404],{"class":74},[68,29801,2773],{"class":2183},[68,29803,648],{"class":74},[68,29805,29011],{"class":373},[68,29807,255],{"class":74},[68,29809,113],{"class":112},[68,29811,29812],{"class":116},"allowedAuthAlgorithms:",[68,29814,89],{"class":112},[68,29816,28560],{"class":1899},[68,29818,29722],{"class":373},[68,29820,404],{"class":74},[68,29822,29525],{"class":373},[68,29824,20480],{"class":74},[68,29826,29827,29829,29831,29833,29835,29837,29839,29841,29844,29846,29848,29850,29852,29854],{"class":70,"line":129},[68,29828,29700],{"class":373},[68,29830,404],{"class":74},[68,29832,2773],{"class":2183},[68,29834,648],{"class":74},[68,29836,29011],{"class":373},[68,29838,255],{"class":74},[68,29840,113],{"class":112},[68,29842,29843],{"class":116},"allowedGroupCiphers:",[68,29845,89],{"class":112},[68,29847,28560],{"class":1899},[68,29849,29722],{"class":373},[68,29851,404],{"class":74},[68,29853,29555],{"class":373},[68,29855,20480],{"class":74},[68,29857,29858,29860,29862,29864,29866,29868,29870,29872,29875,29877,29879,29881,29883,29885],{"class":70,"line":212},[68,29859,29700],{"class":373},[68,29861,404],{"class":74},[68,29863,2773],{"class":2183},[68,29865,648],{"class":74},[68,29867,29011],{"class":373},[68,29869,255],{"class":74},[68,29871,113],{"class":112},[68,29873,29874],{"class":116},"allowedKeyManagement:",[68,29876,89],{"class":112},[68,29878,28560],{"class":1899},[68,29880,29722],{"class":373},[68,29882,404],{"class":74},[68,29884,29584],{"class":373},[68,29886,20480],{"class":74},[68,29888,29889,29891,29893,29895,29897,29899,29901,29903,29906,29908,29910,29912,29914,29916],{"class":70,"line":233},[68,29890,29700],{"class":373},[68,29892,404],{"class":74},[68,29894,2773],{"class":2183},[68,29896,648],{"class":74},[68,29898,29011],{"class":373},[68,29900,255],{"class":74},[68,29902,113],{"class":112},[68,29904,29905],{"class":116},"allowedPairwiseCiphers:",[68,29907,89],{"class":112},[68,29909,28560],{"class":1899},[68,29911,29722],{"class":373},[68,29913,404],{"class":74},[68,29915,29613],{"class":373},[68,29917,20480],{"class":74},[68,29919,29920,29922,29924,29926,29928,29930,29932,29934,29937,29939,29941,29943,29945,29947],{"class":70,"line":268},[68,29921,29700],{"class":373},[68,29923,404],{"class":74},[68,29925,2773],{"class":2183},[68,29927,648],{"class":74},[68,29929,29011],{"class":373},[68,29931,255],{"class":74},[68,29933,113],{"class":112},[68,29935,29936],{"class":116},"allowedProtocols:",[68,29938,89],{"class":112},[68,29940,28560],{"class":1899},[68,29942,29722],{"class":373},[68,29944,404],{"class":74},[68,29946,29641],{"class":373},[68,29948,20480],{"class":74},[68,29950,29951,29953,29955,29957,29959,29961,29963,29965,29968,29970,29972,29974,29976,29979],{"class":70,"line":289},[68,29952,29700],{"class":373},[68,29954,404],{"class":74},[68,29956,2773],{"class":2183},[68,29958,648],{"class":74},[68,29960,29011],{"class":373},[68,29962,255],{"class":74},[68,29964,113],{"class":112},[68,29966,29967],{"class":116},"status:",[68,29969,89],{"class":112},[68,29971,28560],{"class":1899},[68,29973,29722],{"class":373},[68,29975,404],{"class":74},[68,29977,29978],{"class":373},"status",[68,29980,20480],{"class":74},[28,29982,29983],{},"这样，就可以对比，就知道怎样的是对的，哪里不对。",[28,29985,29986],{},"Log 如下：",[58,29988,29990],{"className":361,"code":29989,"language":363,"meta":63,"style":63},"Created Wifi Info\nSSID:\"HADB-ASUS\"\npreSharedKey:*\nhiddenSSID:false\nallowedAuthAlgorithms:{}\nallowedGroupCiphers:{0, 1, 2, 3}\nallowedPairwiseCiphers:{1, 2}\nallowedProtocols:{0, 1}\nstatus:0\nbRet=true\n",[65,29991,29992,30001,30008,30013,30020,30025,30046,30059,30072,30078],{"__ignoreMap":63},[68,29993,29994,29997],{"class":70,"line":71},[68,29995,29996],{"class":373},"Created Wifi ",[68,29998,30000],{"class":29999},"sUdit","Info\n",[68,30002,30003,30005],{"class":70,"line":78},[68,30004,29715],{"class":373},[68,30006,30007],{"class":116},"\"HADB-ASUS\"\n",[68,30009,30010],{"class":70,"line":98},[68,30011,30012],{"class":373},"preSharedKey:*\n",[68,30014,30015,30017],{"class":70,"line":123},[68,30016,29780],{"class":373},[68,30018,30019],{"class":81},"false\n",[68,30021,30022],{"class":70,"line":129},[68,30023,30024],{"class":373},"allowedAuthAlgorithms:{}\n",[68,30026,30027,30030,30032,30034,30036,30038,30040,30042,30044],{"class":70,"line":212},[68,30028,30029],{"class":373},"allowedGroupCiphers:{",[68,30031,768],{"class":81},[68,30033,597],{"class":373},[68,30035,401],{"class":81},[68,30037,597],{"class":373},[68,30039,9366],{"class":81},[68,30041,597],{"class":373},[68,30043,5057],{"class":81},[68,30045,132],{"class":373},[68,30047,30048,30051,30053,30055,30057],{"class":70,"line":233},[68,30049,30050],{"class":373},"allowedPairwiseCiphers:{",[68,30052,401],{"class":81},[68,30054,597],{"class":373},[68,30056,9366],{"class":81},[68,30058,132],{"class":373},[68,30060,30061,30064,30066,30068,30070],{"class":70,"line":268},[68,30062,30063],{"class":373},"allowedProtocols:{",[68,30065,768],{"class":81},[68,30067,597],{"class":373},[68,30069,401],{"class":81},[68,30071,132],{"class":373},[68,30073,30074,30076],{"class":70,"line":289},[68,30075,29967],{"class":373},[68,30077,19556],{"class":81},[68,30079,30080,30083],{"class":70,"line":308},[68,30081,30082],{"class":373},"bRet=",[68,30084,30085],{"class":81},"true\n",[28,30087,30088,30089,30091,30092,30095,30096,19849,30098,30101],{},"当然，除了 ",[65,30090,29759],{}," 输出的是被隐藏了的 ",[65,30093,30094],{},"\"*\"","，因为安全性问题，密码是无法输出的，其它的项有的并不止一个值，后来看文档，发现，这些值其实都是有默认值的，根本不需要手动去设置它们，只需要将 ",[65,30097,29978],{},[65,30099,30100],{},"WifiConfiguration.Status.ENABLED"," 就可以了，密码也是要设一下的，其它的都可以注释掉。",[28,30103,30104],{},"于是，问题完美解决了……啊哈哈哈哈……",[28,30106,30107],{},"这次也给了我自己一个经验，那就是别人的代码也不能完全相信，还是要自己亲自实践才行。但参考代码这个步骤是必须的，因为它能带给你无数的灵感，还能指引你方向，因为有时候你根本不知道从何下手，参考一些代码之后，就会大体了解怎么去做，需要引用哪些包，然后在对这些包进行搜索，查看官方文档，很快，问题就可以迎刃而解了。",[523,30109,30110],{},"html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sUdit, html code.shiki .sUdit{--shiki-light:#91B859;--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":63,"searchDepth":78,"depth":78,"links":30112},[],"2012-07-23","我项目中这部分的代码是参考的这里的：http:\u002F\u002Fblog.csdn.net\u002Fcscmaker\u002Farticle\u002Fdetails\u002F7032277",{},"\u002Fposts\u002F2012\u002Fandroid-wifi-connection",{"text":535,"minutes":30118,"time":30119,"words":30120},2.355,141300,471,{"title":29449,"description":30114},{"loc":30116},"posts\u002F2012\u002F20120723.android-wifi-connection",[543,13759],"vJTWBkh4dzgDTGSPwDOD0eXCB_ihXjI_e_dOQMXLhOM",{"id":30127,"title":30128,"body":30129,"class":528,"cover":528,"coverSize":528,"date":30232,"description":30133,"draft":530,"extension":531,"hideComments":530,"location":27955,"meta":30233,"navigation":415,"path":30234,"readingTime":30235,"seo":30239,"sitemap":30240,"stem":30241,"tags":30242,"time":528,"weather":528,"__hash__":30243},"posts\u002Fposts\u002F2012\u002F20120702.set-eclipse-juno-for-android.md","Eclipse Juno 下搭建 Android 开发环境",{"type":25,"value":30130,"toc":30230},[30131,30134,30137,30146,30148,30151,30154,30157,30159,30162,30164,30167,30170,30172,30175,30183,30185,30188,30190,30193,30201,30203,30206,30208,30211,30214,30217,30219,30222],[28,30132,30133],{},"Eclipse 官方 28 日正式发布了 Eclipse 4.2，代号 Juno。同时，Eclipse 也宣布将使用 Eclipse 4.2 来开发以后的 Eclipse 版本。因此，在我升级 Eclipse 的同时，我也来制作一个 Juno 下搭建 Android 开发环境的教程，其实跟以往的教程是没多大区别的，只是比较新一点儿而已。",[28,30135,30136],{},"前提条件，安装了 java JDK，如何安装可以自己百度下。",[28,30138,30139,30140,30145],{},"首先，去 ",[38,30141,30144],{"href":30142,"rel":30143},"http:\u002F\u002Fwww.eclipse.org\u002F",[42],"Eclipse 官网"," 下载最新的 Eclipse 安装包。",[554,30147],{"filename":556},[28,30149,30150],{},"可以看到，首页已经变成了 Juno 的宣传。点击 Downloads 进入下载页面，我们选择 Eclipse IDE for Java Developers，其实也可以选择 Eclipse for Mobile Developers，他们相差不大。",[28,30152,30153],{},"下载好之后，解压就可以了。",[28,30155,30156],{},"打开 eclipse.exe，可以看到，新的 Logo：",[554,30158],{"filename":1203},[28,30160,30161],{},"首先，会让你设置一个工作目录",[554,30163],{"filename":1710},[28,30165,30166],{},"自己设置一下，将下面的复选框打勾，也就是将这个目录作为默认工作目录，并不再提示，这样以后新建的项目都会在这个目录中。",[28,30168,30169],{},"好，我们进入到熟悉的欢迎界面：",[554,30171],{"filename":1718},[28,30173,30174],{},"下面我们来安装汉化包，我个人还是比较喜欢中文的界面，如果喜欢英文界面的朋友可以跳过这一步。",[28,30176,30177,30178,30182],{},"到 ",[38,30179,26852],{"href":30180,"rel":30181},"http:\u002F\u002Fbuild.eclipse.org\u002Ftechnology\u002Fbabel\u002Fbabel_language_packs\u002F",[42]," 可以找到 Babel 多语言项目的最新包，然后找到简体中文的部分：",[554,30184],{"filename":1729},[28,30186,30187],{},"下载对应的包，解压到 Eclipse 的安装位置，然后重启 Eclipse，就可以看到汉化之后的 Eclipse 界面了。",[554,30189],{"filename":5287},[28,30191,30192],{},"好，下面开始安装 Android 插件",[28,30194,30195,30196,30200],{},"到“帮助-安装新软件”里点击添加，然后如图输入 ",[38,30197,30198],{"href":30198,"rel":30199},"https:\u002F\u002Fdl-ssl.google.com\u002Fandroid\u002Feclipse\u002F",[42]," 这个地址",[554,30202],{"filename":4862},[28,30204,30205],{},"然后，选择 Developer Tools",[554,30207],{"filename":5309},[28,30209,30210],{},"然后就直接下一步下一步，有个地方 Agree 一下，然后就等待一会儿，下载安装。",[28,30212,30213],{},"安装完成之后重启一下 Eclipse。",[28,30215,30216],{},"你会发现多了些 Android 的东西，进入“窗口-首选项”，找到 Android 一栏：",[554,30218],{"filename":5235},[28,30220,30221],{},"由于我之前已经安装过 Android SDK，所以会有这些列表，如果你没有安装过 Android SDK，可以去下载一下，然后在这个 SDK Location 里选择你 SDK 所在的目录。",[28,30223,30224,30225],{},"嗯，至此，Juno 下搭建 Android 的开发环境就完成了，如果不够详细，可以再参考下面的文章：",[38,30226,30229],{"href":30227,"rel":30228},"http:\u002F\u002Fblog.csdn.net\u002Fwebrobot\u002Farticle\u002Fdetails\u002F7304831",[42],"Android 开发环境配置图文教程 (jdk + eclipse + android sdk)",{"title":63,"searchDepth":78,"depth":78,"links":30231},[],"2012-07-02",{},"\u002Fposts\u002F2012\u002Fset-eclipse-juno-for-android",{"text":535,"minutes":30236,"time":30237,"words":30238},2.95,177000,590,{"title":30128,"description":30133},{"loc":30234},"posts\u002F2012\u002F20120702.set-eclipse-juno-for-android",[543,13759],"NVkTRjx9PgNmGCmdwr8JeX-JKYSvXEEiu9oyRhpSHcU",{"id":30245,"title":30246,"body":30247,"class":528,"cover":528,"coverSize":528,"date":30322,"description":30323,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":30324,"navigation":415,"path":30325,"readingTime":30326,"seo":30330,"sitemap":30331,"stem":30332,"tags":30333,"time":528,"weather":528,"__hash__":30334},"posts\u002Fposts\u002F2012\u002F20120628.using-of-android-cleartaskonlaunch.md","android:clearTaskOnLaunch 的用法",{"type":25,"value":30248,"toc":30320},[30249,30256,30264,30267,30276,30279,30291,30300,30303,30306],[28,30250,30251,30252,30255],{},"关于 ",[65,30253,30254],{},"android:clearTaskOnLaunch","，网上的资料很少，唯一有几个资料，还说得很含糊，看着让人摸不着头脑，今天硬着头皮看了下英文文档，再结合自己的尝试，终于是稍微理解了它的用处。",[28,30257,30258,30259,30261,30262],{},"默认情况下，",[65,30260,30254],{}," 的值是 ",[65,30263,26485],{},[28,30265,30266],{},"此时，比如你的应用里有 N 个 Activity，其中有个是设置页面，你从主页面进入到设置页面设置了一些东西之后，突然，按了下 Home 键，回到了 Android 的 Home，这时候你做了些别的事情，然后你再次点击你的应用程序图标进入你的应用程序的时候，依旧是回到设置页面，继续先前的工作，这也是大多数应用的情况。",[28,30268,30269,30270,30272,30273,30275],{},"但是，如果你把 ",[65,30271,30254],{}," 的值设为 ",[65,30274,849],{}," 呢？顾名思义，它就在启动的时候把 Task 给清空了，就是你再次点击应用程序图标进入你的应用程序的时候是回到应用程序的第一个页面，而不会回到先前的设置页面。也就是说不保存先前的设置状态。",[28,30277,30278],{},"至于，什么情况下要这么用，我还没想到，像谷歌的 Zxing 项目，也就是 Android 上的“条码扫描器”，它就是这样的，不管你什么时候重新进入该应用，它显示的都是扫描的界面。",[28,30280,30281,30282,30284,30285,30287,30288,30290],{},"不过呢，我也发现了个意外情况，就是即使你把 ",[65,30283,30254],{}," 的值设为了 ",[65,30286,849],{},"，但是在 Home 界面长按 Home 键，可以调出一个你最近进行的任务，从那个里面点击你的应用是可以回到先前保留的状态的，也就是无视 ",[65,30289,30254],{}," 了，至于为什么，目前还没搞明白，有兴趣的可以去官方查看一下文档，我没有仔细去看。",[28,30292,30251,30293,9810,30295],{},[65,30294,30254],{},[38,30296,30299],{"href":30297,"rel":30298},"http:\u002F\u002Fdeveloper.android.com\u002Fintl\u002Fzh-CN\u002Fguide\u002Ftopics\u002Fmanifest\u002Factivity-element.html#clear",[42],"官方文档",[28,30301,30302],{},"另外，今天更新了下 ADT 20，感觉还不错，Windows 下模拟器的程序图标变了，比以前可爱了，呵呵。以前好像是没图标还是怎么地，完全没印象。这个图标倒还是让人印象深刻。",[28,30304,30305],{},"2012-07-02 补充：",[28,30307,30308,30309,30311,30312,30315,30316,30319],{},"前几天搞得焦头烂额，我下了一个开源的项目，在里面把所有的 ",[65,30310,30254],{}," 全部删除了，可结果还是重新进入的时候从第一个 Activity 开始，我就纳闷了，纠结了，百思不得其解，以为是应用程序更新的时候，有些地方没有完全更新，然后卸载了，重新安装调试，还是这样。没办法，认真检查所有代码，结果发现了这么个东西：",[65,30313,30314],{},"intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);"," 它在启动每个 Activity 的时候加了个 Flag，",[65,30317,30318],{},"Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET","，也达到了那样的效果，删掉，然后就是一般的效果了，也就是不管什么时候重新进入应用，进入的是先前停留的地方。嗯~搞定！",{"title":63,"searchDepth":78,"depth":78,"links":30321},[],"2012-06-28","关于 android:clearTaskOnLaunch，网上的资料很少，唯一有几个资料，还说得很含糊，看着让人摸不着头脑，今天硬着头皮看了下英文文档，再结合自己的尝试，终于是稍微理解了它的用处。",{},"\u002Fposts\u002F2012\u002Fusing-of-android-cleartaskonlaunch",{"text":8419,"minutes":30327,"time":30328,"words":30329},3.625,217500,725,{"title":30246,"description":30323},{"loc":30325},"posts\u002F2012\u002F20120628.using-of-android-cleartaskonlaunch",[543,13759],"KvFbhSxGX4w8b_jobwii3Vfzp6OpZNY3mPRfL2pXZ7w",{"id":30336,"title":30337,"body":30338,"class":528,"cover":528,"coverSize":528,"date":30427,"description":30342,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":30428,"navigation":415,"path":30429,"readingTime":30430,"seo":30434,"sitemap":30435,"stem":30436,"tags":30437,"time":528,"weather":528,"__hash__":30438},"posts\u002Fposts\u002F2012\u002F20120623.website-change-and-seo.md","进一步完善了网站结构并做了一些 SEO 优化",{"type":25,"value":30339,"toc":30425},[30340,30343,30408,30411,30414,30417,30420,30423],[28,30341,30342],{},"早上起来也没闲着，打开百度、谷歌，搜搜“好易思特”，看看收录情况如何，感觉还是不大给力，并没有达到我的要求。仔细反思反思，感觉还是没有做 SEO 优化的问题，之前看到过一些文章，并没有在意。我一直是拿“陌陌”的主页来学习借鉴的，发现，在百度搜索“陌陌”的时候，它的网站链接下面有一段说明文字，是“陌陌（momo）是一款基于地理位置的移动社交工具，你可以通过陌陌……”之类之类的，我打开它的首页，并没有发现这段文字，很纳闷，于是看它网页的源码，发现了这两个标签：",[58,30344,30346],{"className":19900,"code":30345,"language":19902,"meta":63,"style":63},"\u003Cmeta name=\"keywords\" content=\"XXX\" \u002F>\n\u003Cmeta name=\"description\" content=\"XXX\" \u002F>\n",[65,30347,30348,30379],{"__ignoreMap":63},[68,30349,30350,30352,30355,30357,30359,30361,30364,30366,30368,30370,30372,30375,30377],{"class":70,"line":71},[68,30351,727],{"class":74},[68,30353,30354],{"class":730},"meta",[68,30356,17168],{"class":873},[68,30358,877],{"class":74},[68,30360,89],{"class":112},[68,30362,30363],{"class":116},"keywords",[68,30365,89],{"class":112},[68,30367,3732],{"class":873},[68,30369,877],{"class":74},[68,30371,89],{"class":112},[68,30373,30374],{"class":116},"XXX",[68,30376,89],{"class":112},[68,30378,886],{"class":74},[68,30380,30381,30383,30385,30387,30389,30391,30394,30396,30398,30400,30402,30404,30406],{"class":70,"line":78},[68,30382,727],{"class":74},[68,30384,30354],{"class":730},[68,30386,17168],{"class":873},[68,30388,877],{"class":74},[68,30390,89],{"class":112},[68,30392,30393],{"class":116},"description",[68,30395,89],{"class":112},[68,30397,3732],{"class":873},[68,30399,877],{"class":74},[68,30401,89],{"class":112},[68,30403,30374],{"class":116},[68,30405,89],{"class":112},[68,30407,886],{"class":74},[28,30409,30410],{},"于是，一切都懂了！赶紧给自己也加了下，以便以后百度收录的时候可以显示出来。",[28,30412,30413],{},"另外，还给网站加了下 favicon，之前用插件加过一次，但是发现兼容性不行，在 IE 里是不能显示的，于是直接自己做了两个 ico 文件，放在目录下了。呵呵，感觉还是蛮好看的。",[28,30415,30416],{},"另外，尝试着添加了个 Google Analytics 功能，待会儿看看效果怎么样。",[28,30418,30419],{},"对了，所有文章的链接地址也都改变了，改成以文章名为地址，据说这样对搜索引擎更友好。",[28,30421,30422],{},"OK，去吃午饭！",[523,30424,27827],{},{"title":63,"searchDepth":78,"depth":78,"links":30426},[],"2012-06-23",{},"\u002Fposts\u002F2012\u002Fwebsite-change-and-seo",{"text":1217,"minutes":30431,"time":30432,"words":30433},1.845,110700,369,{"title":30337,"description":30342},{"loc":30429},"posts\u002F2012\u002F20120623.website-change-and-seo",[543,10057],"-3iQgogC9nc6FFVwy3EhphC6L715sICdOvOqWnqIAMo",{"id":30440,"title":30441,"body":30442,"class":528,"cover":528,"coverSize":528,"date":30707,"description":30708,"draft":530,"extension":531,"hideComments":530,"location":27955,"meta":30709,"navigation":415,"path":30710,"readingTime":30711,"seo":30714,"sitemap":30715,"stem":30716,"tags":30717,"time":30718,"weather":528,"__hash__":30719},"posts\u002Fposts\u002F2012\u002F20120618.several-intent-filters-in-one-activity.md","Android 中一个 Activity 多个 intent-filter 的调用方法",{"type":25,"value":30443,"toc":30705},[30444,30450,30453,30600,30609,30612,30689,30699,30702],[28,30445,30446,30447,1686],{},"在 Android 中，Activity 允许有很多种调用方式，其中一个方法是使用 ",[65,30448,30449],{},"\u003Cintent-filter>",[28,30451,30452],{},"比如：",[58,30454,30456],{"className":718,"code":30455,"language":720,"meta":63,"style":63},"\u003Cintent-filter>\n    \u003Caction android:name=\"android.intent.action.VIEW\" \u002F>\n    \u003Ccategory android:name=\"android.intent.category.DEFAULT\" \u002F>\n    \u003Ccategory android:name=\"android.intent.category.BROWSABLE\" \u002F>\n    \u003Cdata android:host=\"www.google.com\" android:path=\"m\u002Fproducts\u002Fscan\" android:scheme=\"http\" \u002F>\n\u003C\u002Fintent-filter>\n",[65,30457,30458,30467,30490,30514,30537,30592],{"__ignoreMap":63},[68,30459,30460,30462,30465],{"class":70,"line":71},[68,30461,727],{"class":74},[68,30463,30464],{"class":730},"intent-filter",[68,30466,734],{"class":74},[68,30468,30469,30471,30473,30475,30477,30479,30481,30483,30486,30488],{"class":70,"line":78},[68,30470,739],{"class":74},[68,30472,27620],{"class":730},[68,30474,28674],{"class":873},[68,30476,92],{"class":20247},[68,30478,217],{"class":873},[68,30480,877],{"class":74},[68,30482,89],{"class":112},[68,30484,30485],{"class":116},"android.intent.action.VIEW",[68,30487,89],{"class":112},[68,30489,886],{"class":74},[68,30491,30492,30494,30497,30499,30501,30503,30505,30507,30510,30512],{"class":70,"line":98},[68,30493,739],{"class":74},[68,30495,30496],{"class":730},"category",[68,30498,28674],{"class":873},[68,30500,92],{"class":20247},[68,30502,217],{"class":873},[68,30504,877],{"class":74},[68,30506,89],{"class":112},[68,30508,30509],{"class":116},"android.intent.category.DEFAULT",[68,30511,89],{"class":112},[68,30513,886],{"class":74},[68,30515,30516,30518,30520,30522,30524,30526,30528,30530,30533,30535],{"class":70,"line":123},[68,30517,739],{"class":74},[68,30519,30496],{"class":730},[68,30521,28674],{"class":873},[68,30523,92],{"class":20247},[68,30525,217],{"class":873},[68,30527,877],{"class":74},[68,30529,89],{"class":112},[68,30531,30532],{"class":116},"android.intent.category.BROWSABLE",[68,30534,89],{"class":112},[68,30536,886],{"class":74},[68,30538,30539,30541,30544,30546,30548,30551,30553,30555,30558,30560,30562,30564,30566,30568,30570,30573,30575,30577,30579,30582,30584,30586,30588,30590],{"class":70,"line":129},[68,30540,739],{"class":74},[68,30542,30543],{"class":730},"data",[68,30545,28674],{"class":873},[68,30547,92],{"class":20247},[68,30549,30550],{"class":873},"host",[68,30552,877],{"class":74},[68,30554,89],{"class":112},[68,30556,30557],{"class":116},"www.google.com",[68,30559,89],{"class":112},[68,30561,28674],{"class":873},[68,30563,92],{"class":20247},[68,30565,9584],{"class":873},[68,30567,877],{"class":74},[68,30569,89],{"class":112},[68,30571,30572],{"class":116},"m\u002Fproducts\u002Fscan",[68,30574,89],{"class":112},[68,30576,28674],{"class":873},[68,30578,92],{"class":20247},[68,30580,30581],{"class":873},"scheme",[68,30583,877],{"class":74},[68,30585,89],{"class":112},[68,30587,24688],{"class":116},[68,30589,89],{"class":112},[68,30591,886],{"class":74},[68,30593,30594,30596,30598],{"class":70,"line":212},[68,30595,771],{"class":74},[68,30597,30464],{"class":730},[68,30599,734],{"class":74},[28,30601,30602,30603,30608],{},"一开始我以为，在浏览器中键入 “",[38,30604,30607],{"href":30605,"rel":30606},"http:\u002F\u002Fwww.google.com\u002Fm\u002Fproducts\u002Fscan%E2%80%9D",[42],"http:\u002F\u002Fwww.google.com\u002Fm\u002Fproducts\u002Fscan”"," 就可以调用了的，结果发现浏览器只是正常打开它 T.T",[28,30610,30611],{},"后来发现，它的调用仍然需要使用 intent。",[58,30613,30615],{"className":28891,"code":30614,"language":205,"meta":63,"style":63},"Uri uri = Uri.parse(\"http:\u002F\u002Fwww.google.com\u002Fm\u002Fproducts\u002Fscan\");\nIntent it = new Intent(Intent.ACTION_VIEW, uri);\nstartActivity(it);\n",[65,30616,30617,30646,30677],{"__ignoreMap":63},[68,30618,30619,30622,30625,30627,30630,30632,30635,30637,30639,30642,30644],{"class":70,"line":71},[68,30620,30621],{"class":28925},"Uri",[68,30623,30624],{"class":373}," uri ",[68,30626,877],{"class":1899},[68,30628,30629],{"class":373}," Uri",[68,30631,404],{"class":74},[68,30633,30634],{"class":2183},"parse",[68,30636,648],{"class":74},[68,30638,89],{"class":112},[68,30640,30641],{"class":116},"http:\u002F\u002Fwww.google.com\u002Fm\u002Fproducts\u002Fscan",[68,30643,89],{"class":112},[68,30645,20480],{"class":74},[68,30647,30648,30651,30654,30656,30658,30661,30663,30665,30667,30670,30672,30675],{"class":70,"line":78},[68,30649,30650],{"class":28925},"Intent",[68,30652,30653],{"class":373}," it ",[68,30655,877],{"class":1899},[68,30657,7746],{"class":1790},[68,30659,30660],{"class":2183}," Intent",[68,30662,648],{"class":74},[68,30664,30650],{"class":373},[68,30666,404],{"class":74},[68,30668,30669],{"class":373},"ACTION_VIEW",[68,30671,255],{"class":74},[68,30673,30674],{"class":373}," uri",[68,30676,20480],{"class":74},[68,30678,30679,30682,30684,30687],{"class":70,"line":98},[68,30680,30681],{"class":2183},"startActivity",[68,30683,648],{"class":74},[68,30685,30686],{"class":373},"it",[68,30688,20480],{"class":74},[28,30690,30691,30692,30695,30696,30698],{},"我是在一个 Button 的 ",[65,30693,30694],{},"onClick()"," 方法里写的这些代码，这样按这个按钮，就会调用所有符合要求的含有对应 ",[65,30697,30449],{}," 的 Activity，在我的手机里，有 Chrome Beta、浏览器、快拍二维码、条码扫描器，以及我刚刚创建那个应用的 Activity。",[28,30700,30701],{},"嗯嗯，记录一下，以后会经常把日常遇到的问题记下来，方便遇到同样问题的开发者们一起学习！",[523,30703,30704],{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s9AJx, html code.shiki .s9AJx{--shiki-light:#9C3EDA;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .stp6e, html code.shiki .stp6e{--shiki-light:#39ADB5;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .s_bVq, html code.shiki .s_bVq{--shiki-light:#9C3EDA;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}",{"title":63,"searchDepth":78,"depth":78,"links":30706},[],"2012-06-18","在 Android 中，Activity 允许有很多种调用方式，其中一个方法是使用 \u003Cintent-filter>。",{},"\u002Fposts\u002F2012\u002Fseveral-intent-filters-in-one-activity",{"text":1217,"minutes":30712,"time":30713,"words":7023},1.08,64800,{"title":30441,"description":30708},{"loc":30710},"posts\u002F2012\u002F20120618.several-intent-filters-in-one-activity",[543,13759],"23:13","9WJHWggq3x34OYTuv-1vBdW_1i6CnPU1f0fkhoyDwaI",{"id":30721,"title":30722,"body":30723,"class":528,"cover":528,"coverSize":528,"date":30730,"description":30727,"draft":530,"extension":531,"hideComments":530,"location":528,"meta":30731,"navigation":415,"path":30732,"readingTime":30733,"seo":30737,"sitemap":30738,"stem":30739,"tags":30740,"time":30741,"weather":528,"__hash__":30742},"posts\u002Fposts\u002F2012\u002F20120323.eclipse-not-display-code-hint.md","【已解决】Eclipse 代码提示不显示的问题",{"type":25,"value":30724,"toc":30728},[30725],[28,30726,30727],{},"前些时候重装了系统，在备份 Android SDK 和 Eclipse 的时候出了问题，然后只能重新下载，SDK 下得我都要吐血了，超慢。然后，我发现 Eclipse 有了新版本的，于是就下载了个新版的，结果出了问题了。所有的 Android 代码都没有了代码自动提示了，按 Alt+\u002F，弹出的框里面什么也没有。苦恼了很久，以为是少装了些什么。网上也查了很久，无果。后来在“Windows\u002FPreference\u002FJava\u002FEditor\u002FContent Assist\u002FAdvanced”下面发现了些端倪，没有勾选 Java Proposals，我就很奇怪，然后打开室友的 Eclipse，他的版本跟我之前的版本是一样的，我发现，同样的地方，他选择的是 Java Proposals(Task-Focused)，而 Java Proposals 也没有勾选。而我的新版本里面没有 Java Proposals(Task-Focused)。问题找到了，由于我的配置是使用的之前版本的配置，所以就导致了没有选中 Java Proposals，至于那个(Task-Focused)是什么，还没搞懂……不过，至少我的代码提示是回来了，哈哈哈！可以继续编程了！",{"title":63,"searchDepth":78,"depth":78,"links":30729},[],"2012-03-23",{},"\u002Fposts\u002F2012\u002Feclipse-not-display-code-hint",{"text":1217,"minutes":30734,"time":30735,"words":30736},1.385,83100,277,{"title":30722,"description":30727},{"loc":30732},"posts\u002F2012\u002F20120323.eclipse-not-display-code-hint",[543,13759],"10:39","JD3IkDXryji0MeJ8n7XLoW1nXZXXVdyEXb5z5bEC93k",{"id":30744,"title":30745,"body":30746,"class":528,"cover":528,"coverSize":528,"date":30767,"description":30750,"draft":530,"extension":531,"hideComments":530,"location":30768,"meta":30769,"navigation":415,"path":30770,"readingTime":30771,"seo":30775,"sitemap":30776,"stem":30777,"tags":30778,"time":30779,"weather":528,"__hash__":30780},"posts\u002Fposts\u002F2012\u002F20120219.sqlserver-cannot-delete.md","【SQLServer】“无法对数据库 'XXX' 执行删除，因为它正用于复制” 的解决方法",{"type":25,"value":30747,"toc":30765},[30748,30751,30753,30756,30759,30762],[28,30749,30750],{},"从今天起，把编程中遇到的所有问题都记录下来，以便今后参考，以及方便网友查阅，希望我的问题可以帮助到很多志同道合的人们，我也是受很多前辈的提点，一步一步走来，希望大家都不要吝啬，将自己遇到的问题记录下来，因为你的一个记录，也许就节省了别人很多的时间！希望有一天，我们能做到世界最好！",[4947,30752],{},[28,30754,30755],{},"关于这个错误，是因为我今天在服务器上想把数据库复制到本地，使用了 “发布、订阅” 方案，结果还没成功……尴尬……后来，我就直接把服务器上的 mdf 和 ldf 文件都直接拷到本地上了。后来在本地修改表名的时候出现了这个错误，说“无法对数据库'XXX'执行删除，因为它正用于复制”。",[28,30757,30758],{},"后来我也是查找了网上的一些方法，只需要执行 sp_removedbreplication 'XXX' 就可以了。",[28,30760,30761],{},"这个语句的解释是：从数据库中删除所有复制对象，但不更新分发服务器上的数据。此存储过程在发布服务器的发布数据库或订阅服务器的订阅数据库上执行。",[28,30763,30764],{},"具体不是特别理解，不过，至少能解决我的问题了！",{"title":63,"searchDepth":78,"depth":78,"links":30766},[],"2012-02-19","床上",{},"\u002Fposts\u002F2012\u002Fsqlserver-cannot-delete",{"text":1217,"minutes":30772,"time":30773,"words":30774},1.77,106200,354,{"title":30745,"description":30750},{"loc":30770},"posts\u002F2012\u002F20120219.sqlserver-cannot-delete",[543],"08:22","I69wVN249ZK2VnyhVz1cV20bjitO_rOth6ljL4oCs7M",1777580267368]