LOGO
首页 网站广场 站长动态 活跃度榜 审核查询 逛逛好站 留言交流 提交申请 关于本站

站长动态

站长动态所展示的是已加入好站网成员站长文章
共同步 2237 篇博文
(每2小时更新一次)
浩然
入驻第1年
MCP 是什么?它是如何工作的?
自 2022 年底 ChatGPT 横空出世以来,目前业界已涌现出多个优秀的大语言模型(Large Language Models,LLM),人们的日常生活和工作正在被大语言模型加速改变着。 大语言模型虽然很强大,但也有一些局限性,如:其知识被固定在训练结束之时,无法跟着当下动态的世界进行「自动更新」。所以,如果仅使用大语言模型训练好的数据解决问题,而不借助任何方式获取新的数据的话,会大大限制大语言模型的能力上限。
老刘
入驻第1年
以直报怨,怨未尽而心已定
《论语·宪问》中,有人问孔子: “以德报怨,何如?” 孔子答:“何以报德?以直报怨,以德报德。” 我对这句话,之前一直理解为以牙还牙,昨天看书时候又看到这个词,觉得以牙还牙,爽是爽了,但不应该是这样一种情况,很多时候,我们也无法做到如此的酣畅淋漓。那么“以直报怨”究竟是什么意思?难道报了怨,心里就真的没有怨气了吗? 一、“以直报怨”,不是让怨消失,而是让怨归位 “怨”来自不平。 当你被误解、被伤害、被欺骗时,心里自然会生怨。 因此,与其把怨恨留在心心里,不如给它找到一个出口。 “以直报怨”的“直”,指的是公正、理性、合宜。 不是以牙还牙,也不是逆来顺受, 而是用正直的方式让怨发泄出来。 不久前我们小区发生的事情。小区的车位到现在还没有建好,前段时间,开发商说要建车位,并开始售卖剩余的车位,可是价格确实比几年前卖的价格低的多的多。所以,之前买车位的业主就很难受,去退也没有,开发商根本没钱给你。用一个业主的话说就是: 意难平 意难平,就是怨 虽然最终也不了了之了,但是把这个怨表达出来给了开发商,也算是有个出口,可能有时候还是会觉得窝囊,但是能有什么办法呢。当今的大环境就是如此,车位还好点,算是赔了几万块钱,想想同一个小区的房子呢,最高价的时候14000,现在只有不到一万,所以,想想还有别人比你惨,心里是不是好受点。当了韭菜就要有韭菜的觉悟。 所以,怨未必能尽,但若能“直面”它,怨就不会蔓延成恨。 这就是孔子所谓的“直”。 二、“直”的意义,在于止怨而非无怨 人若被怨气缠久了,容易迷失是非。 有时怨的不是别人,而是自己心里的不甘。 “直”让人不被情绪牵着走, 它像一条沟渠,让积怨之水有路可出。 你可以反击,可以申辩,可以拒绝, 但不必怀恨。 以直报怨,不是清除怨气,而是防止怨气滋生。 怨止于直,这一刻,心定了。 三、“以德报怨”,是怨止之后的更高境界 老子说:“报怨以德。” 那是更高的修养,是怨尽心清之后的平和。 但若怨尚未平,便谈以德相报, 那只是压抑,不是化解。 所以孔子说,先“直”后“德”。 以“直”立界,以“德”安心。 先止怨,后化怨。先明理,后谈仁。 两人讲的,不是冲突的两条路,而是修心的步骤。 四、怨未尽,而心已定 “以直报怨”的真正意义在于: 怨或许仍在,但它已不乱人心。 它变成了经验、成了界限、成了智慧。 怨,是人之常情; 直,是理之常道; 德,是心之常明。 当怨有界,德才有根。 怨未尽,心却已定—— 这,便是孔子的智慧。 怨若未报,会伤己; 怨若乱报,会伤人; 以直报怨,则两者皆安。 怨未必无,但心可以静; 怨未必尽,但人可以明。 “以直报怨”,正是这份有界的清醒。
雅余
入驻第1年
《园冶》:世界最早的造园学名著
最近反复读的一本书,忍不住还是介绍一下。书中内容一是我喜欢的园林,二是书中很多极简、尊重自然的理念是我所爱。 […]
小十
入驻第1年
天津两日纪行
10月25—26日,趁着周末两天,去了一趟天津。 第一天 早上5点半起床,上午约10点30分,抵达天津西站。出站后,搭乘出租车前往位于滨江道步行街内的酒店。 滨江道步行街是天津市最为繁华的商业街之一,其历史可追溯至20世纪初,汇集了众多商场与老字号品牌。午餐选择了“醉喜楼津菜馆”,尝试了特色菜八珍豆腐与皮皮虾陷的锅贴,菜品份量充足。 传统八珍豆腐 25-10-25 · 9mm · f/2.8 · 1/100s · ISO64 · iPhone 皮皮虾扇贝锅贴 25-10-25 · 50mm · f/1.8 · 1/341s · ISO80 · iPhone 午后,首先参观了西开教堂。其全称为天主教西开总堂,始建于1916年,是天津市规模最大的罗马风貌建筑群,建筑风格为罗曼式。
扶苏
入驻第1年
弓守其道,射以观德
周日,2025年度苏州市大众体育联赛之射箭联赛如期举行。 我觉得有必要先介绍下这个大众体育联赛,总计84个赛事活动项目,覆盖足球、篮球、羽毛球、飞盘、滑板等传统与新兴项目,并将赛事场景延伸至姑苏古巷、金鸡湖畔、太湖湾等城市地标,项目面对人群...阅读更多 Source
阿川
入驻第1年
打造全新 Artalk 评论界面
JN
入驻第1年
2025 去東京玩!飛機、住宿篇(圖多)
上個月我去日本東京玩了一趟,結果一直沒有時間整理照片。最近終於把事情忙得差不多了,就來把照片都整理一下發出來~ 其實我去年我就去過一次東京了。今年會再去最大的原因是因為臨時想到要把特休用掉,也可以把公司的旅遊補助請一請。因為決定得很臨時,也沒有什麼時間慢慢研究可以去哪裡,剛好看到 TOKYO CALLING 的時間還不錯,就直接決定要去了 XD 廢話不多說,進入正題! 飛機 上次去東京我搭的是日航(JAL),這次也想一樣從台北松山機場飛(離我住的地方很近很方便)、羽田落(進市區快),但又不想花太多錢在機票,所以這次搭的是全日空(ANA)! 我去的時間剛好在中秋節之前,所以價格還算不錯!來回 NTD $11000 左右!雖然比起廉航還是不算便宜,但不用從台北拉著行李去桃園,到了東京要進市區也方便。我覺得多花這麼一點點錢,享受到傳統航空的服務,我覺得是很划算的! 但時間就不是非常理想,週日中午去、隔週二一大早回來。不過晚一天的早上去、或是提前一天晚上回來,價格都很不漂亮,甚至比多住一、兩個晚上還貴,那我還不如在那邊多住幾天!這也是為什麼這次的旅程拉得那麼長 XD 去程 上次日航的內裝很有年代感,ANA 的則是蠻現代的,除了…遙控器…,看來這是日本飛機的標配了。 上次日航的飛機餐讓我很驚豔,所以我很好奇 ANA 的餐如何。 我這個應該是日式豬肉飯,整體只能說一般般,除了有筷子和飯後綠茶(沒拍到)之外,沒什麼比較值得拿出來說的地方。 落地後 ANA 在羽田落地停靠的是第二航廈,而不是一般國際線的第三航廈,所以外國人不多,入境超級快! 但是 Welcome Suica 只有在第三航廈賣,所以我還花了一點時間坐接駁車過去(超遠~),買到了 Suica 才能去搭 monorial 進市區。 羽田第二航廈 回程的時候一樣也是在第二航廈,這裡根本就是 ANA 的天下,到處都是 ANA 的機器,空橋停靠的也幾乎都是 ANA 的飛機! 比較意外的是,回程的登機門上機的時候的指示很不清楚,跟聯合航班的搞在一起了,我完全搞不清楚到底輪到我上飛機了沒(也有可能是我沒帶腦)。 也可能因為是少數的國際線吧,這次就沒有空橋,要搭接駁車上飛機。 回程 因為回程的飛機餐是在日本做的,所以 ANA 還有扳回一城的機會。 回程的這個親子丼,雖然有比去程的好一些,但還是沒什麼值得拿出來說的地方。 結論就是:ANA 的飛機餐非常一般,不需要有太多期待,就是一些可以填飽肚子的食物,是無法和長榮、日航之類的旗艦航空公司比的。 到是這個生可樂,也許因為是罐裝的,一直到我喝完的時候氣都還很夠!不會像一般瓶裝倒出來的,在高空很快就沒氣了。 飯店 這次我們在東京住在池袋的 HOTEL MYSTAYS,在都電荒川線向原站的旁邊,走路去 JR 山手線大塚站則是大概 10 分鐘。 我是在 Agoda 上訂房間的,飯店方也有主動和我聯絡,應該不會出現預訂漏接那種烏龍。當然,和他們用英文溝通也完全沒有問題! 房間 MYSTAYS 的價格比起 APA(日本的超大連鎖飯店)再便宜一些些,雖然沒有像 APA 又新又有超大電視,但我覺得也沒什麼好挑剔的,畢竟也真的超便宜了。我們兩人在那邊住了 9 個晚上,平均一個人才花不到 NTD $9000 ! 房間的冷氣是分離式的,可以自由調整房間裡面的溫度。廁所和浴室我沒拍到,但就是和 APA 差不多的那種,有浴缸、洗臉盆、馬桶的一般浴室。 廚房 重點是!他們有的房間有廚房!因為中秋節也快到了,我們就在房間吃燒肉!餐具也都是可以免費借用,非常讚! 自助服務 另一個我覺得 MYSTAYS 很棒的地方,就是所有的備品都是自己去大廳取用,不會因為每天的配送造成很多浪費! 但垃圾也需要自己拿去垃圾場,當然也沒有像 APA 會強制進房間整理的規定。 再附上一些飯店看出去的景色: 整體來說,我覺得在 MYSTAYS 住的那幾天蠻滿意的!價格便宜、該有的東西也都有,沒什麼大問題! 如果真的要挑缺點的話,就是從 JR 車站走過去有一大段的上坡。但對我這個不介意走路的人來說,是一個可以好好觀察路上異國風情的好機會~
阿川
入驻第1年
西湖阅读初体验
陈仓颉
入驻第1年
Group Chats Could Be Overwhelming
Ever since instant messaging was invented, group chats […]
姓王者
入驻第1年
1024 重要的日子
1024 重要的日子今天又是一年一度的 中国程序员节,值此之际,祝各位同志节日快乐,身体健康!麦当劳更重要的是,今天有麦当劳活动!,程序员节快乐!巨无霸三件套¥29.9! 10月24日-26日我发现答题太没意思了,所以我选择了github认证,这个挑...
阿川
入驻第1年
时隔四年,晨跑环西湖
小十
入驻第1年
解决林木木老师哔哔广场中同一Memos实例下多用户头像昵称显示错误问题
问题描述 非常感谢 林木木 老师编写的 哔哔广场 ,日常用于调取Memos网站的内容,并实现了同步订阅其他Memos用户及发表 memos 等功能。 但是,我这里有一个较为小众的需求,就是在使用 Memos 广场功能时,如下图界面: 当多个用户来自同一个 Memos 实例时,其动态内容能够正常显示,但用户信息(头像和昵称)却全部显示为同一个用户的信息。 例如,在我的订阅列表中: 小十 (ID: 1, 头像: avatar.png) 泥鳅胡子 (ID: 3, 头像: myh.jpg) 上述两个用户都使用同一个 Memos 实例 https://memos.xiaoten.com,但在广场模式下,所有动态都显示为泥鳅胡子的头像和昵称,即使这些动态实际上是小十发布的。 原因分析 主要原因 用户信息映射逻辑未覆盖这种特殊情况,在原始的代码实现中: 获取用户动态时,代码使用了同一个 Memos 实例下的第一个用户信息来覆盖所有用户的动态信息。 最初使用 creatorName 作为映射键,但当多个用户有相同的显示名时会导致冲突。 在合并用户信息和动态数据时,属性覆盖的顺序问题,会使用户信息被动态数据中的空值覆盖。 技术细节 在 Promise 内部处理动态数据时,原始代码使用了 matchedMemo 而不是当前用户 u 的信息: // 原因 for (let key in matchedMemo) { if (matchedMemo.hasOwnProperty(key)) { item[key] = matchedMemo[key]; } } 剩余 1 行代码 展开剩余代码 这会使同一个 Memos 实例下的所有用户都使用了第一个匹配的用户信息。 解决方式 1. 修改 Promise 内部的用户信息赋值 在获取每个用户的动态时,使用当前用户 u 的信息而不是 matchedMemo:
阿川
入驻第1年
我从义乌搬家到杭州了
JN
入驻第1年
起床好難
早起 最近一年來,我很喜歡晚上 8、9 點就上床睡覺,隔天一大早就自然醒的感覺。上班前能有一段自己的時間,做點想做的事情,不用一醒來就匆匆忙忙趕著出門的感覺,真的很舒服! 但事情不可能總是那麼理想,就算我前一天真的早睡,早上也自己醒來,也不代表我就會立刻跳下床。很多時候,天氣冷、心情不好、有不想做的事情還沒做完、看到手機有通知或新文章,這些都可能讓我決定再睡一下,或是在被窩裡看手機就好。 成功案例 但是,前陣子我學 pixel art 的那幾個禮拜,根本就是作弊。每天一醒來,我就像是背後有一隻手在推我一樣,直接衝到電腦前面,打開教材,一個章節一個章節地邊學邊練習。用老套一點的說法,就是「每天叫醒我的不是鬧鐘,是夢想」。腦袋裡滿了各種想像和期待,迫不及待想讓自己的技術更進一步,然後就瞬間進入心流的狀態,每天早上都一是一段很滿足的 me time。 類似的感覺,在我當替代役的時候也出現過。那時候我會盡量把工作的事情趕快做完,然後就開始刷 LeetCode 的題目、看書、寫信給朋友。雖然每天早上去辦公室打卡對我來說還是頗痛苦,但那段時間真的讓我覺得很充實,很滿足。 掙扎 寫這篇文章的原因,其實也跟最近天氣變涼有關。這幾天早上起床真的有點痛苦,躺在床上掙扎了很久,腦袋裡閃過了很多事情,但就是提不起勁。最後是靠著「寫一篇部落格文章來灌水」這個念頭,才終於成功讓我離開溫暖的被窩。 目標 仔細想想,我會這麼熱衷刷 LeetCode 和學 Pixel Art,它們有個共通點:就是有「明確的目標」,而且這個目標是會讓我感到「渴望」的,同時也有「明確的步驟」可以一步一步跟著做。以刷 Leetcode 來說,目標就是刷完 Leetcode 75 、拿到 badge。而我只要每天按部就班把題目寫完,我就確定能達成。 例行公事 有這樣明確的 routine,就不需要再煩惱起床後該做什麼,這真的能省去很多內心的掙扎。當然,缺點也很明顯:如果 routine 裡的那些事情對我來說門檻太高,或是我的動力不夠支撐那份阻力時,這一連串的計畫就可能就都 miss 掉了。 什麼才是我真正渴望的? 那到底什麼樣的目標,才是我真正渴望、有動力的呢?也許 心動才會行動 是個不錯的參考方向,但我覺得,這其中肯定還有其他一些關鍵的要素在影響著。
小十
入驻第1年
在 Hugo 中集成 Memos 多用户微博系统
摘要 本文详细阐述在 Hugo 静态网站生成器中集成 Memos 0.18.2 多用户微博系统的完整技术方案。通过 RESTful API 调用、Twikoo 评论系统集成、图片资源处理等关键技术,实现了在静态网站中展示动态微博内容的功能。本文涵盖系统架构设计、核心代码实现、性能优化策略以及部署配置指南,为开发者提供完整的实施参考。样例请见本站 说说页面 。 1. 主要流程 1.1 系统组成 前端框架: Hugo 静态网站生成器 微博服务: Memos 0.18.2 RESTful API 评论系统: Twikoo 评论服务 图片处理: ViewImage 灯箱 + Lozad 懒加载 内容渲染: Marked.js Markdown 解析器 1.2 数据流架构 Memos API → 数据获取 → 内容处理 → 评论统计 → HTML 渲染 → 前端展示2. 核心实现方案 2.1 多用户配置管理 在 Hugo 模板中定义用户配置数组: var memosMyList = [ { "creatorName": "用户A", "website": "https://example.com", "link": "https://memos.example.com", "creatorId": "1", "avatar": "/avatars/user-a.png", "twikoo": "https://twikoo.example.com" }, { "creatorName": "用户B", "website": "https://example.org", "link": "https://memos.example.com", "creatorId": "2", "avatar": "/avatars/user-b.png", "twikoo": "https://twikoo.example.com" } ]; 剩余 13 行代码 展开剩余代码 2.2 API 集成与数据处理 2.2.1 并发数据获取 async function getAllUsersMemos() { const userPromises = currentUsers.map(user => getUserMemos(user.link, user.creatorId, user.creatorName, user.avatar) ); const allUserResults = await Promise.allSettled(userPromises); const successfulResults = []; allUserResults.forEach((result) => { if (result.status === 'fulfilled' && Array.isArray(result.value)) { successfulResults.push(...result.value); } }); return successfulResults; } 剩余 11 行代码 展开剩余代码 2.2.2 数据验证与标准化 function validateUserConfig() { const validUsers = currentUsers.filter(user => user.creatorId && user.link && user.creatorName ); return validUsers.length > 0; } function normalizeUrl(baseUrl, path) { const normalizedBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl; const normalizedPath = path.startsWith('/') ? path : '/' + path; return normalizedBase + normalizedPath; } 剩余 8 行代码 展开剩余代码 2.3 评论系统集成 2.3.1 批量评论统计 async function getMemoCount(memos) { const twikooGroups = {}; // 按 Twikoo 环境分组处理 memos.forEach(item => { if (!item?.twikoo) return; const envId = item.twikoo; const memoUrl = normalizeUrl(item.link, `/m/${item.id}`); if (!twikooGroups[envId]) { twikooGroups[envId] = []; } twikooGroups[envId].push({ url: memoUrl }); }); const allTwikooCount = []; for (const [envId, items] of Object.entries(twikooGroups)) { try { const urls = items.map(item => item.url); const res = await twikoo.getCommentsCount({ envId: envId, urls: urls, includeReply: false }); if (Array.isArray(res)) { allTwikooCount.push(...res); } } catch (error) { console.error(`Twikoo 环境 ${envId} 评论数获取失败:`, error); } } // 关联评论数到对应 Memo memos.forEach(item => { if (!item?.twikoo) { item.count = 0; return; } const url = normalizeUrl(item.link, `/m/${item.id}`); const countData = allTwikooCount.find(o => o?.url === url); item.count = countData?.count || 0; }); return memos; } 剩余 44 行代码 展开剩余代码 2.4 内容渲染引擎 2.4.1 Markdown 内容处理 function processMemoContent(content) { const TAG_REGEX = /#([^#\s!.,;:?"'()]+)(?= )/g; const IMAGE_REGEX = /\!\[(.*?)\]\((.*?)\)/g; const LINK_REGEX = /(?<!!)\[(.*?)\]\((.*?)\)/g; let processed = content .replace(TAG_REGEX, '') .replace(IMAGE_REGEX, '') .replace(LINK_REGEX, '<a class="primary" href="$2" target="_blank">$1</a>'); return marked.parse(processed); } 剩余 7 行代码 展开剩余代码 2.4.2 图片资源处理 function processImageResources(content, memo) { let imageHtml = ''; // 处理内联图片 const inlineImages = content.match(IMAGE_REGEX); if (inlineImages?.length > 0) { const imageString = inlineImages.join('').replace(/,/g, ''); imageHtml = imageString.replace(IMAGE_REGEX, '<div class="memo-resource width-100">' + '<img class="lozad" data-src="$2" alt="$1">' + '</div>' ); } // 处理附件图片 if (memo.resourceList?.length > 0) { memo.resourceList.forEach(resource => { if (resource.type?.startsWith('image')) { const imageUrl = resource.externalLink || normalizeUrl(memo.link, `/o/r/${resource.uid || resource.id}`); imageHtml += '<div class="memo-resource w-100">' + '<img class="lozad" data-src="${imageUrl}">' + '</div>'; } }); } return imageHtml ? '<div class="resource-wrapper">' + '<div class="images-wrapper my-2" view-image>' + imageHtml + '</div>' + '</div>' : ''; } 剩余 27 行代码 展开剩余代码 3. 性能优化策略 3.1 分页加载机制 function pagination(data, page, limit) { const startIndex = (page - 1) * limit; const endIndex = startIndex + limit; return data.slice(startIndex, endIndex); } function updateData(data) { const validData = data.filter(item => item && typeof item === 'object'); validData.sort((a, b) => b.createdTs - a.createdTs); const pageData = pagination(validData, currentPage, itemsPerPage); renderMemoList(pageData); const totalPages = Math.ceil(validData.length / itemsPerPage); updatePaginationControls(currentPage, totalPages); } 剩余 11 行代码 展开剩余代码 3.2 图片性能优化 function initializeImageOptimizations() { // 图片懒加载 if (typeof lozad !== 'undefined') { const imageObserver = lozad('.lozad', { rootMargin: '50px 0px', threshold: 0.1 }); imageObserver.observe(); } // 图片灯箱初始化 if (typeof ViewImage !== 'undefined') { ViewImage.init('.images-wrapper img'); } } 剩余 10 行代码 展开剩余代码 3.3 缓存策略 class MemosCache { constructor() { this.cacheKey = 'memos-data-cache'; this.cacheTimeout = 5 * 60 * 1000; // 5分钟 } getCachedData() { const cached = localStorage.getItem(this.cacheKey); if (!cached) return null; const { data, timestamp } = JSON.parse(cached); if (Date.now() - timestamp > this.cacheTimeout) { localStorage.removeItem(this.cacheKey); return null; } return data; } setCachedData(data) { const cacheObject = { data: data, timestamp: Date.now() }; localStorage.setItem(this.cacheKey, JSON.stringify(cacheObject)); } } 剩余 22 行代码 展开剩余代码 4. 错误处理与监控 4.1 健壮性设计 async function getUserMemos(link, userId, userName, userAvatar) { try { // 参数验证 if (!link || !userId) { throw new Error('缺少必要参数'); } const normalizedLink = link.endsWith('/') ? link : link + '/'; const apiUrl = `${normalizedLink}api/v1/memo?creatorId=${userId}&rowStatus=NORMAL&limit=50`; const response = await fetch(apiUrl); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); if (!Array.isArray(data)) { throw new Error('API 返回数据格式错误'); } return data.filter(item => item && typeof item === 'object') .map(item => ({ ...item, link: normalizedLink, avatar: userAvatar, creatorName: userName })); } catch (error) { console.error(`获取用户 ${userName} 数据失败:`, error); return []; // 优雅降级 } } 剩余 29 行代码 展开剩余代码 4.2 性能监控 class PerformanceMonitor { static async measureApiCall(apiCall) { const startTime = performance.now(); try { const result = await apiCall(); const duration = performance.now() - startTime; if (duration > 1000) { // 超过1秒记录警告 console.warn(`API 调用耗时较长: ${duration.toFixed(2)}ms`); } return result; } catch (error) { const duration = performance.now() - startTime; console.error(`API 调用失败,耗时 ${duration.toFixed(2)}ms:`, error); throw error; } } } 剩余 14 行代码 展开剩余代码 5. 部署配置指南 5.1 文件结构规范 hugo-site/ ├── layouts/ │ └── _default/ │ └── memos.html ├── static/ │ └── memos/ │ ├── js/ │ │ ├── memos-core.js │ │ ├── twikoo.min.js │ │ ├── marked.min.js │ │ └── lozad.min.js │ └── css/ │ └── memos-styles.css └── content/ └── memos.md 剩余 10 行代码 展开剩余代码 5.2 Hugo 模板配置 <!-- layouts/_default/memos.html --> {{ define "main" }} <div class="memos-container"> <header class="memos-header"> <h1>{{ .Title }}</h1> </header> <div class="memos-content"> <div id="memo-list" class="memo-list-container"></div> <button id="load-more" class="load-more-button">加载更多</button> </div> </div> <script src="/memos/js/marked.min.js"></script> <script src="/memos/js/lozad.min.js"></script> <script src="/memos/js/twikoo.min.js"></script> <script src="/memos/js/memos-core.js"></script> {{ end }} 剩余 13 行代码 展开剩余代码 5.3 环境变量配置 // 生产环境配置 const MEMOS_CONFIG = { apiEndpoints: { memos: 'https://memos.example.com/api/v1', twikoo: 'https://twikoo.example.com' }, performance: { cacheTimeout: 300000, paginationSize: 10, imageLazyLoad: true }, features: { multiUser: true, comments: true, imageZoom: true } }; 剩余 12 行代码 展开剩余代码 6. 安全考虑 6.1 输入验证 function sanitizeUserInput(input) { if (typeof input !== 'string') return ''; return input .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&#x27;') .replace(/\//g, '&#x2F;'); } 剩余 5 行代码 展开剩余代码 6.2 CSP 配置建议 <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://memos.example.com https://twikoo.example.com; img-src 'self' https: data:; style-src 'self' 'unsafe-inline'; connect-src 'self' https://memos.example.com https://twikoo.example.com"> 剩余 1 行代码 展开剩余代码 7. 测试方案 7.1 单元测试示例 describe('Memos Integration', () => { test('URL normalization', () => { expect(normalizeUrl('https://example.com/', '/m/123')) .toBe('https://example.com/m/123'); expect(normalizeUrl('https://example.com', 'm/123')) .toBe('https://example.com/m/123'); }); test('Content processing', () => { const input = 'Hello [world](https://example.com)'; const processed = processMemoContent(input); expect(processed).toContain('href="https://example.com"'); }); }); 剩余 10 行代码 展开剩余代码 8. 结论 本文提出的 Hugo 与 Memos 多用户微博系统集成方案,通过系统的架构设计和严谨的代码实现,成功在静态网站环境中引入了动态社交功能。关键技术贡献包括:
雅余
入驻第1年
日常漫步 Vol.17 之板樟山小环线
又是一个完美的周末,一周两山。继周六上午到凤凰山巡山之后,感觉没过瘾,周日下午4点又到板樟山走了个小环线,一直 […]
老刘
入驻第1年
把GotoSocial嵌入到Hugo
把博客重新换成Hugo后,我又把GotoSocial安装了起来,就是想发布自己有一些碎语之类的东西,在memos和GotoSocial中纠结了一会,最终选择了GotoSocial,主要原因还是看到memeos的作者随意改动API,为了以后不再有不必要的麻烦,还是选择一个更稳定的最好。整体完成后的效果可以参考: 我的微博 总体思路 由于 GoToSocial 默认开启了严格的 CORS 限制,获取内容的时候需要token,由于Hugo是静态博客,直接把token写在齐物非常不安全,所以我们需要一个中转层。 Cloudflare Workers 可以完美胜任代理转发的功能,作用是: 从 Hugo 齐物发请求 → Cloudflare Worker Worker 向 https://你的域名/api/v1/accounts/…/statuses 请求 GoToSocial 数据 Worker 再把 JSON 数据返回给 Hugo 齐物 Hugo 构建时渲染 获取 GoToSocial 的 toot API 接口 目标概述 我们要: 向 https://你的域名/api/v1/apps 注册一个应用; 通过浏览器授权; 用授权码换取 access_token; 把这个 token 放入 Cloudflare Worker 的环境变量; 然后 Hugo 博客就能安全地从 Worker 获取嘟文数据。 详细步骤 注册一个新应用 在命令行执行以下命令(用 curl,别忘了改你的 app 名称): curl -X POST \ -H "Content-Type: application/json" \ -d '{ "client_name": "hugo-gts-proxy", "redirect_uris": "urn:ietf:wg:oauth:2.0:oob", "scopes": "read" }' \ 'https://你的域名/api/v1/apps' 成功后会返回类似:
陈仓颉
入驻第1年
和平使者不和平
滚导有一种神奇的能力,能把边缘反英雄拍出丰富、动人的情感,也能把一个好 IP 拍成一坨屎。后者自然是《新超人》 […]
JN
入驻第1年
部落格問題挑戰
前幾天看到廢文小天地發了一篇 部落格問題挑戰 看了覺得好像蠻有趣的,我就也來挑戰一下吧~ 你當初為什麼開始寫部落格? 最一開始應該是受到 Wiwi 在 好檸檬 Podcast 的熏陶。那時候我還在讀大四,沒有什麼課要上,但有點求職焦慮。覺得自己會的東西不多,不如就先開始寫部落格吧!所以那個時候寫的東西也都以技術、作品展示居多。(然而其實根本沒什麼好寫,我也根本沒有我想的那麼弱) 我在 過去寫的是廢文、那今天的新文章是……? 有比較詳細地回顧我的部落格。 使用什麼平台來管理你的部落格?為什麼選擇它? 我用的是 Hugo SSG(靜態網頁産生器),佈景主題是 Stack,部署在 Cloudflare Pages 上。 用 Hugo 是因為有不錯的開源佈景主題可以用,要客製化元件也還算容易。另外選擇 SSG 的好處是不太需要煩惱部署 & 維運的事情,甚至可以達成 0 元架站!這讓我能把心思盡量放在寫文章這件事情上。 我在 部落格大搬家:從 Wowchemy 到 Stack 這篇文章有提到比較多關於我最近這次搬家的選擇。 之前有在其他平台上寫過部落格文章嗎? 很久以前有,很小的時候在無名小站有寫過一些東西。不過那個時候大家都還沒開始用 Facebook 就是了,我覺得和現在的部落格不太能相題並論,我那個時候應該也沒有寫什麼值得挖出來的東西。 除了在這邊,我之前也會把文章同步到 Medium、Matters 之類的平台,不過後來就懶了。 (BTW,安安Q 的女裝日記 也是我的另一個部落格,主要記錄和女裝相關的主題) 如何撰寫文章?例如,使用本地編輯工具,還是在部落格的後台/控制面板中編寫? 我用的是 VSCode,搭配 Front Matter(Ivon 的介紹) 就有一個還可以的管理介面! 選擇 VSCode 是因為我寫 code 就已經很習慣了,而且還能整合 AI 直接在編輯器裡面幫我修我的文章!我真的找不到第二個這麼好用的純文字編輯器!(寫 code 的話 Vim 還能與之一戰) 不過 VSCode 也有一個缺點,就是只能在電腦上用。 所以如果我不方便用電腦的時候,我會先在手機上用 LogSeq 先打草稿,然後在能用電腦的時候再貼到 VSCode 裡面把文章編輯之後再發出來。 什麼時候最有寫作靈感? 我的靈感大部分都來自上班無聊的時候生活中事件的記錄。所以我在發現有事情可以寫成文章的時候就會先記到我 LogSeq 裡面;在我之後有空的時候,就會看看有什麼之前的筆記有什麼東西可以寫,或是記一下最近發生了什麼事情。 有的時候其他部落格的文章也會是我的靈感來源,就像這篇文章;或者 Meta 惹毛我的時候我也會有很多靈感。 會在寫完後立即發布,還是會先存成草稿醞釀一下? 通常我在文章寫完之後就會直接發出來。 不過有時候可能因為要補圖片之類的原因,可能就會把文章擺個幾天再發;或是如果那陣子發文比較密集的話,我也可能會為了想要分散一下更新頻率,把文章放個幾天再發出來。 部落格上最喜歡的文章是哪一篇? 老實說我選不太出來,因為我覺得我的每一篇文章都還不是很成熟。但如果真的要選一篇的話,那會是 我把洛克人 X4 全破了!。 因為我覺得這篇文章完整地把我玩落克人的開心、以及為什麼喜歡這個遊戲都記錄了下來。我也覺得這篇應該會是在未來,我會一直回去回味的一篇文章。 對部落格有什麼未來計畫嗎?例如重新設計、搬到另一個平台,或是加入新功能? 短期內我會想要先多累積一些文章吧,也許 50 或 100 篇之類的。一是我想讓我自己再更習慣寫文章這件事情;二是我也想再觀察看看什麼樣的內容比較能收到比較多的回饋(尤其是來自現實的朋友),畢竟那也是維持寫作的一大動力!所以現階段來說可能比較像是我的部落格的探索期吧~ 在我的部落格的類型脈絡比較清晰、文章也多到不容易翻的時候,我可能會需要建一個引導頁來讓新來的朋友比較不會迷路。如果之後找到有什麼是大家特別喜歡看的,而且我也有想要特別經營的話,也許我也會另外拉成一個獨立的站也說不定。 如果不知道寫什麼的時候,我也可能把社群平台上面的貼文慢慢搬過來(那些平台能再活幾年都是未知數);或者我也可能當一個臭老人,一直把以前的事情挖出來寫 XD 至於加入新功能什麼的,暫時沒有太多想法,目前覺得蠻夠用的。如果要說最可能改的東西的話,也許是把留言板系統好好設定一下吧,現在有一些地方還是怪怪的;另外也可能會加的新功能,是把 Mastodon 的貼文內嵌或同步到這個部落格的某個角落。因為我還是有一些比較短的廢文會發在那邊,如果能在這邊一起看到的話感覺蠻棒的~ 結語 部落客之間的互相影響,是我覺得寫部落格很有趣的事情之一。這種感覺很像 10 年前的 YouTube,創作者之間會互相 feat. 來 feat. 去的,互相打通彼此的觀眾,擴大彼此的影響力。 這種真人之間的互相交流,比起在一些平台上看不知道是誰(甚至可能不是人類)的貼文,搞不好還比較能被稱作是「社群」。
小十
入驻第1年
借助 GitHub Actions 自动化部署 Hugo 网站到自有服务器
从手动部署到自动化的工作流演进 在网站开发的初期阶段,为了深入理解整个发布流程,我一直在本地环境中使用 Hugo 手动生成静态网站。这个过程包括:运行 hugo 命令生成静态文件,手动打包 public 目录,然后通过 SCP 或 FTP 上传到服务器,最后在服务器上解压并配置。虽然这种方法让我对网站部署的每个环节都有了清晰的认识,但随着内容的增多,这种重复性工作变得越来越耗时。 虽然现在有 Vercel、Netlify 等优秀的静态网站托管平台,但由于我已经拥有一台国内的轻量级云服务器(主要用于 frp 内网穿透服务),我决定充分利用现有资源,直接在这台服务器上托管我的 Hugo 网站。 自动化部署方案设计 最终实现的解决方案是:通过 GitHub 私有仓库存储 Hugo 源码,利用 GitHub Actions 实现自动化构建和部署,使用 rsync 将生成的静态文件同步到自己的服务器。 这种方案的优势在于: 自动化:代码推送后自动完成整个构建和部署流程 版本控制:所有变更都有完整的历史记录 安全性:私有仓库保护源代码,SSH 密钥保证传输安全 成本效益:充分利用现有服务器资源 服务器端配置 环境准备 我使用 1Panel 面板管理服务器,网站目录为: /opt/1panel/apps/openresty/openresty/www/sites/xiaoten.com/index安装 rsync 由于同步方案使用 rsync,需要在服务器上安装: # CentOS/RHEL 系统 sudo yum install -y rsync # Ubuntu/Debian 系统 sudo apt-get update && sudo apt-get install -y rsync安全配置:SSH 密钥认证 为了实现安全的自动化部署,需要配置 GitHub Actions 对服务器的访问权限。

© 2026 好站网HaoZhan.wang 1.5 版权所有

苏ICP备19065220号-4 萌ICP备20269980号 茶ICP备2026050346号 本站数据 版本历史 关于本站