"إذا أراد العامل أن يؤدي عمله بشكل جيد، فعليه أولاً أن يشحذ أدواته." - كونفوشيوس، "مختارات كونفوشيوس. لو لينجونج"
الصفحة الأمامية > برمجة > كيفية مزامنة جهات الاتصال الخاصة بك مع هاتفك؟ تنفيذ CardDAV في Go!

كيفية مزامنة جهات الاتصال الخاصة بك مع هاتفك؟ تنفيذ CardDAV في Go!

تم النشر بتاريخ 2024-11-07
تصفح:412

How to synchronize your contacts with your phone? Implemeting CardDAV in Go!

لنفترض أنك تساعد في إدارة منظمة صغيرة أو نادي ولديك قاعدة بيانات تخزن جميع تفاصيل الأعضاء (الأسماء، الهاتف، البريد الإلكتروني...).
ألن يكون من الرائع أن تتمكن من الوصول إلى هذه المعلومات المحدثة في كل مكان تحتاج إليها؟ حسنًا، مع CardDAV يمكنك ذلك!

يعد CardDAV معيارًا مفتوحًا مدعومًا جيدًا لإدارة جهات الاتصال؛ يحتوي على تكامل أصلي في تطبيق جهات اتصال iOS والعديد من التطبيقات المتاحة لنظام Android.

من جانب الخادم، تنفيذ CardDAV هو خادم http يستجيب لأساليب http غير العادية (PROPFIND، REPORT بدلاً من GET، POST...). لحسن الحظ، توجد وحدة Go لتبسيط العمل إلى حد كبير: github.com/emersion/go-webdav. تتوقع هذه المكتبة واجهة خلفية مطبقة وتوفر http.Handler قياسي والذي يجب أن يخدم طلبات HTTP بعد المصادقة.

المصادقة

ومن المثير للاهتمام أن المكتبة لا تقدم أي مساعدة فيما يتعلق بمصادقة المستخدم، ولكن بفضل قابلية التركيب Go، فإن هذه ليست مشكلة.
يستخدم CardDAV بيانات اعتماد المصادقة الأساسية. بمجرد التحقق من بيانات الاعتماد، يمكننا حفظ بيانات الاعتماد هذه في السياق (ستكون مفيدة لاحقًا):

package main

import (
    "context"
    "net/http"

    "github.com/emersion/go-webdav/carddav"
)

type (
    ctxKey   struct{}
    ctxValue struct {
        username string
    }
)

func NewCardDAVHandler() http.Handler {
    actualHandler := carddav.Handler{
        Backend: &ownBackend{},
    }

    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        username, password, ok := r.BasicAuth()
        // check username and password: adjust the logic to your system (do NOT store passwords in plaintext)
        if !ok || username != "admin" || password != "s3cr3t" {
            // abort the request handling on failure
            w.Header().Add("WWW-Authenticate", `Basic realm="Please authenticate", charset="UTF-8"`)
            http.Error(w, "HTTP Basic auth is required", http.StatusUnauthorized)
            return
        }

        // user is authenticated: store this info in the context
        ctx := context.WithValue(r.Context(), ctxKey{}, ctxValue{username})
        // delegate the work to the CardDAV handle
        actualHandler.ServeHTTP(w, r.WithContext(ctx))
    })
}

تنفيذ واجهة CardDAV

يجب أن تقوم البنية ownBackend بتنفيذ واجهة Carddav.Backend، وهي ليست رقيقة جدًا، ولكن لا تزال قابلة للإدارة.

يجب أن يوفر CurrentUserPrincipal وAddressBookHomeSetPath عناوين URL (تبدأ وتنتهي بشرطة مائلة). عادةً ما يكون اسم المستخدم/جهات الاتصال. هذا هو المكان الذي تحتاج فيه إلى استخراج اسم المستخدم من السياق (وهو الوسيط الوحيد المتاح):

func currentUsername(ctx context.Context) (string, error) {
    if v, ok := ctx.Value(ctxKey{}).(ctxValue); ok {
        return v.username, nil
    }
    return "", errors.New("not authenticated")
}

type ownBackend struct{}

// must begin and end with a slash
func (b *ownBackend) CurrentUserPrincipal(ctx context.Context) (string, error) {
    username, err := currentUsername(ctx)
    return "/"   url.PathEscape(username)   "/", err
}

// must begin and end with a slash as well
func (b *ownBackend) AddressBookHomeSetPath(ctx context.Context) (string, error) {
    principal, err := b.CurrentUserPrincipal(ctx)
    return principal   "contacts/", err
}

بعد ذلك يمكن أن تبدأ المتعة: تحتاج إلى تنفيذ أساليب AddressBook وGetAddressObject وListAddressObjects.

يرجع AddressBook بنية بسيطة، حيث يجب أن يبدأ المسار بـ AddressBookHomeSetPath أعلاه (وينتهي بشرطة مائلة)

يجب على GetAddressObject وListAddressObjects التحقق من المسار الحالي (للتأكد من أن المستخدم المصادق عليه حاليًا يمكنه الوصول إلى جهات الاتصال هذه) ثم إرجاع جهات الاتصال كـ AddressObject.

AddressObject

يحتوي AddressObject على سمات متعددة، أهمها:

  • المسار لتحديد جهة الاتصال المحددة هذه (يمكن أن يكون عشوائيًا، يبدأ بشرطة مائلة)
  • علامة ETag للسماح للعميل بالتحقق بسرعة من حدوث أي تحديث (إذا نسيته، فلن يعرض iOS أي شيء)
  • البطاقة التي تتوقع VCard

تمثل VCard بيانات جهة الاتصال الفعلية ومن المحتمل أن يتم تكييفها وفقًا لكيفية تخزين جهات الاتصال الخاصة بك. في حالتي، انتهى الأمر على النحو التالي:

func utf8Field(v string) *vcard.Field {
    return &vcard.Field{
        Value: v,
        Params: vcard.Params{
            "CHARSET": []string{"UTF-8"},
        },
    }
}

func vcardFromUser(u graphqlient.User) vcard.Card {
    c := vcard.Card{}

    c.Set(vcard.FieldFormattedName, utf8Field(u.Firstname " " u.Lastname))
    c.SetName(&vcard.Name{
        Field:      utf8Field(""),
        FamilyName: u.Lastname,
        GivenName:  u.Firstname,
    })
    c.SetRevision(u.UpdatedAt)
    c.SetValue(vcard.FieldUID, u.Extid)

    c.Set(vcard.FieldOrganization, utf8Field(u.Unit))

    // addFields sorts the key to ensure a stable order
    addFields := func(fieldName string, values map[string]string) {
        for _, k := range slices.Sorted(maps.Keys(values)) {
            v := values[k]
            c.Add(fieldName, &vcard.Field{
                Value: v,
                Params: vcard.Params{
                    vcard.ParamType: []string{k   ";CHARSET=UTF-8"}, // hacky but prevent maps ordering issues
                    // "CHARSET":       []string{"UTF-8"},
                },
            })
        }
    }

    addFields(vcard.FieldEmail, u.Emails)
    addFields(vcard.FieldTelephone, u.Phones)

    vcard.ToV4(c)
    return c
}

أخذ الاختصار للقراءة فقط

تسمح بعض الطرق بتحديث جهة اتصال. نظرًا لأنني لا أريد تحديث قائمة الأعضاء الخاصة بي عبر CardDAV، أقوم بإرجاع خطأ 403 إلى أساليب الحذف والحذف: return webdav.NewHTTPERror(http.StatusForbidden, Errors.New("carddav: العملية غير مدعومة"))

اختبار محليا

يتطلب iOS أن يعمل خادم CardDAV عبر https. يمكنك إنشاء شهادات موقعة ذاتيًا محليًا باستخدام opensl (استبدل 192.168.XXX.XXX بعنوان IP الخاص بك) ليتم إدخالها في http.ListenAndServeTLS(addr, "localhost.crt", "localhost.key", NewCardDAVHandler())

openssl req -new -subj "/C=US/ST=Utah/CN=192.168.XXX.XXX" -newkey rsa:2048 -nodes -keyout localhost.key -out localhost.csr
openssl x509 -req -days 365 -in localhost.csr -signkey localhost.key -out localhost.crt

بعد ذلك يجب أن تكون قادرًا على التجربة محليًا عن طريق إضافة "حساب اتصال CardDAV" يشير إلى عنوان IP الخاص بك والمنفذ.

خاتمة

يعد تنفيذ خادم CardDAV في Go أمرًا معقدًا بعض الشيء، ولكن من الواضح أنه يستحق ذلك: ستتم مزامنة جهات الاتصال الخاصة بك تلقائيًا مع البيانات الموجودة على خادم مؤسستك!

هل تعرف بروتوكولات رائعة أخرى تسمح بهذا النوع من التكامل الأصلي؟ لا تتردد في مشاركة تجاربك!

بيان الافراج يتم استنساخ هذه المقالة على: https://dev.to/cmdscale/how-to-synchronize-your-contacts-with-hour-phone-edisting-carddav-in-go-9ia؟1 إذا كان هناك أي انتهاك ، فيرجى الاتصال بـ [email protected] لحذفه.
أحدث البرنامج التعليمي أكثر>

تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.

Copyright© 2022 湘ICP备2022001581号-3