Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Bagaimana untuk menyegerakkan kenalan anda dengan telefon anda? Melaksanakan CardDAV dalam Go!

Bagaimana untuk menyegerakkan kenalan anda dengan telefon anda? Melaksanakan CardDAV dalam Go!

DDD
DDDasal
2024-09-19 22:21:191152semak imbas

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

Katakanlah anda membantu menguruskan organisasi atau kelab kecil dan mempunyai pangkalan data yang menyimpan semua butiran ahli (nama, telefon, e-mel...).
Bukankah lebih baik untuk mempunyai akses kepada maklumat terkini ini di mana-mana sahaja anda memerlukannya? Nah, dengan CardDAV anda boleh!

CardDAV ialah standard terbuka yang disokong dengan baik untuk pengurusan hubungan; ia mempunyai integrasi asli dalam Apl Kenalan iOS dan banyak apl tersedia untuk Android.

Sebelah pelayan, melaksanakan CardDAV ialah pelayan http yang bertindak balas kepada kaedah http yang luar biasa (PROPFIND, REPORT dan bukannya GET, POST...). Nasib baik terdapat modul Go untuk memudahkan kerja: github.com/emersion/go-webdav. Pustaka ini menjangkakan Backend yang dilaksanakan dan menyediakan http.Handler standard yang harus menyediakan permintaan HTTP selepas pengesahan.

Pengesahan

Menariknya pustaka tidak memberikan sebarang bantuan berkenaan pengesahan pengguna, namun terima kasih kepada kebolehkomposisian Go, ini bukan satu isu.
CardDAV menggunakan bukti kelayakan Pengesahan Asas. Setelah bukti kelayakan disemak, kami boleh menyimpan bukti kelayakan tersebut dalam konteks (akan berguna kemudian):

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))
    })
}

Melaksanakan antara muka CardDAV

Struktur ownBackend mesti melaksanakan antara muka carddav.Backend, yang tidak terlalu nipis, tetapi masih boleh diurus.

CurrentUserPrincipal dan AddressBookHomeSetPath mesti menyediakan URL (bermula dan berakhir dengan garis miring). Biasanya ia akan menjadi nama pengguna/kenalan. Di sinilah anda perlu mengekstrak nama pengguna daripada konteks (yang merupakan satu-satunya hujah yang tersedia):

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
}

Selepas itu keseronokan boleh bermula: anda perlu melaksanakan kaedah AddressBook, GetAddressObject dan ListAddressObjects.

AddressBook mengembalikan struct mudah, di mana laluan harus bermula dengan AddressBookHomeSetPath di atas (dan berakhir dengan garis miring)

GetAddressObject dan ListAddressObjects mesti menyemak laluan semasa (untuk memastikan bahawa pengguna yang sedang disahkan boleh mengakses kenalan tersebut) dan kemudian mengembalikan kenalan sebagai AddressObject.

AddressObject

AddressObject mempunyai berbilang atribut, yang paling penting:

  • laluan untuk mengenal pasti kenalan tertentu ini (boleh sewenang-wenangnya, mulakan dengan garis miring)
  • ETAG untuk membolehkan pelanggan menyemak dengan cepat sama ada sebarang kemas kini berlaku (jika anda terlupa, iOS tidak akan menunjukkan apa-apa)
  • Kad yang menjangkakan VCard

VCard mewakili data kenalan sebenar dan kemungkinan besar mesti disesuaikan bergantung pada cara anda menyimpan kenalan anda. Dalam kes saya, ia berakhir seperti ini:

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
}

Mengambil jalan pintas Baca Sahaja

Sesetengah kaedah membenarkan untuk mengemas kini kenalan. Memandangkan saya tidak mahu senarai ahli saya dikemas kini melalui CardDAV, saya mengembalikan ralat 403 kepada kaedah Put and Delete: return webdav.NewHTTPError(http.StatusForbidden, errors.New("carddav: operation not supported"))

Menguji secara tempatan

iOS memerlukan pelayan CardDAV untuk berkhidmat melalui https. Anda boleh menjana sijil yang ditandatangani sendiri secara setempat menggunakan openssl (ganti 192.168.XXX.XXX dengan alamat IP anda) untuk dimasukkan ke dalam 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

Selepas itu anda sepatutnya boleh mencuba secara setempat dengan menambahkan "akaun kenalan CardDAV" yang menunjuk ke Alamat IP dan port anda sendiri.

Kesimpulan

Melaksanakan pelayan CardDAV dalam Go agak terlibat, tetapi jelas berbaloi: kenalan anda akan disegerakkan secara automatik dengan data yang anda ada pada pelayan organisasi anda!

Adakah anda tahu protokol hebat lain yang membenarkan integrasi asli seperti ini? Jangan ragu untuk berkongsi pengalaman anda!

Atas ialah kandungan terperinci Bagaimana untuk menyegerakkan kenalan anda dengan telefon anda? Melaksanakan CardDAV dalam Go!. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn