Tự sở hữu dữ liệu của tôi, phần 1: Tích hợp giải pháp lịch tự host
(emilygorcenski.com)- Khi các chuyến công tác/du lịch thường xuyên chồng lên việc duy trì quan hệ qua nhiều múi giờ, một hệ thống lịch tự vận hành trở nên cần thiết để giảm phụ thuộc vào Big Tech và trực tiếp kiểm soát dữ liệu lịch
- Kiến trúc mới lấy máy chủ CalDAV Baïkal làm trung tâm, gom Airtrail, email IMAP và ICS từ trường ngôn ngữ vào CalDAV, rồi xuất bản lại dưới dạng tệp
.icsđể chia sẻ - Cách viết lịch bằng YAML rồi chuyển thành ICS trong CI/CD đủ dùng cho nguyên mẫu, nhưng lỗi thủ công và gánh nặng bảo trì tăng dần nên cuối cùng được thay thế bởi mô hình không còn dựa trên tệp tĩnh
- Script Python cứ mỗi 15 phút sẽ thu thập và phân loại sự kiện, tạo riêng ICS cho gia đình và công việc, đồng thời đánh dấu một số sự kiện trong lịch công việc là PRIVATE
- Google Calendar và Google Script chỉ được dùng để đồng bộ lịch công việc; sau khi tích hợp API Airtrail, chỉ cần nhập chuyến bay một lần là trong vòng 15 phút sẽ xuất hiện ở lịch cá nhân và trong vòng 1 giờ sẽ phản ánh ở lịch công việc
Vì sao việc quản lý lịch trở nên phức tạp
- Do di chuyển thường xuyên, gia đình, bạn bè và đồng nghiệp khó biết tôi đang ở múi giờ nào hoặc đã lên máy bay chưa, và việc sắp xếp lịch hẹn qua nhiều múi giờ luôn đòi hỏi phải tính giờ
- Chuyến bay, tàu, thời gian chặn trước khi lên máy bay và thời gian di chuyển ra sân bay phải nhập lặp lại ở nhiều nơi, khiến gánh nặng quản lý lịch tăng lên
- Sự bất tiện của hệ sinh thái lịch hiện có trải rộng từ tiêu chuẩn, ứng dụng frontend đến toàn bộ trải nghiệm người dùng
- Ngay cả khi nhà cung cấp email tạo mục lịch cho chuyến bay, họ vẫn có thể bỏ sót chặng nối chuyến hoặc dùng sai múi giờ
- Organizer của mục được tạo ra không phải là người dùng, nên việc chia sẻ hoặc chỉnh sửa có thể trở nên khó khăn
- Các điều kiện cần thiết không chỉ dừng ở lưu lịch đơn thuần mà còn bao gồm cả chia sẻ và quyền kiểm soát
- Sự kiện phải hiển thị dưới dạng blocker trong lịch công việc
- Vợ/chồng phải có thể đăng ký theo dõi lịch
- Việc nhập sự kiện chỉ nên diễn ra tối đa một lần
- Phải có thể chỉnh sửa từ nhiều thiết bị
- Dữ liệu phải do chính mình kiểm soát
- Không thể dùng cách chia sẻ lịch công việc với vợ/chồng
- Các yêu cầu bổ sung gồm: nhập tệp đính kèm
.icstrong email, nhập HTTP ICS từ lịch của trường ngôn ngữ, tích hợp trình theo dõi chuyến bay tự host Airtrail, phân biệt màu sắc, cờ riêng tư cho lịch công việc, làm mới thường xuyên và khả năng dùng frontend tùy ý
Điểm mà cách làm YAML·ICS chạm giới hạn
- Cách chia sẻ hiện có thường chỉ cung cấp đầy đủ tính năng khi có cùng nền tảng hoặc cùng môi trường tài khoản
- Hệ sinh thái Gmail hoặc Outlook.com
- Chia sẻ tài khoản trong cùng môi trường như Exchange
- Có hai cách lách phổ biến
- Cung cấp dữ liệu iCal qua HTTP để xuất bản lịch chỉ đọc
- Gửi tệp iCal
.icsqua email
- Phiên bản ban đầu đăng tệp
.icslên website tại URL công khai nhưng khó đoán, đồng thời tạo nhiều URL cho các mục đích khác nhau - Dữ liệu sự kiện được viết bằng YAML rồi được tuần tự hóa lại thành tệp ICS bằng một script nhỏ trong pipeline CI/CD
- Nó đủ dùng cho nguyên mẫu, nhưng khi quy mô tăng lên thì lỗi khi viết YAML thủ công xảy ra thường xuyên hơn, và công việc lẽ ra chỉ cần ít công sức bắt đầu trở nên nặng nề
Kiến trúc chuyển sang CalDAV và Baïkal
- Kiến trúc mới rời bỏ tệp tĩnh để chuyển sang hệ thống CalDAV được host
- CalDAV là một phần mở rộng của đặc tả biên tập phân tán WebDAV, cung cấp các chức năng cần thiết cho ứng dụng lịch
- Khi có máy chủ CalDAV, có thể xem và quản lý sự kiện trên nhiều thiết bị như laptop, điện thoại bằng ứng dụng frontend mà mình chọn
- Vì không có nhiều máy chủ CalDAV cho phép đăng ký dễ dàng mà không cần xác thực, cần có script định kỳ polling máy chủ, trích xuất sự kiện rồi xuất bản thành tệp iCal trên website
- Luồng tổng thể như sau
- Polling sự kiện từ các nguồn dữ liệu
- Xuất bản sự kiện vào CalDAV theo cách lập trình
- Lấy toàn bộ sự kiện từ CalDAV rồi ghi ra tệp
.ics - Cung cấp tệp
.icsqua HTTP
Cài đặt Baïkal và kết nối client
- Công cụ được chọn là Baïkal, một máy chủ CalDAV/CardDAV nhẹ, tự host để quản lý lịch và danh bạ
- Chạy image
ckulka/baikal:0.9.5bằng Docker Compose, chỉ định cổng và volume cục bộ rồi khởi động bằngdocker compose up -d - Baïkal có thể dùng MySQL nhưng cũng chạy được với SQLite, và SQLite đơn giản hơn về quản trị
- Để public ra web, dùng nginx reverse proxy
- Kết nối tới cổng Baïkal cục bộ bằng
proxy_pass - Redirect
/.well-known/caldavsangdav.phpvới mã 301 - Redirect này là cần thiết khi thêm vào ứng dụng lịch trên iPhone hoặc Mac
- Kết nối tới cổng Baïkal cục bộ bằng
- TLS được cấu hình bằng Let’s Encrypt và
certbot, vàcertbottự động cập nhật file cấu hình nginx - Sau khi truy cập domain bằng trình duyệt và tạo tài khoản quản trị, tiếp tục tạo user và calendar
- Đã kết nối với ứng dụng Lịch mặc định trên macOS và iOS; trong phần cài đặt của macOS/iOS, dùng manual settings thay vì automatic hay advanced
Phân loại sự kiện và phạm vi công khai của lịch công việc
- Trong đặc tả iCalendar có thuộc tính tùy chọn
CATEGORIEScủa componentEVENT, nhưng các công cụ đã dùng như macOS Calendar, iOS Calendar, Google Calendar đều không triển khai trường này - Vì phân loại bằng văn bản tự do khó duy trì tính nhất quán, các loại sự kiện được mô hình hóa bằng Python enum
- Ví dụ:
TerminType,CultureType,SocialType,AwayType,TransportType - Bao gồm các giá trị như
MEETUP,CONFERENCE,CLASS,APPOINTMENT,MEETING,EXAM
- Ví dụ:
- Lý do chia enum thành nhiều nhóm là để sắp xếp các loại sự kiện một cách rõ ràng về mặt khái niệm
- Hệ thống phân loại này cũng chứa logic quyết định phạm vi công khai trong lịch công việc
- Các giá trị enum hai chữ số mặc định được xử lý là private trong lịch công việc
- Các sự kiện như khám bác sĩ được tuần tự hóa dưới dạng riêng tư trong tệp lịch công việc riêng
- Việc phân loại này giúp tăng khả năng tìm kiếm và nhận diện trực quan của sự kiện, đồng thời hỗ trợ xem nhanh hơn trên các frontend có phân biệt màu sắc
Script đồng bộ Python và xuất bản ICS
- Để tự động hóa, một script Python được gắn vào cron job
- Script này gom nhiều đầu vào, đưa vào Baïkal rồi lại xuất ra ICS có thể chia sẻ
- Lấy sự kiện từ email qua IMAP
- Trích xuất sự kiện chuyến bay từ API Airtrail
- Nhập tệp ICS do trường ngôn ngữ host
- Push các sự kiện đã thu thập vào Baïkal
- Lấy toàn bộ sự kiện từ Baïkal rồi tuần tự hóa lại thành một hoặc nhiều tệp ICS có thể chia sẻ
- Tích hợp IMAP tự động nhập lời mời lịch nhận qua email và thêm vào lịch, cung cấp trải nghiệm tương tự Google Calendar
- Script điểm vào của cron tạo riêng
Calendarcho gia đình và công việc, rồi lấy sự kiện từ Baïkal để phân nhánh xử lý- Thêm sự kiện vào ICS gia đình
- Chỉ thêm vào ICS công việc khi địa chỉ email công việc không xuất hiện trong kết quả tuần tự hóa sự kiện
- Dựa trên kết quả
is_work_public, đặt classification làPUBLIChoặcPRIVATE
- Script tích hợp Baïkal dùng request
PROPFINDvà xác thực HTTP Digest để lấy sự kiện từ xa - Nếu mô tả sự kiện có mẫu
CATEGORIES:, script sẽ trích category từ trường mô tả vàoevent.categories, đồng thời xóa chuỗi đó khỏi mô tả - Các tệp ICS dùng để chia sẻ được nginx cung cấp tại URL khó đoán; URL được tạo từ chuỗi hash ngẫu nhiên có thêm salt
- Cron job này chạy mỗi 15 phút
Đồng bộ với Google Calendar công việc
- Không chỉ xem sự kiện trong lịch cá nhân, mà lịch công việc cũng cần hiển thị thời gian di chuyển/chuyến bay như thời gian chặn để đồng nghiệp biết trạng thái
- Việc sao chép sang lịch công việc dùng Google Script Engine
- Script nền tảng là phiên bản chỉnh sửa của GAS-ICS-Sync
- Bản sửa đổi có thể đọc thuộc tính
CATEGORIEScủa lịch và áp dụng mã màu cho lịch công việc - Google Script chạy theo chu kỳ 30 phút
- Việc dùng Google Calendar có mâu thuẫn với mục tiêu giảm phụ thuộc Big Tech, nhưng đây được xem là lựa chọn của môi trường công việc chứ không phải sự phụ thuộc cho nhu cầu cá nhân
Kết quả vận hành và chi phí
- Hệ thống này đã được sử dụng khoảng 6 tháng, với một số điều chỉnh và bổ sung nhỏ trong quá trình vận hành
- Thay đổi mới nhất là tích hợp API Airtrail
- Khi đặt chuyến bay, dữ liệu được nhập vào trình theo dõi chuyến bay
- Trong vòng 15 phút, chuyến bay được thêm vào lịch cá nhân
- Trong vòng 1 giờ, nó được tự động sao chép sang lịch công việc
- Với nhu cầu di chuyển phức tạp, thời gian quản lý lịch đã giảm đáng kể, dẫn tới cải thiện chất lượng cuộc sống
- Tổng chi phí có thể được giữ ở mức tối thiểu và hoàn toàn có thể vận hành dễ dàng trên NAS gia đình
- Hiện tại, để chạy website và phần tích hợp, dữ liệu được đặt tại Thụy Sĩ với chi phí thuê thời gian máy chủ khoảng 100 USD mỗi tháng
- Mức chi này hơi quá tay, và sẽ được tối ưu dần theo thời gian
- Việc có thể cấu hình nhanh trên Docker host của một VM instance đủ đáng để chấp nhận chi phí bổ sung, và việc quản lý lịch trở nên dễ hơn đang giúp tiết kiệm lượng thời gian tương đương 100 USD mỗi tháng
1 bình luận
Ý kiến trên Hacker News
Có thể sẽ có người phản đối, nhưng tôi thấy CalDAV không hay lắm
Nếu nó thật sự dễ dùng và tốt, hẳn đã có nhiều giải pháp CalDAV tự host hơn và tính khả dụng cũng tốt hơn nhiều; Radicale cũng không gây ấn tượng với tôi
Thực tế tôi chỉ cần một tệp lịch để cung cấp cho chính mình, nên từng thử tự triển khai máy chủ CalDAV, nhưng ngay cả việc phơi ra một tệp duy nhất cũng quá thiếu trực quan và phức tạp, nên tôi bỏ cuộc
Nếu cần chia sẻ lịch với người khác qua các ứng dụng hỗ trợ CalDAV thì tôi hiểu nhu cầu đó, nhưng với mục đích host một lịch dùng một mình thì tôi cảm thấy gần như là lãng phí thời gian
Thay vào đó, ban đầu tôi đưa một tệp iCal được cập nhật động lên bucket S3 và đồng bộ bằng ICSx5 trên Android. Chỉ HTTPS là đủ, không cần CalDAV
Tuy nhiên, các ứng dụng lịch tự do nguồn mở cho Android vẫn còn thiếu, và ICSx5 không chạy tốt trên GrapheneOS, nên hiện tôi đang dùng Proton Calendar, vốn hỗ trợ liên kết trực tiếp tới tệp iCal, gần như theo cùng một cách
Cách tiếp cận dùng định dạng iCalendar là tốt, nhưng phạm vi hỗ trợ tính năng iCalendar giữa các phần mềm lịch khác nhau rất lớn, nên cuối cùng phần lớn metadata phải bị nhét vào trường mô tả thay vì các thuộc tính chuyên dụng
Trường hợp sử dụng của tôi là phần mềm lấy dữ liệu từ nhiều trang như Eventbrite, Meetup, các đơn vị tổ chức pub quiz, lịch sự kiện công khai, v.v., lọc ra các sự kiện giao lưu mà tôi quan tâm, rồi gộp chúng thành một lịch duy nhất có thể xem trong ứng dụng lịch tôi thường dùng
Hơn nữa, chúng tôi đã rất vất vả xử lý các máy chủ CalDAV không tuân thủ chuẩn của Exchange và Google, còn người dùng thì đổ hết lỗi đó cho chúng tôi
Giờ làm ở Google, tôi thấy các ứng dụng gCal dùng một giao thức hoàn toàn khác
Thay vì là một định dạng nhẹ, nó cố làm quá nhiều thứ cùng lúc nên bị thiết kế quá mức, và kết quả là trở thành một giao thức như búi len rối gồm nhiều ý tưởng khác nhau đan vào nhau
Vì vậy, nhiều dịch vụ nhận thấy phạm vi thực sự họ muốn nằm ngoài những gì cần tuân thủ đầy đủ, nên họ không cố tuân thủ hoàn toàn toàn bộ đặc tả
Các đặc tả định dạng dữ liệu của W3 dường như lúc nào cũng kết thúc như vậy; với các đặc tả giao thức cấp thấp thì tương đối ít giống thế hơn
Đã 15 năm kể từ khi tôi đọc nó, nhưng tôi nhớ sách cũng phần nào nói về việc những giao thức như thế này lạc lối trong các ủy ban như thế nào trong quá trình thiết kế
Chỉ riêng việc dự án này bắt đầu vào khoảng năm 2001 cũng cho thấy vấn đề lịch đã là một thứ đau đầu lâu đời đến mức nào
Tôi khuyên đọc nếu muốn biết vì sao nó không hay; đây cũng là một câu chuyện hay về va chạm giữa startup và mã nguồn mở, về làm sản phẩm và phát triển phần mềm nói chung
http://www.dreamingincode.com/ ; https://en.m.wikipedia.org/wiki/Dreaming_in_Code
Để đồng bộ trên Android, tôi dùng DAVX5, một ứng dụng nguồn mở không cần root chạy dịch vụ đồng bộ nền, nhưng việc cấu hình gặp rất nhiều vấn đề và có thể là do khác biệt trong triển khai CalDAV. Đồng bộ từ DAVX5 sang lịch ZOHO cũng mất nhiều thời gian
Evolution trên Ubuntu hỗ trợ CalDAV khá tốt, nhưng mỗi lần chuyển qua lại giữa chế độ xem mail và lịch, nó lại làm mới toàn bộ lịch. Làm mới lịch ZOHO mất lâu, còn giới hạn tốc độ của ZOHO cũng chặt, nên thực tế tôi bị chặn mềm
Thunderbird mới nhất có điểm không tương thích CalDAV vì kỳ vọng một mã thành công HTTP khác. ZOHO trả về 200 nhưng Thunderbird lại mong một mã khác. Nhân tiện, mã CalDAV của Thunderbird được viết bằng JS
Vấn đề này chỉ xảy ra với lịch thứ hai, và ngay khi thêm lịch thứ hai thì bắt đầu thất bại. Ít nhất với ZOHO thì khá là khổ sở
Một máy chủ CalDAV khác đáng đề xuất là Radicale
Nó được viết bằng Python và được thiết kế khá mô-đun, có thể gắn plugin cho xác thực, phân quyền, v.v.
Dữ liệu được lưu dưới dạng tệp văn bản thuần, được theo dõi và sao lưu bằng git
Theo thời gian, tôi đã tích lũy một số chỉnh sửa thú vị. Xác thực được xử lý bằng pam của hệ thống host, và để chia sẻ lịch, tôi chạy một script định kỳ tạo liên kết tượng trưng lịch cho tất cả người dùng được phép. Tuy nhiên có vấn đề bùng nổ tổ hợp
Tôi cũng đã thêm danh sách kiểm soát truy cập bằng một plugin phân quyền dùng thuộc tính CalDAV tùy chỉnh và một plugin web đã chỉnh sửa
Với lịch công khai, tôi cấp quyền đọc cho người dùng
publicbằng ACL, và dùng một hack nginx để cho phép truy cập bao quát đối với các thao tác đọcLịch công khai này có thể xem trong lịch web: https://gitlab.nomagic.uk/popi/js_calendar_from_ics
Có lẽ tôi nên viết một bài blog về thiết lập này
Đây đúng là bài viết tôi cần
Trên máy chủ cá nhân, tôi dùng lịch SoGO của Mailcow, còn lịch công việc thì dùng Fastmail, nhưng hai bên không ăn khớp với nhau
Tôi chưa tìm được cách kết nối lịch Mailcow cho đúng với các công cụ khác, nên đang tốn thời gian đồng bộ bán thủ công
Tôi đã muốn tách lịch ra một thời gian, nhưng lựa chọn thay thế duy nhất tôi biết là Nextcloud, mà tôi thấy hơi nặng
Baïkal trông có vẻ ổn và tôi muốn sớm thử. Như vậy cũng có thể giúp tôi tìm hiểu việc đổi Mailcow sang một dịch vụ mail tự host khác
Gần đây tôi đang rời khỏi cloud một cách rất quyết liệt. Dù có hàng trăm GB dữ liệu, việc chuyển đổi, dù làm rải rác, cũng chưa đến 2 tuần, và hoạt động tốt hơn mong đợi rất nhiều
Dịch vụ duy nhất còn giao cho bên ngoài là email, vì tôi không muốn xử lý MX record và IMAP/SMTP
Với phần còn lại, chiếc Asus PN40 dùng CPU Celeron lõi kép đã giúp ích rất nhiều. Ưu điểm là dễ tiếp cận, có khe M.2 NVME và khe ổ SATA nên có thể thiết lập backup an toàn ở một nơi
Tôi đồng ý rằng CalDAV hơi đáng ngờ, nhưng Nextcloud đã đáp ứng hầu hết nhu cầu. Dù gắn thêm Redis, một số tác vụ vẫn có thể chậm, nhưng nó xử lý tốt lịch, thay thế Dropbox/Google Drive, tài liệu, ảnh, backup và tự động đồng bộ
Cộng thêm nhiều giải pháp mã nguồn mở khác, mọi thứ từ stream nhạc/video, giám sát, thông báo, VPN, reader, công cụ quản trị cho đến đủ loại tiện ích đều được giải quyết
Chiếc mini PC này có hiệu năng khá thấp, nhưng nhờ ISP rất tốt nên vẫn gánh được tất cả với nhiều dư địa, đến mức có thể nhận thêm 5–6 người như tôi nữa mà không quá tải
Cảm giác như cái cũ lại trở thành cái mới
Đầu những năm 2000, tôi từng dùng Mozilla Sunbird để host lịch qua module IIS WebDAV trên server Win2k cá nhân
Hình như lúc đó chưa có CardDAV, nhưng cũng có thể tôi nhớ nhầm
Cuối cùng tôi đã đưa toàn bộ dữ liệu đó vào Google Calendar, và nếu lục lại đủ xa thì vẫn còn cả những thứ như bài tập đại học, khá thú vị
Tôi từng mất một số lịch hẹn vì chúng không được upload lên server, khi dùng CalDAV cùng server DAViCal
Sunbird đã bị ngừng phát triển, và gần đây tôi cũng chưa tìm xem trên Windows còn lựa chọn thay thế phù hợp nào không
Baikal trông có vẻ thú vị
Trước đây tôi dùng DAViCal để chia sẻ lịch giữa vài thiết bị Apple iOS. Khi chạy thì chạy tốt, nhưng mỗi lần phiên bản iOS thay đổi có vẻ lại kém ổn định hơn, nên cuối cùng tôi bỏ cuộc
Nếu một ngày nào đó xem lại, tôi định sẽ xem DAViCal và Radicale, giờ thêm cả Baikal nữa
https://www.davical.org/
https://radicale.org/v3.html
Gần đây tôi chuyển sang Nextcloud, vì shared hosting lo phần triển khai và có UI web tích hợp nên đỡ phải cấu hình hơn nhiều
Tôi đã dùng Baikal vài năm nay và nó khá vững. Tuy vậy tôi chỉ dùng một mình và không có tất cả yêu cầu như bài gốc
Trên desktop tôi kết nối bằng lịch tích hợp của Thunderbird, còn trên điện thoại dùng DAVx5 và Fossify Calendar từ F-Droid
Fossify Calendar được tách ra từ app Calendar của Simple Mobile Tools, nhưng trái với cái tên, đây là app lịch nhiều tính năng nhất tôi từng thấy
Tôi thích sự đơn giản kiểu hiển thị ngay tất cả tùy chọn, không bắt phải đào qua nhiều menu và hộp thoại
Ở công ty tôi dùng Outlook, ở nhà dùng Google Calendar, nhưng đồng bộ qua HTTP thì hoàn toàn không ổn định
Có vẻ nó liên tục làm hỏng múi giờ; lúc thì publish theo UTC, lúc thì không, khiến giờ họp luôn bị lệch
Tôi không hiểu vì sao hai nhà cung cấp lớn này lại không sửa được một chức năng cơ bản như vậy
Nếu dùng hệ thống riêng mà giải quyết được thì tôi làm ngay, nhưng tôi lo cũng sẽ gặp đúng chuyện các cuộc họp tạo trong Outlook bị import lệch 2 tiếng
Ngoài ra, nhiều công ty thực sự cấm đồng bộ giữa lịch cá nhân và lịch công việc vì vấn đề rò rỉ dữ liệu
Tôi cũng muốn làm kiểu này cho ảnh. Tôi đã muốn rời khỏi Dropbox và Google một thời gian, và có hai ưu tiên
Thứ nhất, tôi muốn tạo một chức năng kiểu kỷ niệm từ ảnh của mình rồi gửi về điện thoại. Cách nhóm có thể dựa trên ngày kỷ niệm, hoạt động tương tự,
tôi và X (X là vợ/chồng, gia đình, bạn bè, v.v.), hoặc sự thay đổi theo thời gianThứ hai, ảnh chụp từ thiết bị phải lưu được lên server của tôi
Tôi đã chuẩn bị 3 máy TrueNAS ở các địa điểm khác nhau, mỗi máy có 2TB dung lượng, và muốn từ từ xây dựng chức năng này
https://www.synology.com/en-global/dsm/feature/photos
https://www.synology.com/en-global/dsm/feature/moments
Một cách khác là dùng DecSync mà không cần server
Thunderbird hoặc Evolution đồng bộ với app Android, và lịch, danh bạ, tác vụ, RSS đều được sao chép sang các peer khác. Cũng có thể tải từ F-Droid
https://github.com/39aldo39/DecSync